001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.marshall.serializer;
018
019import static org.apache.juneau.commons.utils.IoUtils.*;
020import static org.apache.juneau.commons.utils.Shorts.*;
021import java.io.*;
022import java.nio.charset.*;
023
024import org.apache.juneau.commons.io.*;
025import org.apache.juneau.commons.utils.*;
026
027/**
028 * A wrapper around an object that a serializer sends its output to.
029 *
030 * <p>
031 * For character-based serializers, the output object can be any of the following:
032 * <ul>
033 *    <li>{@link Writer}
034 *    <li>{@link OutputStream} - Output will be written as UTF-8 encoded stream.
035 * </ul>
036 *
037 * <p>
038 * For stream-based serializers, the output object must be an {@link OutputStream}.
039 *
040 * <h5 class='section'>See Also:</h5><ul>
041 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/SerializersAndParsers">Serializers and Parsers</a>
042 * </ul>
043 */
044@SuppressWarnings({
045   "resource" // outputStream/writer wrap the caller-supplied output; the caller retains ownership and close() only flushes.
046})
047public class SerializerPipe implements Closeable {
048
049   private final Object output;
050
051   private OutputStream outputStream;
052   private Writer writer;
053   private final Charset charset;
054
055   /**
056    * Stream-based constructor.
057    *
058    * @param output The object to pipe the serializer output to.
059    */
060   SerializerPipe(Object output) {
061      this.output = output;
062      this.charset = null;
063   }
064
065   /**
066    * Writer-based constructor.
067    *
068    * @param output The object to pipe the serializer output to.
069    * @param streamCharset The charset to use when writing to an {@link OutputStream}.  If <jk>null</jk>, defaults to UTF-8.
070    */
071   SerializerPipe(Object output, Charset streamCharset) {
072      this.output = output;
073      this.charset = or(streamCharset, UTF8);
074   }
075
076   /**
077    * Closes the output pipe.
078    */
079   @Override /* Overridden from Closeable */
080   public void close() {
081      try {
082         IoUtils.flush(writer, outputStream);
083      } catch (IOException e) {
084         throw brex(e);
085      }
086   }
087
088   /**
089    * Wraps the specified output object inside an output stream.
090    *
091    * <p>
092    * Subclasses can override this method to implement their own specialized output streams.
093    *
094    * <p>
095    * This method can be used if the output object is an {@link OutputStream}.
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 ioex("Output cannot be null.");
106
107      if (output instanceof OutputStream output2)
108         outputStream = output2;
109      else
110         throw ioex("Cannot convert object of type %s to an OutputStream.", cn(output));
111
112      return new NoCloseOutputStream(outputStream);
113   }
114
115   /**
116    * Returns the raw output object passed into this session.
117    *
118    * @return The raw output object passed into this session.
119    */
120   public Object getRawOutput() { return output; }
121
122   /**
123    * Wraps the specified output object inside a writer.
124    *
125    * <p>
126    * Subclasses can override this method to implement their own specialized writers.
127    *
128    * <p>
129    * This method can be used if the output object is any of the following class types:
130    * <ul>
131    *    <li>{@link Writer}
132    *    <li>{@link OutputStream} - Output will be written as UTF-8 encoded stream.
133    * </ul>
134    *
135    * @return
136    *    The output object wrapped in a writer.
137    *    Calling {@link Writer#close()} on the returned object simply flushes the response and does not close
138    *    the underlying writer.
139    * @throws SerializeException If object could not be converted to a writer.
140    */
141   public Writer getWriter() throws SerializeException {
142      if (output == null)
143         throw new SerializeException("Output cannot be null.");
144
145      if (output instanceof Writer output2)
146         writer = output2;
147      else if (output instanceof OutputStream output2)
148         writer = new OutputStreamWriter(output2, charset);
149      else
150         throw new SerializeException("Cannot convert object of type " + cn(output) + " to a Writer.");
151
152      return new NoCloseWriter(writer);
153   }
154
155   /**
156    * Overwrites the output stream in this pipe.
157    *
158    * <p>
159    * Used when wrapping the stream returned by {@link #getOutputStream()} so that the wrapped stream will be flushed
160    * when {@link #close()} is called.
161    *
162    * @param outputStream The wrapped stream.
163    */
164   public void setOutputStream(OutputStream outputStream) { this.outputStream = outputStream; }
165
166   /**
167    * Overwrites the writer in this pipe.
168    *
169    * <p>
170    * Used when wrapping the writer returned by {@link #getWriter()} so that the wrapped writer will be flushed
171    * and closed when {@link #close()} is called.
172    *
173    * @param writer The wrapped writer.
174    */
175   public void setWriter(Writer writer) { this.writer = writer; }
176}