View Javadoc
1   /*
2    * Copyright 2007 Werner Guttmann
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  package org.castor.core.util;
15  
16  import java.io.IOException;
17  import java.io.OutputStream;
18  import java.io.ByteArrayOutputStream;
19  
20  /**
21   * Hex encoder/decoder implementation (borrowed from BouncyCastle=.
22   * 
23   * @author Johan Lindquist
24   * @since 1.1.1
25   * @version $Revision$
26   */
27  public final class HexDecoder {
28  
29    /**
30     * Identifies the data type supported by this decoder.
31     */
32    public static final String DATA_TYPE = "hexBinary";
33  
34    /**
35     * Initial size of the decoding table.
36     */
37    private static final int DECODING_TABLE_SIZE = 128;
38  
39    /**
40     * Encoding table.
41     */
42    protected static final byte[] ENCODING_TABLE = {(byte) '0', (byte) '1', (byte) '2', (byte) '3',
43        (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'A',
44        (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F'};
45  
46    /**
47     * Decoding table.
48     */
49    protected static final byte[] DECODING_TABLE = new byte[DECODING_TABLE_SIZE];
50  
51    /**
52     * Initialize the decoding table.
53     */
54    protected static void initialiseDecodingTable() {
55      for (int i = 0; i < ENCODING_TABLE.length; i++) {
56        DECODING_TABLE[ENCODING_TABLE[i]] = (byte) i;
57      }
58  
59      // deal with lower case letters as well
60      DECODING_TABLE['a'] = DECODING_TABLE['A'];
61      DECODING_TABLE['b'] = DECODING_TABLE['B'];
62      DECODING_TABLE['c'] = DECODING_TABLE['C'];
63      DECODING_TABLE['d'] = DECODING_TABLE['D'];
64      DECODING_TABLE['e'] = DECODING_TABLE['E'];
65      DECODING_TABLE['f'] = DECODING_TABLE['F'];
66    }
67  
68    static {
69      initialiseDecodingTable();
70    }
71  
72    /**
73     * Creates an instance of this class.
74     */
75    private HexDecoder() {
76      // Nothing to do ...
77    }
78  
79    /**
80     * Encodes the input data producing a Hex output stream.
81     * 
82     * @param data The input data to be HEX encoded
83     * @param off Initiak offset
84     * @param length Initial length of the input data array
85     * @param out The {@link OutputStream} instance holding the encoded input data.
86     * @return the number of bytes produced.
87     * @throws IOException If encoding fails.
88     */
89    public static int encode(final byte[] data, final int off, final int length,
90        final OutputStream out) throws IOException {
91      for (int i = off; i < (off + length); i++) {
92        int v = data[i] & 0xff;
93  
94        out.write(ENCODING_TABLE[(v >>> 4)]);
95        out.write(ENCODING_TABLE[v & 0xf]);
96      }
97  
98      return length * 2;
99    }
100 
101   /**
102    * Indicates whether a given character should be ignored during en-/decoding.
103    * 
104    * @param c The character at question.
105    * @return True if the given character should be ignored.
106    */
107   private static boolean ignore(final char c) {
108     return (c == '\n' || c == '\r' || c == '\t' || c == ' ');
109   }
110 
111   /**
112    * Decodes the Hex encoded byte data writing it to the given output stream, whitespace characters
113    * will be ignored.
114    * 
115    * @param data The data to be encoded
116    * @param off Initial offset.
117    * @param length Initial length
118    * @param out The {@link OutputStream} instance
119    * @return the number of bytes produced.
120    * @throws IOException If encoding failed.
121    */
122   public static int decode(final byte[] data, final int off, final int length,
123       final OutputStream out) throws IOException {
124     byte b1, b2;
125     int outLen = 0;
126 
127     int end = off + length;
128 
129     while (end > off) {
130       if (!ignore((char) data[end - 1])) {
131         break;
132       }
133 
134       end--;
135     }
136 
137     int i = off;
138     while (i < end) {
139       while (i < end && ignore((char) data[i])) {
140         i++;
141       }
142 
143       b1 = DECODING_TABLE[data[i++]];
144 
145       while (i < end && ignore((char) data[i])) {
146         i++;
147       }
148 
149       b2 = DECODING_TABLE[data[i++]];
150 
151       out.write((b1 << 4) | b2);
152 
153       outLen++;
154     }
155 
156     return outLen;
157   }
158 
159   /**
160    * Decodes the Hex encoded String data writing it to the given output stream, whitespace
161    * characters will be ignored.
162    * 
163    * @param data The data to be encoded
164    * @param out The {@link OutputStream} instance
165    * @return the number of bytes produced.
166    * @throws IOException If encoding failed.
167    */
168   public static int decode(final String data, final OutputStream out) throws IOException {
169     byte b1, b2;
170     int length = 0;
171 
172     int end = data.length();
173 
174     while (end > 0) {
175       if (!ignore(data.charAt(end - 1))) {
176         break;
177       }
178 
179       end--;
180     }
181 
182     int i = 0;
183     while (i < end) {
184       while (i < end && ignore(data.charAt(i))) {
185         i++;
186       }
187 
188       b1 = DECODING_TABLE[data.charAt(i++)];
189 
190       while (i < end && ignore(data.charAt(i))) {
191         i++;
192       }
193 
194       b2 = DECODING_TABLE[data.charAt(i++)];
195 
196       out.write((b1 << 4) | b2);
197 
198       length++;
199     }
200 
201     return length;
202   }
203 
204   /**
205    * Encodes the input data producing a Hex output stream.
206    * 
207    * @param data Input data to encode.
208    * @return the number of bytes produced.
209    */
210   public static String encode(final byte[] data) {
211     try {
212       final ByteArrayOutputStream out = new ByteArrayOutputStream();
213       encode(data, 0, data.length, out);
214       out.close();
215       return new String(out.toByteArray());
216     } catch (IOException e) {
217       e.printStackTrace();
218       throw new RuntimeException(e.getMessage(), e);
219     }
220   }
221 
222   /**
223    * Decodes the HEX input data producing a output stream.
224    * 
225    * @param data Input data to be decoded.
226    * @return A byte array representing the decoded input data.
227    */
228   public static byte[] decode(final String data) {
229     try {
230       final ByteArrayOutputStream out = new ByteArrayOutputStream();
231       decode(data, out);
232       out.close();
233       return out.toByteArray();
234     } catch (IOException e) {
235       e.printStackTrace();
236       throw new RuntimeException(e.getMessage(), e);
237     }
238   }
239 
240 }