--- /dev/null
+/*
+ * Copyright 2011 BitMeister Inc.
+ *
+ * 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 jp.bitmeister.asn1.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import jp.bitmeister.asn1.type.ASN1Module;
+import jp.bitmeister.asn1.type.ASN1Type;
+
+/**
+ * Indicates ASN.1 types which are included in this module.
+ *
+ * <p>
+ * {@code ASN1DefinedTypes} annotation indicates ASN.1 types which are included
+ * in this module but defined at outside of the ASN.1 module class. ASN.1 types
+ * defined as static public member classes of an ASN.1 module class will be
+ * registered to the module automatically.
+ * </p>
+ *
+ * @author WATANABE, Jun. <jwat at bitmeister.jp>
+ *
+ * @see ASN1Module
+ * @see ASN1Type
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ASN1DefinedTypes {
+
+ /**
+ * Indicates ASN.1 type classes defined at outside of the module class.
+ *
+ * @return An array of {@code Class} object of {@code ASN1Type}.
+ */
+ public Class<? extends ASN1Type>[] value();
+
+}
--- /dev/null
+/*
+ * Copyright 2011 BitMeister Inc.
+ *
+ * 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 jp.bitmeister.asn1.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import jp.bitmeister.asn1.type.ASN1Module;
+
+/**
+ * Indicates ASN.1 modules which are imported by this module.
+ *
+ * <p>
+ * {@code ASN1ImportedModules} annotation indicates ASN.1 modules which are
+ * imported by this module.
+ * </p>
+ *
+ * @author WATANABE, Jun. <jwat at bitmeister.jp>
+ *
+ * @see ASN1Module
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ASN1ImportedModules {
+
+ /**
+ * Indicates ASN.1 module classes imported by the ASN.1 module.
+ *
+ * @return An array of {@code Class} object of {@code ASN1Module}.
+ */
+ public Class<? extends ASN1Module>[] value();
+
+}
--- /dev/null
+/*
+ * Copyright 2011 BitMeister Inc.
+ *
+ * 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 jp.bitmeister.asn1.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates xml type name for an ASN.1 type.
+ *
+ * <p>
+ * If an {@code @ASN1XmlTypeName} annotation is present on a type,
+ * this value is used as xml tag name instead of their class name that taken
+ * by calling {@code Class.getSimpleName()} when the XER encoding process.
+ * If an ASN.1 identifier includes whitespace, this annotation shall be used.
+ * </p>
+ *
+ * @author WATANABE, Jun. <jwat at bitmeister.jp>
+ */
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ASN1XmlTypeName {
+
+ /**
+ * Indicates the xml tag name for this ASN.1 type.
+ *
+ * @return The xml tag name.
+ */
+ public String value();
+
+}
* Thrown when an error occurred while the decoding process.
*/
public ASN1Type decode() throws ASN1DecodingException;
-
+
}
import jp.bitmeister.asn1.codec.ASN1Decoder;
import jp.bitmeister.asn1.exception.ASN1DecodingException;
import jp.bitmeister.asn1.processor.ASN1Visitor;
-import jp.bitmeister.asn1.type.ASN1Modules;
+import jp.bitmeister.asn1.type.ASN1Module;
+import jp.bitmeister.asn1.type.ASN1ModuleManager;
import jp.bitmeister.asn1.type.ASN1TagClass;
import jp.bitmeister.asn1.type.ASN1TagMode;
import jp.bitmeister.asn1.type.ASN1TagValue;
import jp.bitmeister.asn1.type.builtin.OBJECT_IDENTIFIER;
import jp.bitmeister.asn1.type.builtin.OCTET_STRING;
import jp.bitmeister.asn1.type.builtin.REAL;
+import jp.bitmeister.asn1.type.builtin.RELATIVE_OID;
import jp.bitmeister.asn1.type.builtin.SEQUENCE;
import jp.bitmeister.asn1.type.builtin.SEQUENCE_OF;
import jp.bitmeister.asn1.type.builtin.SET;
*/
public class BerDecoder implements ASN1Decoder,
ASN1Visitor<Void, ASN1DecodingException> {
+
+ private Class<? extends ASN1Module> module;
private InputStream in;
public BerDecoder(InputStream in) {
this.in = in;
}
+
+ /**
+ * Instantiates a {@code BerDecoder}.
+ *
+ * @param module
+ * The ASN.1 module used for decoding.
+ * @param in
+ * The {@code InputStream} to be read.
+ */
+ public BerDecoder(Class<? extends ASN1Module> module, InputStream in) {
+ this(in);
+ this.module = module;
+ }
/*
* (non-Javadoc)
@Override
public <T extends ASN1Type> T decode(Class<T> type)
throws ASN1DecodingException {
- return decodeImpl(ASN1Type.instantiate(type));
+ T data = ASN1Type.instantiate(type);
+ if (module == null) {
+ module = data.specification().module();
+ }
+ return decodeImpl(data);
}
/**
throws ASN1DecodingException {
readTag();
if (data == null) {
- data = (T) ASN1Modules.instantiate(tagClass, tagNumber);
+ data = (T) ASN1ModuleManager.instantiate(module, tagClass, tagNumber);
}
TypeSpecification specification = data.specification();
do {
byte[] octets = readStream(1);
oids.add(octets[0] / 40);
oids.add(octets[0] % 40);
- processMultipleOctets(length - 1, new ElementProcessor() {
+ decodeOids(oids, length - 1);
+ data.set(oids);
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type.builtin.RELATIVE_OID)
+ */
+ @Override
+ public Void visit(RELATIVE_OID data) throws ASN1DecodingException {
+ List<Integer> oids = new ArrayList<Integer>();
+ decodeOids(oids, readLength());
+ data.set(oids);
+ return null;
+ }
+
+ private void decodeOids(final List<Integer> oids, int length) throws ASN1DecodingException {
+ processMultipleOctets(length, new ElementProcessor() {
public void process() throws ASN1DecodingException {
oids.add(readMultipleOctets());
}
});
- data.set(oids);
- return null;
}
/*
@Override
public Void visit(ANY data) throws ASN1DecodingException {
if (data.value() == null) {
- data.set(ASN1Modules.instantiate(tagClass, tagNumber));
+ data.set(ASN1ModuleManager.instantiate(module, tagClass, tagNumber));
}
data.value().accept(this);
return null;
import jp.bitmeister.asn1.type.builtin.OBJECT_IDENTIFIER;
import jp.bitmeister.asn1.type.builtin.OCTET_STRING;
import jp.bitmeister.asn1.type.builtin.REAL;
+import jp.bitmeister.asn1.type.builtin.RELATIVE_OID;
import jp.bitmeister.asn1.type.builtin.SEQUENCE;
import jp.bitmeister.asn1.type.builtin.SEQUENCE_OF;
import jp.bitmeister.asn1.type.builtin.SET;
data.getClass(), null, data);
throw ex;
}
- int size = 1;
- for (int i = 2; i < data.value().size(); i++) {
+ byte[] encoded = new byte[calculateEncodedOidSize(data, 2) + 1];
+ encoded[0] = (byte) (data.value().get(0) * 40 + data.value().get(1));
+ encodeOid(data, encoded, 2, 1);
+ return newPrimitiveOctets(encoded);
+ }
+
+ /* (non-Javadoc)
+ * @see jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type.builtin.RELATIVE_OID)
+ */
+ @Override
+ public EncodedOctets visit(RELATIVE_OID data) throws ASN1EncodingException {
+ byte[] encoded = new byte[calculateEncodedOidSize(data, 0)];
+ encodeOid(data, encoded, 0, 0);
+ return newPrimitiveOctets(encoded);
+ }
+
+ private int calculateEncodedOidSize(OBJECT_IDENTIFIER data, int start) throws ASN1EncodingException {
+ int size = 0;
+ for (int i = start; i < data.value().size(); i++) {
if (data.value().get(i) < 0) {
ASN1EncodingException ex = new ASN1EncodingException();
ex.setMessage(
}
size += sizeBy7bits(data.value().get(i));
}
- byte[] encoded = new byte[size];
- encoded[0] = (byte) (data.value().get(0) * 40 + data.value().get(1));
- int offset = 1;
- for (int i = 2; i < data.value().size(); i++) {
- offset += encodeToMutipleOctets(encoded, offset, data.value()
- .get(i));
+ return size;
+ }
+
+ private void encodeOid(OBJECT_IDENTIFIER data, byte[] dest, int start, int offset) {
+ for (int i = start; i < data.value().size(); i++) {
+ offset += encodeToMutipleOctets(dest, offset, data.value().get(i));
}
- return newPrimitiveOctets(encoded);
}
-
+
/*
* (non-Javadoc)
*
--- /dev/null
+/*
+ * Copyright 2011 BitMeister Inc.
+ *
+ * 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 jp.bitmeister.asn1.codec.xer;
+
+/**
+ * XER constants.
+ *
+ * <p>
+ * This class contains some constants used by XER codec.
+ * </p>
+ *
+ * @author WATANABE, Jun. <jwat at bitmeister.jp>
+ */
+class XerConstants {
+ static final String XML_PROLOG = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+ static final String TAG_START = "<";
+ static final String END_TAG_START = "</";
+ static final String TAG_END = ">";
+ static final String SINGLE_TAG_END = "/>";
+ static final String BOOLEAN_TRUE = "true";
+ static final String BOOLEAN_FALSE = "false";
+ static final String REAL_PLUS_INFINITY = "PLUS-INFINITY";
+ static final String REAL_MINUS_INFINITY = "MINUS-INFINITY";
+ static final String REAL_ZERO = "0";
+}
--- /dev/null
+/*
+ * Copyright 2011 BitMeister Inc.
+ *
+ * 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 jp.bitmeister.asn1.codec.xer;
+
+import static jp.bitmeister.asn1.codec.xer.XerConstants.BOOLEAN_FALSE;
+import static jp.bitmeister.asn1.codec.xer.XerConstants.BOOLEAN_TRUE;
+import static jp.bitmeister.asn1.codec.xer.XerConstants.REAL_MINUS_INFINITY;
+import static jp.bitmeister.asn1.codec.xer.XerConstants.REAL_PLUS_INFINITY;
+
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import jp.bitmeister.asn1.codec.ASN1Decoder;
+import jp.bitmeister.asn1.exception.ASN1DecodingException;
+import jp.bitmeister.asn1.processor.ASN1Visitor;
+import jp.bitmeister.asn1.type.ASN1Module;
+import jp.bitmeister.asn1.type.ASN1ModuleManager;
+import jp.bitmeister.asn1.type.ASN1Type;
+import jp.bitmeister.asn1.type.CollectionType;
+import jp.bitmeister.asn1.type.NamedTypeSpecification;
+import jp.bitmeister.asn1.type.StringType;
+import jp.bitmeister.asn1.type.StructuredType;
+import jp.bitmeister.asn1.type.TimeType;
+import jp.bitmeister.asn1.type.UnknownType;
+import jp.bitmeister.asn1.type.builtin.ANY;
+import jp.bitmeister.asn1.type.builtin.BIT_STRING;
+import jp.bitmeister.asn1.type.builtin.BOOLEAN;
+import jp.bitmeister.asn1.type.builtin.CHOICE;
+import jp.bitmeister.asn1.type.builtin.ENUMERATED;
+import jp.bitmeister.asn1.type.builtin.INTEGER;
+import jp.bitmeister.asn1.type.builtin.NULL;
+import jp.bitmeister.asn1.type.builtin.OBJECT_IDENTIFIER;
+import jp.bitmeister.asn1.type.builtin.OCTET_STRING;
+import jp.bitmeister.asn1.type.builtin.REAL;
+import jp.bitmeister.asn1.type.builtin.RELATIVE_OID;
+import jp.bitmeister.asn1.type.builtin.SEQUENCE;
+import jp.bitmeister.asn1.type.builtin.SEQUENCE_OF;
+import jp.bitmeister.asn1.type.builtin.SET;
+import jp.bitmeister.asn1.type.builtin.SET_OF;
+import jp.bitmeister.asn1.value.BinString;
+import jp.bitmeister.asn1.value.HexString;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+/**
+ * XER (XML Encoding Rules) decoder.
+ *
+ * <p>
+ * {@code XerDecoder} is an implementation of {@code ASN1Decoder}. It reads a
+ * number of bytes from an {@code InputStream} that is specified when a decoder
+ * is instantiated, and decodes them to an ASN.1 data with XML Encoding Rules
+ * (XER).
+ * </p>
+ *
+ * @author WATANABE, Jun. <jwat at bitmeister.jp>
+ *
+ * @see ASN1Decoder
+ * @see XerEncoder
+ */
+public class XerDecoder implements ASN1Decoder,
+ ASN1Visitor<XerDecoder.DataDecoder, SAXException> {
+
+ /**
+ * Abstract handler class used for decoding an XML element to an ASN.1 data.
+ *
+ * @author WATANABE, Jun. <jwat at bitmeister.jp>
+ */
+ abstract class DataDecoder {
+
+ /**
+ * Callback method that is called when the data element started.
+ */
+ void startElement(String qName) throws SAXException {
+ }
+
+ /**
+ * Callback method that is called when a child element started.
+ *
+ * @param qName
+ * The element name.
+ * @throws SAXException
+ * Thrown when an error occurred while the process.
+ */
+ void startChildElement(String qName) throws SAXException {
+ }
+
+ /**
+ * Callback method that is called when a character sequence detected in
+ * the XML element.
+ *
+ * @param characters
+ * The character sequence.
+ */
+ void characters(String characters) {
+ }
+
+ /**
+ * Callback method that is called when a child element ended.
+ *
+ * @param qName
+ * The element name.
+ * @throws SAXException
+ * Thrown when an error occurred while the process.
+ */
+ void endChildElement(String qName) {
+ }
+
+ /**
+ * Callback method that is called when the data element ended.
+ */
+ void endElement() {
+ }
+
+ }
+
+ /**
+ * Handler class for constructed data ({@code CollectionType} and
+ * {@code StructuredType}).
+ *
+ * @author WATANABE, Jun. <jwat at bitmeister.jp>
+ */
+ private abstract class ConstructedDataDecoder extends DataDecoder {
+
+ ElementDecoder decoder;
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.DataDecoder#characters(java
+ * .lang.String)
+ */
+ @Override
+ void characters(String characters) {
+ decoder.characters(characters);
+ }
+
+ }
+
+ /**
+ * Abstract handler class used for decoding a sequence of XML elements to an
+ * ASN.1 type data.
+ *
+ * @author WATANABE, Jun. <jwat at bitmeister.jp>
+ */
+ private abstract class ElementDecoder {
+
+ DataDecoder decoder;
+
+ /**
+ * Instantiate an {@code ElementDecoder}.
+ *
+ * @param decoder
+ * The {@code DataDecoder} used for decode the data.
+ */
+ ElementDecoder(DataDecoder decoder) {
+ this.decoder = decoder;
+ }
+
+ /**
+ * Callback method that is called when an XML element started.
+ *
+ * @param qName
+ * The tag name of the element.
+ * @throws SAXException
+ * Thrown when an error occurred while the process.
+ */
+ abstract void startElement(String qName) throws SAXException;
+
+ /**
+ * Callback method that is called when a character sequence detected in
+ * an XML element.
+ *
+ * @param characters
+ * The character sequence.
+ */
+ void characters(String characters) {
+ decoder.characters(characters);
+ }
+
+ /**
+ * Callback method that is called when an XML element ended.
+ *
+ * @param qName
+ * The tag name of the element.
+ * @return {@code true} when the element is enclosing element.
+ */
+ abstract boolean endElement(String qName);
+
+ }
+
+ /**
+ * Handler class used for decoding a sequence of data elements that enclosed
+ * by an element.
+ *
+ * @author WATANABE, Jun. <jwat at bitmeister.jp>
+ */
+ private class NamedElementDecoder extends ElementDecoder {
+
+ private String tagName;
+
+ /**
+ * Instantiate a {@code NamedElementDecoder}.
+ *
+ * @param tagName
+ * The tag name of enclosing element.
+ * @param decoder
+ * The {@code DataDecoder} used for decoding data elements.
+ */
+ NamedElementDecoder(String tagName, DataDecoder decoder) {
+ super(decoder);
+ this.tagName = tagName;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.ElementDecoder#startElement
+ * (java.lang.String)
+ */
+ @Override
+ public void startElement(String qName) throws SAXException {
+ if (qName.equals(tagName)) {
+ decoder.startElement(qName);
+ } else {
+ decoder.startChildElement(qName);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.ElementDecoder#endElement
+ * (java.lang.String)
+ */
+ @Override
+ public boolean endElement(String qName) {
+ if (qName.equals(tagName)) {
+ decoder.endElement();
+ return true;
+ } else {
+ decoder.endChildElement(qName);
+ return false;
+ }
+ }
+
+ }
+
+ /**
+ * Handler class used for decoding an data element that is not enclosed by
+ * element.
+ *
+ * @author WATANABE, Jun. <jwat at bitmeister.jp>
+ */
+ private class ValueListElementDecoder extends ElementDecoder {
+
+ /**
+ * Instantiate a {@code ValueListElementDecoder}.
+ *
+ * @param decoder
+ * The {@code DataDecoder} used for decoding the data
+ * element.
+ */
+ ValueListElementDecoder(DataDecoder decoder) {
+ super(decoder);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.ElementDecoder#startElement
+ * (java.lang.String)
+ */
+ @Override
+ public void startElement(String qName) throws SAXException {
+ decoder.startElement(qName);
+ decoder.startChildElement(qName);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.ElementDecoder#endElement
+ * (java.lang.String)
+ */
+ @Override
+ public boolean endElement(String qName) {
+ decoder.endChildElement(qName);
+ decoder.endElement();
+ return true;
+ }
+
+ }
+
+ /**
+ * Abstract handler class used for decosing an XML document to an ASN.1
+ * data.
+ *
+ * @author WATANABE, Jun. <jwat at bitmeister.jp>
+ */
+ private abstract class XerHandler<T extends ASN1Type> extends
+ DefaultHandler {
+
+ T data;
+
+ private ElementDecoder decoder;
+
+ /**
+ * Instantiates an ASN.1 data and sets it to this instance.
+ *
+ * @param qName
+ * The tag name of the head element of the XML document.
+ * @throws SAXException
+ * Thrown when an error occurred while the process.
+ */
+ abstract void initializeData(String qName) throws SAXException;
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String,
+ * java.lang.String, java.lang.String, org.xml.sax.Attributes)
+ */
+ @Override
+ public void startElement(String uri, String localName, String qName,
+ Attributes attributes) throws SAXException {
+ if (data == null) {
+ initializeData(qName);
+ decoder = new NamedElementDecoder(qName,
+ data.accept(XerDecoder.this));
+ }
+ decoder.startElement(qName);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
+ */
+ @Override
+ public void characters(char[] ch, int start, int length) {
+ decoder.characters(String.valueOf(Arrays.copyOfRange(ch, start,
+ start + length)));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String,
+ * java.lang.String, java.lang.String)
+ */
+ @Override
+ public void endElement(String uri, String localName, String qName) {
+ decoder.endElement(qName);
+ }
+
+ }
+
+ private Class<? extends ASN1Module> module;
+
+ private InputStream in;
+
+ private XMLReader reader;
+
+ /**
+ * Instantiates a {@code XerDecoder}.
+ *
+ * @param in
+ * The {@code InputStream} to be read.
+ */
+ public XerDecoder(InputStream in) {
+ this.in = in;
+ }
+
+ /**
+ * Instantiates a {@code XerDecoder}.
+ *
+ * @param in
+ * The {@code InputStream} to be read.
+ */
+ public XerDecoder(Class<? extends ASN1Module> module, InputStream in) {
+ this(in);
+ this.module = module;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see jp.bitmeister.asn1.codec.ASN1Decoder#decode(java.lang.Class)
+ */
+ @Override
+ public <T extends ASN1Type> T decode(final Class<T> type)
+ throws ASN1DecodingException {
+ XerHandler<T> handler = new XerHandler<T>() {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.XerHandler#initializeData
+ * (java.lang.String)
+ */
+ @Override
+ void initializeData(String qName) throws SAXException {
+ data = ASN1Type.instantiate(type);
+ if (module == null) {
+ module = data.specification().module();
+ }
+ if (!(data instanceof ANY)
+ && !qName.equals(data.specification().xmlTypeName(module))) {
+ throw new SAXException("Unexpected xml tag name '" + qName
+ + "'. '" + data.specification().xmlTypeName(module)
+ + "' is expected.");
+ }
+ }
+
+ };
+ parse(handler);
+ return handler.data;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see jp.bitmeister.asn1.codec.ASN1Decoder#decode()
+ */
+ @Override
+ public ASN1Type decode() throws ASN1DecodingException {
+ XerHandler<ASN1Type> handler = new XerHandler<ASN1Type>() {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.XerHandler#initializeData
+ * (java.lang.String)
+ */
+ @Override
+ void initializeData(String qName) {
+ data = ASN1ModuleManager.instantiate(module, qName);
+ }
+
+ };
+ parse(handler);
+ return handler.data;
+ }
+
+ /**
+ * Decodes the source XML data read from the {@code InputStream} to the
+ * ASN.1 data.
+ *
+ * @throws ASN1DecodingException
+ * When an error occurred while the decoding process.
+ */
+ private void parse(XerHandler<?> handler) throws ASN1DecodingException {
+ try {
+ reader = XMLReaderFactory.createXMLReader();
+ reader.setContentHandler(handler);
+ reader.parse(new InputSource(in));
+ } catch (Exception e) {
+ ASN1DecodingException ex = new ASN1DecodingException();
+ ex.setMessage("An exception thrown while decoding process.", e,
+ null, null, null);
+ throw ex;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.BOOLEAN)
+ */
+ @Override
+ public DataDecoder visit(final BOOLEAN data) {
+ return new DataDecoder() {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.DataDecoder#startChildElement
+ * (java.lang.String)
+ */
+ @Override
+ void startChildElement(String qName) {
+ if (qName.equals(BOOLEAN_TRUE)) {
+ data.set(true);
+ } else if (qName.equals(BOOLEAN_FALSE)) {
+ data.set(false);
+ }
+ }
+
+ };
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.INTEGER)
+ */
+ @Override
+ public DataDecoder visit(final INTEGER data) {
+ return new DataDecoder() {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.DataDecoder#startChildElement
+ * (java.lang.String)
+ */
+ @Override
+ void startChildElement(String qName) {
+ data.set(qName);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.DataDecoder#characters
+ * (java.lang.String)
+ */
+ @Override
+ public void characters(String characters) {
+ data.set(new BigInteger(characters));
+ }
+
+ };
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.BIT_STRING)
+ */
+ @Override
+ public DataDecoder visit(final BIT_STRING data) {
+ return new DataDecoder() {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.DataDecoder#startChildElement
+ * (java.lang.String)
+ */
+ @Override
+ public void startChildElement(String qName) {
+ data.set(qName);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.DataDecoder#characters
+ * (java.lang.String)
+ */
+ @Override
+ public void characters(String characters) {
+ data.set(new BinString(characters));
+ }
+
+ };
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.OCTET_STRING)
+ */
+ @Override
+ public DataDecoder visit(final OCTET_STRING data) {
+ return new DataDecoder() {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.DataDecoder#characters
+ * (java.lang.String)
+ */
+ @Override
+ public void characters(String characters) {
+ data.set(new HexString(characters));
+ }
+
+ };
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.NULL)
+ */
+ @Override
+ public DataDecoder visit(NULL data) {
+ return new DataDecoder() {
+ };
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.OBJECT_IDENTIFIER)
+ */
+ @Override
+ public DataDecoder visit(final OBJECT_IDENTIFIER data) {
+ return new DataDecoder() {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.DataDecoder#characters
+ * (java.lang.String)
+ */
+ @Override
+ public void characters(String characters) {
+ List<Integer> oids = new ArrayList<Integer>();
+ for (String e : characters.split("\\.")) {
+ e.trim();
+ if (Character.isLetter(e.charAt(0))) {
+ if (!e.endsWith(")")) {
+ oids.add(OBJECT_IDENTIFIER.nameFormToInt(oids, e));
+ continue;
+ }
+ e = e.substring(e.indexOf('(') + 1, e.indexOf(')'));
+ }
+ oids.add(Integer.parseInt(e));
+ }
+ data.set(oids);
+ }
+
+ };
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.REAL)
+ */
+ @Override
+ public DataDecoder visit(final REAL data) {
+ return new DataDecoder() {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.DataDecoder#startChildElement
+ * (java.lang.String)
+ */
+ @Override
+ public void startChildElement(String qName) {
+ if (qName.equals(REAL_PLUS_INFINITY)) {
+ data.set(Double.POSITIVE_INFINITY);
+ } else if (qName.equals(REAL_MINUS_INFINITY)) {
+ data.set(Double.NEGATIVE_INFINITY);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.DataDecoder#characters
+ * (java.lang.String)
+ */
+ @Override
+ public void characters(String characters) {
+ data.set(Double.parseDouble(characters));
+ }
+
+ };
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.ENUMERATED)
+ */
+ @Override
+ public DataDecoder visit(ENUMERATED data) {
+ return visit((INTEGER) data);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.RELATIVE_OID)
+ */
+ @Override
+ public DataDecoder visit(RELATIVE_OID data) {
+ return visit((OBJECT_IDENTIFIER) data);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.ANY)
+ */
+ @Override
+ public DataDecoder visit(final ANY data) {
+ return new DataDecoder() {
+
+ private DataDecoder decoder;
+
+ @Override
+ void startElement(String qName) throws SAXException {
+ data.set(ASN1ModuleManager.instantiate(module, qName));
+ decoder = data.value().accept(XerDecoder.this);
+ decoder.startElement(qName);
+ }
+
+ @Override
+ void startChildElement(String qName) throws SAXException {
+ decoder.startChildElement(qName);
+ }
+
+ @Override
+ void characters(String characters) {
+ decoder.characters(characters);
+ }
+
+ @Override
+ void endChildElement(String qName) {
+ decoder.endChildElement(qName);
+ }
+
+ @Override
+ void endElement() {
+ decoder.endElement();
+ }
+
+ };
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.CHOICE)
+ */
+ @Override
+ public DataDecoder visit(CHOICE data) {
+ return decoderForStructuredType(data);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.SEQUENCE_OF)
+ */
+ @Override
+ public DataDecoder visit(SEQUENCE_OF<? extends ASN1Type> data) {
+ return decoderForCollectionType(data);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.SEQUENCE)
+ */
+ @Override
+ public DataDecoder visit(SEQUENCE data) {
+ return decoderForStructuredType(data);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.SET_OF)
+ */
+ @Override
+ public DataDecoder visit(SET_OF<? extends ASN1Type> data) {
+ return decoderForCollectionType(data);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.SET)
+ */
+ @Override
+ public DataDecoder visit(SET data) {
+ return decoderForStructuredType(data);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .StringType)
+ */
+ @Override
+ public DataDecoder visit(final StringType data) {
+ return new DataDecoder() {
+
+ private StringBuilder builder = new StringBuilder();
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.DataDecoder#startChildElement
+ * (java.lang.String)
+ */
+ @Override
+ public void startChildElement(String qName) {
+ builder.append(XerStringEscapeUtil.unescapeCtrl(qName));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.DataDecoder#characters
+ * (java.lang.String)
+ */
+ @Override
+ public void characters(String characters) {
+ builder.append(characters);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.DataDecoder#endElement()
+ */
+ @Override
+ void endElement() {
+ if (builder.length() > 0) {
+ data.set(builder.toString());
+ }
+ }
+
+ };
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .TimeType)
+ */
+ @Override
+ public DataDecoder visit(TimeType data) {
+ return visit((StringType) data);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .UnknownType)
+ */
+ @Override
+ public DataDecoder visit(UnknownType data) throws SAXException {
+ throw new SAXException("UnkownType is not supported by XerDecoder.");
+ }
+
+ /**
+ * Returns handler for the {@code StructuredType}.
+ *
+ * @param data
+ * The {@code StructuredType} data.
+ * @return Handler for the data.
+ */
+ private DataDecoder decoderForStructuredType(final StructuredType data) {
+ return new ConstructedDataDecoder() {
+
+ private NamedTypeSpecification namedType;
+
+ private ASN1Type element;
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.DataDecoder#startChildElement
+ * (java.lang.String)
+ */
+ @Override
+ void startChildElement(String qName) throws SAXException {
+ if (decoder == null) {
+ namedType = data.getElement(qName);
+ element = namedType.instantiate();
+ decoder = new NamedElementDecoder(qName,
+ element.accept(XerDecoder.this));
+ }
+ decoder.startElement(qName);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.DataDecoder#endChildElement
+ * (java.lang.String)
+ */
+ @Override
+ void endChildElement(String qName) {
+ if (decoder.endElement(qName)) {
+ data.set(namedType, element);
+ decoder = null;
+ }
+ }
+
+ };
+ }
+
+ /**
+ * Returns handler for the {@code CollectionType}.
+ *
+ * @param data
+ * The {@code CollectionType} data.
+ * @return Handler for the data.
+ */
+ private <T extends ASN1Type> DataDecoder decoderForCollectionType(
+ final CollectionType<T> data) {
+ return new ConstructedDataDecoder() {
+
+ private T component;
+
+ private boolean useXmlValueList = false;
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.DataDecoder#startElement
+ * ()
+ */
+ @Override
+ void startElement(String qName) {
+ if (BOOLEAN.class.isAssignableFrom(data.componentType())
+ || ENUMERATED.class.isAssignableFrom(data.componentType())
+ || CHOICE.class.isAssignableFrom(data.componentType())) {
+ useXmlValueList = true;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.DataDecoder#startChildElement
+ * (java.lang.String)
+ */
+ @Override
+ void startChildElement(String qName) throws SAXException {
+ if (decoder == null) {
+ component = ASN1Type.instantiate(data.componentType());
+ if (useXmlValueList) {
+ decoder = new ValueListElementDecoder(
+ component.accept(XerDecoder.this));
+ } else {
+ decoder = new NamedElementDecoder(qName,
+ component.accept(XerDecoder.this));
+ }
+ }
+ decoder.startElement(qName);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.codec.xer.XerDecoder.DataDecoder#endChildElement
+ * (java.lang.String)
+ */
+ @Override
+ void endChildElement(String qName) {
+ if (decoder.endElement(qName)) {
+ data.collection().add(component);
+ decoder = null;
+ }
+ }
+
+ };
+ }
+
+}
--- /dev/null
+/*
+ * Copyright 2011 BitMeister Inc.
+ *
+ * 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 jp.bitmeister.asn1.codec.xer;
+
+import static jp.bitmeister.asn1.codec.xer.XerConstants.BOOLEAN_FALSE;
+import static jp.bitmeister.asn1.codec.xer.XerConstants.BOOLEAN_TRUE;
+import static jp.bitmeister.asn1.codec.xer.XerConstants.END_TAG_START;
+import static jp.bitmeister.asn1.codec.xer.XerConstants.REAL_MINUS_INFINITY;
+import static jp.bitmeister.asn1.codec.xer.XerConstants.REAL_PLUS_INFINITY;
+import static jp.bitmeister.asn1.codec.xer.XerConstants.REAL_ZERO;
+import static jp.bitmeister.asn1.codec.xer.XerConstants.SINGLE_TAG_END;
+import static jp.bitmeister.asn1.codec.xer.XerConstants.TAG_END;
+import static jp.bitmeister.asn1.codec.xer.XerConstants.TAG_START;
+import static jp.bitmeister.asn1.codec.xer.XerConstants.XML_PROLOG;
+
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.math.BigDecimal;
+
+import jp.bitmeister.asn1.codec.ASN1Encoder;
+import jp.bitmeister.asn1.exception.ASN1EncodingException;
+import jp.bitmeister.asn1.processor.ASN1Visitor;
+import jp.bitmeister.asn1.type.ASN1Module;
+import jp.bitmeister.asn1.type.ASN1Type;
+import jp.bitmeister.asn1.type.CollectionType;
+import jp.bitmeister.asn1.type.ConstructiveType;
+import jp.bitmeister.asn1.type.ElementSpecification;
+import jp.bitmeister.asn1.type.StringType;
+import jp.bitmeister.asn1.type.TimeType;
+import jp.bitmeister.asn1.type.UnknownType;
+import jp.bitmeister.asn1.type.builtin.ANY;
+import jp.bitmeister.asn1.type.builtin.BIT_STRING;
+import jp.bitmeister.asn1.type.builtin.BOOLEAN;
+import jp.bitmeister.asn1.type.builtin.CHOICE;
+import jp.bitmeister.asn1.type.builtin.ENUMERATED;
+import jp.bitmeister.asn1.type.builtin.INTEGER;
+import jp.bitmeister.asn1.type.builtin.NULL;
+import jp.bitmeister.asn1.type.builtin.OBJECT_IDENTIFIER;
+import jp.bitmeister.asn1.type.builtin.OCTET_STRING;
+import jp.bitmeister.asn1.type.builtin.REAL;
+import jp.bitmeister.asn1.type.builtin.RELATIVE_OID;
+import jp.bitmeister.asn1.type.builtin.SEQUENCE;
+import jp.bitmeister.asn1.type.builtin.SEQUENCE_OF;
+import jp.bitmeister.asn1.type.builtin.SET;
+import jp.bitmeister.asn1.type.builtin.SET_OF;
+import jp.bitmeister.asn1.value.BinString;
+import jp.bitmeister.asn1.value.HexString;
+
+/**
+ * XER (XML Encoding Rules) encoder.
+ *
+ * <p>
+ * {@code XerEncoder} is an implementation of {@code ASN1Encoder}. It encodes an
+ * ASN.1 data to an XML document with XML Encoding Rules(XER) and writes the
+ * result to {@code OutputStream} that is specified when the encoder was
+ * instantiated. XER encoding process is light-weight compared with Canonical
+ * XML Encoding Rules (CXER) encoding because some restrictions on CXER are not
+ * considered.
+ * </p>
+ *
+ * @author WATANABE, Jun. <jwat at bitmeister.jp>
+ *
+ * @see ASN1Encoder
+ * @see XerDecoder
+ */
+public class XerEncoder implements ASN1Encoder,
+ ASN1Visitor<String, ASN1EncodingException> {
+
+ private Class<? extends ASN1Module> module;
+
+ private OutputStream out;
+
+ private StringBuilder builder;
+
+ private boolean prologIsEmpty = false;
+
+ /**
+ * Instantiates a {@code XEREncoder}.
+ *
+ * @param out
+ * The {@code OutputStream} that encoded octets will be written.
+ */
+ public XerEncoder(OutputStream out) {
+ this.out = out;
+ }
+
+ /**
+ * Instantiates a {@code XEREncoder}.
+ *
+ * @param module
+ * The ASN.1 module used for encoding.
+ * @param out
+ * The {@code OutputStream} that encoded octets will be written.
+ */
+ public XerEncoder(Class<? extends ASN1Module> module, OutputStream out) {
+ this(out);
+ this.module = module;
+ }
+
+ /**
+ * Instantiates a {@code XEREncoder}.
+ *
+ * @param out
+ * The {@code OutputStream} that encoded octets will be written.
+ * @param prologIsEmpty
+ * If {@code true}, the XML prolog for result XML document will
+ * be empty.
+ */
+ public XerEncoder(OutputStream out, boolean prologIsEmpty) {
+ this(out);
+ this.prologIsEmpty = prologIsEmpty;
+ }
+
+ /**
+ * Instantiates a {@code XEREncoder}.
+ *
+ * @param module
+ * The ASN.1 module used for encoding.
+ * @param out
+ * The {@code OutputStream} that encoded octets will be written.
+ * @param prologIsEmpty
+ * If {@code true}, the XML prolog for result XML document will
+ * be empty.
+ */
+ public XerEncoder(Class<? extends ASN1Module> module, OutputStream out, boolean prologIsEmpty) {
+ this(module, out);
+ this.prologIsEmpty = prologIsEmpty;
+ }
+
+ /**
+ * Encodes an ASN.1 data.
+ *
+ * @param data
+ * The ASN.1 data to be encoded.
+ * @return The size of encoded octets.
+ * @throws ASN1EncodingException
+ * When an error occurred while the encoding process.
+ */
+ @Override
+ public int encode(ASN1Type data) throws ASN1EncodingException {
+ if (module == null) {
+ module = data.specification().module();
+ }
+ builder = new StringBuilder();
+ if (!prologIsEmpty) {
+ builder.append(XML_PROLOG);
+ }
+ encodeImpl(data, data.specification().xmlTypeName(module));
+ try {
+ byte[] result = builder.toString().getBytes("UTF-8");
+ out.write(result);
+ return result.length;
+ } catch (Exception e) {
+ ASN1EncodingException ex = new ASN1EncodingException();
+ ex.setMessage("Failed to write result to stream.", e, null, null,
+ data);
+ throw ex;
+ }
+ }
+
+ /**
+ * Encodes the source data to xml documents and writes it to the
+ * {@code builder}.
+ *
+ * @param data
+ * The ASN.1 data to be encoded.
+ * @param xmlTagName
+ * The xml tag name to be set.
+ * @throws ASN1EncodingException
+ * When an error occurred while the encoding process.
+ */
+ private void encodeImpl(ASN1Type data, String xmlTagName)
+ throws ASN1EncodingException {
+ String contents = data.accept(this);
+ if (contents == null) {
+ return;
+ } else if (contents.length() == 0) {
+ builder.append(TAG_START).append(xmlTagName).append(SINGLE_TAG_END);
+ } else {
+ builder.append(TAG_START).append(xmlTagName).append(TAG_END);
+ builder.append(contents);
+ builder.append(END_TAG_START).append(xmlTagName).append(TAG_END);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.BOOLEAN)
+ */
+ @Override
+ public String visit(BOOLEAN data) {
+ return TAG_START + (data.value() ? BOOLEAN_TRUE : BOOLEAN_FALSE)
+ + SINGLE_TAG_END;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.INTEGER)
+ */
+ @Override
+ public String visit(INTEGER data) {
+ String numberId = data.identifier();
+ if (numberId != null) {
+ return TAG_START + numberId + SINGLE_TAG_END;
+ } else {
+ return data.value().toString();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.BIT_STRING)
+ */
+ @Override
+ public String visit(BIT_STRING data) {
+ if (data.hasNamedBits()) {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < data.size(); i++) {
+ if (data.bit(i)) {
+ String name = data.nameOfBit(i);
+ if (name != null) {
+ builder.append(TAG_START).append(name)
+ .append(SINGLE_TAG_END);
+ }
+ }
+ }
+ return builder.toString();
+ } else {
+ return new BinString(data.value()).toString();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.OCTET_STRING)
+ */
+ @Override
+ public String visit(OCTET_STRING data) {
+ return new HexString(data.value()).toString();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.NULL)
+ */
+ @Override
+ public String visit(NULL data) {
+ return "";
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.OBJECT_IDENTIFIER)
+ */
+ @Override
+ public String visit(OBJECT_IDENTIFIER data) {
+ StringBuilder builder = new StringBuilder();
+ if (data.value().size() > 0) {
+ builder.append(data.value().get(0));
+ for (int i = 1; i < data.value().size(); i++) {
+ builder.append('.').append(data.value().get(i));
+ }
+ }
+ return builder.toString();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.RELATIVE_OID)
+ */
+ @Override
+ public String visit(RELATIVE_OID data) {
+ return visit((OBJECT_IDENTIFIER) data);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.REAL)
+ */
+ @Override
+ public String visit(REAL data) {
+ if (data.value().isInfinite()) {
+ return TAG_START
+ + (data.value() == Double.POSITIVE_INFINITY ? REAL_PLUS_INFINITY
+ : REAL_MINUS_INFINITY) + SINGLE_TAG_END;
+ }
+ if (data.value() == 0) {
+ return REAL_ZERO;
+ }
+ return BigDecimal.valueOf(data.value()).stripTrailingZeros()
+ .toPlainString();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.ENUMERATED)
+ */
+ @Override
+ public String visit(ENUMERATED data) {
+ return visit((INTEGER) data);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.ANY)
+ */
+ @Override
+ public String visit(ANY data) throws ASN1EncodingException {
+ encodeImpl(data.value(),
+ data.value().specification().xmlTypeName(module));
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.CHOICE)
+ */
+ @Override
+ public String visit(CHOICE data) throws ASN1EncodingException {
+ StringBuilder enclosure = builder;
+ builder = new StringBuilder();
+ encodeImpl(data.selectedValue(), data.selectedIdentifier());
+ String result = builder.toString();
+ builder = enclosure;
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.SEQUENCE_OF)
+ */
+ @Override
+ public String visit(SEQUENCE_OF<? extends ASN1Type> data)
+ throws ASN1EncodingException {
+ return processCollection(data);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.SEQUENCE)
+ */
+ @Override
+ public String visit(SEQUENCE data) throws ASN1EncodingException {
+ return processConstructive(data);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.SET_OF)
+ */
+ @Override
+ public String visit(SET_OF<? extends ASN1Type> data)
+ throws ASN1EncodingException {
+ return processCollection(data);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .builtin.SET)
+ */
+ @Override
+ public String visit(SET data) throws ASN1EncodingException {
+ return processConstructive(data);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .StringType)
+ */
+ @Override
+ public String visit(StringType data) throws ASN1EncodingException {
+ try {
+ return new String(XerStringEscapeUtil.escape(data.stringValue())
+ .getBytes("Shift_JIS"));
+ } catch (UnsupportedEncodingException e) {
+ throw new ASN1EncodingException();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .TimeType)
+ */
+ @Override
+ public String visit(TimeType data) throws ASN1EncodingException {
+ return data.stringValue();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
+ * .UnknownType)
+ */
+ @Override
+ public String visit(UnknownType data) throws ASN1EncodingException {
+ ASN1EncodingException ex = new ASN1EncodingException();
+ ex.setMessage("Can't encode unknown type.", null, data.getClass(),
+ null, data);
+ throw ex;
+ }
+
+ /**
+ * Encodes each element of the {@code ConstructiveType} data.
+ *
+ * @param data
+ * The data to be encoded.
+ * @throws ASN1EncodingException
+ * When an error occurred while the encoding process.
+ */
+ private String processConstructive(ConstructiveType data)
+ throws ASN1EncodingException {
+ StringBuilder enclosure = builder;
+ builder = new StringBuilder();
+ for (ElementSpecification e : data.getElementTypeList()) {
+ ASN1Type component = data.getComponent(e);
+ if (component != null) {
+ encodeImpl(component, e.identifier());
+ }
+ }
+ String result = builder.toString();
+ builder = enclosure;
+ return result;
+ }
+
+ /**
+ * Encodes each component of the {@code CollectionType} data.
+ *
+ * @param data
+ * The data to be encoded.
+ * @throws ASN1EncodingException
+ * When an error occurred while the encoding process.
+ */
+ private String processCollection(CollectionType<? extends ASN1Type> data)
+ throws ASN1EncodingException {
+ StringBuilder enclosure = builder;
+ builder = new StringBuilder();
+ if (BOOLEAN.class.isAssignableFrom(data.componentType())
+ || ENUMERATED.class.isAssignableFrom(data.componentType())
+ || CHOICE.class.isAssignableFrom(data.componentType())) {
+ for (ASN1Type e : data.collection()) {
+ builder.append(e.accept(this));
+ }
+ } else {
+ for (ASN1Type e : data.collection()) {
+ encodeImpl(e, e.specification().xmlTypeName(module));
+ }
+ }
+ String result = builder.toString();
+ builder = enclosure;
+ return result;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright 2011 BitMeister Inc.
+ *
+ * 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 jp.bitmeister.asn1.codec.xer;
+
+import static jp.bitmeister.asn1.codec.xer.XerConstants.SINGLE_TAG_END;
+import static jp.bitmeister.asn1.codec.xer.XerConstants.TAG_START;
+
+/**
+ * Utility class used for escaping/unescaping strings.
+ *
+ * <p>
+ * This class provides utility methods used for escaping and unescaping strings
+ * to follow XER encoding rules.
+ * </p>
+ *
+ * @author WATANABE, Jun. <jwat at bitmeister.jp>
+ */
+class XerStringEscapeUtil {
+
+ private static final String AMP = "amp";
+ private static final String LT = "lt";
+ private static final String GT = "gt";
+
+ private static final String CTRL[] = { "nul", "soh", "stx", "etx", "eot",
+ "enq", "ack", "bel", "bs", "", "", "vt", "ff", "", "so", "si",
+ "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb", "can",
+ "em", "sub", "esc", "is4", "is3", "is2", "is1" };
+
+ /**
+ * Converts the source string to XER escaped string.
+ *
+ * @param string
+ * The source string.
+ * @return Escaped string.
+ */
+ static String escape(String string) {
+ StringBuilder builder = new StringBuilder();
+ for (char c : string.toCharArray()) {
+ if (c < 32 && c != 9 && c != 10 && c != 13) {
+ builder.append(TAG_START).append(CTRL[c])
+ .append(SINGLE_TAG_END);
+ } else if (c == '&') {
+ builder.append('&').append(AMP).append(';');
+ } else if (c == '<') {
+ builder.append('&').append(LT).append(';');
+ } else if (c == '>') {
+ builder.append('&').append(GT).append(';');
+ } else {
+ builder.append(c);
+ }
+ }
+ return builder.toString();
+ }
+
+ /**
+ * Returns a control character specified by the string represents a control
+ * code.
+ *
+ * @param string
+ * The string represents a control code.
+ * @return A control character.
+ */
+ static char unescapeCtrl(String string) {
+ for (char i = 0; i < CTRL.length; i++) {
+ if (string.equals(CTRL[i])) {
+ return i;
+ }
+ }
+ return 0;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright 2011 BitMeister Inc.
+ *
+ * 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.
+ */
+/**
+ * Contains the codec classes used for XER(XML Encoding Rules) encoding and decoding.
+ */
+package jp.bitmeister.asn1.codec.xer;
\ No newline at end of file
import jp.bitmeister.asn1.type.builtin.OBJECT_IDENTIFIER;
import jp.bitmeister.asn1.type.builtin.OCTET_STRING;
import jp.bitmeister.asn1.type.builtin.REAL;
+import jp.bitmeister.asn1.type.builtin.RELATIVE_OID;
import jp.bitmeister.asn1.type.builtin.SEQUENCE;
import jp.bitmeister.asn1.type.builtin.SEQUENCE_OF;
import jp.bitmeister.asn1.type.builtin.SET;
*/
@Override
public Void visit(OBJECT_IDENTIFIER data) {
- builder.append(OID_OPEN).append(SPACE);
- for (int i : data.value()) {
- builder.append(i).append(SPACE);
+ builder.append(OID_OPEN);
+ if (data.value().size() > 0) {
+ builder.append(SPACE).append(data.value().get(0));
+ for (int i = 1; i < data.value().size(); i++) {
+ builder.append('.').append(data.value().get(i));
+ }
+ builder.append(SPACE);
}
builder.append(OID_CLOSE);
return null;
}
+
+ /* (non-Javadoc)
+ * @see jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type.builtin.RELATIVE_OID)
+ */
+ @Override
+ public Void visit(RELATIVE_OID data) {
+ return visit((OBJECT_IDENTIFIER)data);
+ }
/*
* (non-Javadoc)
import jp.bitmeister.asn1.type.builtin.OBJECT_IDENTIFIER;
import jp.bitmeister.asn1.type.builtin.OCTET_STRING;
import jp.bitmeister.asn1.type.builtin.REAL;
+import jp.bitmeister.asn1.type.builtin.RELATIVE_OID;
import jp.bitmeister.asn1.type.builtin.SEQUENCE;
import jp.bitmeister.asn1.type.builtin.SEQUENCE_OF;
import jp.bitmeister.asn1.type.builtin.SET;
public R visit(ENUMERATED data) throws E;
/**
+ * Visits the {@code RELATIVE_OID} data.
+ *
+ * @param data
+ * The data to be visited.
+ * @return Result.
+ * @throws E
+ * When an error occured in the {@code accept} method.
+ */
+ public R visit(RELATIVE_OID data) throws E;
+
+ /**
* Visits the {@code ANY} data.
*
* @param data
import jp.bitmeister.asn1.type.builtin.OBJECT_IDENTIFIER;
import jp.bitmeister.asn1.type.builtin.OCTET_STRING;
import jp.bitmeister.asn1.type.builtin.REAL;
+import jp.bitmeister.asn1.type.builtin.RELATIVE_OID;
import jp.bitmeister.asn1.type.builtin.SEQUENCE;
import jp.bitmeister.asn1.type.builtin.SEQUENCE_OF;
import jp.bitmeister.asn1.type.builtin.SET;
return null;
}
+ /* (non-Javadoc)
+ * @see jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type.builtin.RELATIVE_OID)
+ */
+ @Override
+ public R visit(RELATIVE_OID data) throws E {
+ return null;
+ }
+
/*
* (non-Javadoc)
*
import jp.bitmeister.asn1.codec.ber.BerDecoder;
import jp.bitmeister.asn1.codec.ber.BerEncoder;
import jp.bitmeister.asn1.codec.ber.DerEncoder;
+import jp.bitmeister.asn1.codec.xer.XerDecoder;
+import jp.bitmeister.asn1.codec.xer.XerEncoder;
import jp.bitmeister.asn1.exception.ASN1DecodingException;
import jp.bitmeister.asn1.exception.ASN1EncodingException;
import jp.bitmeister.asn1.sample.FrightStatusTypes.Airport;
+import jp.bitmeister.asn1.sample.FrightStatusTypes.AllFrights;
import jp.bitmeister.asn1.sample.FrightStatusTypes.FrightNumber;
import jp.bitmeister.asn1.sample.FrightStatusTypes.Information;
import jp.bitmeister.asn1.sample.FrightStatusTypes.Status;
-import jp.bitmeister.asn1.type.ASN1Modules;
import jp.bitmeister.asn1.type.ASN1TagClass;
import jp.bitmeister.asn1.type.ASN1Type;
import jp.bitmeister.asn1.type.builtin.INTEGER;
-import jp.bitmeister.asn1.type.builtin.OCTET_STRING;
import jp.bitmeister.asn1.type.useful.UTCTime;
+import jp.bitmeister.asn1.value.HexString;
public class FrightStatusMain {
public static void main(String[] args) {
-
- ASN1Modules.using(new FrightStatusTypes());
-
+
FrightStatus ontime = new FrightStatus(
new FrightNumber("JP041"),
new Information(
new UTCTime("110627090000"),
null
)
- );
-
- derEncAndDec(ontime);
+ );
FrightStatus delay = new FrightStatus(
new FrightNumber("NI022"),
new Status(ASN1TagClass.CONTEXT_SPECIFIC, 1, new INTEGER(10))
);
- derEncAndDec(delay);
-
+ AllFrights frights = new AllFrights();
+ frights.collection().add(delay);
+ frights.collection().add(ontime);
+
+ System.out.println("Source data:");
+ System.out.println(frights);
+
+ System.out.println("BER encoding:");
+ derEncAndDec(frights);
+
+ System.out.println("XER encoding:");
+ xerEncAndDec(frights);
}
static void derEncAndDec(ASN1Type data) {
-
- System.out.println(data);
ByteArrayOutputStream bo = new ByteArrayOutputStream();
BerEncoder enc = new DerEncoder(bo);
try {
e.printStackTrace();
}
- System.out.println(new OCTET_STRING(bo.toByteArray()));
+ System.out.println(new HexString(bo.toByteArray()));
+
+ ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
+ BerDecoder dec = new BerDecoder(FrightStatusTypes.class, bi);
+ try {
+ ASN1Type result = dec.decode();
+ System.out.println("BER decoding:");
+ System.out.println(result);
+ }
+ catch (ASN1DecodingException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ static void xerEncAndDec(ASN1Type data) {
+
+ ByteArrayOutputStream bo = new ByteArrayOutputStream();
+ XerEncoder enc = new XerEncoder(bo);
+ try {
+ enc.encode(data);
+ }
+ catch (ASN1EncodingException e) {
+ e.printStackTrace();
+ }
+ System.out.println(new String(bo.toByteArray()));
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
- BerDecoder dec = new BerDecoder(bi);
+ XerDecoder dec = new XerDecoder(FrightStatusTypes.class, bi);
try {
ASN1Type result = dec.decode();
+ System.out.println("XER decoding:");
System.out.println(result);
}
catch (ASN1DecodingException e) {
Information ::= SEQUENCE {
airport Airport,
- scheduled GeneralizedTime,
- actual GeneralizedTime OPTIONAL
+ scheduled UTCTime,
+ actual UTCTime OPTIONAL
}
Status ::= CHOICE {
status [2] Status DEFAULT onTime:NULL
}
- AllFrights ::= SEQUENCE OF FrightStatus
+ AllFrights ::= [APPLICATION 1] IMPLICIT SEQUENCE OF FrightStatus
END
import java.math.BigInteger;
import jp.bitmeister.asn1.annotation.ASN1Alternative;
+import jp.bitmeister.asn1.annotation.ASN1DefinedTypes;
import jp.bitmeister.asn1.annotation.ASN1Element;
import jp.bitmeister.asn1.annotation.ASN1Enumeration;
import jp.bitmeister.asn1.annotation.ASN1ModuleTags;
+import jp.bitmeister.asn1.annotation.ASN1Tag;
import jp.bitmeister.asn1.type.ASN1Module;
import jp.bitmeister.asn1.type.ASN1TagClass;
import jp.bitmeister.asn1.type.ASN1TagDefault;
+import jp.bitmeister.asn1.type.ASN1TagMode;
import jp.bitmeister.asn1.type.ASN1Type;
import jp.bitmeister.asn1.type.builtin.CHOICE;
import jp.bitmeister.asn1.type.builtin.ENUMERATED;
import jp.bitmeister.asn1.type.useful.UTCTime;
@ASN1ModuleTags(ASN1TagDefault.AUTOMATIC_TAGS)
+@ASN1DefinedTypes(FrightStatus.class)
public class FrightStatusTypes extends ASN1Module {
-
- public FrightStatusTypes() {
- register(FrightStatus.class);
- }
public static class FrightNumber extends PrintableString {
}
+ @ASN1Tag(value = 1, tagClass = ASN1TagClass.APPLICATION, tagMode = ASN1TagMode.IMPLICIT)
public static class AllFrights extends SEQUENCE_OF<FrightStatus> {
public AllFrights() {
*/
package jp.bitmeister.asn1.type;
-import static java.lang.reflect.Modifier.ABSTRACT;
-import static java.lang.reflect.Modifier.INTERFACE;
-import static java.lang.reflect.Modifier.PUBLIC;
-import static java.lang.reflect.Modifier.STATIC;
-
-import java.util.HashMap;
-import java.util.Map;
-
+import jp.bitmeister.asn1.annotation.ASN1DefinedTypes;
import jp.bitmeister.asn1.annotation.ASN1Identifier;
+import jp.bitmeister.asn1.annotation.ASN1ImportedModules;
import jp.bitmeister.asn1.annotation.ASN1ModuleRef;
import jp.bitmeister.asn1.annotation.ASN1ModuleTags;
-import jp.bitmeister.asn1.exception.ASN1IllegalDefinition;
/**
* The base class for classes which represents an ASN.1 module.
*
* <p>
- * An instance of sub-class of {@code ASN1Module} represents an ASN.1 module and
- * provides {@code instantiate} methods that instantiates an ASN.1 data from an
- * ASN.1 tag or a type identifier. ASN.1 types can indicate a module that they
- * are included in by {@code ASN1ModuleRef} annotation. In addition, ASN.1 types
- * declared as {@code public} {@code static} member classes of an sub-class of
+ * An instance of sub-class of {@code ASN1Module} represents an ASN.1 module.
+ * ASN.1 types can indicate a module that they are included in by
+ * {@code ASN1ModuleRef} annotation. In addition, ASN.1 types declared as
+ * {@code public} {@code static} member classes of an sub-class of
* {@code ASN1Module} class are automatically included in the module.
* </p>
* <p>
- * Default tagging mode for this module may be indicated by an
- * {@code @ASN1ModuleTags} annotation or explicit tagging is used as default.
+ * Default tagging mode for a module may be indicated by {@code @ASN1ModuleTags}
+ * annotation or explicit tagging is used as default.
* </p>
*
* @author WATANABE, Jun. <jwat at bitmeister.jp>
*
- * @see ASN1Modules
+ * @see ASN1ModuleManager
* @see ASN1ModuleTags
* @see ASN1ModuleRef
*/
/**
* Returns the default tagging mode of the ASN.1 module.
*
- * @param moduleref
+ * @param module
* The ASN.1 module.
* @return The default tagging mode of the module.
*/
- static ASN1TagDefault tagDefault(Class<? extends ASN1Module> moduleref) {
- if (moduleref.isAnnotationPresent(ASN1ModuleTags.class)) {
- return moduleref.getAnnotation(ASN1ModuleTags.class).value();
+ static ASN1TagDefault tagDefault(Class<? extends ASN1Module> module) {
+ if (module.isAnnotationPresent(ASN1ModuleTags.class)) {
+ return module.getAnnotation(ASN1ModuleTags.class).value();
}
return ASN1TagDefault.EXPLICIT_TAGS;
}
/**
* Returns the identifier of the ASN.1 module.
*
- * @param moduleref
+ * @param module
* The ASN.1 module.
* @return The identifier of the module.
*/
- static String identifier(Class<? extends ASN1Module> moduleref) {
- if (moduleref.isAnnotationPresent(ASN1Identifier.class)) {
- return moduleref.getAnnotation(ASN1Identifier.class).value();
- }
- return moduleref.getSimpleName();
- }
-
- private String identifier;
-
- private Map<String, Class<? extends ASN1Type>> identifierMap = new HashMap<String, Class<? extends ASN1Type>>();
-
- private Map<ASN1TagClass, Map<Integer, Class<? extends ASN1Type>>> tagMap = new HashMap<ASN1TagClass, Map<Integer, Class<? extends ASN1Type>>>();
-
- /**
- * Instantiates an {@code ASN1Module}. The parameter indicates that this
- * module is universal or not.
- *
- * @param isUniversal
- * {@code true} if this module is universal.
- */
- ASN1Module(boolean isUniversal) {
- identifier = identifier(getClass());
- if (isUniversal) {
- tagMap.put(ASN1TagClass.UNIVERSAL,
- new HashMap<Integer, Class<? extends ASN1Type>>());
- } else {
- tagMap.put(ASN1TagClass.APPLICATION,
- new HashMap<Integer, Class<? extends ASN1Type>>());
- tagMap.put(ASN1TagClass.PRIVATE,
- new HashMap<Integer, Class<? extends ASN1Type>>());
+ static String identifier(Class<? extends ASN1Module> module) {
+ if (module.isAnnotationPresent(ASN1Identifier.class)) {
+ return module.getAnnotation(ASN1Identifier.class).value();
}
- for (Class<?> e : getClass().getDeclaredClasses()) {
- if (ASN1Type.class.isAssignableFrom(e)) {
- @SuppressWarnings("unchecked")
- Class<? extends ASN1Type> type = (Class<? extends ASN1Type>) e;
- register(type);
- }
- }
- }
-
- /**
- * Instantiates an {@code ASN1Module}.
- */
- protected ASN1Module() {
- this(false);
+ return module.getSimpleName();
}
/**
- * Returns the identifier of this module.
+ * Tests if the module is the universal module.
*
- * @return The identifier of this module.
+ * @param module
+ * The module to be tested.
+ * @return {@code true} if the module is the universal module.
*/
- public String identifier() {
- return identifier;
+ static boolean isUniversal(Class<? extends ASN1Module> module) {
+ return module == BuiltInModule.class;
}
/**
- * Registers the ASN.1 type to this module.
+ * Registers ASN.1 types which are contained in the ASN.1 module to the
+ * specification.
*
- * @param type
- * The type to be registered.
+ * @param module
+ * The ASN.1 module.
+ * @param spec
+ * The module specification.
*/
- protected void register(Class<? extends ASN1Type> type) {
- if ((type.getModifiers() & (ABSTRACT | INTERFACE)) != 0) {
- ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
- ex.setMessage("An ASN.1 type class must be instantiatable.", null,
- type, null, null);
- throw ex;
- }
- if ((type.getModifiers() & PUBLIC) == 0) {
- ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
- ex.setMessage("An ASN.1 type class must be a public class", null,
- type, null, null);
- throw ex;
- }
- if (type.isMemberClass() && (type.getModifiers() & STATIC) == 0) {
- ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
- ex.setMessage(
- "If an ASN.1 type class is a member class, it must be static.",
- null, type, null, null);
- throw ex;
- }
- TypeSpecification typeSpec = TypeSpecification.getSpecification(type);
- if (typeSpec.tag() != null) {
- Map<Integer, Class<? extends ASN1Type>> map = tagMap.get(typeSpec
- .tag().tagClass());
- if (map == null) {
- ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
- ex.setMessage("Tag class '" + typeSpec.tag().tagClass()
- + "'is not allowed to this module.", null, type, null,
- null);
- throw ex;
- }
- if (map.containsKey(typeSpec.tag().tagNumber())) {
- ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
- ex.setMessage("Tag number '" + typeSpec.tag().tagNumber()
- + "' is dupulicated.", null, type, null, null);
- throw ex;
+ static void registerTypes(Class<? extends ASN1Module> module,
+ ModuleSpecification spec) {
+ for (Class<?> e : module.getDeclaredClasses()) {
+ if (ASN1Type.class.isAssignableFrom(e)) {
+ @SuppressWarnings("unchecked")
+ Class<? extends ASN1Type> type = (Class<? extends ASN1Type>) e;
+ spec.register(type);
}
- map.put(typeSpec.tag().tagNumber(), type);
}
- identifierMap.put(typeSpec.identifier(), type);
- }
-
- /**
- * Instantiates an ASN.1 data specified by the tag.
- *
- * @param tagClass
- * The tag class.
- * @param tagNumber
- * The tag number.
- * @return An instance of ASN.1 data.
- */
- public ASN1Type instantiate(ASN1TagClass tagClass, int tagNumber) {
- Class<? extends ASN1Type> type = tagMap.get(tagClass).get(tagNumber);
- if (type != null) {
- return ASN1Type.instantiate(type);
+ if (module.isAnnotationPresent(ASN1DefinedTypes.class)) {
+ for (Class<? extends ASN1Type> e : module.getAnnotation(
+ ASN1DefinedTypes.class).value()) {
+ spec.register(e);
+ }
}
- return null;
}
/**
- * Instantiates an ASN.1 data specified by the type identifier.
+ * Registers ASN.1 modules which are imported by the ASN.1 module to the
+ * specification.
*
- * @param typeIdentifier
- * The identifier of the type.
- * @return An instance of ASN.1 data.
+ * @param module
+ * The ASN.1 module.
+ * @param spec
+ * The module specification.
*/
- public ASN1Type instantiate(String typeIdentifier) {
- Class<? extends ASN1Type> type = identifierMap.get(typeIdentifier);
- if (type != null) {
- return ASN1Type.instantiate(type);
+ static void importModules(Class<? extends ASN1Module> module,
+ ModuleSpecification spec) {
+ if (module.isAnnotationPresent(ASN1ImportedModules.class)) {
+ for (Class<? extends ASN1Module> e : module.getAnnotation(
+ ASN1ImportedModules.class).value()) {
+ spec.imports(e);
+ }
}
- return null;
}
}
--- /dev/null
+/*
+ * Copyright 2011 BitMeister Inc.
+ *
+ * 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 jp.bitmeister.asn1.type;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Manager of all ASN.1 modules.
+ *
+ * <p>
+ * This class manages all of the ASN.1 modules which are sub-class of
+ * {@code ASN1Module} and provides {@code instantiate} methods which instantiate
+ * an ASN.1 data from an ASN.1 tag or identifier.
+ * </p>
+ *
+ * @author WATANABE, Jun. <jwat at bitmeister.jp>
+ *
+ * @see ASN1Module
+ */
+public class ASN1ModuleManager {
+
+ /**
+ * The manager of ASN.1 modules.
+ */
+ private static ASN1ModuleManager instance = new ASN1ModuleManager();
+
+ /**
+ * Instantiates an ASN.1 data specified by the ASN.1 tag and the tag class.
+ *
+ * @param module
+ * The {@code Class} object of an {@code ASN1Module}.
+ * @param tagClass
+ * The tag class.
+ * @param tagNumber
+ * The tag number.
+ * @return An instance of ASN.1 data.
+ */
+ public static ASN1Type instantiate(Class<? extends ASN1Module> module,
+ ASN1TagClass tagClass, int tagNumber) {
+ if (tagClass == ASN1TagClass.UNIVERSAL) {
+ return specification(BuiltInModule.class).instantiate(tagClass,
+ tagNumber);
+ } else if (module != null) {
+ return specification(module).instantiate(tagClass, tagNumber);
+ }
+ return null;
+ }
+
+ /**
+ * Instantiates an ASN.1 data specified by the type identifier.
+ *
+ * @param module
+ * The {@code Class} object of an {@code ASN1Module}.
+ * @param typeIdentifier
+ * The identifier of a type.
+ * @return An instance of ASN.1 data.
+ */
+ public static ASN1Type instantiate(Class<? extends ASN1Module> module,
+ String typeIdentifier) {
+ ASN1Type data = specification(BuiltInModule.class).instantiate(
+ typeIdentifier);
+ if (data == null && module != null) {
+ data = specification(module).instantiate(typeIdentifier);
+ }
+ return data;
+ }
+
+ /**
+ * Returns a {@code ModuleSpecification} instance contains specifications of
+ * the module.
+ *
+ * @param module
+ * The {@code Class} object of an {@code ASN1Module}.
+ * @return A {@code ModuleSpecification} instance.
+ */
+ static ModuleSpecification specification(Class<? extends ASN1Module> module) {
+ if (module == null) {
+ module = BuiltInModule.class;
+ }
+ ModuleSpecification spec = instance.definedModules.get(module);
+ if (spec == null) {
+ spec = new ModuleSpecification(module);
+ instance.definedModules.put(module, spec);
+ }
+ return spec;
+ }
+
+ /**
+ * Returns default tag mode of the module.
+ *
+ * @param module
+ * The {@code Class} object of the {@code ASN1Module}.
+ * @return Default tag mode of the module.
+ */
+ static ASN1TagDefault tagDefault(Class<? extends ASN1Module> module) {
+ return specification(module).tagDefault();
+ }
+
+ /**
+ * Returns identifier of the module.
+ *
+ * @param module
+ * The {@code Class} object of the {@code ASN1Module}.
+ * @return Identifier of this module.
+ */
+ public static String identifier(Class<? extends ASN1Module> module) {
+ return specification(module).identifier();
+ }
+
+ private Map<Class<? extends ASN1Module>, ModuleSpecification> definedModules = new HashMap<Class<? extends ASN1Module>, ModuleSpecification>();
+
+ /**
+ * Instantiates an {@code ASN1ModuleManager}.
+ */
+ private ASN1ModuleManager() {
+
+ }
+
+}
throw ex;
}
}
-
+
/**
* The constructor with no argument.
*/
* @return {@code true} when this ASN.1 data has value.
*/
public abstract boolean hasValue();
-
+
/**
* Accepts the {@link ASN1Visitor} and calls a {@code visit} method of the
* visitor.
package jp.bitmeister.asn1.type;
import jp.bitmeister.asn1.annotation.ASN1BuiltIn;
+import jp.bitmeister.asn1.annotation.ASN1DefinedTypes;
import jp.bitmeister.asn1.annotation.ASN1Identifier;
import jp.bitmeister.asn1.annotation.ASN1ModuleTags;
import jp.bitmeister.asn1.annotation.ASN1Tag;
import jp.bitmeister.asn1.type.builtin.OBJECT_IDENTIFIER;
import jp.bitmeister.asn1.type.builtin.OCTET_STRING;
import jp.bitmeister.asn1.type.builtin.REAL;
+import jp.bitmeister.asn1.type.builtin.RELATIVE_OID;
import jp.bitmeister.asn1.type.builtin.SEQUENCE_OF;
import jp.bitmeister.asn1.type.builtin.SET_OF;
import jp.bitmeister.asn1.type.useful.BMPString;
* @author WATANABE, Jun. <jwat at bitmeister.jp>
*/
@ASN1ModuleTags(ASN1TagDefault.AUTOMATIC_TAGS)
-public class BuiltInModule extends ASN1Module {
-
- /**
- * Registers all of the UNIVERSAL ASN.1 types to this module.
- */
- BuiltInModule() {
- super(true);
- register(BOOLEAN.class); // UNIVERSAL 1
- register(INTEGER.class); // UNIVERSAL 2
- register(BIT_STRING.class); // UNIVERSAL 3
- register(OCTET_STRING.class); // UNIVERSAL 4
- register(NULL.class); // UNIVERSAL 5
- register(OBJECT_IDENTIFIER.class); // UNIVERSAL 6
- register(ObjectDescriptor.class); // UNIVERSAL 7
- register(EXTERNAL.class); // UNIVERSAL 8
- register(REAL.class); // UNIVERSAL 9
+@ASN1DefinedTypes({
+ BOOLEAN.class, // UNIVERSAL 1
+ INTEGER.class, // UNIVERSAL 2
+ BIT_STRING.class, // UNIVERSAL 3
+ OCTET_STRING.class, // UNIVERSAL 4
+ NULL.class, // UNIVERSAL 5
+ OBJECT_IDENTIFIER.class, // UNIVERSAL 6
+ ObjectDescriptor.class, // UNIVERSAL 7
+ EXTERNAL.class, // UNIVERSAL 8
+ REAL.class, // UNIVERSAL 9
// UnkonwnEnumerated is registered automatically for UNIVERSAL 10.
- register(EMBEDDED_PDV.class); // UNIVERSAL 11
- register(UTF8String.class); // UNIVERSAL 12
- // 13-15 reserved
+ EMBEDDED_PDV.class, // UNIVERSAL 11
+ UTF8String.class, // UNIVERSAL 12
+ RELATIVE_OID.class, // UNIVERSAL 13
+ // 14-15 reserved
// UnkownSequence is registered automatically for UNIVERSAL16
// UnkownSet is registered automatically for UNIVERSAL 17
- register(NumericString.class); // UNIVERSAL 18
- register(PrintableString.class); // UNIVERSAL 19
- register(TeletexString.class); // UNIVERSAL 20
- register(VideotexString.class); // UNIVERSAL 21
- register(IA5String.class); // UNIVERSAL 22
- register(UTCTime.class); // UNIVERSAL 23
- register(GeneralizedTime.class); // UNIVERSAL 24
- register(GraphicString.class); // UNIVERSAL 25
- register(VisibleString.class); // UNIVERSAL 26
- register(GeneralString.class); // UNIVERSAL27
- register(UniversalString.class); // UNIVERSAL 28
- register(CHARACTER_STRING.class); // UNIVERSAL 29
- register(BMPString.class); // UNIVERSAL 30
- }
+ NumericString.class, // UNIVERSAL 18
+ PrintableString.class, // UNIVERSAL 19
+ TeletexString.class, // UNIVERSAL 20
+ VideotexString.class, // UNIVERSAL 21
+ IA5String.class, // UNIVERSAL 22
+ UTCTime.class, // UNIVERSAL 23
+ GeneralizedTime.class, // UNIVERSAL 24
+ GraphicString.class, // UNIVERSAL 25
+ VisibleString.class, // UNIVERSAL 26
+ GeneralString.class, // UNIVERSAL27
+ UniversalString.class, // UNIVERSAL 28
+ CHARACTER_STRING.class, // UNIVERSAL 29
+ BMPString.class // UNIVERSAL 30
+})
+class BuiltInModule extends ASN1Module {
/**
* Represents unknown 'ENUMERATED' type.
public boolean hasValue() {
return true;
}
-
+
/*
* (non-Javadoc)
*
*/
@Override
public void set(String elementName, ASN1Type component) {
- for (ElementSpecification e : getElementTypeList()) {
- if (e.identifier().equals(elementName)) {
- e.assign(this, component);
- return;
- }
- }
- ASN1IllegalArgument ex = new ASN1IllegalArgument();
- ex.setMessage("No such element '" + elementName + "' in this type.",
- null, getClass(), null, null);
- throw ex;
+ set(getElement(elementName), component);
}
/*
*/
@Override
public ASN1Type get(String elementName) {
+ return getElement(elementName).retrieve(this);
+ }
+
+ /* (non-Javadoc)
+ * @see jp.bitmeister.asn1.type.StructuredType#getElement(java.lang.String)
+ */
+ @Override
+ public NamedTypeSpecification getElement(String elementName) {
for (ElementSpecification e : getElementTypeList()) {
if (e.identifier().equals(elementName)) {
- return e.retrieve(this);
+ return e;
}
}
ASN1IllegalArgument ex = new ASN1IllegalArgument();
--- /dev/null
+/*
+ * Copyright 2011 BitMeister Inc.
+ *
+ * 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 jp.bitmeister.asn1.type;
+
+import static java.lang.reflect.Modifier.ABSTRACT;
+import static java.lang.reflect.Modifier.INTERFACE;
+import static java.lang.reflect.Modifier.PUBLIC;
+import static java.lang.reflect.Modifier.STATIC;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import jp.bitmeister.asn1.exception.ASN1IllegalDefinition;
+
+/**
+ * Specifications of an ASN.1 module.
+ *
+ * <p>
+ * An instance of this class contains static information of an ASN.1 module.
+ * </p>
+ *
+ * @author WATANABE, Jun. <jwat at bitmeister.jp>
+ */
+class ModuleSpecification {
+
+ private String identifier;
+
+ private ASN1TagDefault tagDefault;
+
+ private Map<String, Class<? extends ASN1Type>> identifierMap = new HashMap<String, Class<? extends ASN1Type>>();
+
+ private Map<ASN1TagClass, Map<Integer, Class<? extends ASN1Type>>> tagMap = new HashMap<ASN1TagClass, Map<Integer, Class<? extends ASN1Type>>>();
+
+ private Map<String, Class<? extends ASN1Module>> importedModules = new HashMap<String, Class<? extends ASN1Module>>();
+
+ /**
+ * Instantiates a {@code ModuleSpecification} contains specifications of the
+ * ASN.1 module.
+ *
+ * @param module
+ * The {@code Class} object of an {@code ASN1Module}.
+ */
+ ModuleSpecification(Class<? extends ASN1Module> module) {
+ identifier = ASN1Module.identifier(module);
+ tagDefault = ASN1Module.tagDefault(module);
+ if (ASN1Module.isUniversal(module)) {
+ tagMap.put(ASN1TagClass.UNIVERSAL,
+ new HashMap<Integer, Class<? extends ASN1Type>>());
+ } else {
+ tagMap.put(ASN1TagClass.APPLICATION,
+ new HashMap<Integer, Class<? extends ASN1Type>>());
+ tagMap.put(ASN1TagClass.PRIVATE,
+ new HashMap<Integer, Class<? extends ASN1Type>>());
+ }
+ ASN1Module.registerTypes(module, this);
+ ASN1Module.importModules(module, this);
+ }
+
+ /**
+ * Registers an imported module to this module.
+ *
+ * @param module
+ * An ASN.1 module imported by this module.
+ */
+ void imports(Class<? extends ASN1Module> module) {
+ importedModules.put(ASN1Module.identifier(module), module);
+ }
+
+ /**
+ * Registers a ASN.1 type to this module.
+ *
+ * @param type
+ * An ASN.1 type contained in this module.
+ */
+ void register(Class<? extends ASN1Type> type) {
+ if ((type.getModifiers() & (ABSTRACT | INTERFACE)) != 0) {
+ ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
+ ex.setMessage("An ASN.1 type class must be instantiatable.", null,
+ type, null, null);
+ throw ex;
+ }
+ if ((type.getModifiers() & PUBLIC) == 0) {
+ ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
+ ex.setMessage("An ASN.1 type class must be a public class", null,
+ type, null, null);
+ throw ex;
+ }
+ if (type.isMemberClass() && (type.getModifiers() & STATIC) == 0) {
+ ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
+ ex.setMessage(
+ "If an ASN.1 type class is a member class, it must be static.",
+ null, type, null, null);
+ throw ex;
+ }
+ TypeSpecification typeSpec = TypeSpecification.getSpecification(type);
+ if (typeSpec.tag() != null) {
+ Map<Integer, Class<? extends ASN1Type>> map = tagMap.get(typeSpec
+ .tag().tagClass());
+ if (map == null) {
+ ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
+ ex.setMessage("Tag class '" + typeSpec.tag().tagClass()
+ + "'is not allowed to this module.", null, type, null,
+ null);
+ throw ex;
+ }
+ if (map.containsKey(typeSpec.tag().tagNumber())) {
+ ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
+ ex.setMessage("Tag number '" + typeSpec.tag().tagNumber()
+ + "' is dupulicated.", null, type, null, null);
+ throw ex;
+ }
+ map.put(typeSpec.tag().tagNumber(), type);
+ }
+ identifierMap.put(typeSpec.xmlTypeName(), type);
+ }
+
+ /**
+ * Returns identifier of this module.
+ *
+ * @return Identifier.
+ */
+ String identifier() {
+ return identifier;
+ }
+
+ /**
+ * Returns default tag mode of this module.
+ *
+ * @return Default tag mode.
+ */
+ ASN1TagDefault tagDefault() {
+ return tagDefault;
+ }
+
+ /**
+ * Instantiates an ASN.1 data specified by the tag.
+ *
+ * @param tagClass
+ * The tag class.
+ * @param tagNumber
+ * The tag number.
+ * @return An instance of ASN.1 data.
+ */
+ ASN1Type instantiate(ASN1TagClass tagClass, int tagNumber) {
+ Class<? extends ASN1Type> type = tagMap.get(tagClass).get(tagNumber);
+ if (type != null) {
+ return ASN1Type.instantiate(type);
+ }
+ for (Entry<String, Class<? extends ASN1Module>> e : importedModules
+ .entrySet()) {
+ ModuleSpecification spec = ASN1ModuleManager.specification(e
+ .getValue());
+ ASN1Type data = spec.instantiate(tagClass, tagNumber);
+ if (data != null) {
+ return data;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Instantiates an ASN.1 data specified by the type identifier.
+ *
+ * @param typeIdentifier
+ * The identifier of the type.
+ * @return An instance of ASN.1 data.
+ */
+ ASN1Type instantiate(String typeIdentifier) {
+ if (typeIdentifier.indexOf('.') >= 0) {
+ String[] identifier = typeIdentifier.split(".");
+ return instantiate(identifier[0], identifier[1]);
+ }
+ Class<? extends ASN1Type> type = identifierMap.get(typeIdentifier);
+ if (type != null) {
+ return ASN1Type.instantiate(type);
+ }
+ return null;
+ }
+
+ /**
+ * Instantiates an ASN.1 data specified by the module identifier and the
+ * type identifier.
+ *
+ * @param moduleIdentifier
+ * The identifier of the module.
+ * @param typeIdentifier
+ * The identifier of the type.
+ * @return An instance of ASN.1 data.
+ */
+ private ASN1Type instantiate(String moduleIdentifier, String typeIdentifier) {
+ ModuleSpecification spec = ASN1ModuleManager
+ .specification(importedModules.get(moduleIdentifier));
+ if (spec != null) {
+ return spec.instantiate(typeIdentifier);
+ }
+ return null;
+ }
+
+}
ASN1IllegalArgument ex = new ASN1IllegalArgument();
ex.setMessage(
"The type '"
- + data.specification().identifier()
+ + data.specification().fullIdentifier()
+ "' of the data to be assigned is not the same type of this element.",
null, enclosure.getClass(), identifier, null);
throw ex;
*/
@Override
public void set(String elementName, ASN1Type component) {
- for (NamedTypeSpecification e : getAlternativeTypeList(getClass())) {
- if (e.identifier().equals(elementName)) {
- set(e, component);
- return;
- }
- }
- ASN1IllegalArgument ex = new ASN1IllegalArgument();
- ex.setMessage(
- "No such alternative '" + elementName + "' in this type.",
- null, getClass(), null, null);
- throw ex;
+ set(getElement(elementName), component);
}
/*
*/
@Override
public ASN1Type get(String elementName) {
+ return getElement(elementName).retrieve(this);
+ }
+
+ /* (non-Javadoc)
+ * @see jp.bitmeister.asn1.type.StructuredType#getElement(java.lang.String)
+ */
+ @Override
+ public NamedTypeSpecification getElement(String elementName) {
for (NamedTypeSpecification e : getAlternativeTypeList(getClass())) {
if (e.identifier().equals(elementName)) {
- return e.retrieve(this);
+ return e;
}
}
ASN1IllegalArgument ex = new ASN1IllegalArgument();
- ex.setMessage(
- "No such alternative '" + elementName + "' in this type.",
+ ex.setMessage("No such alternative '" + elementName + "' in this type.",
null, getClass(), null, null);
throw ex;
}
* The elements to be tagged.
*/
static void generateAutomaticTags(NamedTypeSpecification[] array) {
- for (NamedTypeSpecification e: array) {
+ for (NamedTypeSpecification e : array) {
if (e.tag() != null) {
return;
}
*/
public abstract ASN1Type get(String elementName);
+ /**
+ * Returns an element specification specified by the element name.
+ *
+ * @param elementName
+ * The element name.
+ * @return The element specification specified by the name.
+ */
+ public abstract NamedTypeSpecification getElement(String elementName);
+
}
import jp.bitmeister.asn1.annotation.ASN1Identifier;
import jp.bitmeister.asn1.annotation.ASN1ModuleRef;
import jp.bitmeister.asn1.annotation.ASN1Tag;
+import jp.bitmeister.asn1.annotation.ASN1XmlTypeName;
import jp.bitmeister.asn1.exception.ASN1IllegalDefinition;
/**
} else {
specification.identifier = type.getSimpleName();
}
+ ASN1XmlTypeName xmlName = type.getAnnotation(ASN1XmlTypeName.class);
+ if (xmlName != null) {
+ specification.xmlTypeName = xmlName.value();
+ }
+ else {
+ specification.xmlTypeName = specification.identifier;
+ }
}
// set tag.
private String identifier;
/**
+ * XML type name of this type.
+ */
+ private String xmlTypeName;
+
+ /**
* ASN.1 tag assigned to this type.
*/
private ASN1TagValue tag;
}
/**
+ * Returns the {@code Class} object of an ASN.1 module that this type is defined.
+ *
+ * @return The {@code Class} object of an ASN.1 module.
+ */
+ public Class<? extends ASN1Module> module() {
+ return module;
+ }
+
+ /**
* Returns the default tagging mode for the ASN.1 module that this type is
* defined.
*
* @return The default tagging mode.
*/
- public ASN1TagDefault tagDefault() {
- return ASN1Module.tagDefault(module);
+ ASN1TagDefault tagDefault() {
+ return ASN1ModuleManager.tagDefault(module);
}
/**
}
/**
- * Returns the full identifier of this type that made from ASN.1 module
- * identifier and type identifier.
+ * Returns the ASN.1 identifier with the module identifier of this type.
*
- * @return The full identifier of this type.
+ * @return The ASN.1 identifier of this type.
*/
public String fullIdentifier() {
- if (module != BuiltInModule.class) {
- return ASN1Module.identifier(module) + "." + identifier();
+ return ASN1ModuleManager.identifier(module) + "." + identifier();
+ }
+
+ /**
+ * Returns the XML type name of this type.
+ *
+ * @return The XML type name of this type.
+ */
+ public String xmlTypeName() {
+ return xmlTypeName;
+ }
+
+ /**
+ * Returns the XML type name of this type. If the type
+ *
+ * @return The XML type name of this type.
+ */
+ public String xmlTypeName(Class<? extends ASN1Module> module) {
+ if (!hasIdentifier()) {
+ return reference.xmlTypeName(module);
}
- return identifier();
+ if (this.module != BuiltInModule.class && this.module != module) {
+ return ASN1ModuleManager.identifier(this.module) + "." + xmlTypeName;
+ }
+ return xmlTypeName;
}
-
+
/**
* Tests if this type is tagged or references a tagged type.
*
*
* <p>
* When a decoder meets a source data with a tag that is not registered to
- * {@code ASN1Modules}, decoder decodes the data as an {@code UnknownType}. An
+ * {@code ASN1ModuleManager}, decoder decodes the data as an {@code UnknownType}. An
* instance of this class contains a tag class, a tag number and an array of
* {@code byte} represents row contents octets.
* </p>
*
* @author WATANABE, Jun. <jwat at bitmeister.jp>
*
- * @see ASN1Modules
+ * @see ASN1ModuleManager
* @see BerDecoder
*/
@ASN1BuiltIn
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import java.util.Map.Entry;
import jp.bitmeister.asn1.annotation.ASN1BuiltIn;
import jp.bitmeister.asn1.annotation.ASN1Identifier;
import jp.bitmeister.asn1.annotation.ASN1NamedBit;
import jp.bitmeister.asn1.annotation.ASN1Tag;
+import jp.bitmeister.asn1.annotation.ASN1XmlTypeName;
import jp.bitmeister.asn1.exception.ASN1IllegalArgument;
import jp.bitmeister.asn1.exception.ASN1IllegalDefinition;
import jp.bitmeister.asn1.exception.ASN1RuntimeException;
*/
@ASN1BuiltIn
@ASN1Identifier("BIT STRING")
+@ASN1XmlTypeName("BIT_STRING")
@ASN1Tag(tagClass = ASN1TagClass.UNIVERSAL, value = 3, tagMode = ASN1TagMode.IMPLICIT)
public class BIT_STRING extends PrimitiveType<boolean[]> implements
Concatenatable<BIT_STRING>, SizeCountable {
throw ex;
}
if (map.containsKey(value)) {
- ASN1RuntimeException ex = new ASN1RuntimeException();
+ ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
ex.setMessage(
"A value of named bit shall be distinct from all other named bits in the type.",
null, type, fieldId, null);
* to hold the bit specified by the biggest index, it will be expanded
* automatically.
*
- * @param index
+ * @param indexes
* Indexes of bits to be set true.
*/
public void set(int ...indexes) {
value()[e] = true;
}
}
+
+ /**
+ * Sets {@code true} to bits specified by the name of bit.
+ *
+ * @param nameOfBit
+ * The name of bit.
+ */
+ public void set(String nameOfBit) {
+ for (Entry<Integer, String> e: getNamedBitMap(getClass()).entrySet()) {
+ if (e.getValue().equals(nameOfBit)) {
+ set(e.getKey());
+ return;
+ }
+ }
+ ASN1IllegalArgument e = new ASN1IllegalArgument();
+ e.setMessage("The name '" + nameOfBit + "' is not defined in this type.", null, getClass(), null, null);
+ throw e;
+ }
/**
* Sets {@code false} to bits specified by index numbers. If the
* to hold the bit specified by the biggest index, it will be expanded
* automatically.
*
- * @param index
+ * @param indexes
* Indexes of bits to be set false.
*/
public void unset(int ...indexes) {
ASN1IllegalArgument ex = new ASN1IllegalArgument();
ex.setMessage(
"The type '"
- + data.specification().identifier()
+ + data.specification().fullIdentifier()
+ "' of the data to be concatenated is not the same type of this data.",
null, getClass(), null, null);
throw ex;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
+import java.util.Map.Entry;
import jp.bitmeister.asn1.annotation.ASN1BuiltIn;
import jp.bitmeister.asn1.annotation.ASN1Enumeration;
throw ex;
}
if (map.containsKey(value)) {
- ASN1RuntimeException ex = new ASN1RuntimeException();
+ ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
ex.setMessage(
"Each number of enumerations shall be distinct from all other enumerations in the type.",
null, type, fieldId, null);
}
/**
+ * Sets the value specified by the name of number to this data.
+ *
+ * @param nameOfNumber
+ * The name of number.
+ */
+ public void set(String nameOfNumber) {
+ for (Entry<BigInteger, String> e: getNamedNumberMap(getClass()).entrySet()) {
+ if (e.getValue().equals(nameOfNumber)) {
+ set(e.getKey());
+ return;
+ }
+ }
+ ASN1IllegalArgument e = new ASN1IllegalArgument();
+ e.setMessage("The name '" + nameOfNumber + "' is not defined in this type.", null, getClass(), null, null);
+ throw e;
+ }
+
+ /**
* Returns an identifier related to the value of this data if the type has
* {@code @ASN1Enumeration} fields.
*
package jp.bitmeister.asn1.type.builtin;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import jp.bitmeister.asn1.annotation.ASN1BuiltIn;
import jp.bitmeister.asn1.annotation.ASN1Identifier;
import jp.bitmeister.asn1.annotation.ASN1Tag;
+import jp.bitmeister.asn1.annotation.ASN1XmlTypeName;
+import jp.bitmeister.asn1.exception.ASN1IllegalArgument;
import jp.bitmeister.asn1.processor.ASN1Visitor;
import jp.bitmeister.asn1.type.ASN1TagClass;
import jp.bitmeister.asn1.type.ASN1TagMode;
*/
@ASN1BuiltIn
@ASN1Identifier("OBJECT IDENTIFIER")
+@ASN1XmlTypeName("OBJECT_IDENTIFIER")
@ASN1Tag(tagClass = ASN1TagClass.UNIVERSAL, value = 6, tagMode = ASN1TagMode.IMPLICIT)
public class OBJECT_IDENTIFIER extends PrimitiveType<List<Integer>> {
-
+
+ private static Map<String, Integer> NAMES_ROOT = new HashMap<String, Integer>();
+ private static Map<String, Integer> NAMES_ITU_T = new HashMap<String, Integer>();
+ private static Map<String, Integer> NAMES_ISO = new HashMap<String, Integer>();
+ private static Map<String, Integer> NAMES_ITU_T_REC = new HashMap<String, Integer>();
+
+ static {
+ // top level
+ NAMES_ROOT.put("itu-t", 0);
+ NAMES_ROOT.put("ccitt", 0);
+ NAMES_ROOT.put("iso", 1);
+ NAMES_ROOT.put("joint-iso-itu-t", 2);
+ NAMES_ROOT.put("joint-iso-ccitt", 2);
+ // itu-t/ccitt
+ NAMES_ITU_T.put("recommendation", 0);
+ NAMES_ITU_T.put("question", 1);
+ NAMES_ITU_T.put("administration", 2);
+ NAMES_ITU_T.put("network-operator", 3);
+ NAMES_ITU_T.put("identified-organization", 4);
+ NAMES_ITU_T.put("r-recommendation", 5);
+ // iso
+ NAMES_ISO.put("standard", 0);
+ NAMES_ISO.put("registration-authority", 1);
+ NAMES_ISO.put("member-body", 2);
+ NAMES_ISO.put("identified-organization", 3);
+ // itu-t recommendation
+ for (char c = 'a'; c <= 'z'; c++) {
+ NAMES_ITU_T_REC.put(String.valueOf(c), c - 'a');
+ }
+ }
+
+ public static int nameFormToInt(List<Integer> list, String nameForm) {
+ Integer number = null;
+ if (list.size() == 0) {
+ number = NAMES_ROOT.get(nameForm);
+ }
+ else if (list.size() == 1) {
+ if (list.get(0) == 0) {
+ number = NAMES_ITU_T.get(nameForm);
+ }
+ else if (list.get(0) == 1) {
+ number = NAMES_ISO.get(nameForm);
+ }
+ }
+ else if (list.size() == 2 && list.get(0) == 0 && list.get(1) == 0) {
+ number = NAMES_ITU_T_REC.get(nameForm);
+ }
+ if (number == null) {
+ ASN1IllegalArgument e = new ASN1IllegalArgument();
+ e.setMessage("Invalid OID name form '" + nameForm + "'.", null, OBJECT_IDENTIFIER.class, null, null);
+ throw e;
+ }
+ return number;
+ }
+
/**
* Instantiates an empty {@code OBJECT_IDENTIFIER}.
*/
}
set(oids);
}
-
+
/*
* (non-Javadoc)
*
import jp.bitmeister.asn1.annotation.ASN1BuiltIn;
import jp.bitmeister.asn1.annotation.ASN1Identifier;
import jp.bitmeister.asn1.annotation.ASN1Tag;
+import jp.bitmeister.asn1.annotation.ASN1XmlTypeName;
import jp.bitmeister.asn1.exception.ASN1IllegalArgument;
import jp.bitmeister.asn1.processor.ASN1Visitor;
import jp.bitmeister.asn1.type.ASN1TagClass;
*/
@ASN1BuiltIn
@ASN1Identifier("OCTET STRING")
+@ASN1XmlTypeName("OCTET_STRING")
@ASN1Tag(tagClass = ASN1TagClass.UNIVERSAL, value = 4, tagMode = ASN1TagMode.IMPLICIT)
public class OCTET_STRING extends PrimitiveType<byte[]> implements
Concatenatable<OCTET_STRING>, SizeCountable {
ASN1IllegalArgument ex = new ASN1IllegalArgument();
ex.setMessage(
"The type '"
- + data.specification().identifier()
+ + data.specification().fullIdentifier()
+ "' of the data to be concatenated is not the same type of this instance.",
null, getClass(), null, null);
throw ex;
--- /dev/null
+/*
+ * Copyright 2011 BitMeister Inc.
+ *
+ * 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 jp.bitmeister.asn1.type.builtin;
+
+import java.util.List;
+
+import jp.bitmeister.asn1.annotation.ASN1BuiltIn;
+import jp.bitmeister.asn1.annotation.ASN1Identifier;
+import jp.bitmeister.asn1.annotation.ASN1Tag;
+import jp.bitmeister.asn1.annotation.ASN1XmlTypeName;
+import jp.bitmeister.asn1.processor.ASN1Visitor;
+import jp.bitmeister.asn1.type.ASN1TagClass;
+import jp.bitmeister.asn1.type.ASN1TagMode;
+
+/**
+ * Represents ASN.1 'RELATIVE-OID' type.
+ *
+ * <p>
+ * An instance of this class represents an 'RELATIVE-OID' type data and
+ * contains a {@code List} of {@code Integer}. Each element of the {@code List}
+ * represents an object id component.
+ * </p>
+ *
+ * @author WATANABE, Jun. <jwat at bitmeister.jp>
+ */
+@ASN1BuiltIn
+@ASN1Identifier("RELATIVE-OID")
+@ASN1XmlTypeName("RELATIVE_OID")
+@ASN1Tag(tagClass = ASN1TagClass.UNIVERSAL, value = 13, tagMode = ASN1TagMode.IMPLICIT)
+public class RELATIVE_OID extends OBJECT_IDENTIFIER {
+
+ /**
+ * Instantiates an empty {@code RELATIVE_OID}.
+ */
+ public RELATIVE_OID() {
+ }
+
+ /**
+ * Instantiates an {@code RELATIVE_OID} and initialize it with the
+ * {@code List<Integer>} value.
+ *
+ * @param value
+ * the {@code List} of {@code Integer} represents an object
+ * identifier.
+ */
+ public RELATIVE_OID(List<Integer> value) {
+ set(value);
+ }
+
+ /**
+ * Instantiates an {@code RELATIVE_OID} and initialize it with the
+ * array of {@code int} value.
+ *
+ * @param value
+ * the array of {@code int} represents an object identifier.
+ */
+ public RELATIVE_OID(int... value) {
+ set(value);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.type.ASN1Type#accept(jp.bitmeister.asn1.processor.
+ * ASN1Visitor)
+ */
+ @Override
+ public <R, E extends Throwable> R accept(ASN1Visitor<R, E> visitor) throws E {
+ return visitor.visit(this);
+ }
+
+}
import jp.bitmeister.asn1.annotation.ASN1BuiltIn;
import jp.bitmeister.asn1.annotation.ASN1Identifier;
import jp.bitmeister.asn1.annotation.ASN1Tag;
+import jp.bitmeister.asn1.annotation.ASN1XmlTypeName;
import jp.bitmeister.asn1.processor.ASN1Visitor;
import jp.bitmeister.asn1.type.ASN1TagClass;
import jp.bitmeister.asn1.type.ASN1TagMode;
*/
@ASN1BuiltIn
@ASN1Identifier("SEQUENCE")
+@ASN1XmlTypeName("SEQUENCE_OF")
@ASN1Tag(tagClass = ASN1TagClass.UNIVERSAL, value = 16, tagMode = ASN1TagMode.IMPLICIT)
-public class SEQUENCE_OF<T extends ASN1Type> extends CollectionType<T> {
+public abstract class SEQUENCE_OF<T extends ASN1Type> extends CollectionType<T> {
/**
* Instantiate an empty {@code SEQUENCE_OF}.
import jp.bitmeister.asn1.annotation.ASN1BuiltIn;
import jp.bitmeister.asn1.annotation.ASN1Identifier;
import jp.bitmeister.asn1.annotation.ASN1Tag;
+import jp.bitmeister.asn1.annotation.ASN1XmlTypeName;
import jp.bitmeister.asn1.processor.ASN1Visitor;
import jp.bitmeister.asn1.type.ASN1TagClass;
import jp.bitmeister.asn1.type.ASN1TagMode;
*/
@ASN1BuiltIn
@ASN1Identifier("SET")
+@ASN1XmlTypeName("SET_OF")
@ASN1Tag(tagClass = ASN1TagClass.UNIVERSAL, value = 17, tagMode = ASN1TagMode.IMPLICIT)
-public class SET_OF<T extends ASN1Type> extends CollectionType<T> {
+public abstract class SET_OF<T extends ASN1Type> extends CollectionType<T> {
/**
* Instantiate an empty {@code SET_OF}.
import jp.bitmeister.asn1.annotation.ASN1BuiltIn;
import jp.bitmeister.asn1.annotation.ASN1Identifier;
import jp.bitmeister.asn1.annotation.ASN1Tag;
+import jp.bitmeister.asn1.annotation.ASN1XmlTypeName;
import jp.bitmeister.asn1.type.ASN1TagClass;
import jp.bitmeister.asn1.type.ASN1TagMode;
import jp.bitmeister.asn1.type.builtin.OCTET_STRING;
*/
@ASN1BuiltIn
@ASN1Identifier("CHARACTER STRING")
+@ASN1XmlTypeName("CHARACTER_STRING")
@ASN1Tag(tagClass = ASN1TagClass.UNIVERSAL, value = 29, tagMode = ASN1TagMode.IMPLICIT)
public class CHARACTER_STRING extends EMBEDDED_PDV {
import jp.bitmeister.asn1.annotation.ASN1Element;
import jp.bitmeister.asn1.annotation.ASN1Identifier;
import jp.bitmeister.asn1.annotation.ASN1Tag;
+import jp.bitmeister.asn1.annotation.ASN1XmlTypeName;
import jp.bitmeister.asn1.type.ASN1TagClass;
import jp.bitmeister.asn1.type.ASN1TagMode;
import jp.bitmeister.asn1.type.ASN1Type;
*/
@ASN1BuiltIn
@ASN1Identifier("EMBEDDED PDV")
+@ASN1XmlTypeName("SEQUENCE")
@ASN1Tag(tagClass = ASN1TagClass.UNIVERSAL, value = 11, tagMode = ASN1TagMode.IMPLICIT)
public class EMBEDDED_PDV extends SEQUENCE {
* The {@code String} consist of '0's and '1's.
*/
public BinString(String string) {
- if (!string.matches("[01]*")) {
+ if (!string.matches("[01 ]*")) {
ASN1IllegalArgument ex = new ASN1IllegalArgument();
ex.setMessage("Invalid string '" + string
+ "'. bString shall consist of '0's and '1's.", null, null,
null, null);
throw ex;
}
- this.string = string;
+ this.string = string.trim();
}
/**
null, null, null);
throw ex;
}
- this.string = string.toUpperCase();
+ this.string = string.trim().toUpperCase();
}
/**
* @return An array of {@code byte}.
*/
public byte[] toByteArray();
-
+
}