package org.uli.util; import java.nio.charset.Charset; import java.util.LinkedList; import java.util.List; public class HexDump { static private final int DEFAULT_BYTES_PER_LINE = 16; static private final boolean DEFAULT_DUMP_HEX = true; static private final boolean DEFAULT_DUMP_TEXT = true; static private final String LINE_SEPARATOR = System.getProperty("line.separator"); private final int bytesPerLine; private final boolean fDumpHex; private final boolean fDumpText; public static abstract class Builder<T extends Builder<T>> { private int bytesPerLine = DEFAULT_BYTES_PER_LINE; private boolean fDumpHex = DEFAULT_DUMP_HEX; private boolean fDumpText = DEFAULT_DUMP_TEXT; protected abstract T self(); public T bytesPerLine(int bytesPerLine) { this.bytesPerLine = bytesPerLine; return self(); } public T dumpHex(boolean fDumpHex) { this.fDumpHex = fDumpHex; return self(); } public T dumpText(boolean fDumpText) { this.fDumpText = fDumpText; return self(); } public HexDump build() { return new HexDump(this); } } private static class Builder2 extends Builder<Builder2> { @Override protected Builder2 self() { return this; } } public static Builder<?> builder() { return new Builder2(); } protected HexDump(Builder<?> builder) { this.bytesPerLine = builder.bytesPerLine; this.fDumpHex = builder.fDumpHex; this.fDumpText = builder.fDumpText; } static public String hexDump(byte[] array) { return hexDump(array, 0, array.length); } static public String hexDump(byte[] array, int offset, int length) { HexDump hd = HexDump.builder().build(); return hd.dump(array, offset, length); } public String dump(byte[] array) { return this.dump(array, 0, array.length); } public String dump(byte[] array, int offset, int length) { StringBuffer sb = new StringBuffer(length * 3 + 100); int remainingLength = length; int thisLineOffset = offset; while (remainingLength > 0) { dumpLineResult dlr = this.dumpLine(array, thisLineOffset, remainingLength); sb.append(dlr.line); sb.append(LINE_SEPARATOR); remainingLength = dlr.remainingLength; thisLineOffset = dlr.nextOffset; } return sb.toString(); } public List<String> dumpToList(byte[] array) { return this.dumpToList(array, 0, array.length); } public List<String> dumpToList(byte[] array, int offset, int length) { List<String> l = new LinkedList<String>(); int remainingLength = length; int thisLineOffset = offset; while (remainingLength > 0) { dumpLineResult dlr = this.dumpLine(array, thisLineOffset, remainingLength); l.add(dlr.line); remainingLength = dlr.remainingLength; thisLineOffset = dlr.nextOffset; } return l; } private StringBuffer append(StringBuffer sb, String a, int i) { if (i % 4 == 0) { sb.append(" "); } sb.append(a); return sb; } private String address(int offset) { return String.format("%08x", offset); } private String text(byte n) { String result; if (isPrintable(n)) { result = new String(new byte[] { n }, Charset.forName("US-ASCII")); } else { result = "."; } return result; } private boolean isPrintable(byte n) { return n >= 32 && n < 127; } public static void main(String[] args) { System.out.println(HexDump.hexDump(args[0].getBytes(Charset.defaultCharset()))); } private dumpLineResult dumpLine(byte[] array, int offset, int length) { dumpLineResult dlr = new dumpLineResult("", offset, length); StringBuffer sb = new StringBuffer(); if (length > 0) { StringBuffer hexBuffer = new StringBuffer(3 * this.bytesPerLine); StringBuffer textBuffer = new StringBuffer(this.bytesPerLine); sb.append(address(offset)); sb.append(":"); int i = 0; for (; i < length && i < this.bytesPerLine; i++) { byte b = array[offset + i]; if (this.fDumpHex) { append(hexBuffer, String.format("%02x", b), i); } if (this.fDumpText) { textBuffer.append(text(b)); } } if (this.fDumpHex && this.fDumpText) { for (; i < this.bytesPerLine; i++) { append(hexBuffer, " ", i); } } if (this.fDumpHex) { sb.append(hexBuffer); } if (this.fDumpText) { sb.append(" '"); sb.append(textBuffer); sb.append("'"); } dlr.line = sb.toString(); dlr.nextOffset = offset + this.bytesPerLine; dlr.remainingLength = length - this.bytesPerLine; } return dlr; } private static class dumpLineResult { private String line; private int nextOffset; private int remainingLength; private dumpLineResult(String line, int nextOffset, int remainingLength) { this.line = line; this.nextOffset = nextOffset; this.remainingLength = remainingLength; } } }