1 /********************************************************************************
2 * $Id: HexDumpOutputStream.java 395 2007-09-09 17:40:33Z pgmjsd $
3 * $Author: pgmjsd $
4 * $Date: 2007-09-09 13:40:33 -0400 (Sun, 09 Sep 2007) $
5 *
6 * Copyright 2002-2003 YAJUL Developers, Joshua Davis, Kent Vogel.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 *
26 ******************************************************************************/
27 package org.yajul.io;
28
29 import org.yajul.util.Bytes;
30
31 import java.io.*;
32
33 /***
34 * An output stream that prints lines of hexadecimal output containing the byte
35 * offset (in hex), the hex representation of the types, and the ASCII representation
36 * of the bytes.
37 * User: josh
38 * Date: Jan 11, 2004
39 * Time: 9:23:13 PM
40 */
41 public class HexDumpOutputStream
42 extends HexEncodingOutputStream {
43 /***
44 * The width of each line (# of bytes). *
45 */
46 private int width;
47 /***
48 * The nubmer of bytes on the current line. *
49 */
50 private int count;
51 /***
52 * The total number of bytes. *
53 */
54 private int total;
55 /***
56 * A buffer of the current bytes. *
57 */
58 private byte[] buf;
59 /***
60 * Another buffer, used in converting the stream position into hex. *
61 */
62 private byte[] intBytes;
63
64 private final byte[] SEPARATOR = " | ".getBytes();
65 private static final char NON_PRINTABLE = '.';
66 private static final char NEWLINE = '\n';
67
68 /***
69 * Returns a hex representation of the buffer.
70 *
71 * @param buf The buffer.
72 * @param length The length
73 * @return String - a hex representation of the buffer.
74 */
75 public static String toHexString(byte[] buf, int length) {
76 ByteArrayInputStream bais = new ByteArrayInputStream(buf);
77 ByteArrayOutputStream baos = new ByteArrayOutputStream();
78 HexDumpOutputStream out = new HexDumpOutputStream(baos, 16);
79 try {
80 StreamCopier.unsyncCopy(bais, out, StreamCopier.DEFAULT_BUFFER_SIZE, length);
81 out.flush();
82 }
83 catch (IOException ignore) {
84
85 }
86 return new String(baos.toByteArray());
87 }
88
89 /***
90 * Creates an output stream filter built on top of the specified
91 * underlying output stream.
92 *
93 * @param out the underlying output stream to be assigned to
94 * the field <tt>this.out</tt> for later use, or
95 * <code>null</code> if this instance is to be
96 * created without an underlying stream.
97 * @param width The number of bytes to print in a line of output.
98 */
99 public HexDumpOutputStream(OutputStream out, int width) {
100 super(out);
101 this.width = width;
102 this.count = 0;
103 this.total = 0;
104 this.buf = new byte[width];
105 this.intBytes = new byte[4];
106 }
107
108 /***
109 * Writes the specified <code>byte</code> to this output stream.
110 * <p/>
111 * The <code>write</code> method of <code>FilterOutputStream</code>
112 * calls the <code>write</code> method of its underlying output stream,
113 * that is, it performs <tt>out.write(b)</tt>.
114 * <p/>
115 * Implements the abstract <tt>write</tt> method of <tt>OutputStream</tt>.
116 *
117 * @param b the <code>byte</code>.
118 * @throws IOException if an I/O error occurs.
119 */
120 public void write(int b) throws IOException {
121
122
123 if (count == width) {
124 writeASCII();
125 out.write(NEWLINE);
126 count = 0;
127 }
128
129
130
131 if (count == 0) {
132 writeTotal();
133 out.write(SEPARATOR);
134 }
135
136 buf[count] = (byte) (0x000000FF & b);
137 writeHex(b);
138 count++;
139 total++;
140 }
141
142 /***
143 * Flushes this output stream and forces any buffered output bytes
144 * to be written out to the stream.
145 * <p/>
146 * The <code>flush</code> method of <code>FilterOutputStream</code>
147 * calls the <code>flush</code> method of its underlying output stream.
148 *
149 * @throws IOException if an I/O error occurs.
150 * @see FilterOutputStream#out
151 */
152 public void flush() throws IOException {
153
154 if (count > 0) {
155
156 int pad = 2 * (width - count);
157 for (int i = 0; i < pad; i++)
158 out.write(' ');
159
160 writeASCII();
161 out.write(NEWLINE);
162 }
163
164 writeTotal();
165 super.flush();
166 }
167
168 private void writeASCII() throws IOException {
169 out.write(SEPARATOR);
170 for (int i = 0; i < count; i++) {
171 if (buf[i] >= 32 && buf[i] < 127)
172 out.write(buf[i]);
173 else
174 out.write(NON_PRINTABLE);
175 }
176 }
177
178 private void writeTotal() throws IOException {
179 Bytes.toBytes(total, intBytes);
180
181 Bytes.hexBytes(Bytes.HEX_BYTES_LOWER, intBytes, buf, 4);
182 out.write(buf, 0, 8);
183 }
184 }