--- /dev/null
+/*
+ * Copyright 2011-2012 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.pojo;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+
+import jp.bitmeister.asn1.codec.ASN1Encoder;
+import jp.bitmeister.asn1.exception.ASN1DecodingException;
+import jp.bitmeister.asn1.exception.ASN1EncodingException;
+import jp.bitmeister.asn1.exception.ASN1IllegalArgument;
+import jp.bitmeister.asn1.pojo.annotation.ASN1JavaList;
+import jp.bitmeister.asn1.pojo.annotation.ASN1JavaObject;
+import jp.bitmeister.asn1.processor.ASN1Visitor;
+import jp.bitmeister.asn1.processor.ASN1VisitorAdaptor;
+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.SelectiveType;
+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.BigENUMERATED;
+import jp.bitmeister.asn1.type.builtin.BigINTEGER;
+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;
+
+/**
+ * ASN.1 to POJO data encoder.
+ *
+ * <p>
+ * {@code PojoEncoder} is an implementation of {@code ASN1Encoder}. It
+ * translates an ASN.1 data to a POJO data. Correspondence of an ASN.1 class to
+ * a POJO class is indicated by {@code @ASN1JavaObject} annotation.
+ * </p>
+ *
+ * @author WATANABE, Jun. <jwat at bitmeister.jp>
+ *
+ * @see ASN1Encoder
+ * @see PojoDecoder
+ * @see ASN1JavaObject
+ */
+public class PojoEncoder implements ASN1Encoder {
+
+ /**
+ * Destination of translation.
+ */
+ private Object object;
+
+ /**
+ * Instantiates {@code PojoEncoder} with a destination object.
+ *
+ * @param object
+ * The {@code @ASN1JavaObject} object.
+ */
+ public PojoEncoder(Object object) {
+ this.object = object;
+ }
+
+ /**
+ * Translates an ASN.1 data to corresponding POJO data.
+ *
+ * @param data
+ * The ASN.1 data to be translated.
+ * @return 0
+ * @throws ASN1EncodingException
+ * When an error occurred while the encoding process.
+ */
+ public int encode(ASN1Type data) throws ASN1EncodingException {
+ try {
+ if (data.accept(new StructureEncoder(object)) == null) {
+ ASN1EncodingException ex = new ASN1EncodingException();
+ ex.setMessage(
+ "Only structured classes (SEQUENCE, SET, CHOICE) can be translated to POJO.",
+ null, data.getClass(), null, data);
+ throw ex;
+ }
+ } catch (Exception e) {
+ ASN1EncodingException ex = new ASN1EncodingException();
+ ex.setMessage("Exception thrown while encoding process.", e,
+ data.getClass(), null, data);
+ throw ex;
+ }
+ return 0;
+ }
+
+ /**
+ * Internal processor class that processes {@code @ASN1JavaObject} class.
+ *
+ * @author WATANABE, Jun. <jwat at bitmeister.jp>
+ *
+ */
+ private class StructureEncoder extends
+ ASN1VisitorAdaptor<Boolean, Exception> {
+
+ /**
+ * Destination.
+ */
+ private Object object;
+
+ /**
+ * Instantiates {@code StructureEncoder} with a destination object.
+ *
+ * @param object
+ * The {@code @ASN1JavaObject} object.
+ */
+ StructureEncoder(Object object) {
+ this.object = object;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1VisitorAdaptor#visit(jp.bitmeister
+ * .asn1.type.builtin.CHOICE)
+ */
+ @Override
+ public Boolean visit(CHOICE data) throws Exception {
+ processSelective(data);
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1VisitorAdaptor#visit(jp.bitmeister
+ * .asn1.type.builtin.SEQUENCE)
+ */
+ @Override
+ public Boolean visit(SEQUENCE data) throws Exception {
+ processConstructive(data);
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1VisitorAdaptor#visit(jp.bitmeister
+ * .asn1.type.builtin.SET)
+ */
+ @Override
+ public Boolean visit(SET data) throws Exception {
+ processConstructive(data);
+ return true;
+ }
+
+ /**
+ * Processes constructive type (i.e. 'SEQUENCE' and 'SET') object.
+ *
+ * @param data
+ * The ASN.1 constructive type object.
+ * @throws Exception
+ * When an error occurred while translation process.
+ */
+ private void processConstructive(ConstructiveType data)
+ throws Exception {
+ PojoSpecification spec = PojoSpecification.getSpecification(object
+ .getClass());
+ if (!spec.checkTarget(data.getClass())) {
+ ASN1IllegalArgument ex = new ASN1IllegalArgument();
+ ex.setMessage("Incompatible ASN.1 type data is specified.",
+ null, data.getClass(), null, null);
+ throw ex;
+ }
+ for (ElementSpecification e : data.getElementTypeList()) {
+ Field field = spec.getField(e);
+ if (field == null) {
+ continue;
+ }
+ ASN1Type component = data.getComponent(e);
+ if (component != null) {
+
+ field.set(object,
+ component.accept(new ElementEncoder(field)));
+ }
+ }
+ }
+
+ /**
+ * Processes selective type (i.e. 'CHOICE') object.
+ *
+ * @param data
+ * The ASN.1 selective type object.
+ * @throws Exception
+ * When an error occurred while translation process.
+ */
+ private void processSelective(SelectiveType data) throws Exception {
+ PojoSpecification spec = PojoSpecification.getSpecification(object
+ .getClass());
+ if (!spec.checkTarget(data.getClass())) {
+ ASN1IllegalArgument ex = new ASN1IllegalArgument();
+ ex.setMessage("Incompatible ASN.1 type data is specified.",
+ null, data.getClass(), null, null);
+ throw ex;
+ }
+ String selection = data.selectedIdentifier();
+ Field field = spec.getField(data.getElement(selection));
+ if (field != null) {
+ field.set(object,
+ data.selectedValue().accept(new ElementEncoder(field)));
+ }
+ }
+
+ /**
+ * Internal processor class that processes {@code @ASN1JavaField} field.
+ *
+ * @author WATANABE, Jun. <jwat at bitmeister.jp>
+ *
+ */
+ private class ElementEncoder implements ASN1Visitor<Object, Exception> {
+
+ /**
+ * Destination field.
+ */
+ Field field;
+
+ /**
+ * Instantiates {@code ElementEncoder} with a destination field.
+ *
+ * @param field
+ * The destination field.
+ */
+ ElementEncoder(Field field) {
+ this.field = field;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.
+ * asn1.type.builtin.BOOLEAN)
+ */
+ public Object visit(BOOLEAN data) throws Exception {
+ return data.value();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.
+ * asn1.type.builtin.INTEGER)
+ */
+ public Object visit(INTEGER data) throws Exception {
+ return data.value();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.
+ * asn1.type.builtin.BigINTEGER)
+ */
+ public Object visit(BigINTEGER data) throws Exception {
+ return data.longValue();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.
+ * asn1.type.builtin.BIT_STRING)
+ */
+ public Object visit(BIT_STRING data) throws Exception {
+ return new BinString(data.value()).toByteArray();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.
+ * asn1.type.builtin.OCTET_STRING)
+ */
+ public Object visit(OCTET_STRING data) throws Exception {
+ byte[] value = new byte[data.size()];
+ System.arraycopy(data.value(), 0, value, 0, value.length);
+ return value;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.
+ * asn1.type.builtin.NULL)
+ */
+ public Object visit(NULL data) throws Exception {
+ return new Object();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.
+ * asn1.type.builtin.OBJECT_IDENTIFIER)
+ */
+ public Object visit(OBJECT_IDENTIFIER data) throws Exception {
+ int[] value = new int[data.value().size()];
+ for (int i = 0; i < value.length; i++) {
+ value[i] = data.value().get(i);
+ }
+ return value;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.
+ * asn1.type.builtin.REAL)
+ */
+ public Object visit(REAL data) throws Exception {
+ return data.value();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.
+ * asn1.type.builtin.ENUMERATED)
+ */
+ public Object visit(ENUMERATED data) throws Exception {
+ return data.value();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.
+ * asn1.type.builtin.BigENUMERATED)
+ */
+ public Object visit(BigENUMERATED data) throws Exception {
+ return data.longValue();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.
+ * asn1.type.builtin.RELATIVE_OID)
+ */
+ public Object visit(RELATIVE_OID data) throws Exception {
+ int[] value = new int[data.value().size()];
+ for (int i = 0; i < value.length; i++) {
+ value[i] = data.value().get(i);
+ }
+ return value;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.
+ * asn1.type.builtin.ANY)
+ */
+ public Object visit(ANY data) throws Exception {
+ return data.value().accept(new ElementEncoder(field));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.
+ * asn1.type.builtin.CHOICE)
+ */
+ public Object visit(CHOICE data) throws Exception {
+ return processStructured(data);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.
+ * asn1.type.builtin.SEQUENCE_OF)
+ */
+ public Object visit(SEQUENCE_OF<? extends ASN1Type> data)
+ throws Exception {
+ return processCollection(data);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.
+ * asn1.type.builtin.SEQUENCE)
+ */
+ public Object visit(SEQUENCE data) throws Exception {
+ return processStructured(data);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.
+ * asn1.type.builtin.SET_OF)
+ */
+ public Object visit(SET_OF<? extends ASN1Type> data)
+ throws Exception {
+ return processCollection(data);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.
+ * asn1.type.builtin.SET)
+ */
+ public Object visit(SET data) throws Exception {
+ return processStructured(data);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.
+ * asn1.type.StringType)
+ */
+ public Object visit(StringType data) throws Exception {
+ return data.stringValue();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.
+ * asn1.type.TimeType)
+ */
+ public Object visit(TimeType data) throws Exception {
+ return data.date();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.
+ * asn1.type.UnknownType)
+ */
+ public Object visit(UnknownType data) throws Exception {
+ ASN1DecodingException ex = new ASN1DecodingException();
+ ex.setMessage("UnkonwnType is not supported.", null,
+ UnknownType.class, null, data);
+ throw ex;
+ }
+
+ /**
+ * Processes structured type (i.e. 'SEQUENCE', 'SET' and 'CHOICE')
+ * object.
+ *
+ * @param data
+ * The ASN.1 structured type object.
+ * @return Translated POJO instance.
+ * @throws Exception
+ * When an error occurred while translation process.
+ */
+ private Object processStructured(StructuredType data)
+ throws Exception {
+ ASN1JavaList javaList = field.getAnnotation(ASN1JavaList.class);
+ Object component;
+ if (javaList != null) {
+ component = javaList.value().newInstance();
+ } else {
+ component = field.getType().newInstance();
+ }
+ data.accept(new StructureEncoder(component));
+ return component;
+ }
+
+ /**
+ * Processes collection type (i.e. 'SEQUENCE OF' and 'SET OF')
+ * object.
+ *
+ * @param data
+ * The ASN.1 collection type object.
+ * @return Translated POJO instance.
+ * @throws Exception
+ * When an error occurred while translation process.
+ */
+ private Object processCollection(CollectionType<?, ?> data)
+ throws Exception {
+ List<Object> list = new ArrayList<Object>();
+ for (ASN1Type component : data.collection()) {
+ list.add(component.accept(new ElementEncoder(field)));
+ }
+ return list;
+ }
+
+ }
+
+ }
+
+}