• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.tradefed.util;
17 
18 import com.android.tradefed.result.InputStreamSource;
19 
20 import com.google.common.io.ByteStreams;
21 
22 import java.io.BufferedInputStream;
23 import java.io.BufferedReader;
24 import java.io.ByteArrayOutputStream;
25 import java.io.Closeable;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.InputStreamReader;
29 import java.io.OutputStream;
30 import java.io.PrintStream;
31 import java.io.Reader;
32 import java.io.Writer;
33 import java.security.DigestInputStream;
34 import java.security.MessageDigest;
35 import java.security.NoSuchAlgorithmException;
36 import java.util.Base64;
37 import java.util.Objects;
38 import java.util.zip.GZIPOutputStream;
39 import java.util.zip.ZipOutputStream;
40 
41 /**
42  * Utility class for managing input streams.
43  */
44 public class StreamUtil {
45 
46     // 16K buffer size
47     private static final int BUF_SIZE = 16 * 1024;
48 
StreamUtil()49     private StreamUtil() {
50     }
51 
52     /**
53      * Retrieves a {@link String} from an {@link InputStreamSource}.
54      *
55      * @param source the {@link InputStreamSource}
56      * @return a {@link String} containing the stream contents
57      * @throws IOException if failure occurred reading the stream
58      */
getStringFromSource(InputStreamSource source)59     public static String getStringFromSource(InputStreamSource source) throws IOException {
60         final InputStream stream = source.createInputStream();
61         final String contents;
62         try {
63             contents = getStringFromStream(stream);
64         } finally {
65             close(stream);
66         }
67         return contents;
68     }
69 
70     /**
71      * Count number of lines in an {@link InputStreamSource}
72      * @param source the {@link InputStreamSource}
73      * @return number of lines
74      * @throws IOException if failure occurred reading the stream
75      */
countLinesFromSource(InputStreamSource source)76     public static int countLinesFromSource(InputStreamSource source) throws IOException {
77         int lineCount = 0;
78         try (BufferedReader br =
79                 new BufferedReader(new InputStreamReader(source.createInputStream()))) {
80             while (br.readLine() != null) {
81                 lineCount++;
82             }
83         }
84         return lineCount;
85     }
86 
87     /**
88      * Retrieves a {@link ByteArrayList} from an {@link InputStreamSource}.
89      *
90      * @param source the {@link InputStreamSource}
91      * @return a {@link ByteArrayList} containing the stream contents
92      * @throws IOException if failure occurred reading the stream
93      */
getByteArrayListFromSource(InputStreamSource source)94     public static ByteArrayList getByteArrayListFromSource(InputStreamSource source)
95             throws IOException {
96         final InputStream stream = source.createInputStream();
97         final ByteArrayList contents;
98         try {
99             contents = getByteArrayListFromStream(stream);
100         } finally {
101             close(stream);
102         }
103         return contents;
104     }
105 
106     /**
107      * Retrieves a {@link String} from a character stream.
108      *
109      * @param stream the {@link InputStream}
110      * @return a {@link String} containing the stream contents
111      * @throws IOException if failure occurred reading the stream
112      */
getStringFromStream(InputStream stream)113     public static String getStringFromStream(InputStream stream) throws IOException {
114         int irChar = -1;
115         StringBuilder builder = new StringBuilder();
116         try (Reader ir = new BufferedReader(new InputStreamReader(stream))) {
117             while ((irChar = ir.read()) != -1) {
118                 builder.append((char) irChar);
119             }
120         }
121         return builder.toString();
122     }
123 
124     /**
125      * Retrieves a {@link ByteArrayList} from a byte stream.
126      *
127      * @param stream the {@link InputStream}
128      * @return a {@link ByteArrayList} containing the stream contents
129      * @throws IOException if failure occurred reading the stream
130      */
getByteArrayListFromStream(InputStream stream)131     public static ByteArrayList getByteArrayListFromStream(InputStream stream) throws IOException {
132         InputStream is = new BufferedInputStream(stream);
133         int inputByte = -1;
134         ByteArrayList list = new ByteArrayList();
135         while ((inputByte = is.read()) != -1) {
136             list.add((byte)inputByte);
137         }
138         list.trimToSize();
139         return list;
140     }
141 
142     /**
143      * Return a BuffferedReader to read the contents from the given InputstreamSource.
144      *
145      * @param stream the {@link InputStreamSource}
146      * @return a BuffferedReader
147      */
getBufferedReaderFromStreamSrc(InputStreamSource stream)148     public static BufferedReader getBufferedReaderFromStreamSrc(InputStreamSource stream) {
149         return new BufferedReader(new InputStreamReader(stream.createInputStream()));
150     }
151 
152     /**
153      * Copies contents of origStream to destStream.
154      * <p/>
155      * Recommended to provide a buffered stream for input and output
156      *
157      * @param inStream the {@link InputStream}
158      * @param outStream the {@link OutputStream}
159      * @throws IOException
160      */
copyStreams(InputStream inStream, OutputStream outStream)161     public static void copyStreams(InputStream inStream, OutputStream outStream)
162             throws IOException {
163         copyStreams(inStream, outStream, 0);
164     }
165 
166     /**
167      * Copies contents of origStream to destStream.
168      *
169      * <p>Recommended to provide a buffered stream for input and output
170      *
171      * @param inStream the {@link InputStream}
172      * @param outStream the {@link OutputStream}
173      * @param offset The offset of when to start copying the data.
174      * @throws IOException
175      */
copyStreams(InputStream inStream, OutputStream outStream, int offset)176     public static void copyStreams(InputStream inStream, OutputStream outStream, int offset)
177             throws IOException {
178         inStream.skip(offset);
179         byte[] buf = new byte[BUF_SIZE];
180         int size = -1;
181         while ((size = inStream.read(buf)) != -1) {
182             outStream.write(buf, 0, size);
183         }
184     }
185 
186     /**
187      * Copies contents of inStream to writer.
188      * <p/>
189      * Recommended to provide a buffered stream for input and output
190      *
191      * @param inStream the {@link InputStream}
192      * @param writer the {@link Writer} destination
193      * @throws IOException
194      */
copyStreamToWriter(InputStream inStream, Writer writer)195     public static void copyStreamToWriter(InputStream inStream, Writer writer) throws IOException {
196         byte[] buf = new byte[BUF_SIZE];
197         int size = -1;
198         while ((size = inStream.read(buf)) != -1) {
199             writer.write(new String(buf, 0, size));
200         }
201     }
202 
203     /**
204      * Gets the stack trace as a {@link String}.
205      *
206      * @param throwable the {@link Throwable} to convert.
207      * @return a {@link String} stack trace
208      */
getStackTrace(Throwable throwable)209     public static String getStackTrace(Throwable throwable) {
210         // dump the print stream results to the ByteArrayOutputStream, so contents can be evaluated
211         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
212         PrintStream bytePrintStream = new PrintStream(outputStream);
213         throwable.printStackTrace(bytePrintStream);
214         return outputStream.toString();
215     }
216 
217     /**
218      * @deprecated use {@link #close(Closeable)} instead.
219      */
220     @Deprecated
closeStream(OutputStream out)221     public static void closeStream(OutputStream out) {
222         close(out);
223     }
224 
225     /**
226      * @deprecated use {@link #close(Closeable)} instead.
227      */
228     @Deprecated
closeStream(InputStream in)229     public static void closeStream(InputStream in) {
230         close(in);
231     }
232 
233     /**
234      * Attempts to flush the given output stream, and then closes it.
235      *
236      * @param outStream the {@link OutputStream}. No action taken if outStream is null.
237      */
flushAndCloseStream(OutputStream outStream)238     public static void flushAndCloseStream(OutputStream outStream) {
239         if (outStream != null) {
240             try {
241                 outStream.flush();
242             } catch (IOException e) {
243                 // ignore
244             }
245             try {
246                 outStream.close();
247             } catch (IOException e) {
248                 // ignore
249             }
250         }
251     }
252 
253     /**
254      * Closes given zip output stream.
255      *
256      * @param outStream the {@link ZipOutputStream}. No action taken if outStream is null.
257      */
closeZipStream(ZipOutputStream outStream)258     public static void closeZipStream(ZipOutputStream outStream) {
259         if (outStream != null) {
260             try {
261                 outStream.closeEntry();
262                 outStream.close();
263             } catch (IOException e) {
264                 // ignore
265             }
266         }
267     }
268 
269     /**
270      * Closes given gzip output stream.
271      *
272      * @param outStream the {@link ZipOutputStream}. No action taken if outStream is null.
273      */
closeGZipStream(GZIPOutputStream outStream)274     public static void closeGZipStream(GZIPOutputStream outStream) {
275         if (outStream != null) {
276             try {
277                 outStream.finish();
278                 outStream.close();
279             } catch (IOException e) {
280                 // ignore
281             }
282         }
283     }
284 
285     /**
286      * Closes the given {@link Closeable}.
287      *
288      * @param closeable the {@link Closeable}. No action taken if <code>null</code>.
289      */
close(Closeable closeable)290     public static void close(Closeable closeable) {
291         if (closeable != null) {
292             try {
293                 closeable.close();
294             } catch (IOException e) {
295                 // ignore
296             }
297         }
298     }
299 
300     /**
301      * Cancels the given {@link InputStreamSource} if non-null.
302      */
cancel(InputStreamSource outputSource)303     public static void cancel(InputStreamSource outputSource) {
304         if (outputSource != null) {
305             outputSource.close();
306         }
307     }
308 
309     /**
310      * Create a {@link OutputStream} that discards all writes.
311      */
nullOutputStream()312     public static OutputStream nullOutputStream() {
313         return ByteStreams.nullOutputStream();
314     }
315 
316     /**
317      * Helper method to calculate md5 for a inputStream. The inputStream will be consumed and
318      * closed.
319      *
320      * @param inputSource used to create inputStream
321      * @return md5 of the stream
322      * @throws IOException
323      */
calculateMd5(InputStream inputSource)324     public static String calculateMd5(InputStream inputSource) throws IOException {
325         return bytesToHexString(calculateMd5Digest(inputSource));
326     }
327 
328     /**
329      * Helper method to calculate base64 md5 for a inputStream. The inputStream will be consumed and
330      * closed.
331      *
332      * @param inputSource used to create inputStream
333      * @return base64 md5 of the stream
334      * @throws IOException
335      */
calculateBase64Md5(InputStream inputSource)336     public static String calculateBase64Md5(InputStream inputSource) throws IOException {
337         return Base64.getEncoder().encodeToString(calculateMd5Digest(inputSource));
338     }
339 
calculateMd5Digest(InputStream inputSource)340     private static byte[] calculateMd5Digest(InputStream inputSource) throws IOException {
341         MessageDigest md = null;
342         try {
343             md = MessageDigest.getInstance("md5");
344         } catch (NoSuchAlgorithmException e) {
345             // This should not happen
346             throw new RuntimeException(e);
347         }
348         InputStream input = new BufferedInputStream(new DigestInputStream(inputSource, md));
349         byte[] buf = new byte[BUF_SIZE];
350         while (input.read(buf) != -1) {
351             // Read through the stream to update digest.
352         }
353         input.close();
354         return md.digest();
355     }
356 
357     private static final char[] HEX_CHARS = {
358         '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
359     };
360 
361     /**
362      * Converts a byte array into a String of hexadecimal characters.
363      *
364      * @param bytes an array of bytes
365      * @return hex string representation of bytes array
366      */
bytesToHexString(byte[] bytes)367     private static String bytesToHexString(byte[] bytes) {
368         Objects.requireNonNull(bytes);
369         StringBuilder sb = new StringBuilder(2 * bytes.length);
370         for (int i = 0; i < bytes.length; i++) {
371             int b = 0x0f & (bytes[i] >> 4);
372             sb.append(HEX_CHARS[b]);
373             b = 0x0f & bytes[i];
374             sb.append(HEX_CHARS[b]);
375         }
376         return sb.toString();
377     }
378 }
379