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.zip.GZIPOutputStream; 37 import java.util.zip.ZipOutputStream; 38 39 import javax.xml.bind.DatatypeConverter; 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 * Copies contents of origStream to destStream. 144 * <p/> 145 * Recommended to provide a buffered stream for input and output 146 * 147 * @param inStream the {@link InputStream} 148 * @param outStream the {@link OutputStream} 149 * @throws IOException 150 */ 151 public static void copyStreams(InputStream inStream, OutputStream outStream) 152 throws IOException { 153 byte[] buf = new byte[BUF_SIZE]; 154 int size = -1; 155 while ((size = inStream.read(buf)) != -1) { 156 outStream.write(buf, 0, size); 157 } 158 } 159 160 /** 161 * Copies contents of inStream to writer. 162 * <p/> 163 * Recommended to provide a buffered stream for input and output 164 * 165 * @param inStream the {@link InputStream} 166 * @param writer the {@link Writer} destination 167 * @throws IOException 168 */ 169 public static void copyStreamToWriter(InputStream inStream, Writer writer) throws IOException { 170 byte[] buf = new byte[BUF_SIZE]; 171 int size = -1; 172 while ((size = inStream.read(buf)) != -1) { 173 writer.write(new String(buf, 0, size)); 174 } 175 } 176 177 /** 178 * Gets the stack trace as a {@link String}. 179 * 180 * @param throwable the {@link Throwable} to convert. 181 * @return a {@link String} stack trace 182 */ 183 public static String getStackTrace(Throwable throwable) { 184 // dump the print stream results to the ByteArrayOutputStream, so contents can be evaluated 185 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 186 PrintStream bytePrintStream = new PrintStream(outputStream); 187 throwable.printStackTrace(bytePrintStream); 188 return outputStream.toString(); 189 } 190 191 /** 192 * @deprecated use {@link #close(Closeable)} instead. 193 */ 194 @Deprecated 195 public static void closeStream(OutputStream out) { 196 close(out); 197 } 198 199 /** 200 * @deprecated use {@link #close(Closeable)} instead. 201 */ 202 @Deprecated 203 public static void closeStream(InputStream in) { 204 close(in); 205 } 206 207 /** 208 * Attempts to flush the given output stream, and then closes it. 209 * 210 * @param outStream the {@link OutputStream}. No action taken if outStream is null. 211 */ 212 public static void flushAndCloseStream(OutputStream outStream) { 213 if (outStream != null) { 214 try { 215 outStream.flush(); 216 } catch (IOException e) { 217 // ignore 218 } 219 try { 220 outStream.close(); 221 } catch (IOException e) { 222 // ignore 223 } 224 } 225 } 226 227 /** 228 * Closes given zip output stream. 229 * 230 * @param outStream the {@link ZipOutputStream}. No action taken if outStream is null. 231 */ 232 public static void closeZipStream(ZipOutputStream outStream) { 233 if (outStream != null) { 234 try { 235 outStream.closeEntry(); 236 outStream.close(); 237 } catch (IOException e) { 238 // ignore 239 } 240 } 241 } 242 243 /** 244 * Closes given gzip output stream. 245 * 246 * @param outStream the {@link ZipOutputStream}. No action taken if outStream is null. 247 */ 248 public static void closeGZipStream(GZIPOutputStream outStream) { 249 if (outStream != null) { 250 try { 251 outStream.finish(); 252 outStream.close(); 253 } catch (IOException e) { 254 // ignore 255 } 256 } 257 } 258 259 /** 260 * Closes the given {@link Closeable}. 261 * 262 * @param closeable the {@link Closeable}. No action taken if <code>null</code>. 263 */ 264 public static void close(Closeable closeable) { 265 if (closeable != null) { 266 try { 267 closeable.close(); 268 } catch (IOException e) { 269 // ignore 270 } 271 } 272 } 273 274 /** 275 * Cancels the given {@link InputStreamSource} if non-null. 276 */ 277 public static void cancel(InputStreamSource outputSource) { 278 if (outputSource != null) { 279 outputSource.close(); 280 } 281 } 282 283 /** 284 * Create a {@link OutputStream} that discards all writes. 285 */ 286 public static OutputStream nullOutputStream() { 287 return ByteStreams.nullOutputStream(); 288 } 289 290 /** 291 * Helper method to calculate md5 for a inputStream. The inputStream will be consumed and 292 * closed. 293 * 294 * @param inputSource used to create inputStream 295 * @return md5 of the stream 296 * @throws IOException 297 */ 298 public static String calculateMd5(InputStream inputSource) throws IOException { 299 MessageDigest md = null; 300 try { 301 md = MessageDigest.getInstance("md5"); 302 } catch (NoSuchAlgorithmException e) { 303 // This should not happen 304 throw new RuntimeException(e); 305 } 306 InputStream input = new BufferedInputStream(new DigestInputStream(inputSource, md)); 307 byte[] buf = new byte[BUF_SIZE]; 308 while (input.read(buf) != -1) { 309 // Read through the stream to update digest. 310 } 311 input.close(); 312 String md5 = DatatypeConverter.printHexBinary(md.digest()).toLowerCase(); 313 return md5; 314 } 315 } 316