001: /*
002: * Copyright 2005-2007 Noelios Consulting.
003: *
004: * The contents of this file are subject to the terms of the Common Development
005: * and Distribution License (the "License"). You may not use this file except in
006: * compliance with the License.
007: *
008: * You can obtain a copy of the license at
009: * http://www.opensource.org/licenses/cddl1.txt See the License for the specific
010: * language governing permissions and limitations under the License.
011: *
012: * When distributing Covered Code, include this CDDL HEADER in each file and
013: * include the License file at http://www.opensource.org/licenses/cddl1.txt If
014: * applicable, add the following below this CDDL HEADER, with the fields
015: * enclosed by brackets "[]" replaced with your own identifying information:
016: * Portions Copyright [yyyy] [name of copyright owner]
017: */
018:
019: package com.noelios.restlet.application;
020:
021: import java.io.IOException;
022: import java.io.InputStream;
023: import java.io.OutputStream;
024: import java.nio.channels.ReadableByteChannel;
025: import java.nio.channels.WritableByteChannel;
026: import java.util.ArrayList;
027: import java.util.Arrays;
028: import java.util.Iterator;
029: import java.util.List;
030: import java.util.zip.GZIPInputStream;
031: import java.util.zip.InflaterInputStream;
032: import java.util.zip.ZipInputStream;
033:
034: import org.restlet.data.Encoding;
035: import org.restlet.resource.Representation;
036: import org.restlet.util.ByteUtils;
037: import org.restlet.util.WrapperRepresentation;
038:
039: /**
040: * Representation that decodes a wrapped representation if its encoding is
041: * supported.<br/>If at least one encoding of the wrapped representation is not
042: * supported, then the wrapped representation is not decoded.
043: *
044: * @author Jerome Louvel (contact@noelios.com)
045: */
046: public class DecodeRepresentation extends WrapperRepresentation {
047: /** Indicates if the decoding can happen. */
048: private boolean canDecode;
049:
050: /** List of encodings still applied to the decodeRepresentation */
051: private List<Encoding> wrappedEncodings;
052:
053: /**
054: * Constructor.
055: *
056: * @param wrappedRepresentation
057: * The wrapped representation.
058: */
059: public DecodeRepresentation(Representation wrappedRepresentation) {
060: super (wrappedRepresentation);
061: this .canDecode = getSupportedEncodings().containsAll(
062: wrappedRepresentation.getEncodings());
063: wrappedEncodings = new ArrayList<Encoding>();
064: wrappedEncodings.addAll(wrappedRepresentation.getEncodings());
065: }
066:
067: /**
068: * Indicates if the decoding can happen.
069: *
070: * @return True if the decoding can happen.
071: */
072: public boolean canDecode() {
073: return this .canDecode;
074: }
075:
076: /**
077: * Returns the encodings applied to the entity.
078: *
079: * @return The encodings applied to the entity.
080: */
081: public List<Encoding> getEncodings() {
082: if (canDecode()) {
083: return new ArrayList<Encoding>();
084: } else {
085: return wrappedEncodings;
086: }
087: }
088:
089: /**
090: * Returns a readable byte channel. If it is supported by a file a read-only
091: * instance of FileChannel is returned.
092: *
093: * @return A readable byte channel.
094: */
095: public ReadableByteChannel getChannel() throws IOException {
096: if (canDecode()) {
097: return ByteUtils.getChannel(getStream());
098: } else {
099: return getWrappedRepresentation().getChannel();
100: }
101: }
102:
103: /**
104: * Returns a stream with the representation's content.
105: *
106: * @return A stream with the representation's content.
107: */
108: public InputStream getStream() throws IOException {
109: InputStream result = null;
110:
111: if (canDecode()) {
112: result = getWrappedRepresentation().getStream();
113: for (int i = wrappedEncodings.size() - 1; i >= 0; i--) {
114: if (!wrappedEncodings.get(i).equals(Encoding.IDENTITY)) {
115: result = getDecodedStream(wrappedEncodings.get(i),
116: result);
117: }
118: }
119: }
120:
121: return result;
122: }
123:
124: /**
125: * Returns a decoded stream for a given encoding and coded stream.
126: *
127: * @param encoding
128: * The encoding to use.
129: * @param encodedStream
130: * The encoded stream.
131: * @return The decoded stream.
132: * @throws IOException
133: */
134: private InputStream getDecodedStream(Encoding encoding,
135: InputStream encodedStream) throws IOException {
136: InputStream result = null;
137:
138: if (encoding.equals(Encoding.GZIP)) {
139: result = new GZIPInputStream(encodedStream);
140: } else if (encoding.equals(Encoding.DEFLATE)) {
141: result = new InflaterInputStream(encodedStream);
142: } else if (encoding.equals(Encoding.ZIP)) {
143: result = new ZipInputStream(encodedStream);
144: } else if (encoding.equals(Encoding.IDENTITY)) {
145: throw new IOException(
146: "Decoder unecessary for identity decoding");
147: }
148:
149: return result;
150: }
151:
152: /**
153: * Writes the representation to a byte channel.
154: *
155: * @param writableChannel
156: * A writable byte channel.
157: */
158: public void write(WritableByteChannel writableChannel)
159: throws IOException {
160: if (canDecode()) {
161: write(ByteUtils.getStream(writableChannel));
162: } else {
163: getWrappedRepresentation().write(writableChannel);
164: }
165: }
166:
167: /**
168: * Writes the representation to a byte stream.
169: *
170: * @param outputStream
171: * The output stream.
172: */
173: public void write(OutputStream outputStream) throws IOException {
174: if (canDecode()) {
175: ByteUtils.write(getStream(), outputStream);
176: } else {
177: getWrappedRepresentation().write(outputStream);
178: }
179: }
180:
181: /**
182: * Converts the representation to a string value. Be careful when using this
183: * method as the conversion of large content to a string fully stored in
184: * memory can result in OutOfMemoryErrors being thrown.
185: *
186: * @return The representation as a string value.
187: */
188: public String getText() throws IOException {
189: String result = null;
190:
191: if (canDecode()) {
192: result = ByteUtils.toString(getStream(), getCharacterSet());
193: } else {
194: result = getWrappedRepresentation().getText();
195: }
196:
197: return result;
198: }
199:
200: /**
201: * Returns the size in bytes of the decoded representation if known,
202: * UNKNOWN_SIZE (-1) otherwise.
203: *
204: * @return The size in bytes if known, UNKNOWN_SIZE (-1) otherwise.
205: */
206: public long getSize() {
207: long result = UNKNOWN_SIZE;
208:
209: if (canDecode()) {
210: boolean identity = true;
211: for (Iterator<Encoding> iter = getEncodings().iterator(); identity
212: && iter.hasNext();) {
213: identity = (iter.next().equals(Encoding.IDENTITY));
214: }
215: if (identity) {
216: result = getWrappedRepresentation().getSize();
217: }
218: } else {
219: result = getWrappedRepresentation().getSize();
220: }
221:
222: return result;
223: }
224:
225: /**
226: * Returns the list of supported encodings.
227: *
228: * @return The list of supported encodings.
229: */
230: public static List<Encoding> getSupportedEncodings() {
231: return Arrays.<Encoding> asList(Encoding.GZIP,
232: Encoding.DEFLATE, Encoding.ZIP, Encoding.IDENTITY);
233: }
234: }
|