View Javadoc
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   * <ul>
36   * <li><b>Methods that return a stream:</b> These methods should return a <i>new</i>, independent
37   *     instance each time they are called. The caller is responsible for ensuring that the returned
38   *     stream is closed.
39   * <li><b>Convenience methods:</b> These are implementations of common operations that are typically
40   *     implemented by opening a stream using one of the methods in the first category, doing
41   *     something and finally closing the stream or channel that was opened.
42   * </ul>
43   *
44   * @since 14.0
45   * @author Colin Decker
46   */
47  @GwtIncompatible
48  public abstract class ByteSink {
49  
50    /**
51     * Constructor for use by subclasses.
52     */
53    protected ByteSink() {}
54  
55    /**
56     * Returns a {@link CharSink} view of this {@code ByteSink} that writes characters to this sink as
57     * bytes encoded with the given {@link Charset charset}.
58     */
59    public CharSink asCharSink(Charset charset) {
60      return new AsCharSink(charset);
61    }
62  
63    /**
64     * Opens a new {@link OutputStream} for writing to this sink. This method returns a new,
65     * independent stream each time it is called.
66     *
67     * <p>The caller is responsible for ensuring that the returned stream is closed.
68     *
69     * @throws IOException if an I/O error occurs while opening the stream
70     */
71    public abstract OutputStream openStream() throws IOException;
72  
73    /**
74     * Opens a new buffered {@link OutputStream} for writing to this sink. The returned stream is not
75     * required to be a {@link BufferedOutputStream} in order to allow implementations to simply
76     * delegate to {@link #openStream()} when the stream returned by that method does not benefit from
77     * additional buffering (for example, a {@code ByteArrayOutputStream}). This method returns a new,
78     * independent stream each time it is called.
79     *
80     * <p>The caller is responsible for ensuring that the returned stream is closed.
81     *
82     * @throws IOException if an I/O error occurs while opening the stream
83     * @since 15.0 (in 14.0 with return type {@link BufferedOutputStream})
84     */
85    public OutputStream openBufferedStream() throws IOException {
86      OutputStream out = openStream();
87      return (out instanceof BufferedOutputStream)
88          ? (BufferedOutputStream) out
89          : new BufferedOutputStream(out);
90    }
91  
92    /**
93     * Writes all the given bytes to this sink.
94     *
95     * @throws IOException if an I/O occurs while writing to this sink
96     */
97    public void write(byte[] bytes) throws IOException {
98      checkNotNull(bytes);
99  
100     Closer closer = Closer.create();
101     try {
102       OutputStream out = closer.register(openStream());
103       out.write(bytes);
104       out.flush(); // https://code.google.com/p/guava-libraries/issues/detail?id=1330
105     } catch (Throwable e) {
106       throw closer.rethrow(e);
107     } finally {
108       closer.close();
109     }
110   }
111 
112   /**
113    * Writes all the bytes from the given {@code InputStream} to this sink. Does not close
114    * {@code input}.
115    *
116    * @return the number of bytes written
117    * @throws IOException if an I/O occurs while reading from {@code input} or writing to this sink
118    */
119   @CanIgnoreReturnValue
120   public long writeFrom(InputStream input) throws IOException {
121     checkNotNull(input);
122 
123     Closer closer = Closer.create();
124     try {
125       OutputStream out = closer.register(openStream());
126       long written = ByteStreams.copy(input, out);
127       out.flush(); // https://code.google.com/p/guava-libraries/issues/detail?id=1330
128       return written;
129     } catch (Throwable e) {
130       throw closer.rethrow(e);
131     } finally {
132       closer.close();
133     }
134   }
135 
136   /**
137    * A char sink that encodes written characters with a charset and writes resulting bytes to this
138    * byte sink.
139    */
140   private final class AsCharSink extends CharSink {
141 
142     private final Charset charset;
143 
144     private AsCharSink(Charset charset) {
145       this.charset = checkNotNull(charset);
146     }
147 
148     @Override
149     public Writer openStream() throws IOException {
150       return new OutputStreamWriter(ByteSink.this.openStream(), charset);
151     }
152 
153     @Override
154     public String toString() {
155       return ByteSink.this.toString() + ".asCharSink(" + charset + ")";
156     }
157   }
158 }