OSDN Git Service

* The 1st release of ASN.1 / POJO translation feature.
[bm-asn1/bm-asn1.git] / jp / bitmeister / asn1 / pojo / PojoEncoder.java
diff --git a/jp/bitmeister/asn1/pojo/PojoEncoder.java b/jp/bitmeister/asn1/pojo/PojoEncoder.java
new file mode 100644 (file)
index 0000000..e288312
--- /dev/null
@@ -0,0 +1,541 @@
+/*
+ * 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;
+                       }
+
+               }
+
+       }
+
+}