View Javadoc

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              // ignore
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]; // Create a buffer for the bytes.
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         // If the width has been reached, finish the current line and
122         // start a new one.
123         if (count == width) {
124             writeASCII();
125             out.write(NEWLINE);
126             count = 0;
127         }
128 
129         // If this is the beginning of a line, write the total bytes
130         // as a hex string.
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         // If there are bytes on the line.
154         if (count > 0) {
155             // Pad out to the first 'ascii' column.
156             int pad = 2 * (width - count);
157             for (int i = 0; i < pad; i++)
158                 out.write(' ');
159             // Write the separator and the remaining bytes as ascii.
160             writeASCII();
161             out.write(NEWLINE);
162         }
163         // Write the total number of bytes.
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);  // Convert the integer into bytes.
180         // Re-use 'buf' to hold the hex representation.
181         Bytes.hexBytes(Bytes.HEX_BYTES_LOWER, intBytes, buf, 4);
182         out.write(buf, 0, 8);
183     }
184 }