1 /*
2  * Copyright (C) 2012 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package com.google.common.io;
16 
17 import static com.google.common.base.Preconditions.checkNotNull;
18 
19 import com.google.common.annotations.GwtIncompatible;
20 import com.google.errorprone.annotations.CanIgnoreReturnValue;
21 import java.io.BufferedOutputStream;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.OutputStream;
25 import java.io.OutputStreamWriter;
26 import java.io.Writer;
27 import java.nio.charset.Charset;
28 
29 /**
30  * A destination to which bytes can be written, such as a file. Unlike an {@link OutputStream}, a
31  * {@code ByteSink} is not an open, stateful stream that can be written to and closed. Instead, it
32  * is an immutable <i>supplier</i> of {@code OutputStream} instances.
33  *
34  * <p>{@code ByteSink} provides two kinds of methods:
35  *
36  * <ul>
37  *   <li><b>Methods that return a stream:</b> These methods should return a <i>new</i>, independent
38  *       instance each time they are called. The caller is responsible for ensuring that the
39  *       returned stream is closed.
40  *   <li><b>Convenience methods:</b> These are implementations of common operations that are
41  *       typically implemented by opening a stream using one of the methods in the first category,
42  *       doing something and finally closing the stream or channel that was opened.
43  * </ul>
44  *
45  * @since 14.0
46  * @author Colin Decker
47  */
48 @GwtIncompatible
49 public abstract class ByteSink {
50 
51   /** Constructor for use by subclasses. */
ByteSink()52   protected ByteSink() {}
53 
54   /**
55    * Returns a {@link CharSink} view of this {@code ByteSink} that writes characters to this sink as
56    * bytes encoded with the given {@link Charset charset}.
57    */
asCharSink(Charset charset)58   public CharSink asCharSink(Charset charset) {
59     return new AsCharSink(charset);
60   }
61 
62   /**
63    * Opens a new {@link OutputStream} for writing to this sink. This method returns a new,
64    * independent stream each time it is called.
65    *
66    * <p>The caller is responsible for ensuring that the returned stream is closed.
67    *
68    * @throws IOException if an I/O error occurs while opening the stream
69    */
openStream()70   public abstract OutputStream openStream() throws IOException;
71 
72   /**
73    * Opens a new buffered {@link OutputStream} for writing to this sink. The returned stream is not
74    * required to be a {@link BufferedOutputStream} in order to allow implementations to simply
75    * delegate to {@link #openStream()} when the stream returned by that method does not benefit from
76    * additional buffering (for example, a {@code ByteArrayOutputStream}). This method returns a new,
77    * independent stream each time it is called.
78    *
79    * <p>The caller is responsible for ensuring that the returned stream is closed.
80    *
81    * @throws IOException if an I/O error occurs while opening the stream
82    * @since 15.0 (in 14.0 with return type {@link BufferedOutputStream})
83    */
openBufferedStream()84   public OutputStream openBufferedStream() throws IOException {
85     OutputStream out = openStream();
86     return (out instanceof BufferedOutputStream)
87         ? (BufferedOutputStream) out
88         : new BufferedOutputStream(out);
89   }
90 
91   /**
92    * Writes all the given bytes to this sink.
93    *
94    * @throws IOException if an I/O occurs while writing to this sink
95    */
write(byte[] bytes)96   public void write(byte[] bytes) throws IOException {
97     checkNotNull(bytes);
98 
99     Closer closer = Closer.create();
100     try {
101       OutputStream out = closer.register(openStream());
102       out.write(bytes);
103       out.flush(); // https://code.google.com/p/guava-libraries/issues/detail?id=1330
104     } catch (Throwable e) {
105       throw closer.rethrow(e);
106     } finally {
107       closer.close();
108     }
109   }
110 
111   /**
112    * Writes all the bytes from the given {@code InputStream} to this sink. Does not close {@code
113    * input}.
114    *
115    * @return the number of bytes written
116    * @throws IOException if an I/O occurs while reading from {@code input} or writing to this sink
117    */
118   @CanIgnoreReturnValue
writeFrom(InputStream input)119   public long writeFrom(InputStream input) throws IOException {
120     checkNotNull(input);
121 
122     Closer closer = Closer.create();
123     try {
124       OutputStream out = closer.register(openStream());
125       long written = ByteStreams.copy(input, out);
126       out.flush(); // https://code.google.com/p/guava-libraries/issues/detail?id=1330
127       return written;
128     } catch (Throwable e) {
129       throw closer.rethrow(e);
130     } finally {
131       closer.close();
132     }
133   }
134 
135   /**
136    * A char sink that encodes written characters with a charset and writes resulting bytes to this
137    * byte sink.
138    */
139   private final class AsCharSink extends CharSink {
140 
141     private final Charset charset;
142 
AsCharSink(Charset charset)143     private AsCharSink(Charset charset) {
144       this.charset = checkNotNull(charset);
145     }
146 
147     @Override
openStream()148     public Writer openStream() throws IOException {
149       return new OutputStreamWriter(ByteSink.this.openStream(), charset);
150     }
151 
152     @Override
toString()153     public String toString() {
154       return ByteSink.this.toString() + ".asCharSink(" + charset + ")";
155     }
156   }
157 }
158