View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.juneau.serializer;
18  
19  import static org.apache.juneau.commons.utils.IoUtils.*;
20  import static org.apache.juneau.commons.utils.ThrowableUtils.*;
21  import static org.apache.juneau.commons.utils.Utils.*;
22  
23  import java.io.*;
24  import java.nio.charset.*;
25  
26  import org.apache.juneau.commons.io.*;
27  import org.apache.juneau.commons.utils.*;
28  
29  /**
30   * A wrapper around an object that a serializer sends its output to.
31   *
32   * <p>
33   * For character-based serializers, the output object can be any of the following:
34   * <ul>
35   * 	<li>{@link Writer}
36   * 	<li>{@link OutputStream} - Output will be written as UTF-8 encoded stream.
37   * 	<li>{@link File} - Output will be written as system-default encoded stream.
38   * 	<li>{@link StringBuilder}
39   * </ul>
40   *
41   * <p>
42   * For stream-based serializers, the output object can be any of the following:
43   * <ul>
44   * 	<li>{@link OutputStream}
45   * 	<li>{@link File}
46   * </ul>
47   *
48   * <h5 class='section'>See Also:</h5><ul>
49   * 	<li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/SerializersAndParsers">Serializers and Parsers</a>
50   * </ul>
51   */
52  public class SerializerPipe implements Closeable {
53  
54  	private final Object output;
55  	private final boolean autoClose;
56  
57  	private OutputStream outputStream;
58  	private Writer writer;
59  	private Charset charset;
60  
61  	/**
62  	 * Stream-based constructor.
63  	 *
64  	 * @param output The object to pipe the serializer output to.
65  	 */
66  	SerializerPipe(Object output) {
67  		this.output = output;
68  		this.autoClose = false;
69  		this.charset = null;
70  	}
71  
72  	/**
73  	 * Writer-based constructor.
74  	 *
75  	 * @param output The object to pipe the serializer output to.
76  	 */
77  	SerializerPipe(Object output, Charset streamCharset, Charset fileCharset) {
78  		boolean isFile = (output instanceof File);
79  		this.output = output;
80  		this.autoClose = isFile;
81  		Charset cs = isFile ? fileCharset : streamCharset;
82  		if (cs == null)
83  			cs = isFile ? Charset.defaultCharset() : UTF8;
84  		this.charset = cs;
85  	}
86  
87  	/**
88  	 * Closes the output pipe.
89  	 */
90  	@Override /* Overridden from Closeable */
91  	public void close() {
92  		try {
93  			IoUtils.flush(writer, outputStream);
94  			if (autoClose)
95  				IoUtils.close(writer, outputStream);
96  		} catch (IOException e) {
97  			throw bex(e);
98  		}
99  	}
100 
101 	/**
102 	 * Wraps the specified output object inside an output stream.
103 	 *
104 	 * <p>
105 	 * Subclasses can override this method to implement their own specialized output streams.
106 	 *
107 	 * <p>
108 	 * This method can be used if the output object is any of the following class types:
109 	 * <ul>
110 	 * 	<li>{@link OutputStream}
111 	 * 	<li>{@link File}
112 	 * </ul>
113 	 *
114 	 * @return
115 	 * 	The output object wrapped in an output stream.
116 	 * 	Calling {@link OutputStream#close()} on the returned object simply flushes the response and does not close
117 	 * 	the underlying stream.
118 	 * @throws IOException If object could not be converted to an output stream.
119 	 */
120 	public OutputStream getOutputStream() throws IOException {
121 		if (output == null)
122 			throw ioex("Output cannot be null.");
123 
124 		if (output instanceof OutputStream output2)
125 			outputStream = output2;
126 		else if (output instanceof File output2)
127 			outputStream = new BufferedOutputStream(new FileOutputStream(output2));
128 		else
129 			throw ioex("Cannot convert object of type {0} to an OutputStream.", cn(output));
130 
131 		return new NoCloseOutputStream(outputStream);
132 	}
133 
134 	/**
135 	 * Returns the raw output object passed into this session.
136 	 *
137 	 * @return The raw output object passed into this session.
138 	 */
139 	public Object getRawOutput() { return output; }
140 
141 	/**
142 	 * Wraps the specified output object inside a writer.
143 	 *
144 	 * <p>
145 	 * Subclasses can override this method to implement their own specialized writers.
146 	 *
147 	 * <p>
148 	 * This method can be used if the output object is any of the following class types:
149 	 * <ul>
150 	 * 	<li>{@link Writer}
151 	 * 	<li>{@link OutputStream} - Output will be written as UTF-8 encoded stream.
152 	 * 	<li>{@link File} - Output will be written as system-default encoded stream.
153 	 * </ul>
154 	 *
155 	 * @return
156 	 * 	The output object wrapped in a writer.
157 	 * 	Calling {@link Writer#close()} on the returned object simply flushes the response and does not close
158 	 * 	the underlying writer.
159 	 * @throws SerializeException If object could not be converted to a writer.
160 	 */
161 	public Writer getWriter() throws SerializeException {
162 		if (output == null)
163 			throw new SerializeException("Output cannot be null.");
164 
165 		try {
166 			if (output instanceof Writer output2)
167 				writer = output2;
168 			else if (output instanceof OutputStream output2)
169 				writer = new OutputStreamWriter(output2, charset);
170 			else if (output instanceof File output2)
171 				writer = new OutputStreamWriter(new BufferedOutputStream(new FileOutputStream(output2)));
172 			else if (output instanceof StringBuilder output2)
173 				writer = new StringBuilderWriter(output2);
174 			else
175 				throw new SerializeException("Cannot convert object of type " + cn(output) + " to a Writer.");
176 		} catch (FileNotFoundException e) {
177 			throw castException(SerializeException.class, e);
178 		}
179 
180 		return new NoCloseWriter(writer);
181 	}
182 
183 	/**
184 	 * Overwrites the output stream in this pipe.
185 	 *
186 	 * <p>
187 	 * Used when wrapping the stream returned by {@link #getOutputStream()} so that the wrapped stream will be flushed
188 	 * when {@link #close()} is called.
189 	 *
190 	 * @param outputStream The wrapped stream.
191 	 */
192 	public void setOutputStream(OutputStream outputStream) { this.outputStream = outputStream; }
193 
194 	/**
195 	 * Overwrites the writer in this pipe.
196 	 *
197 	 * <p>
198 	 * Used when wrapping the writer returned by {@link #getWriter()} so that the wrapped writer will be flushed
199 	 * and closed when {@link #close()} is called.
200 	 *
201 	 * @param writer The wrapped writer.
202 	 */
203 	public void setWriter(Writer writer) { this.writer = writer; }
204 }