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