001/*
002 * Copyright (C) 2007 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.google.common.io;
018
019import static com.google.common.base.Preconditions.checkNotNull;
020import static com.google.common.base.Preconditions.checkPositionIndexes;
021
022import com.google.common.annotations.Beta;
023
024import java.io.Closeable;
025import java.io.EOFException;
026import java.io.IOException;
027import java.io.Reader;
028import java.io.Writer;
029import java.nio.CharBuffer;
030import java.util.ArrayList;
031import java.util.List;
032
033/**
034 * Provides utility methods for working with character streams.
035 *
036 * <p>All method parameters must be non-null unless documented otherwise.
037 *
038 * <p>Some of the methods in this class take arguments with a generic type of
039 * {@code Readable & Closeable}. A {@link java.io.Reader} implements both of
040 * those interfaces. Similarly for {@code Appendable & Closeable} and
041 * {@link java.io.Writer}.
042 *
043 * @author Chris Nokleberg
044 * @author Bin Zhu
045 * @author Colin Decker
046 * @since 1.0
047 */
048@Beta
049public final class CharStreams {
050  private static final int BUF_SIZE = 0x800; // 2K chars (4K bytes)
051
052  private CharStreams() {}
053
054  /**
055   * Copies all characters between the {@link Readable} and {@link Appendable}
056   * objects. Does not close or flush either object.
057   *
058   * @param from the object to read from
059   * @param to the object to write to
060   * @return the number of characters copied
061   * @throws IOException if an I/O error occurs
062   */
063  public static long copy(Readable from, Appendable to) throws IOException {
064    checkNotNull(from);
065    checkNotNull(to);
066    CharBuffer buf = CharBuffer.allocate(BUF_SIZE);
067    long total = 0;
068    while (from.read(buf) != -1) {
069      buf.flip();
070      to.append(buf);
071      total += buf.remaining();
072      buf.clear();
073    }
074    return total;
075  }
076
077  /**
078   * Reads all characters from a {@link Readable} object into a {@link String}.
079   * Does not close the {@code Readable}.
080   *
081   * @param r the object to read from
082   * @return a string containing all the characters
083   * @throws IOException if an I/O error occurs
084   */
085  public static String toString(Readable r) throws IOException {
086    return toStringBuilder(r).toString();
087  }
088
089  /**
090   * Reads all characters from a {@link Readable} object into a new
091   * {@link StringBuilder} instance. Does not close the {@code Readable}.
092   *
093   * @param r the object to read from
094   * @return a {@link StringBuilder} containing all the characters
095   * @throws IOException if an I/O error occurs
096   */
097  private static StringBuilder toStringBuilder(Readable r) throws IOException {
098    StringBuilder sb = new StringBuilder();
099    copy(r, sb);
100    return sb;
101  }
102
103  /**
104   * Reads all of the lines from a {@link Readable} object. The lines do
105   * not include line-termination characters, but do include other
106   * leading and trailing whitespace.
107   *
108   * <p>Does not close the {@code Readable}. If reading files or resources you
109   * should use the {@link Files#readLines} and {@link Resources#readLines}
110   * methods.
111   *
112   * @param r the object to read from
113   * @return a mutable {@link List} containing all the lines
114   * @throws IOException if an I/O error occurs
115   */
116  public static List<String> readLines(Readable r) throws IOException {
117    List<String> result = new ArrayList<String>();
118    LineReader lineReader = new LineReader(r);
119    String line;
120    while ((line = lineReader.readLine()) != null) {
121      result.add(line);
122    }
123    return result;
124  }
125
126  /**
127   * Streams lines from a {@link Readable} object, stopping when the processor
128   * returns {@code false} or all lines have been read and returning the result
129   * produced by the processor. Does not close {@code readable}. Note that this
130   * method may not fully consume the contents of {@code readable} if the
131   * processor stops processing early.
132   *
133   * @throws IOException if an I/O error occurs
134   * @since 14.0
135   */
136  public static <T> T readLines(
137      Readable readable, LineProcessor<T> processor) throws IOException {
138    checkNotNull(readable);
139    checkNotNull(processor);
140
141    LineReader lineReader = new LineReader(readable);
142    String line;
143    while ((line = lineReader.readLine()) != null) {
144      if (!processor.processLine(line)) {
145        break;
146      }
147    }
148    return processor.getResult();
149  }
150
151  /**
152   * Discards {@code n} characters of data from the reader. This method
153   * will block until the full amount has been skipped. Does not close the
154   * reader.
155   *
156   * @param reader the reader to read from
157   * @param n the number of characters to skip
158   * @throws EOFException if this stream reaches the end before skipping all
159   *     the characters
160   * @throws IOException if an I/O error occurs
161   */
162  public static void skipFully(Reader reader, long n) throws IOException {
163    checkNotNull(reader);
164    while (n > 0) {
165      long amt = reader.skip(n);
166      if (amt == 0) {
167        throw new EOFException();
168      }
169      n -= amt;
170    }
171  }
172
173  /**
174   * Returns a {@link Writer} that simply discards written chars.
175   *
176   * @since 15.0
177   */
178  public static Writer nullWriter() {
179    return NullWriter.INSTANCE;
180  }
181
182  private static final class NullWriter extends Writer {
183
184    private static final NullWriter INSTANCE = new NullWriter();
185
186    @Override
187    public void write(int c) {
188    }
189
190    @Override
191    public void write(char[] cbuf) {
192      checkNotNull(cbuf);
193    }
194
195    @Override
196    public void write(char[] cbuf, int off, int len) {
197      checkPositionIndexes(off, off + len, cbuf.length);
198    }
199
200    @Override
201    public void write(String str) {
202      checkNotNull(str);
203    }
204
205    @Override
206    public void write(String str, int off, int len) {
207      checkPositionIndexes(off, off + len, str.length());
208    }
209
210    @Override
211    public Writer append(CharSequence csq) {
212      checkNotNull(csq);
213      return this;
214    }
215
216    @Override
217    public Writer append(CharSequence csq, int start, int end) {
218      checkPositionIndexes(start, end, csq.length());
219      return this;
220    }
221
222    @Override
223    public Writer append(char c) {
224      return this;
225    }
226
227    @Override
228    public void flush() {
229    }
230
231    @Override
232    public void close() {
233    }
234
235    @Override
236    public String toString() {
237      return "CharStreams.nullWriter()";
238    }
239  }
240
241  /**
242   * Returns a Writer that sends all output to the given {@link Appendable}
243   * target. Closing the writer will close the target if it is {@link
244   * Closeable}, and flushing the writer will flush the target if it is {@link
245   * java.io.Flushable}.
246   *
247   * @param target the object to which output will be sent
248   * @return a new Writer object, unless target is a Writer, in which case the
249   *     target is returned
250   */
251  public static Writer asWriter(Appendable target) {
252    if (target instanceof Writer) {
253      return (Writer) target;
254    }
255    return new AppendableWriter(target);
256  }
257
258  // TODO(cgdecker): Remove these once Input/OutputSupplier methods are removed
259
260  static Reader asReader(final Readable readable) {
261    checkNotNull(readable);
262    if (readable instanceof Reader) {
263      return (Reader) readable;
264    }
265    return new Reader() {
266      @Override
267      public int read(char[] cbuf, int off, int len) throws IOException {
268        return read(CharBuffer.wrap(cbuf, off, len));
269      }
270
271      @Override
272      public int read(CharBuffer target) throws IOException {
273        return readable.read(target);
274      }
275
276      @Override
277      public void close() throws IOException {
278        if (readable instanceof Closeable) {
279          ((Closeable) readable).close();
280        }
281      }
282    };
283  }
284}