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 }