Index: src/test/java/org/castor/util/TestHex.java
===================================================================
--- src/test/java/org/castor/util/TestHex.java (Revision 0)
+++ src/test/java/org/castor/util/TestHex.java (Revision 0)
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+package org.castor.util;
+
+import java.util.Arrays;
+import java.util.Random;
+
+import junit.framework.TestCase;
+
+/**
+ * JUnit test case for HEX en-/coding.
+ * @author Johan Lindquist
+ * @version $Revision$
+ */
+public class TestHex extends TestCase {
+
+ public void testEncodeDecode() {
+ byte[] bytes = new byte[256];
+
+ new Random(1L).nextBytes(bytes);
+ final String encodedBytes = Hex.encode(bytes);
+ assertEquals(
+ "Bad encoded data",
+ "73d51abbd89cb8196f0efb6892f94d68fccc2c35f0b84609e5f12c55dd85aba8d5d9bef76808f3b572e5900112b81927ba5bb5f67e1bda28b4049bf0e4aed78db15d7bf2fc0c34e9a99de4ef3bc2b17c8137ad659878f9e93df1f658367aca286452474b9ef3765e24e9a88173724dddfb04b01dcceb0c8aead641c58dad569581baeea87c10d40a47902028e61cfdc243d9d16008aabc9fb77cc723a56017e14f1ce8b1698341734a6823ce02043e016b544901214a2ddab82fec85c0b9fe0549c475be5b887bb4b8995b24fb5c6846f88b527b4f9d4c1391f1678b23ba4f9c9cd7bc93eb5776f4f03675344864294661c5949faf17b130fcf6482f971a5500",
+ encodedBytes);
+
+ final byte[] decodedBytes = Hex.decode(encodedBytes);
+
+ assertTrue("Bad decoded bytes", Arrays.equals(bytes, decodedBytes));
+
+ }
+
+}
Index: src/bugs/xml/bug423/mapping.xml
===================================================================
--- src/bugs/xml/bug423/mapping.xml (Revision 0)
+++ src/bugs/xml/bug423/mapping.xml (Revision 0)
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: src/bugs/xml/bug423/README.txt
===================================================================
--- src/bugs/xml/bug423/README.txt (Revision 0)
+++ src/bugs/xml/bug423/README.txt (Revision 0)
@@ -0,0 +1,3 @@
+bug number: 423
+description: Handling of hex binary / base64 encoding
+castor: version trunk
Index: src/bugs/xml/bug423/TestTemplate.java
===================================================================
--- src/bugs/xml/bug423/TestTemplate.java (Revision 0)
+++ src/bugs/xml/bug423/TestTemplate.java (Revision 0)
@@ -0,0 +1,90 @@
+package xml.bug423;
+
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+import org.exolab.castor.mapping.Mapping;
+import org.exolab.castor.xml.Marshaller;
+import org.exolab.castor.xml.Unmarshaller;
+import org.xml.sax.InputSource;
+
+/**
+ * @author Johan Lindquist
+ */
+public final class TestTemplate extends TestCase {
+
+ private static final String SAMPLE_FILE = "entity.xml";
+ private static final String MAPPING_FILE = "mapping.xml";
+
+ private static final byte[] EXPECTED_HEX_ELEMENT_ARRAY = new byte[]{0x01, 0x04, 0x03, 0x02, 0x01, 0x01};
+ private static final byte[] EXPECTED_HEX_ATTRIBUTE_ARRAY = new byte[]{0x01, 0x04, 0x03, 0x02, 0x01, 0x02};
+ private static final byte[] EXPECTED_BASE64_ELEMENT_ARRAY = new byte[]{104, -55, 0, 0};
+ private static final byte[] EXPECTED_BASE64_ATTRIBUTE_ARRAY = new byte[]{104, -55, 0, 0};
+ private static final String EXPECTED_XML = "\n" +
+ "" +
+ "aMkAAA==" +
+ "010403020101" +
+ "";
+
+
+ public TestTemplate() {
+ super();
+ }
+
+ public TestTemplate(final String name) {
+ super(name);
+ }
+
+ /**
+ * Test method.
+ * @throws Exception For any exception thrown.
+ */
+ public void testUnmarshalEntity() throws Exception {
+
+ Mapping mapping = new Mapping();
+ mapping.loadMapping(getClass().getResource(MAPPING_FILE).toExternalForm());
+
+ Unmarshaller unmarshaller = new Unmarshaller (Entity.class);
+ unmarshaller.setMapping(mapping);
+
+ Entity entity = (Entity) unmarshaller.unmarshal(new InputSource(getClass().getResource(SAMPLE_FILE).toExternalForm()));
+
+ assertNotNull (entity);
+
+ assertTrue("Invalid hex value in element", Arrays.equals(EXPECTED_HEX_ELEMENT_ARRAY,entity.getElementHex()));
+ assertTrue("Invalid hex value in attribute", Arrays.equals(EXPECTED_HEX_ATTRIBUTE_ARRAY, entity.getAttributeHex()));
+ assertTrue("Invalid base64 value in element", Arrays.equals(EXPECTED_BASE64_ELEMENT_ARRAY, entity.getElementBase64()));
+ assertTrue("Invalid base64 value in attribute", Arrays.equals(EXPECTED_BASE64_ATTRIBUTE_ARRAY, entity.getAttributeBase64()));
+ }
+
+ /**
+ * Test method.
+ * @throws Exception For any exception thrown.
+ */
+ public void testMarshalEntity() throws Exception {
+ Mapping mapping = new Mapping();
+ mapping.loadMapping(getClass().getResource(MAPPING_FILE).toExternalForm());
+
+ StringWriter stringWriter = new StringWriter();
+ Marshaller marshaller = new Marshaller (stringWriter);
+ marshaller.setMapping(mapping);
+
+ Entity entity = new Entity();
+ entity.setElementHex(EXPECTED_HEX_ELEMENT_ARRAY);
+ entity.setAttributeHex(EXPECTED_HEX_ATTRIBUTE_ARRAY);
+ entity.setElementBase64(EXPECTED_BASE64_ELEMENT_ARRAY);
+ entity.setAttributeBase64(EXPECTED_BASE64_ATTRIBUTE_ARRAY);
+ marshaller.setEncoding("UTF-8");
+ marshaller.marshal(entity);
+
+ String xml = stringWriter.toString();
+ System.out.println(xml.toString());
+ assertEquals("XML marshalling produced invalid value",EXPECTED_XML.trim(),xml.trim());
+
+ }
+
+}
Index: src/bugs/xml/bug423/entity.xml
===================================================================
--- src/bugs/xml/bug423/entity.xml (Revision 0)
+++ src/bugs/xml/bug423/entity.xml (Revision 0)
@@ -0,0 +1,5 @@
+
+
+ 010403020101
+ aMkAAA==
+
Index: src/bugs/xml/bug423/Entity.java
===================================================================
--- src/bugs/xml/bug423/Entity.java (Revision 0)
+++ src/bugs/xml/bug423/Entity.java (Revision 0)
@@ -0,0 +1,49 @@
+package xml.bug423;
+
+public final class Entity {
+
+ private byte[] _attributeHex;
+ private byte[] _attributeBase64;
+ private byte[] _elementHex;
+ private byte[] _elementBase64;
+
+ public byte[] getAttributeHex()
+ {
+ return _attributeHex;
+ }
+
+ public void setAttributeHex(final byte[] attributeHex)
+ {
+ _attributeHex = attributeHex;
+ }
+
+ public byte[] getAttributeBase64()
+ {
+ return _attributeBase64;
+ }
+
+ public void setAttributeBase64(final byte[] attributeBase64)
+ {
+ _attributeBase64 = attributeBase64;
+ }
+
+ public byte[] getElementHex()
+ {
+ return _elementHex;
+ }
+
+ public void setElementHex(final byte[] elementHex)
+ {
+ _elementHex = elementHex;
+ }
+
+ public byte[] getElementBase64()
+ {
+ return _elementBase64;
+ }
+
+ public void setElementBase64(final byte[] elementBase64)
+ {
+ _elementBase64 = elementBase64;
+ }
+}
Index: src/main/java/org/exolab/castor/xml/UnmarshalHandler.java
===================================================================
--- src/main/java/org/exolab/castor/xml/UnmarshalHandler.java (Revision 6910)
+++ src/main/java/org/exolab/castor/xml/UnmarshalHandler.java (Arbeitskopie)
@@ -50,12 +50,14 @@
//-- Castor imports
import org.castor.mapping.BindingType;
import org.castor.util.Base64Decoder;
+import org.castor.util.Hex;
import org.exolab.castor.util.Configuration;
import org.exolab.castor.util.ObjectFactory;
import org.exolab.castor.util.DefaultObjectFactory;
import org.exolab.castor.xml.descriptors.PrimitivesClassDescriptor;
import org.exolab.castor.xml.descriptors.StringClassDescriptor;
import org.exolab.castor.xml.util.*;
+import org.exolab.castor.builder.types.XSHexBinary;
import org.exolab.castor.mapping.ClassDescriptor;
import org.exolab.castor.mapping.ExtendedFieldHandler;
import org.exolab.castor.mapping.FieldHandler;
@@ -801,8 +803,12 @@
if (str == null)
state.object = new byte[0];
else {
- //-- Base64 decoding
- state.object = Base64Decoder.decode(str);
+ //-- Base64/HexBinary decoding
+ if (XSHexBinary.NAME.equals(descriptor.getSchemaType())) {
+ state.object = Hex.decode(str);
+ } else {
+ state.object = Base64Decoder.decode(str);
+ }
}
}
else if (state.args != null) {
@@ -827,10 +833,14 @@
value = toPrimitiveObject(cdesc.getFieldType(), (String)value, state.fieldDesc);
else {
Class valueType = cdesc.getFieldType();
- //-- handle base64
+ //-- handle base64/hexBinary
if (valueType.isArray()
&& (valueType.getComponentType() == Byte.TYPE)) {
- value = Base64Decoder.decode((String) value);
+ if (XSHexBinary.NAME.equals(descriptor.getSchemaType())) {
+ value = Hex.decode((String) value);
+ } else {
+ value = Base64Decoder.decode((String) value);
+ }
}
}
@@ -3024,8 +3034,12 @@
if (attValue == null)
value = new byte[0];
else {
- //-- Base64 decoding
- value = Base64Decoder.decode(attValue);
+ //-- Base64/hexbinary decoding
+ if (XSHexBinary.NAME.equals(descriptor.getSchemaType())) {
+ value = Hex.decode(attValue);
+ } else {
+ value = Base64Decoder.decode(attValue);
+ }
}
}
Index: src/main/java/org/exolab/castor/xml/Marshaller.java
===================================================================
--- src/main/java/org/exolab/castor/xml/Marshaller.java (Revision 6910)
+++ src/main/java/org/exolab/castor/xml/Marshaller.java (Arbeitskopie)
@@ -55,6 +55,8 @@
import org.castor.mapping.MappingUnmarshaller;
import org.castor.util.Base64Encoder;
import org.castor.util.Messages;
+import org.castor.util.Hex;
+import org.exolab.castor.builder.types.XSHexBinary;
import org.exolab.castor.mapping.CollectionHandler;
import org.exolab.castor.mapping.MapItem;
import org.exolab.castor.mapping.Mapping;
@@ -1636,8 +1638,13 @@
char[] chars = null;
Class objType = obj.getClass();
if (objType.isArray() && (objType.getComponentType() == Byte.TYPE)) {
- //-- handle base64 content
- chars = Base64Encoder.encode((byte[]) obj);
+ //-- handle base64/hexbinary content
+ final String schemaType = descriptor.getSchemaType();
+ if (XSHexBinary.NAME.equals(schemaType)) {
+ chars = new String(Hex.encode((byte[]) obj)).toCharArray();
+ } else {
+ chars = Base64Encoder.encode((byte[]) obj);
+ }
} else {
//-- all other types
String str = obj.toString();
@@ -1670,11 +1677,17 @@
}
// special case for byte[]
else if (byteArray) {
- //-- Base64Encoding
- char[] chars = Base64Encoder.encode((byte[]) object);
+ //-- Base64Encoding / HexBinary
+ String schemaType = descriptor.getSchemaType();
+ char[] chars = new char[0];
+ if (XSHexBinary.NAME.equals(schemaType)) {
+ chars = new String(Hex.encode((byte[]) object)).toCharArray();
+ } else {
+ chars = Base64Encoder.encode((byte[]) object);
+ }
try {
handler.characters(chars, 0, chars.length);
- } catch (org.xml.sax.SAXException sx) {
+ } catch (org.xml.sax.SAXException sx) {
throw new MarshalException(sx);
}
}
@@ -2367,7 +2380,12 @@
//-- handle base64 content
Class objType = value.getClass();
if (objType.isArray() && (objType.getComponentType() == Byte.TYPE)) {
- value = Base64Encoder.encode((byte[]) value);
+ final String schemaType = attDescriptor.getSchemaType();
+ if (XSHexBinary.NAME.equals(schemaType)) {
+ value = new String(Hex.encode((byte[]) value));
+ } else {
+ value = new String(Base64Encoder.encode((byte[]) value));
+ }
}
}
Index: src/main/java/org/castor/util/Hex.java
===================================================================
--- src/main/java/org/castor/util/Hex.java (Revision 0)
+++ src/main/java/org/castor/util/Hex.java (Revision 0)
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+package org.castor.util;
+
+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 Hex {
+
+ /**
+ * 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;
+ }
+
+ 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 Hex() {
+ // 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);
+ }
+ }
+
+}
Index: codegen/src/main/java/org/exolab/castor/builder/descriptors/DescriptorSourceFactory.java
===================================================================
--- codegen/src/main/java/org/exolab/castor/builder/descriptors/DescriptorSourceFactory.java (Revision 6910)
+++ codegen/src/main/java/org/exolab/castor/builder/descriptors/DescriptorSourceFactory.java (Arbeitskopie)
@@ -431,6 +431,8 @@
addSpecialHandlerLogic(member, xsType, jsc);
}
+ // Add the schema type as defined in the schema
+ jsc.add("desc.setSchemaType(\"" + xsType.getName() + "\");");
jsc.add("desc.setHandler(handler);");
//-- container