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 BufferedReader br = null; 78 int lineCount = 0; 79 try { 80 br = new BufferedReader(new InputStreamReader(source.createInputStream())); 81 while (br.readLine() != null) { 82 lineCount++; 83 } 84 } finally { 85 close(br); 86 } 87 return lineCount; 88 } 89 90 /** 91 * Retrieves a {@link ByteArrayList} from an {@link InputStreamSource}. 92 * 93 * @param source the {@link InputStreamSource} 94 * @return a {@link ByteArrayList} containing the stream contents 95 * @throws IOException if failure occurred reading the stream 96 */ 97 public static ByteArrayList getByteArrayListFromSource(InputStreamSource source) 98 throws IOException { 99 final InputStream stream = source.createInputStream(); 100 final ByteArrayList contents; 101 try { 102 contents = getByteArrayListFromStream(stream); 103 } finally { 104 close(stream); 105 } 106 return contents; 107 } 108 109 /** 110 * Retrieves a {@link String} from a character stream. 111 * 112 * @param stream the {@link InputStream} 113 * @return a {@link String} containing the stream contents 114 * @throws IOException if failure occurred reading the stream 115 */ 116 public static String getStringFromStream(InputStream stream) throws IOException { 117 Reader ir = new BufferedReader(new InputStreamReader(stream)); 118 int irChar = -1; 119 StringBuilder builder = new StringBuilder(); 120 while ((irChar = ir.read()) != -1) { 121 builder.append((char)irChar); 122 } 123 StreamUtil.close(ir); 124 return builder.toString(); 125 } 126 127 /** 128 * Retrieves a {@link ByteArrayList} from a byte stream. 129 * 130 * @param stream the {@link InputStream} 131 * @return a {@link ByteArrayList} containing the stream contents 132 * @throws IOException if failure occurred reading the stream 133 */ 134 public static ByteArrayList getByteArrayListFromStream(InputStream stream) throws IOException { 135 InputStream is = new BufferedInputStream(stream); 136 int inputByte = -1; 137 ByteArrayList list = new ByteArrayList(); 138 while ((inputByte = is.read()) != -1) { 139 list.add((byte)inputByte); 140 } 141 list.trimToSize(); 142 return list; 143 } 144 145 /** 146 * Copies contents of origStream to destStream. 147 * <p/> 148 * Recommended to provide a buffered stream for input and output 149 * 150 * @param inStream the {@link InputStream} 151 * @param outStream the {@link OutputStream} 152 * @throws IOException 153 */ 154 public static void copyStreams(InputStream inStream, OutputStream outStream) 155 throws IOException { 156 byte[] buf = new byte[BUF_SIZE]; 157 int size = -1; 158 while ((size = inStream.read(buf)) != -1) { 159 outStream.write(buf, 0, size); 160 } 161 } 162 163 /** 164 * Copies contents of inStream to writer. 165 * <p/> 166 * Recommended to provide a buffered stream for input and output 167 * 168 * @param inStream the {@link InputStream} 169 * @param writer the {@link Writer} destination 170 * @throws IOException 171 */ 172 public static void copyStreamToWriter(InputStream inStream, Writer writer) throws IOException { 173 byte[] buf = new byte[BUF_SIZE]; 174 int size = -1; 175 while ((size = inStream.read(buf)) != -1) { 176 writer.write(new String(buf, 0, size)); 177 } 178 } 179 180 /** 181 * Gets the stack trace as a {@link String}. 182 * 183 * @param throwable the {@link Throwable} to convert. 184 * @return a {@link String} stack trace 185 */ 186 public static String getStackTrace(Throwable throwable) { 187 // dump the print stream results to the ByteArrayOutputStream, so contents can be evaluated 188 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 189 PrintStream bytePrintStream = new PrintStream(outputStream); 190 throwable.printStackTrace(bytePrintStream); 191 return outputStream.toString(); 192 } 193 194 /** 195 * @deprecated use {@link #close(Closeable)} instead. 196 */ 197 @Deprecated 198 public static void closeStream(OutputStream out) { 199 close(out); 200 } 201 202 /** 203 * @deprecated use {@link #close(Closeable)} instead. 204 */ 205 @Deprecated 206 public static void closeStream(InputStream in) { 207 close(in); 208 } 209 210 /** 211 * Attempts to flush the given output stream, and then closes it. 212 * 213 * @param outStream the {@link OutputStream}. No action taken if outStream is null. 214 */ 215 public static void flushAndCloseStream(OutputStream outStream) { 216 if (outStream != null) { 217 try { 218 outStream.flush(); 219 } catch (IOException e) { 220 // ignore 221 } 222 try { 223 outStream.close(); 224 } catch (IOException e) { 225 // ignore 226 } 227 } 228 } 229 230 /** 231 * Closes given zip output stream. 232 * 233 * @param outStream the {@link ZipOutputStream}. No action taken if outStream is null. 234 */ 235 public static void closeZipStream(ZipOutputStream outStream) { 236 if (outStream != null) { 237 try { 238 outStream.closeEntry(); 239 outStream.close(); 240 } catch (IOException e) { 241 // ignore 242 } 243 } 244 } 245 246 /** 247 * Closes given gzip output stream. 248 * 249 * @param outStream the {@link ZipOutputStream}. No action taken if outStream is null. 250 */ 251 public static void closeGZipStream(GZIPOutputStream outStream) { 252 if (outStream != null) { 253 try { 254 outStream.finish(); 255 outStream.close(); 256 } catch (IOException e) { 257 // ignore 258 } 259 } 260 } 261 262 /** 263 * Closes the given {@link Closeable}. 264 * 265 * @param closeable the {@link Closeable}. No action taken if <code>null</code>. 266 */ 267 public static void close(Closeable closeable) { 268 if (closeable != null) { 269 try { 270 closeable.close(); 271 } catch (IOException e) { 272 // ignore 273 } 274 } 275 } 276 277 /** 278 * Cancels the given {@link InputStreamSource} if non-null. 279 */ 280 public static void cancel(InputStreamSource outputSource) { 281 if (outputSource != null) { 282 outputSource.cancel(); 283 } 284 } 285 286 /** 287 * Create a {@link OutputStream} that discards all writes. 288 */ 289 public static OutputStream nullOutputStream() { 290 return ByteStreams.nullOutputStream(); 291 } 292 293 /** 294 * Helper method to calculate md5 for a inputStream. The inputStream will be consumed and 295 * closed. 296 * 297 * @param inputSource used to create inputStream 298 * @return md5 of the stream 299 * @throws IOException 300 */ 301 public static String calculateMd5(InputStream inputSource) throws IOException { 302 MessageDigest md = null; 303 try { 304 md = MessageDigest.getInstance("md5"); 305 } catch (NoSuchAlgorithmException e) { 306 // This should not happen 307 throw new RuntimeException(e); 308 } 309 InputStream input = new BufferedInputStream(new DigestInputStream(inputSource, md)); 310 byte[] buf = new byte[BUF_SIZE]; 311 while (input.read(buf) != -1) { 312 // Read through the stream to update digest. 313 } 314 input.close(); 315 String md5 = DatatypeConverter.printHexBinary(md.digest()).toLowerCase(); 316 return md5; 317 } 318 } 319