/*
* Copyright 2007 Werner Guttmann
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOException;
import java.io.OutputStream;
import java.io.ByteArrayOutputStream;
/**
* Hex encoder/decoder implementation (borrowed from BouncyCastle=.
*
* @author Johan Lindquist
* @since 1.1.1
* @version $Revision$
*/
public final class HexDecoder {
/**
* Identifies the data type supported by this decoder.
*/
public static final String DATA_TYPE = "hexBinary";
/**
* Initial size of the decoding table.
*/
private static final int DECODING_TABLE_SIZE = 128;
/**
* Encoding table.
*/
protected static final byte[] ENCODING_TABLE = {
(byte) '0', (byte) '1', (byte) '2', (byte) '3',
(byte) '4', (byte) '5', (byte) '6', (byte) '7',
(byte) '8', (byte) '9', (byte) 'A', (byte) 'B',
(byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F'
};
/**
* Decoding table.
*/
protected static final byte[] DECODING_TABLE = new byte[DECODING_TABLE_SIZE];
/**
* Initialize the decoding table.
*/
protected static void initialiseDecodingTable() {
for (int i = 0; i < ENCODING_TABLE.length; i++) {
DECODING_TABLE[ENCODING_TABLE[i]] = (byte) i;
}
// deal with lower case letters as well
DECODING_TABLE['a'] = DECODING_TABLE['A'];
DECODING_TABLE['b'] = DECODING_TABLE['B'];
DECODING_TABLE['c'] = DECODING_TABLE['C'];
DECODING_TABLE['d'] = DECODING_TABLE['D'];
DECODING_TABLE['e'] = DECODING_TABLE['E'];
DECODING_TABLE['f'] = DECODING_TABLE['F'];
}
static {
initialiseDecodingTable();
}
/**
* Creates an instance of this class.
*/
private HexDecoder() {
// Nothing to do ...
}
/**
* Encodes the input data producing a Hex output stream.
* @param data The input data to be HEX encoded
* @param off Initiak offset
* @param length Initial length of the input data array
* @param out The {@link OutputStream} instance holding the encoded input data.
* @return the number of bytes produced.
* @throws IOException If encoding fails.
*/
public static int encode(final byte[] data, final int off, final int length,
final OutputStream out) throws IOException {
for (int i = off; i < (off + length); i++) {
int v = data[i] & 0xff;
out.write(ENCODING_TABLE[(v >>> 4)]);
out.write(ENCODING_TABLE[v & 0xf]);
}
return length * 2;
}
/**
* Indicates whether a given character should be ignored during en-/decoding.
* @param c The character at question.
* @return True if the given character should be ignored.
*/
private static boolean ignore(final char c) {
return (c == '\n' || c == '\r' || c == '\t' || c == ' ');
}
/**
* Decodes the Hex encoded byte data writing it to the given output stream,
* whitespace characters will be ignored.
* @param data The data to be encoded
* @param off Initial offset.
* @param length Initial length
* @param out The {@link OutputStream} instance
* @return the number of bytes produced.
* @throws IOException If encoding failed.
*/
public static int decode(final byte[] data, final int off, final int length,
final OutputStream out) throws IOException {
byte b1, b2;
int outLen = 0;
int end = off + length;
while (end > off) {
if (!ignore((char) data[end - 1])) {
break;
}
end--;
}
int i = off;
while (i < end) {
while (i < end && ignore((char) data[i])) {
i++;
}
b1 = DECODING_TABLE[data[i++]];
while (i < end && ignore((char) data[i])) {
i++;
}
b2 = DECODING_TABLE[data[i++]];
out.write((b1 << 4) | b2);
outLen++;
}
return outLen;
}
/**
* Decodes the Hex encoded String data writing it to the given output stream,
* whitespace characters will be ignored.
*
* @param data The data to be encoded
* @param out The {@link OutputStream} instance
* @return the number of bytes produced.
* @throws IOException If encoding failed.
*/
public static int decode(final String data, final OutputStream out) throws IOException {
byte b1, b2;
int length = 0;
int end = data.length();
while (end > 0) {
if (!ignore(data.charAt(end - 1))) {
break;
}
end--;
}
int i = 0;
while (i < end) {
while (i < end && ignore(data.charAt(i))) {
i++;
}
b1 = DECODING_TABLE[data.charAt(i++)];
while (i < end && ignore(data.charAt(i))) {
i++;
}
b2 = DECODING_TABLE[data.charAt(i++)];
out.write((b1 << 4) | b2);
length++;
}
return length;
}
/**
* Encodes the input data producing a Hex output stream.
* @param data Input data to encode.
* @return the number of bytes produced.
*/
public static String encode(final byte[] data) {
try {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
encode(data, 0, data.length, out);
out.close();
return new String(out.toByteArray());
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage(), e);
}
}
/**
* Decodes the HEX input data producing a output stream.
* @param data Input data to be decoded.
* @return A byte array representing the decoded input data.
*/
public static byte[] decode(final String data) {
try {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
decode(data, out);
out.close();
return out.toByteArray();
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage(), e);
}
}
}