001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * 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 is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.internal;
014
015import static org.apache.juneau.internal.ThrowableUtils.*;
016
017import java.io.*;
018import java.nio.charset.*;
019
020import org.apache.juneau.*;
021import org.apache.juneau.utils.*;
022
023/**
024 * Various I/O related utility methods.
025 */
026public final class IOUtils {
027
028   /** UTF-8 charset */
029   public static final Charset UTF8 = Charset.forName("UTF-8");
030
031   /**
032    * Reads the contents of a file into a string.
033    * 
034    * @param path The path of the file to read using default character encoding.
035    * @return The contents of the reader as a string, or <jk>null</jk> if file does not exist.
036    * @throws IOException If a problem occurred trying to read from the reader.
037    */
038   public static String readFile(String path) throws IOException {
039      return read(new File(path));
040   }
041
042   /**
043    * Reads the contents of a file into a string.
044    * 
045    * @param in The file to read using default character encoding.
046    * @return The contents of the reader as a string, or <jk>null</jk> if file does not exist.
047    * @throws IOException If a problem occurred trying to read from the reader.
048    */
049   public static String read(File in) throws IOException {
050      if (in == null || ! in.exists())
051         return null;
052      try (Reader r = FileReaderBuilder.create(in).build()) {
053         return read(r, 0, 1024);
054      }
055   }
056
057   /**
058    * Reads the specified object to a <code>String</code>.
059    * 
060    * <p>
061    * Can be any of the following object types:
062    * <ul>
063    *    <li>{@link CharSequence}
064    *    <li>{@link File}
065    *    <li>{@link Reader}
066    *    <li>{@link InputStream}
067    *    <li><code><jk>byte</jk>[]</code>
068    * </ul>
069    * 
070    * @param o The object to read.
071    * @return The object serialized to a string, or <jk>null</jk> if it wasn't a supported type.
072    * @throws IOException
073    */
074   public static String read(Object o) throws IOException {
075      if (o instanceof CharSequence)
076         return o.toString();
077      if (o instanceof File)
078         return read((File)o);
079      if (o instanceof Reader)
080         return read((Reader)o);
081      if (o instanceof InputStream)
082         return read((InputStream)o);
083      if (o instanceof byte[])
084         return read(new ByteArrayInputStream((byte[])o));
085      return null;
086   }
087
088   /**
089    * Writes the contents of the specified <code>Reader</code> to the specified file.
090    * 
091    * @param out The file to write the output to.
092    * @param in The reader to pipe from.
093    * @return The number of characters written to the file.
094    * @throws IOException
095    */
096   public static int write(File out, Reader in) throws IOException {
097      assertFieldNotNull(out, "out");
098      assertFieldNotNull(in, "in");
099      try (Writer w = FileWriterBuilder.create(out).build()) {
100         return IOPipe.create(in, w).run();
101      }
102   }
103
104   /**
105    * Writes the contents of the specified <code>InputStream</code> to the specified file.
106    * 
107    * @param out The file to write the output to.
108    * @param in The input stream to pipe from.
109    * @return The number of characters written to the file.
110    * @throws IOException
111    */
112   public static int write(File out, InputStream in) throws IOException {
113      assertFieldNotNull(out, "out");
114      assertFieldNotNull(in, "in");
115      try (OutputStream os = new FileOutputStream(out)) {
116         return IOPipe.create(in, os).run();
117      }
118   }
119
120   /**
121    * Reads the contents of a reader into a string.
122    * 
123    * @param in The input reader.
124    * @return The contents of the reader as a string.
125    * @throws IOException If a problem occurred trying to read from the reader.
126    */
127   public static String read(Reader in) throws IOException {
128      return read(in, 0, 1024);
129   }
130
131   /**
132    * Reads the contents of an input stream into a string using the specified charset.
133    * 
134    * @param in The input stream.
135    * @param cs The charset of the contents of the input stream.
136    * @return The contents of the reader as a string.  <jk>null</jk> if input stream was null.
137    * @throws IOException If a problem occurred trying to read from the input stream.
138    */
139   public static String read(InputStream in, Charset cs) throws IOException {
140      if (in == null)
141         return null;
142      return read(new InputStreamReader(in, cs));
143   }
144
145   /**
146    * Reads the contents of an input stream into a string using the system default charset.
147    * 
148    * @param in The input stream.
149    * @return The contents of the reader as a string, or <jk>null</jk> if the input stream is null.
150    * @throws IOException If a problem occurred trying to read from the input stream.
151    */
152   public static String read(InputStream in) throws IOException {
153      if (in == null)
154         return null;
155      return read(new InputStreamReader(in, Charset.defaultCharset()));
156   }
157
158   /**
159    * Read the specified input stream into a byte array and closes the stream.
160    * 
161    * @param in The input stream.
162    * @param bufferSize The expected size of the buffer.
163    * @return The contents of the stream as a byte array.
164    * @throws IOException Thrown by underlying stream.
165    */
166   public static byte[] readBytes(InputStream in, int bufferSize) throws IOException {
167      if (in == null)
168         return null;
169      ByteArrayOutputStream buff = new ByteArrayOutputStream(bufferSize);
170      int nRead;
171      byte[] b = new byte[Math.min(bufferSize, 8192)];
172
173      try {
174         while ((nRead = in.read(b, 0, b.length)) != -1)
175            buff.write(b, 0, nRead);
176         buff.flush();
177
178         return buff.toByteArray();
179      } finally {
180         in.close();
181      }
182   }
183
184   /**
185    * Reads a raw stream of bytes from the specified file.
186    * 
187    * @param f The file to read.
188    * @return A byte array containing the contents of the file.
189    * @throws IOException
190    */
191   public static byte[] readBytes(File f) throws IOException {
192      if (f == null || ! (f.exists() && f.canRead()))
193         return null;
194
195      try (FileInputStream fis = new FileInputStream(f)) {
196         return readBytes(fis, (int)f.length());
197      }
198   }
199
200   /**
201    * Reads the specified input into a {@link String} until the end of the input is reached.
202    * 
203    * <p>
204    * The {@code Reader} is automatically closed.
205    * 
206    * <p>
207    * If the {@code Reader} is not an instance of a {@code BufferedReader}, then it gets wrapped in a
208    * {@code BufferedReader}.
209    * 
210    * @param in The input reader.
211    * @param length Specify a positive number if the length of the input is known.
212    * @param bufferSize Specify the buffer size to use.
213    * @return The contents of the reader as a string.  <jk>null</jk> if reader was null.
214    * @throws IOException If a problem occurred trying to read from the reader.
215    */
216   public static String read(Reader in, int length, int bufferSize) throws IOException {
217      if (in == null)
218         return null;
219      length = (length <= 0 ? bufferSize : length);
220      StringBuilder sb = new StringBuilder(length); // Assume they're ASCII characters.
221      try {
222         char[] buf = new char[Math.min(bufferSize, length)];
223         int i = 0;
224         while ((i = in.read(buf)) != -1)
225            sb.append(buf, 0, i);
226         return sb.toString();
227      } finally {
228         in.close();
229      }
230   }
231
232   /**
233    * Pipes the contents of the specified reader into the writer.
234    * 
235    * <p>
236    * The reader is closed, the writer is not.
237    * 
238    * @param in
239    *    The reader to pipe from.
240    * @param out
241    *    The writer to pipe to.
242    * @throws IOException
243    */
244   public static void pipe(Reader in, Writer out) throws IOException {
245      assertFieldNotNull(out, "out");
246      assertFieldNotNull(in, "in");
247      IOPipe.create(in, out).run();
248   }
249
250   /**
251    * Pipes the contents of the specified object into the writer.
252    * 
253    * <p>
254    * The reader is closed, the writer is not.
255    * 
256    * @param in
257    *    The input to pipe from.
258    *    Can be any of the types defined by {@link #toReader(Object)}.
259    * @param out
260    *    The writer to pipe to.
261    * @throws IOException
262    */
263   public static void pipe(Object in, Writer out) throws IOException {
264      pipe(toReader(in), out);
265   }
266
267   /**
268    * Pipes the contents of the specified streams.
269    * 
270    * <p>
271    * The input stream is closed, the output stream is not.
272    * 
273    * @param in
274    *    The reader to pipe from.
275    * @param out
276    *    The writer to pipe to.
277    * @throws IOException
278    */
279   public static void pipe(InputStream in, OutputStream out) throws IOException {
280      assertFieldNotNull(out, "out");
281      assertFieldNotNull(in, "in");
282      IOPipe.create(in, out).run();
283   }
284
285   /**
286    * Pipes the contents of the specified object into the output stream.
287    * 
288    * <p>
289    * The input stream is closed, the output stream is not.
290    * 
291    * @param in
292    *    The input to pipe from.
293    *    Can be any of the types defined by {@link #toInputStream(Object)}.
294    * @param out
295    *    The writer to pipe to.
296    * @throws IOException
297    */
298   public static void pipe(Object in, OutputStream out) throws IOException {
299      pipe(toInputStream(in), out);
300   }
301
302   /**
303    * Wraps the specified reader in a buffered reader.
304    * 
305    * @param r The reader being wrapped.
306    * @return
307    *    The reader wrapped in a {@link BufferedReader}, or the original {@link Reader} if it's already a buffered
308    *    reader.
309    */
310   public static Reader getBufferedReader(Reader r) {
311      if (r == null || r instanceof BufferedReader || r instanceof StringReader)
312         return r;
313      return new BufferedReader(r);
314   }
315
316   /**
317    * Counts the number of bytes in the input stream and then closes the stream.
318    * 
319    * @param is The input stream to read from.
320    * @return The number of bytes read.
321    * @throws IOException
322    */
323   public static long count(InputStream is) throws IOException {
324      assertFieldNotNull(is, "is");
325      long c = 0;
326      long i;
327      try {
328         while ((i = is.skip(1024)) != 0)
329            c += i;
330      } finally {
331         is.close();
332      }
333      return c;
334   }
335
336   /**
337    * Counts the number of characters in the reader and then closes the reader.
338    * 
339    * @param r The reader to read from.
340    * @return The number of characters read.
341    * @throws IOException
342    */
343   public static long count(Reader r) throws IOException {
344      assertFieldNotNull(r, "r");
345      long c = 0;
346      long i;
347      try {
348         while ((i = r.skip(1024)) != 0)
349            c += i;
350      } finally {
351         r.close();
352      }
353      return c;
354   }
355
356   /**
357    * Given the specified <js>"Content-Length"</js> header value, return an appropriate buffer size.
358    * 
359    * <p>
360    * The maximum buffer size is 1MB.
361    * 
362    * @param contentLength The value of the <js>"Content-Length"</js> header.
363    * @return The appropriate buffer size.
364    */
365   public static int getBufferSize(String contentLength) {
366      try {
367         if (! StringUtils.isEmpty(contentLength)) {
368            long l = Long.decode(contentLength);
369            if (l > 1048576)
370               return 1048576;
371            if (l <= 0)
372               return 8192;
373            return (int)l;
374         }
375      } catch (Exception e) {
376         return 8192;
377      }
378      return 8192;
379   }
380
381   /**
382    * Close input stream and ignore any exceptions.
383    * 
384    * <p>
385    * No-op if input stream is <jk>null</jk>.
386    * 
387    * @param is The input stream to close.
388    */
389   public static void closeQuietly(InputStream is) {
390      try {
391         if (is != null)
392            is.close();
393      } catch (IOException e) {}
394   }
395
396   /**
397    * Close output stream and ignore any exceptions.
398    * 
399    * <p>
400    * No-op if output stream is <jk>null</jk>.
401    * 
402    * @param os The output stream to close.
403    */
404   public static void closeQuietly(OutputStream os) {
405      try {
406         if (os != null)
407            os.close();
408      } catch (IOException e) {}
409   }
410
411   /**
412    * Close reader and ignore any exceptions.
413    * 
414    * <p>
415    * No-op if reader is <jk>null</jk>.
416    * 
417    * @param r The reader to close.
418    */
419   public static void closeQuietly(Reader r) {
420      try {
421         if (r != null)
422            r.close();
423      } catch (IOException e) {}
424   }
425
426   /**
427    * Close writer and ignore any exceptions.
428    * 
429    * <p>
430    * No-op if writer is <jk>null</jk>.
431    * 
432    * @param w The writer to close.
433    */
434   public static void closeQuietly(Writer w) {
435      try {
436         if (w != null)
437            w.close();
438      } catch (IOException e) {}
439   }
440
441   /**
442    * Quietly close all specified input streams, output streams, readers, and writers.
443    * 
444    * @param o The list of all objects to quietly close.
445    */
446   public static void closeQuietly(Object...o) {
447      for (Object o2 : o) {
448         if (o2 instanceof InputStream)
449            closeQuietly((InputStream)o2);
450         if (o2 instanceof OutputStream)
451            closeQuietly((OutputStream)o2);
452         if (o2 instanceof Reader)
453            closeQuietly((Reader)o2);
454         if (o2 instanceof Writer)
455            closeQuietly((Writer)o2);
456      }
457   }
458
459   /**
460    * Flushes multiple output streams and writers in a single call.
461    * 
462    * @param o
463    *    The objects to flush.
464    *    <jk>null</jk> entries are ignored.
465    * @throws IOException
466    */
467   public static void flush(Object...o) throws IOException {
468      IOException ex = null;
469      for (Object o2 : o) {
470         try {
471            if (o2 instanceof OutputStream)
472               ((OutputStream)o2).flush();
473            if (o2 instanceof Writer)
474               ((Writer)o2).flush();
475         } catch (IOException e) {
476            ex = e;
477         }
478      }
479      if (ex != null)
480         throw ex;
481   }
482
483   /**
484    * Close all specified input streams, output streams, readers, and writers.
485    * 
486    * @param o
487    *    The list of all objects to close.
488    *    <jk>null</jk> entries are ignored.
489    * @throws IOException
490    */
491   public static void close(Object...o) throws IOException {
492      IOException ex = null;
493      for (Object o2 : o) {
494         try {
495            if (o2 instanceof InputStream)
496               ((InputStream)o2).close();
497            if (o2 instanceof OutputStream)
498               ((OutputStream)o2).close();
499            if (o2 instanceof Reader)
500               ((Reader)o2).close();
501            if (o2 instanceof Writer)
502               ((Writer)o2).close();
503         } catch (IOException e) {
504            ex = e;
505         }
506      }
507      if (ex != null)
508         throw ex;
509   }
510
511   /**
512    * Converts an object to a <code>Reader</code>.
513    * 
514    * @param o
515    *    The object to convert to a reader.
516    *    Can be any of the following:
517    *    <ul>
518    *       <li>{@link InputStream}
519    *       <li>{@link Reader}
520    *       <li>{@link File}
521    *       <li>{@link CharSequence}
522    *       <li><code><jk>byte</jk>[]</code>
523    *       <li><code><jk>null</jk></code> - Returns <jk>null</jk>.
524    *    </ul>
525    * @return The object converted to a reader.
526    * @throws IOException If file could not be read.
527    * @throws IllegalArgumentException If invalid object passed in.
528    */
529   public static Reader toReader(Object o) throws IOException {
530      if (o == null)
531         return null;
532      if (o instanceof CharSequence)
533         return new StringReader(o.toString());
534      if (o instanceof File)
535         return new FileReader((File)o);
536      if (o instanceof Reader)
537         return (Reader)o;
538      if (o instanceof InputStream)
539         return new InputStreamReader((InputStream)o, "UTF-8");
540      if (o instanceof byte[])
541         return new InputStreamReader(new ByteArrayInputStream((byte[])o), "UTF-8");
542      throw new FormattedIllegalArgumentException("Invalid object of type {0} passed to IOUtils.toReader(Object)", o.getClass());
543   }
544
545   /**
546    * Converts an object to an <code>InputStream</code>.
547    * 
548    * @param o
549    *    The object to convert to an input stream.
550    *    Can be any of the following:
551    *    <ul>
552    *       <li>{@link InputStream}
553    *       <li>{@link Reader}
554    *       <li>{@link File}
555    *       <li>{@link CharSequence} - Converted to UTF-8 stream.
556    *       <li><code><jk>byte</jk>[]</code>
557    *       <li><code><jk>null</jk></code> - Returns <jk>null</jk>.
558    *    </ul>
559    * @return The object converted to an input stream.
560    * @throws IOException If file could not be read.
561    * @throws IllegalArgumentException If invalid object passed in.
562    */
563   public static InputStream toInputStream(Object o) throws IOException {
564      if (o == null)
565         return null;
566      if (o instanceof InputStream)
567         return (InputStream)o;
568      if (o instanceof File)
569         return new FileInputStream((File)o);
570      if (o instanceof byte[])
571         return new ByteArrayInputStream((byte[])o);
572      if (o instanceof CharSequence)
573         return new ByteArrayInputStream(((CharSequence)o).toString().getBytes(UTF8));
574      if (o instanceof Reader)
575         return new ByteArrayInputStream(IOUtils.read((Reader)o).getBytes(UTF8));
576      throw new FormattedIllegalArgumentException("Invalid object of type {0} passed to IOUtils.toInputStream(Object)", o.getClass());
577   }
578}