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.Beta;
20  import com.google.common.annotations.GwtIncompatible;
21  import com.google.errorprone.annotations.CanIgnoreReturnValue;
22  import java.io.BufferedWriter;
23  import java.io.IOException;
24  import java.io.Reader;
25  import java.io.Writer;
26  import java.nio.charset.Charset;
27  import java.util.Iterator;
28  import java.util.stream.Stream;
29  
30  /**
31   * A destination to which characters can be written, such as a text file. Unlike a {@link Writer}, a
32   * {@code CharSink} is not an open, stateful stream that can be written to and closed. Instead, it
33   * is an immutable <i>supplier</i> of {@code Writer} instances.
34   *
35   * <p>{@code CharSink} provides two kinds of methods:
36   * <ul>
37   * <li><b>Methods that return a writer:</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 returned
39   *     writer is closed.
40   * <li><b>Convenience methods:</b> These are implementations of common operations that are typically
41   *     implemented by opening a writer using one of the methods in the first category, doing
42   *     something and finally closing the writer that was opened.
43   * </ul>
44   *
45   * <p>Any {@link ByteSink} may be viewed as a {@code CharSink} with a specific {@linkplain Charset
46   * character encoding} using {@link ByteSink#asCharSink(Charset)}. Characters written to the
47   * resulting {@code CharSink} will written to the {@code ByteSink} as encoded bytes.
48   *
49   * @since 14.0
50   * @author Colin Decker
51   */
52  @GwtIncompatible
53  public abstract class CharSink {
54  
55    /**
56     * Constructor for use by subclasses.
57     */
58    protected CharSink() {}
59  
60    /**
61     * Opens a new {@link Writer} for writing to this sink. This method returns a new, independent
62     * writer each time it is called.
63     *
64     * <p>The caller is responsible for ensuring that the returned writer is closed.
65     *
66     * @throws IOException if an I/O error occurs while opening the writer
67     */
68    public abstract Writer openStream() throws IOException;
69  
70    /**
71     * Opens a new buffered {@link Writer} for writing to this sink. The returned stream is not
72     * required to be a {@link BufferedWriter} in order to allow implementations to simply delegate to
73     * {@link #openStream()} when the stream returned by that method does not benefit from additional
74     * buffering. This method returns a new, independent writer each time it is called.
75     *
76     * <p>The caller is responsible for ensuring that the returned writer is closed.
77     *
78     * @throws IOException if an I/O error occurs while opening the writer
79     * @since 15.0 (in 14.0 with return type {@link BufferedWriter})
80     */
81    public Writer openBufferedStream() throws IOException {
82      Writer writer = openStream();
83      return (writer instanceof BufferedWriter)
84          ? (BufferedWriter) writer
85          : new BufferedWriter(writer);
86    }
87  
88    /**
89     * Writes the given character sequence to this sink.
90     *
91     * @throws IOException if an I/O error while writing to this sink
92     */
93    public void write(CharSequence charSequence) throws IOException {
94      checkNotNull(charSequence);
95  
96      Closer closer = Closer.create();
97      try {
98        Writer out = closer.register(openStream());
99        out.append(charSequence);
100       out.flush(); // https://code.google.com/p/guava-libraries/issues/detail?id=1330
101     } catch (Throwable e) {
102       throw closer.rethrow(e);
103     } finally {
104       closer.close();
105     }
106   }
107 
108   /**
109    * Writes the given lines of text to this sink with each line (including the last) terminated with
110    * the operating system's default line separator. This method is equivalent to
111    * {@code writeLines(lines, System.getProperty("line.separator"))}.
112    *
113    * @throws IOException if an I/O error occurs while writing to this sink
114    */
115   public void writeLines(Iterable<? extends CharSequence> lines) throws IOException {
116     writeLines(lines, System.getProperty("line.separator"));
117   }
118 
119   /**
120    * Writes the given lines of text to this sink with each line (including the last) terminated with
121    * the given line separator.
122    *
123    * @throws IOException if an I/O error occurs while writing to this sink
124    */
125   public void writeLines(Iterable<? extends CharSequence> lines, String lineSeparator)
126       throws IOException {
127     writeLines(lines.iterator(), lineSeparator);
128   }
129 
130   /**
131    * Writes the given lines of text to this sink with each line (including the last) terminated with
132    * the operating system's default line separator. This method is equivalent to {@code
133    * writeLines(lines, System.getProperty("line.separator"))}.
134    *
135    * @throws IOException if an I/O error occurs while writing to this sink
136    * @since 22.0
137    */
138   @Beta
139   public void writeLines(Stream<? extends CharSequence> lines) throws IOException {
140     writeLines(lines, System.getProperty("line.separator"));
141   }
142 
143   /**
144    * Writes the given lines of text to this sink with each line (including the last) terminated with
145    * the given line separator.
146    *
147    * @throws IOException if an I/O error occurs while writing to this sink
148    * @since 22.0
149    */
150   @Beta
151   public void writeLines(Stream<? extends CharSequence> lines, String lineSeparator)
152       throws IOException {
153     writeLines(lines.iterator(), lineSeparator);
154   }
155 
156   private void writeLines(Iterator<? extends CharSequence> lines, String lineSeparator)
157       throws IOException {
158     checkNotNull(lineSeparator);
159 
160     try (Writer out = openBufferedStream()) {
161       while (lines.hasNext()) {
162         out.append(lines.next()).append(lineSeparator);
163       }
164     }
165   }
166 
167   /**
168    * Writes all the text from the given {@link Readable} (such as a {@link Reader}) to this sink.
169    * Does not close {@code readable} if it is {@code Closeable}.
170    *
171    * @return the number of characters written
172    * @throws IOException if an I/O error occurs while reading from {@code readable} or writing to
173    *     this sink
174    */
175   @CanIgnoreReturnValue
176   public long writeFrom(Readable readable) throws IOException {
177     checkNotNull(readable);
178 
179     Closer closer = Closer.create();
180     try {
181       Writer out = closer.register(openStream());
182       long written = CharStreams.copy(readable, out);
183       out.flush(); // https://code.google.com/p/guava-libraries/issues/detail?id=1330
184       return written;
185     } catch (Throwable e) {
186       throw closer.rethrow(e);
187     } finally {
188       closer.close();
189     }
190   }
191 }