| /* ==================================================================== |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| ==================================================================== */ |
| |
| package org.apache.poi.util; |
| |
| import java.io.BufferedInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.io.OutputStreamWriter; |
| import java.io.PrintStream; |
| import java.nio.charset.Charset; |
| |
| import org.apache.commons.codec.CharEncoding; |
| |
| /** |
| * dump data in hexadecimal format |
| */ |
| @Internal |
| public class HexDump { |
| public static final String EOL = System.getProperty("line.separator"); |
| public static final Charset UTF8 = Charset.forName(CharEncoding.UTF_8); |
| |
| private HexDump() { |
| // all static methods, so no need for a public constructor |
| } |
| |
| /** |
| * dump an array of bytes to an OutputStream |
| * |
| * @param data the byte array to be dumped |
| * @param offset its offset, whatever that might mean |
| * @param stream the OutputStream to which the data is to be |
| * written |
| * @param index initial index into the byte array |
| * @param length number of characters to output |
| * |
| * @exception IOException is thrown if anything goes wrong writing |
| * the data to stream |
| * @exception ArrayIndexOutOfBoundsException if the index is |
| * outside the data array's bounds |
| * @exception IllegalArgumentException if the output stream is |
| * null |
| */ |
| public static void dump(final byte [] data, final long offset, |
| final OutputStream stream, final int index, final int length) |
| throws IOException, ArrayIndexOutOfBoundsException, IllegalArgumentException { |
| if (stream == null) { |
| throw new IllegalArgumentException("cannot write to nullstream"); |
| } |
| |
| OutputStreamWriter osw = new OutputStreamWriter(stream, UTF8); |
| osw.write(dump(data, offset, index, length)); |
| osw.flush(); |
| } |
| |
| /** |
| * dump an array of bytes to an OutputStream |
| * |
| * @param data the byte array to be dumped |
| * @param offset its offset, whatever that might mean |
| * @param stream the OutputStream to which the data is to be |
| * written |
| * @param index initial index into the byte array |
| * |
| * @exception IOException is thrown if anything goes wrong writing |
| * the data to stream |
| * @exception ArrayIndexOutOfBoundsException if the index is |
| * outside the data array's bounds |
| * @exception IllegalArgumentException if the output stream is |
| * null |
| */ |
| |
| public synchronized static void dump(final byte [] data, final long offset, |
| final OutputStream stream, final int index) |
| throws IOException, ArrayIndexOutOfBoundsException, IllegalArgumentException { |
| dump(data, offset, stream, index, Integer.MAX_VALUE); |
| } |
| |
| /** |
| * dump an array of bytes to a String |
| * |
| * @param data the byte array to be dumped |
| * @param offset its offset, whatever that might mean |
| * @param index initial index into the byte array |
| * |
| * @exception ArrayIndexOutOfBoundsException if the index is |
| * outside the data array's bounds |
| * @return output string |
| */ |
| |
| public static String dump(final byte [] data, final long offset, final int index) { |
| return dump(data, offset, index, Integer.MAX_VALUE); |
| } |
| |
| /** |
| * dump an array of bytes to a String |
| * |
| * @param data the byte array to be dumped |
| * @param offset its offset, whatever that might mean |
| * @param index initial index into the byte array |
| * @param length number of characters to output |
| * |
| * @exception ArrayIndexOutOfBoundsException if the index is |
| * outside the data array's bounds |
| * @return output string |
| */ |
| |
| public static String dump(final byte [] data, final long offset, final int index, final int length) { |
| if (data == null || data.length == 0) { |
| return "No Data"+EOL; |
| } |
| |
| int data_length = (length == Integer.MAX_VALUE || length < 0 || index+length < 0) |
| ? data.length |
| : Math.min(data.length,index+length); |
| |
| |
| if ((index < 0) || (index >= data.length)) { |
| String err = "illegal index: "+index+" into array of length "+data.length; |
| throw new ArrayIndexOutOfBoundsException(err); |
| } |
| |
| long display_offset = offset + index; |
| StringBuilder buffer = new StringBuilder(74); |
| |
| for (int j = index; j < data_length; j += 16) { |
| int chars_read = data_length - j; |
| |
| if (chars_read > 16) { |
| chars_read = 16; |
| } |
| |
| writeHex(buffer, display_offset, 8, ""); |
| for (int k = 0; k < 16; k++) { |
| if (k < chars_read) { |
| writeHex(buffer, data[ k + j ], 2, " "); |
| } else { |
| buffer.append(" "); |
| } |
| } |
| buffer.append(' '); |
| for (int k = 0; k < chars_read; k++) { |
| buffer.append(toAscii(data[ k + j ])); |
| } |
| buffer.append(EOL); |
| display_offset += chars_read; |
| } |
| return buffer.toString(); |
| } |
| |
| public static char toAscii(int dataB) { |
| char charB = (char)(dataB & 0xFF); |
| if (Character.isISOControl(charB)) return '.'; |
| |
| switch (charB) { |
| // printable, but not compilable with current compiler encoding |
| case 0xFF: |
| case 0xDD: |
| charB = '.'; |
| break; |
| default: |
| break; |
| } |
| return charB; |
| } |
| |
| /** |
| * Converts the parameter to a hex value. |
| * |
| * @param value The value to convert |
| * @return A String representing the array of bytes |
| */ |
| public static String toHex(final byte[] value) |
| { |
| StringBuilder retVal = new StringBuilder(); |
| retVal.append('['); |
| if (value != null && value.length > 0) |
| { |
| for(int x = 0; x < value.length; x++) |
| { |
| if (x>0) { |
| retVal.append(", "); |
| } |
| retVal.append(toHex(value[x])); |
| } |
| } |
| retVal.append(']'); |
| return retVal.toString(); |
| } |
| |
| /** |
| * Converts the parameter to a hex value. |
| * |
| * @param value The value to convert |
| * @return A String representing the array of shorts |
| */ |
| public static String toHex(final short[] value) |
| { |
| StringBuilder retVal = new StringBuilder(); |
| retVal.append('['); |
| for(int x = 0; x < value.length; x++) |
| { |
| if (x>0) { |
| retVal.append(", "); |
| } |
| retVal.append(toHex(value[x])); |
| } |
| retVal.append(']'); |
| return retVal.toString(); |
| } |
| |
| /** |
| * <p>Converts the parameter to a hex value breaking the results into |
| * lines.</p> |
| * |
| * @param value The value to convert |
| * @param bytesPerLine The maximum number of bytes per line. The next byte |
| * will be written to a new line |
| * @return A String representing the array of bytes |
| */ |
| public static String toHex(final byte[] value, final int bytesPerLine) { |
| if (value.length == 0) { |
| return ": 0"; |
| } |
| final int digits = (int) Math.round(Math.log(value.length) / Math.log(10) + 0.5); |
| StringBuilder retVal = new StringBuilder(); |
| writeHex(retVal, 0, digits, ""); |
| retVal.append(": "); |
| for(int x=0, i=-1; x < value.length; x++) { |
| if (++i == bytesPerLine) { |
| retVal.append('\n'); |
| writeHex(retVal, x, digits, ""); |
| retVal.append(": "); |
| i = 0; |
| } else if (x>0) { |
| retVal.append(", "); |
| } |
| retVal.append(toHex(value[x])); |
| } |
| return retVal.toString(); |
| } |
| |
| /** |
| * Converts the parameter to a hex value. |
| * |
| * @param value The value to convert |
| * @return The result right padded with 0 |
| */ |
| public static String toHex(short value) { |
| StringBuilder sb = new StringBuilder(4); |
| writeHex(sb, value & 0xFFFF, 4, ""); |
| return sb.toString(); |
| } |
| |
| /** |
| * Converts the parameter to a hex value. |
| * |
| * @param value The value to convert |
| * @return The result right padded with 0 |
| */ |
| public static String toHex(byte value) { |
| StringBuilder sb = new StringBuilder(2); |
| writeHex(sb, value & 0xFF, 2, ""); |
| return sb.toString(); |
| } |
| |
| /** |
| * Converts the parameter to a hex value. |
| * |
| * @param value The value to convert |
| * @return The result right padded with 0 |
| */ |
| public static String toHex(int value) { |
| StringBuilder sb = new StringBuilder(8); |
| writeHex(sb, value & 0xFFFFFFFFL, 8, ""); |
| return sb.toString(); |
| } |
| |
| /** |
| * Converts the parameter to a hex value. |
| * |
| * @param value The value to convert |
| * @return The result right padded with 0 |
| */ |
| public static String toHex(long value) { |
| StringBuilder sb = new StringBuilder(16); |
| writeHex(sb, value, 16, ""); |
| return sb.toString(); |
| } |
| |
| /** |
| * Converts the string to a string of hex values. |
| * |
| * @param value The value to convert |
| * @return The resulted hex string |
| */ |
| public static String toHex(String value) { |
| return (value == null || value.length() == 0) |
| ? "[]" |
| : toHex(value.getBytes(LocaleUtil.CHARSET_1252)); |
| } |
| |
| /** |
| * Dumps <code>bytesToDump</code> bytes to an output stream. |
| * |
| * @param in The stream to read from |
| * @param out The output stream |
| * @param start The index to use as the starting position for the left hand side label |
| * @param bytesToDump The number of bytes to output. Use -1 to read until the end of file. |
| */ |
| public static void dump( InputStream in, PrintStream out, int start, int bytesToDump ) throws IOException |
| { |
| ByteArrayOutputStream buf = new ByteArrayOutputStream(); |
| if (bytesToDump == -1) |
| { |
| int c = in.read(); |
| while (c != -1) |
| { |
| buf.write(c); |
| c = in.read(); |
| } |
| } |
| else |
| { |
| int bytesRemaining = bytesToDump; |
| while (bytesRemaining-- > 0) |
| { |
| int c = in.read(); |
| if (c == -1) { |
| break; |
| } |
| buf.write(c); |
| } |
| } |
| |
| byte[] data = buf.toByteArray(); |
| dump(data, 0, out, start, data.length); |
| } |
| |
| /** |
| * @return string of 16 (zero padded) uppercase hex chars and prefixed with '0x' |
| */ |
| public static String longToHex(long value) { |
| StringBuilder sb = new StringBuilder(18); |
| writeHex(sb, value, 16, "0x"); |
| return sb.toString(); |
| } |
| |
| /** |
| * @return string of 8 (zero padded) uppercase hex chars and prefixed with '0x' |
| */ |
| public static String intToHex(int value) { |
| StringBuilder sb = new StringBuilder(10); |
| writeHex(sb, value & 0xFFFFFFFFL, 8, "0x"); |
| return sb.toString(); |
| } |
| |
| /** |
| * @return string of 4 (zero padded) uppercase hex chars and prefixed with '0x' |
| */ |
| public static String shortToHex(int value) { |
| StringBuilder sb = new StringBuilder(6); |
| writeHex(sb, value & 0xFFFFL, 4, "0x"); |
| return sb.toString(); |
| } |
| |
| /** |
| * @return string of 2 (zero padded) uppercase hex chars and prefixed with '0x' |
| */ |
| public static String byteToHex(int value) { |
| StringBuilder sb = new StringBuilder(4); |
| writeHex(sb, value & 0xFFL, 2, "0x"); |
| return sb.toString(); |
| } |
| |
| /** |
| * @see Integer#toHexString(int) |
| * @see Long#toHexString(long) |
| */ |
| private static void writeHex(StringBuilder sb, long value, int nDigits, String prefix) { |
| sb.append(prefix); |
| char[] buf = new char[nDigits]; |
| long acc = value; |
| for(int i=nDigits-1; i>=0; i--) { |
| int digit = (int)(acc & 0x0F); |
| buf[i] = (char) (digit < 10 ? ('0' + digit) : ('A' + digit - 10)); |
| acc >>>= 4; |
| } |
| sb.append(buf); |
| } |
| |
| |
| public static void main(String[] args) throws Exception { |
| File file = new File(args[0]); |
| InputStream in = new BufferedInputStream(new FileInputStream(file)); |
| byte[] b = new byte[(int)file.length()]; |
| in.read(b); |
| System.out.println(HexDump.dump(b, 0, 0)); |
| in.close(); |
| } |
| } |