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.Arrays;
027: import java.util.Collection;
028: import java.util.Iterator;
029: import java.util.List;
030: import java.util.zip.DeflaterOutputStream;
031: import java.util.zip.GZIPOutputStream;
032: import java.util.zip.ZipOutputStream;
033:
034: import org.restlet.data.Encoding;
035: import org.restlet.resource.Representation;
036: import org.restlet.util.ByteUtils;
037: import org.restlet.util.WrapperList;
038: import org.restlet.util.WrapperRepresentation;
039:
040: /**
041: * Content that encodes a wrapped content. Allows to apply only one encoding.
042: *
043: * @author Jerome Louvel (contact@noelios.com)
044: */
045: public class EncodeRepresentation extends WrapperRepresentation {
046: /** Indicates if the encoding can happen. */
047: private boolean canEncode;
048:
049: /** The encoding to apply. */
050: private Encoding encoding;
051:
052: /** The applied encodings. */
053: private List<Encoding> encodings;
054:
055: /**
056: * Constructor.
057: *
058: * @param encoding
059: * Encoder algorithm.
060: * @param wrappedRepresentation
061: * The wrapped representation.
062: */
063: public EncodeRepresentation(Encoding encoding,
064: Representation wrappedRepresentation) {
065: super (wrappedRepresentation);
066: this .canEncode = getSupportedEncodings().contains(encoding);
067: this .encodings = null;
068: this .encoding = encoding;
069: }
070:
071: /**
072: * Indicates if the encoding can happen.
073: *
074: * @return True if the encoding can happen.
075: */
076: public boolean canEncode() {
077: return this .canEncode;
078: }
079:
080: /**
081: * Returns the size in bytes of the encoded representation if known,
082: * UNKNOWN_SIZE (-1) otherwise.
083: *
084: * @return The size in bytes if known, UNKNOWN_SIZE (-1) otherwise.
085: */
086: public long getSize() {
087: long result = UNKNOWN_SIZE;
088:
089: if (canEncode()) {
090: if (this .encoding.equals(Encoding.IDENTITY)) {
091: result = getWrappedRepresentation().getSize();
092: }
093: } else {
094: result = getWrappedRepresentation().getSize();
095: }
096:
097: return result;
098: }
099:
100: /**
101: * Returns the applied encodings.
102: *
103: * @return The applied encodings.
104: */
105: public List<Encoding> getEncodings() {
106: if (this .encodings == null) {
107: encodings = new WrapperList<Encoding>() {
108:
109: @Override
110: public void add(int index, Encoding element) {
111: if (element == null) {
112: throw new IllegalArgumentException(
113: "Cannot add a null encoding.");
114: } else {
115: super .add(index, element);
116: }
117: }
118:
119: @Override
120: public boolean add(Encoding element) {
121: if (element == null) {
122: throw new IllegalArgumentException(
123: "Cannot add a null encoding.");
124: } else {
125: return super .add(element);
126: }
127: }
128:
129: @Override
130: public boolean addAll(
131: Collection<? extends Encoding> elements) {
132: boolean addNull = (elements == null);
133: if (!addNull) {
134: for (Iterator<? extends Encoding> iterator = elements
135: .iterator(); !addNull
136: && iterator.hasNext();) {
137: addNull = (iterator.next() == null);
138: }
139: }
140: if (addNull) {
141: throw new IllegalArgumentException(
142: "Cannot add a null encoding.");
143: } else {
144: return super .addAll(elements);
145: }
146: }
147:
148: @Override
149: public boolean addAll(int index,
150: Collection<? extends Encoding> elements) {
151: boolean addNull = (elements == null);
152: if (!addNull) {
153: for (Iterator<? extends Encoding> iterator = elements
154: .iterator(); !addNull
155: && iterator.hasNext();) {
156: addNull = (iterator.next() == null);
157: }
158: }
159: if (addNull) {
160: throw new IllegalArgumentException(
161: "Cannot add a null encoding.");
162: } else {
163: return super .addAll(index, elements);
164: }
165: }
166: };
167: encodings.addAll(getWrappedRepresentation().getEncodings());
168: if (canEncode()) {
169: encodings.add(this .encoding);
170: }
171: }
172: return this .encodings;
173: }
174:
175: /**
176: * Returns a readable byte channel. If it is supported by a file a read-only
177: * instance of FileChannel is returned.
178: *
179: * @return A readable byte channel.
180: */
181: public ReadableByteChannel getChannel() throws IOException {
182: if (canEncode()) {
183: return ByteUtils.getChannel(getStream());
184: } else {
185: return getWrappedRepresentation().getChannel();
186: }
187: }
188:
189: /**
190: * Returns a stream with the representation's content.
191: *
192: * @return A stream with the representation's content.
193: */
194: public InputStream getStream() throws IOException {
195: if (canEncode()) {
196: return ByteUtils.getStream(this );
197: } else {
198: return getWrappedRepresentation().getStream();
199: }
200: }
201:
202: /**
203: * Writes the representation to a byte channel.
204: *
205: * @param writableChannel
206: * A writable byte channel.
207: */
208: public void write(WritableByteChannel writableChannel)
209: throws IOException {
210: if (canEncode()) {
211: write(ByteUtils.getStream(writableChannel));
212: } else {
213: getWrappedRepresentation().write(writableChannel);
214: }
215: }
216:
217: /**
218: * Writes the representation to a byte stream.
219: *
220: * @param outputStream
221: * The output stream.
222: */
223: public void write(OutputStream outputStream) throws IOException {
224: if (canEncode()) {
225: DeflaterOutputStream encoderOutputStream = null;
226:
227: if (this .encoding.equals(Encoding.GZIP)) {
228: encoderOutputStream = new GZIPOutputStream(outputStream);
229: } else if (this .encoding.equals(Encoding.DEFLATE)) {
230: encoderOutputStream = new DeflaterOutputStream(
231: outputStream);
232: } else if (this .encoding.equals(Encoding.ZIP)) {
233: encoderOutputStream = new ZipOutputStream(outputStream);
234: } else if (this .encoding.equals(Encoding.IDENTITY)) {
235: // Encoder unecessary for identity encoding
236: }
237:
238: if (encoderOutputStream != null) {
239: getWrappedRepresentation().write(encoderOutputStream);
240: encoderOutputStream.finish();
241: } else {
242: getWrappedRepresentation().write(outputStream);
243: }
244: } else {
245: getWrappedRepresentation().write(outputStream);
246: }
247: }
248:
249: /**
250: * Converts the representation to a string value. Be careful when using this
251: * method as the conversion of large content to a string fully stored in
252: * memory can result in OutOfMemoryErrors being thrown.
253: *
254: * @return The representation as a string value.
255: */
256: public String getText() throws IOException {
257: String result = null;
258:
259: if (canEncode()) {
260: result = ByteUtils.toString(getStream(), getCharacterSet());
261: } else {
262: result = getWrappedRepresentation().getText();
263: }
264:
265: return result;
266: }
267:
268: /**
269: * Returns the list of supported encodings.
270: *
271: * @return The list of supported encodings.
272: */
273: public static List<Encoding> getSupportedEncodings() {
274: return Arrays.<Encoding> asList(Encoding.GZIP,
275: Encoding.DEFLATE, Encoding.ZIP, Encoding.IDENTITY);
276: }
277: }
|