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.serializer; 018 019import static org.apache.juneau.commons.utils.IoUtils.*; 020import static org.apache.juneau.commons.utils.ThrowableUtils.*; 021import static org.apache.juneau.commons.utils.Utils.*; 022 023import java.io.*; 024import java.nio.charset.*; 025 026import org.apache.juneau.commons.io.*; 027import org.apache.juneau.commons.utils.*; 028 029/** 030 * A wrapper around an object that a serializer sends its output to. 031 * 032 * <p> 033 * For character-based serializers, the output object can be any of the following: 034 * <ul> 035 * <li>{@link Writer} 036 * <li>{@link OutputStream} - Output will be written as UTF-8 encoded stream. 037 * <li>{@link File} - Output will be written as system-default encoded stream. 038 * <li>{@link StringBuilder} 039 * </ul> 040 * 041 * <p> 042 * For stream-based serializers, the output object can be any of the following: 043 * <ul> 044 * <li>{@link OutputStream} 045 * <li>{@link File} 046 * </ul> 047 * 048 * <h5 class='section'>See Also:</h5><ul> 049 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/SerializersAndParsers">Serializers and Parsers</a> 050 * </ul> 051 */ 052public class SerializerPipe implements Closeable { 053 054 private final Object output; 055 private final boolean autoClose; 056 057 private OutputStream outputStream; 058 private Writer writer; 059 private Charset charset; 060 061 /** 062 * Stream-based constructor. 063 * 064 * @param output The object to pipe the serializer output to. 065 */ 066 SerializerPipe(Object output) { 067 this.output = output; 068 this.autoClose = false; 069 this.charset = null; 070 } 071 072 /** 073 * Writer-based constructor. 074 * 075 * @param output The object to pipe the serializer output to. 076 */ 077 SerializerPipe(Object output, Charset streamCharset, Charset fileCharset) { 078 boolean isFile = (output instanceof File); 079 this.output = output; 080 this.autoClose = isFile; 081 Charset cs = isFile ? fileCharset : streamCharset; 082 if (cs == null) 083 cs = isFile ? Charset.defaultCharset() : UTF8; 084 this.charset = cs; 085 } 086 087 /** 088 * Closes the output pipe. 089 */ 090 @Override /* Overridden from Closeable */ 091 public void close() { 092 try { 093 IoUtils.flush(writer, outputStream); 094 if (autoClose) 095 IoUtils.close(writer, outputStream); 096 } catch (IOException e) { 097 throw bex(e); 098 } 099 } 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}