001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.serializer;
014
015import static org.apache.juneau.common.internal.IOUtils.*;
016import static org.apache.juneau.common.internal.ThrowableUtils.*;
017import static org.apache.juneau.internal.ClassUtils.*;
018
019import java.io.*;
020import java.nio.charset.*;
021
022import org.apache.juneau.*;
023import org.apache.juneau.common.internal.*;
024import org.apache.juneau.internal.*;
025
026/**
027 * A wrapper around an object that a serializer sends its output to.
028 *
029 * <p>
030 * For character-based serializers, the output object can be any of the following:
031 * <ul>
032 *    <li>{@link Writer}
033 *    <li>{@link OutputStream} - Output will be written as UTF-8 encoded stream.
034 *    <li>{@link File} - Output will be written as system-default encoded stream.
035 *    <li>{@link StringBuilder}
036 * </ul>
037 *
038 * <p>
039 * For stream-based serializers, the output object can be any of the following:
040 * <ul>
041 *    <li>{@link OutputStream}
042 *    <li>{@link File}
043 * </ul>
044 *
045 * <h5 class='section'>See Also:</h5><ul>
046 *    <li class='link'><a class="doclink" href="../../../../index.html#jm.SerializersAndParsers">Serializers and Parsers</a>
047 * </ul>
048 */
049public final class SerializerPipe implements Closeable {
050
051   private final Object output;
052   private final boolean autoClose;
053
054   private OutputStream outputStream;
055   private Writer writer;
056   private Charset charset;
057
058   /**
059    * Writer-based constructor.
060    *
061    * @param output The object to pipe the serializer output to.
062    */
063   SerializerPipe(Object output, Charset streamCharset, Charset fileCharset) {
064      boolean isFile = (output instanceof File);
065      this.output = output;
066      this.autoClose = isFile;
067      Charset cs = isFile ? fileCharset : streamCharset;
068      if (cs == null)
069         cs = isFile ? Charset.defaultCharset() : UTF8;
070      this.charset = cs;
071   }
072
073   /**
074    * Stream-based constructor.
075    *
076    * @param output The object to pipe the serializer output to.
077    */
078   SerializerPipe(Object output) {
079      this.output = output;
080      this.autoClose = false;
081      this.charset = null;
082   }
083
084   /**
085    * Wraps the specified output object inside an output stream.
086    *
087    * <p>
088    * Subclasses can override this method to implement their own specialized output streams.
089    *
090    * <p>
091    * This method can be used if the output object is any of the following class types:
092    * <ul>
093    *    <li>{@link OutputStream}
094    *    <li>{@link File}
095    * </ul>
096    *
097    * @return
098    *    The output object wrapped in an output stream.
099    *    Calling {@link OutputStream#close()} on the returned object simply flushes the response and does not close
100    *    the underlying stream.
101    * @throws IOException If object could not be converted to an output stream.
102    */
103   public OutputStream getOutputStream() throws IOException {
104      if (output == null)
105         throw new IOException("Output cannot be null.");
106
107      if (output instanceof OutputStream)
108         outputStream = (OutputStream)output;
109      else if (output instanceof File)
110         outputStream = new BufferedOutputStream(new FileOutputStream((File)output));
111      else
112         throw new IOException("Cannot convert object of type "+className(output)+" to an OutputStream.");
113
114      return new NoCloseOutputStream(outputStream);
115   }
116
117
118   /**
119    * Wraps the specified output object inside a writer.
120    *
121    * <p>
122    * Subclasses can override this method to implement their own specialized writers.
123    *
124    * <p>
125    * This method can be used if the output object is any of the following class types:
126    * <ul>
127    *    <li>{@link Writer}
128    *    <li>{@link OutputStream} - Output will be written as UTF-8 encoded stream.
129    *    <li>{@link File} - Output will be written as system-default encoded stream.
130    * </ul>
131    *
132    * @return
133    *    The output object wrapped in a writer.
134    *    Calling {@link Writer#close()} on the returned object simply flushes the response and does not close
135    *    the underlying writer.
136    * @throws SerializeException If object could not be converted to a writer.
137    */
138   public Writer getWriter() throws SerializeException {
139      if (output == null)
140         throw new SerializeException("Output cannot be null.");
141
142      try {
143         if (output instanceof Writer)
144            writer = (Writer)output;
145         else if (output instanceof OutputStream)
146            writer = new OutputStreamWriter((OutputStream)output, charset);
147         else if (output instanceof File)
148            writer = new OutputStreamWriter(new BufferedOutputStream(new FileOutputStream((File)output)));
149         else if (output instanceof StringBuilder)
150            writer = new StringBuilderWriter((StringBuilder)output);
151         else
152            throw new SerializeException("Cannot convert object of type "+className(output)+" to a Writer.");
153      } catch (FileNotFoundException e) {
154         throw cast(SerializeException.class, e);
155      }
156
157      return new NoCloseWriter(writer);
158   }
159
160   /**
161    * Overwrites the writer in this pipe.
162    *
163    * <p>
164    * Used when wrapping the writer returned by {@link #getWriter()} so that the wrapped writer will be flushed
165    * and closed when {@link #close()} is called.
166    *
167    * @param writer The wrapped writer.
168    */
169   public void setWriter(Writer writer) {
170      this.writer = writer;
171   }
172
173   /**
174    * Overwrites the output stream in this pipe.
175    *
176    * <p>
177    * Used when wrapping the stream returned by {@link #getOutputStream()} so that the wrapped stream will be flushed
178    * when {@link #close()} is called.
179    *
180    * @param outputStream The wrapped stream.
181    */
182   public void setOutputStream(OutputStream outputStream) {
183      this.outputStream = outputStream;
184   }
185
186   /**
187    * Returns the raw output object passed into this session.
188    *
189    * @return The raw output object passed into this session.
190    */
191   public Object getRawOutput() {
192      return output;
193   }
194
195   /**
196    * Closes the output pipe.
197    */
198   @Override /* Closeable */
199   public void close() {
200      try {
201         IOUtils.flush(writer, outputStream);
202         if (autoClose)
203            IOUtils.close(writer, outputStream);
204      } catch (IOException e) {
205         throw new BeanRuntimeException(e);
206      }
207   }
208}