/* A collection of IO utility functions. * * Copyright (C) 2010, 2011 Darrell Karbott * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.0 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: djk@isFiaD04zgAgnrEC5XJt1i4IE7AkNPqhBG5bONi6Yks * * This file was developed as component of * "fniki" (a wiki implementation running over Freenet). */ package wormarc; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Random; public class IOUtil { private final static Random sRandom = new Random(); private final static int BUF_LEN = 1024 * 32; // Don't change without reviewing code. public final static String UTF8 = "utf8"; public static void silentlyClose(InputStream stream) { if (stream == null) { return; } try { stream.close(); } catch (IOException ioe) { // NOP } } public final static void copyAndClose(InputStream fromStream, OutputStream toStream) throws IOException { if (fromStream == null || toStream == null) { throw new IllegalArgumentException(); } try { byte[] buffer = new byte[BUF_LEN]; while (true) { int bytesRead = fromStream.read(buffer); if (bytesRead == -1) { return; } toStream.write(buffer, 0, bytesRead); } } finally { try { fromStream.close(); } finally { toStream.close(); } } } // Closes stream. public final static LinkDigest getFileDigest(InputStream fromStream) throws IOException { MessageDigest sha1 = null; try { try { sha1 = MessageDigest.getInstance("SHA"); } catch (NoSuchAlgorithmException nsae) { throw new IOException("Couldn't load SHA1 algorithm."); } // DCI: Better to use a wrapper stream filter from the java crypto lib? byte[] buffer = new byte[BUF_LEN]; while (true) { int bytesRead = fromStream.read(buffer); if (bytesRead == -1) { break; } sha1.update(buffer, 0, bytesRead); } return new LinkDigest(sha1.digest()); } finally { fromStream.close(); } } public final static void copyAndClose(InputStream fromStream, String toFileName) throws IOException { copyAndClose(fromStream, new FileOutputStream(toFileName)); } public final static byte[] readAndClose(InputStream fromStream) throws IOException { if (fromStream == null) { throw new IllegalArgumentException(); } ByteArrayOutputStream toStream = new ByteArrayOutputStream(); copyAndClose(fromStream, toStream); byte[] bytes = toStream.toByteArray(); return bytes; } public final static byte[] readFully(String inputFile) throws IOException { return readAndClose(new FileInputStream(inputFile)); } public final static void writeFully(byte[] data, String outputFile) throws IOException { copyAndClose(new ByteArrayInputStream(data), new FileOutputStream(outputFile)); } public final static String readUtf8StringAndClose(InputStream fromStream) throws IOException { ByteArrayOutputStream toStream = new ByteArrayOutputStream(); copyAndClose(fromStream, toStream); return new String(toStream.toByteArray(), UTF8); } public final static InputStream toStreamAsUtf8(String value) throws IOException { return new ByteArrayInputStream(value.getBytes(UTF8)); } // ATTRIBUTION: mmyers, SO // http://stackoverflow.com/questions/140131/convert-a-string-representation-of-a-hex-dump-to-a-byte-array-using-java public final static byte[] fromHexString(final String encoded) { if ((encoded.length() % 2) != 0) throw new IllegalArgumentException("Input string must contain an even number of characters"); final byte result[] = new byte[encoded.length()/2]; final char enc[] = encoded.toCharArray(); for (int i = 0; i < enc.length; i += 2) { StringBuilder curr = new StringBuilder(2); curr.append(enc[i]).append(enc[i + 1]); result[i/2] = (byte) Integer.parseInt(curr.toString(), 16); } return result; } // ATTRIBUTION: Peter Lawrey, SO // http://stackoverflow.com/questions/332079/in-java-how-do-i-convert-a-byte-array-to-a-string-of-hex-digits-while-keeping-le public final static String toHexString(byte[] bytes, int maxBytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { sb.append(String.format("%1$02x", b)); maxBytes -= 1; if (maxBytes == 0) { break; } } return sb.toString(); } public final static String randomHexString(int length) { if (length < 1) { throw new IllegalArgumentException(); } byte[] bytes = new byte[length]; sRandom.nextBytes(bytes); return toHexString(bytes, bytes.length); } // ATTRIBUTION: erikson, SO // http://stackoverflow.com/questions/779519/delete-files-recursively-in-java public final static void delete(File f) throws IOException { if (f.isDirectory()) { for (File c : f.listFiles()) { delete(c); } } if (!f.delete()) { throw new FileNotFoundException("Failed to delete file: " + f); } } public final static void delete(String fileOrDirectory) throws IOException { delete( new File(fileOrDirectory) ); } //////////////////////////////////////////////////////////// // Binary IO //////////////////////////////////////////////////////////// public static byte[] readBytes(DataInputStream source, int numberOfBytes) throws IOException { byte[] data = new byte[numberOfBytes]; int count = 0; while (count < data.length) { int bytesRead = source.read(data, count, data.length - count); if (bytesRead == -1) { throw new IOException("Unexpected EOF reading bytes"); } count += bytesRead; } return data; } // DCI: remove? is there other code that should be using this? // public static void writeString(DataOutputStream outputStream, String value) throws IOException { // byte[] bytes = value.getBytes(UTF8); // if (bytes.length > 32767) { // DCI: is this required? // throw new IOException("Length doesn't fit in a signed short"); // } // outputStream.writeShort(bytes.length); // outputStream.write(bytes); // } public static String readString(DataInputStream source) throws IOException { int length = source.readShort(); if (length > 32767) { // DCI: is this required? throw new IOException("Length doesn't fit in a signed short"); } return new String(readBytes(source, length), UTF8); } }