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