001/*
002 * Copyright (C) 2012 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License
010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011 * or implied. See the License for the specific language governing permissions and limitations under
012 * the License.
013 */
014
015package com.google.common.io;
016
017import static com.google.common.base.Preconditions.checkNotNull;
018import static com.google.common.base.StandardSystemProperty.LINE_SEPARATOR;
019
020import com.google.common.annotations.GwtIncompatible;
021import com.google.common.annotations.J2ktIncompatible;
022import com.google.errorprone.annotations.CanIgnoreReturnValue;
023import java.io.BufferedWriter;
024import java.io.IOException;
025import java.io.Reader;
026import java.io.Writer;
027import java.nio.charset.Charset;
028import java.util.Iterator;
029import java.util.stream.Stream;
030
031/**
032 * A destination to which characters can be written, such as a text file. Unlike a {@link Writer}, a
033 * {@code CharSink} is not an open, stateful stream that can be written to and closed. Instead, it
034 * is an immutable <i>supplier</i> of {@code Writer} instances.
035 *
036 * <p>{@code CharSink} provides two kinds of methods:
037 *
038 * <ul>
039 *   <li><b>Methods that return a writer:</b> These methods should return a <i>new</i>, independent
040 *       instance each time they are called. The caller is responsible for ensuring that the
041 *       returned writer is closed.
042 *   <li><b>Convenience methods:</b> These are implementations of common operations that are
043 *       typically implemented by opening a writer using one of the methods in the first category,
044 *       doing something and finally closing the writer that was opened.
045 * </ul>
046 *
047 * <p>Any {@link ByteSink} may be viewed as a {@code CharSink} with a specific {@linkplain Charset
048 * character encoding} using {@link ByteSink#asCharSink(Charset)}. Characters written to the
049 * resulting {@code CharSink} will written to the {@code ByteSink} as encoded bytes.
050 *
051 * @since 14.0
052 * @author Colin Decker
053 */
054@J2ktIncompatible
055@GwtIncompatible
056@ElementTypesAreNonnullByDefault
057public abstract class CharSink {
058
059  /** Constructor for use by subclasses. */
060  protected CharSink() {}
061
062  /**
063   * Opens a new {@link Writer} for writing to this sink. This method returns a new, independent
064   * writer each time it is called.
065   *
066   * <p>The caller is responsible for ensuring that the returned writer is closed.
067   *
068   * @throws IOException if an I/O error occurs while opening the writer
069   */
070  public abstract Writer openStream() throws IOException;
071
072  /**
073   * Opens a new buffered {@link Writer} for writing to this sink. The returned stream is not
074   * required to be a {@link BufferedWriter} in order to allow implementations to simply delegate to
075   * {@link #openStream()} when the stream returned by that method does not benefit from additional
076   * buffering. This method returns a new, independent writer each time it is called.
077   *
078   * <p>The caller is responsible for ensuring that the returned writer is closed.
079   *
080   * @throws IOException if an I/O error occurs while opening the writer
081   * @since 15.0 (in 14.0 with return type {@link BufferedWriter})
082   */
083  public Writer openBufferedStream() throws IOException {
084    Writer writer = openStream();
085    return (writer instanceof BufferedWriter)
086        ? (BufferedWriter) writer
087        : new BufferedWriter(writer);
088  }
089
090  /**
091   * Writes the given character sequence to this sink.
092   *
093   * @throws IOException if an I/O error while writing to this sink
094   */
095  public void write(CharSequence charSequence) throws IOException {
096    checkNotNull(charSequence);
097
098    try (Writer out = openStream()) {
099      out.append(charSequence);
100    }
101  }
102
103  /**
104   * Writes the given lines of text to this sink with each line (including the last) terminated with
105   * the operating system's default line separator. This method is equivalent to {@code
106   * writeLines(lines, System.getProperty("line.separator"))}.
107   *
108   * @throws IOException if an I/O error occurs while writing to this sink
109   */
110  public void writeLines(Iterable<? extends CharSequence> lines) throws IOException {
111    writeLines(lines, System.getProperty("line.separator"));
112  }
113
114  /**
115   * Writes the given lines of text to this sink with each line (including the last) terminated with
116   * the given line separator.
117   *
118   * @throws IOException if an I/O error occurs while writing to this sink
119   */
120  public void writeLines(Iterable<? extends CharSequence> lines, String lineSeparator)
121      throws IOException {
122    writeLines(lines.iterator(), lineSeparator);
123  }
124
125  /**
126   * Writes the given lines of text to this sink with each line (including the last) terminated with
127   * the operating system's default line separator. This method is equivalent to {@code
128   * writeLines(lines, System.getProperty("line.separator"))}.
129   *
130   * @throws IOException if an I/O error occurs while writing to this sink
131   * @since 22.0 (but only since 33.4.0 in the Android flavor)
132   */
133  public void writeLines(Stream<? extends CharSequence> lines) throws IOException {
134    writeLines(lines, LINE_SEPARATOR.value());
135  }
136
137  /**
138   * Writes the given lines of text to this sink with each line (including the last) terminated with
139   * the given line separator.
140   *
141   * @throws IOException if an I/O error occurs while writing to this sink
142   * @since 22.0 (but only since 33.4.0 in the Android flavor)
143   */
144  public void writeLines(Stream<? extends CharSequence> lines, String lineSeparator)
145      throws IOException {
146    writeLines(lines.iterator(), lineSeparator);
147  }
148
149  private void writeLines(Iterator<? extends CharSequence> lines, String lineSeparator)
150      throws IOException {
151    checkNotNull(lineSeparator);
152
153    try (Writer out = openBufferedStream()) {
154      while (lines.hasNext()) {
155        out.append(lines.next()).append(lineSeparator);
156      }
157    }
158  }
159
160  /**
161   * Writes all the text from the given {@link Readable} (such as a {@link Reader}) to this sink.
162   * Does not close {@code readable} if it is {@code Closeable}.
163   *
164   * @return the number of characters written
165   * @throws IOException if an I/O error occurs while reading from {@code readable} or writing to
166   *     this sink
167   */
168  @CanIgnoreReturnValue
169  public long writeFrom(Readable readable) throws IOException {
170    checkNotNull(readable);
171
172    try (Writer out = openStream()) {
173      return CharStreams.copy(readable, out);
174    }
175  }
176}