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