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        // force a blocking read
168        if (reader.read() == -1) {
169          throw new EOFException();
170        }
171        n--;
172      } else {
173        n -= amt;
174      }
175    }
176  }
177
178  /**
179   * Returns a {@link Writer} that simply discards written chars.
180   *
181   * @since 15.0
182   */
183  public static Writer nullWriter() {
184    return NullWriter.INSTANCE;
185  }
186
187  private static final class NullWriter extends Writer {
188
189    private static final NullWriter INSTANCE = new NullWriter();
190
191    @Override
192    public void write(int c) {
193    }
194
195    @Override
196    public void write(char[] cbuf) {
197      checkNotNull(cbuf);
198    }
199
200    @Override
201    public void write(char[] cbuf, int off, int len) {
202      checkPositionIndexes(off, off + len, cbuf.length);
203    }
204
205    @Override
206    public void write(String str) {
207      checkNotNull(str);
208    }
209
210    @Override
211    public void write(String str, int off, int len) {
212      checkPositionIndexes(off, off + len, str.length());
213    }
214
215    @Override
216    public Writer append(CharSequence csq) {
217      checkNotNull(csq);
218      return this;
219    }
220
221    @Override
222    public Writer append(CharSequence csq, int start, int end) {
223      checkPositionIndexes(start, end, csq.length());
224      return this;
225    }
226
227    @Override
228    public Writer append(char c) {
229      return this;
230    }
231
232    @Override
233    public void flush() {
234    }
235
236    @Override
237    public void close() {
238    }
239
240    @Override
241    public String toString() {
242      return "CharStreams.nullWriter()";
243    }
244  }
245
246  /**
247   * Returns a Writer that sends all output to the given {@link Appendable}
248   * target. Closing the writer will close the target if it is {@link
249   * Closeable}, and flushing the writer will flush the target if it is {@link
250   * java.io.Flushable}.
251   *
252   * @param target the object to which output will be sent
253   * @return a new Writer object, unless target is a Writer, in which case the
254   *     target is returned
255   */
256  public static Writer asWriter(Appendable target) {
257    if (target instanceof Writer) {
258      return (Writer) target;
259    }
260    return new AppendableWriter(target);
261  }
262
263  // TODO(user): Remove these once Input/OutputSupplier methods are removed
264
265  static Reader asReader(final Readable readable) {
266    checkNotNull(readable);
267    if (readable instanceof Reader) {
268      return (Reader) readable;
269    }
270    return new Reader() {
271      @Override
272      public int read(char[] cbuf, int off, int len) throws IOException {
273        return read(CharBuffer.wrap(cbuf, off, len));
274      }
275
276      @Override
277      public int read(CharBuffer target) throws IOException {
278        return readable.read(target);
279      }
280
281      @Override
282      public void close() throws IOException {
283        if (readable instanceof Closeable) {
284          ((Closeable) readable).close();
285        }
286      }
287    };
288  }
289}