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.Objects; 37 import java.util.zip.GZIPOutputStream; 38 import java.util.zip.ZipOutputStream; 39 40 /** 41 * Utility class for managing input streams. 42 */ 43 public class StreamUtil { 44 45 // 16K buffer size 46 private static final int BUF_SIZE = 16 * 1024; 47 StreamUtil()48 private StreamUtil() { 49 } 50 51 /** 52 * Retrieves a {@link String} from an {@link InputStreamSource}. 53 * 54 * @param source the {@link InputStreamSource} 55 * @return a {@link String} containing the stream contents 56 * @throws IOException if failure occurred reading the stream 57 */ getStringFromSource(InputStreamSource source)58 public static String getStringFromSource(InputStreamSource source) throws IOException { 59 final InputStream stream = source.createInputStream(); 60 final String contents; 61 try { 62 contents = getStringFromStream(stream); 63 } finally { 64 close(stream); 65 } 66 return contents; 67 } 68 69 /** 70 * Count number of lines in an {@link InputStreamSource} 71 * @param source the {@link InputStreamSource} 72 * @return number of lines 73 * @throws IOException if failure occurred reading the stream 74 */ countLinesFromSource(InputStreamSource source)75 public static int countLinesFromSource(InputStreamSource source) throws IOException { 76 int lineCount = 0; 77 try (BufferedReader br = 78 new BufferedReader(new InputStreamReader(source.createInputStream()))) { 79 while (br.readLine() != null) { 80 lineCount++; 81 } 82 } 83 return lineCount; 84 } 85 86 /** 87 * Retrieves a {@link ByteArrayList} from an {@link InputStreamSource}. 88 * 89 * @param source the {@link InputStreamSource} 90 * @return a {@link ByteArrayList} containing the stream contents 91 * @throws IOException if failure occurred reading the stream 92 */ getByteArrayListFromSource(InputStreamSource source)93 public static ByteArrayList getByteArrayListFromSource(InputStreamSource source) 94 throws IOException { 95 final InputStream stream = source.createInputStream(); 96 final ByteArrayList contents; 97 try { 98 contents = getByteArrayListFromStream(stream); 99 } finally { 100 close(stream); 101 } 102 return contents; 103 } 104 105 /** 106 * Retrieves a {@link String} from a character stream. 107 * 108 * @param stream the {@link InputStream} 109 * @return a {@link String} containing the stream contents 110 * @throws IOException if failure occurred reading the stream 111 */ getStringFromStream(InputStream stream)112 public static String getStringFromStream(InputStream stream) throws IOException { 113 int irChar = -1; 114 StringBuilder builder = new StringBuilder(); 115 try (Reader ir = new BufferedReader(new InputStreamReader(stream))) { 116 while ((irChar = ir.read()) != -1) { 117 builder.append((char) irChar); 118 } 119 } 120 return builder.toString(); 121 } 122 123 /** 124 * Retrieves a {@link ByteArrayList} from a byte stream. 125 * 126 * @param stream the {@link InputStream} 127 * @return a {@link ByteArrayList} containing the stream contents 128 * @throws IOException if failure occurred reading the stream 129 */ getByteArrayListFromStream(InputStream stream)130 public static ByteArrayList getByteArrayListFromStream(InputStream stream) throws IOException { 131 InputStream is = new BufferedInputStream(stream); 132 int inputByte = -1; 133 ByteArrayList list = new ByteArrayList(); 134 while ((inputByte = is.read()) != -1) { 135 list.add((byte)inputByte); 136 } 137 list.trimToSize(); 138 return list; 139 } 140 141 /** 142 * Return a BuffferedReader to read the contents from the given InputstreamSource. 143 * 144 * @param stream the {@link InputStreamSource} 145 * @return a BuffferedReader 146 */ getBufferedReaderFromStreamSrc(InputStreamSource stream)147 public static BufferedReader getBufferedReaderFromStreamSrc(InputStreamSource stream) { 148 return new BufferedReader(new InputStreamReader(stream.createInputStream())); 149 } 150 151 /** 152 * Copies contents of origStream to destStream. 153 * <p/> 154 * Recommended to provide a buffered stream for input and output 155 * 156 * @param inStream the {@link InputStream} 157 * @param outStream the {@link OutputStream} 158 * @throws IOException 159 */ copyStreams(InputStream inStream, OutputStream outStream)160 public static void copyStreams(InputStream inStream, OutputStream outStream) 161 throws IOException { 162 byte[] buf = new byte[BUF_SIZE]; 163 int size = -1; 164 while ((size = inStream.read(buf)) != -1) { 165 outStream.write(buf, 0, size); 166 } 167 } 168 169 /** 170 * Copies contents of inStream to writer. 171 * <p/> 172 * Recommended to provide a buffered stream for input and output 173 * 174 * @param inStream the {@link InputStream} 175 * @param writer the {@link Writer} destination 176 * @throws IOException 177 */ copyStreamToWriter(InputStream inStream, Writer writer)178 public static void copyStreamToWriter(InputStream inStream, Writer writer) throws IOException { 179 byte[] buf = new byte[BUF_SIZE]; 180 int size = -1; 181 while ((size = inStream.read(buf)) != -1) { 182 writer.write(new String(buf, 0, size)); 183 } 184 } 185 186 /** 187 * Gets the stack trace as a {@link String}. 188 * 189 * @param throwable the {@link Throwable} to convert. 190 * @return a {@link String} stack trace 191 */ getStackTrace(Throwable throwable)192 public static String getStackTrace(Throwable throwable) { 193 // dump the print stream results to the ByteArrayOutputStream, so contents can be evaluated 194 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 195 PrintStream bytePrintStream = new PrintStream(outputStream); 196 throwable.printStackTrace(bytePrintStream); 197 return outputStream.toString(); 198 } 199 200 /** 201 * @deprecated use {@link #close(Closeable)} instead. 202 */ 203 @Deprecated closeStream(OutputStream out)204 public static void closeStream(OutputStream out) { 205 close(out); 206 } 207 208 /** 209 * @deprecated use {@link #close(Closeable)} instead. 210 */ 211 @Deprecated closeStream(InputStream in)212 public static void closeStream(InputStream in) { 213 close(in); 214 } 215 216 /** 217 * Attempts to flush the given output stream, and then closes it. 218 * 219 * @param outStream the {@link OutputStream}. No action taken if outStream is null. 220 */ flushAndCloseStream(OutputStream outStream)221 public static void flushAndCloseStream(OutputStream outStream) { 222 if (outStream != null) { 223 try { 224 outStream.flush(); 225 } catch (IOException e) { 226 // ignore 227 } 228 try { 229 outStream.close(); 230 } catch (IOException e) { 231 // ignore 232 } 233 } 234 } 235 236 /** 237 * Closes given zip output stream. 238 * 239 * @param outStream the {@link ZipOutputStream}. No action taken if outStream is null. 240 */ closeZipStream(ZipOutputStream outStream)241 public static void closeZipStream(ZipOutputStream outStream) { 242 if (outStream != null) { 243 try { 244 outStream.closeEntry(); 245 outStream.close(); 246 } catch (IOException e) { 247 // ignore 248 } 249 } 250 } 251 252 /** 253 * Closes given gzip output stream. 254 * 255 * @param outStream the {@link ZipOutputStream}. No action taken if outStream is null. 256 */ closeGZipStream(GZIPOutputStream outStream)257 public static void closeGZipStream(GZIPOutputStream outStream) { 258 if (outStream != null) { 259 try { 260 outStream.finish(); 261 outStream.close(); 262 } catch (IOException e) { 263 // ignore 264 } 265 } 266 } 267 268 /** 269 * Closes the given {@link Closeable}. 270 * 271 * @param closeable the {@link Closeable}. No action taken if <code>null</code>. 272 */ close(Closeable closeable)273 public static void close(Closeable closeable) { 274 if (closeable != null) { 275 try { 276 closeable.close(); 277 } catch (IOException e) { 278 // ignore 279 } 280 } 281 } 282 283 /** 284 * Cancels the given {@link InputStreamSource} if non-null. 285 */ cancel(InputStreamSource outputSource)286 public static void cancel(InputStreamSource outputSource) { 287 if (outputSource != null) { 288 outputSource.close(); 289 } 290 } 291 292 /** 293 * Create a {@link OutputStream} that discards all writes. 294 */ nullOutputStream()295 public static OutputStream nullOutputStream() { 296 return ByteStreams.nullOutputStream(); 297 } 298 299 /** 300 * Helper method to calculate md5 for a inputStream. The inputStream will be consumed and 301 * closed. 302 * 303 * @param inputSource used to create inputStream 304 * @return md5 of the stream 305 * @throws IOException 306 */ calculateMd5(InputStream inputSource)307 public static String calculateMd5(InputStream inputSource) throws IOException { 308 MessageDigest md = null; 309 try { 310 md = MessageDigest.getInstance("md5"); 311 } catch (NoSuchAlgorithmException e) { 312 // This should not happen 313 throw new RuntimeException(e); 314 } 315 InputStream input = new BufferedInputStream(new DigestInputStream(inputSource, md)); 316 byte[] buf = new byte[BUF_SIZE]; 317 while (input.read(buf) != -1) { 318 // Read through the stream to update digest. 319 } 320 input.close(); 321 String md5 = bytesToHexString(md.digest()); 322 return md5; 323 } 324 325 private static final char[] HEX_CHARS = { 326 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' 327 }; 328 329 /** 330 * Converts a byte array into a String of hexadecimal characters. 331 * 332 * @param bytes an array of bytes 333 * @return hex string representation of bytes array 334 */ bytesToHexString(byte[] bytes)335 private static String bytesToHexString(byte[] bytes) { 336 Objects.requireNonNull(bytes); 337 StringBuilder sb = new StringBuilder(2 * bytes.length); 338 for (int i = 0; i < bytes.length; i++) { 339 int b = 0x0f & (bytes[i] >> 4); 340 sb.append(HEX_CHARS[b]); 341 b = 0x0f & bytes[i]; 342 sb.append(HEX_CHARS[b]); 343 } 344 return sb.toString(); 345 } 346 } 347