summaryrefslogtreecommitdiffstats
path: root/src/org/uic/barcode/asn1
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/uic/barcode/asn1')
-rw-r--r--src/org/uic/barcode/asn1/datatypes/Alphabet.java20
-rw-r--r--src/org/uic/barcode/asn1/datatypes/AlphabetBuilder.java32
-rw-r--r--src/org/uic/barcode/asn1/datatypes/Asn1AnonymousType.java15
-rw-r--r--src/org/uic/barcode/asn1/datatypes/Asn1BigInteger.java72
-rw-r--r--src/org/uic/barcode/asn1/datatypes/Asn1Default.java12
-rw-r--r--src/org/uic/barcode/asn1/datatypes/Asn1Integer.java56
-rw-r--r--src/org/uic/barcode/asn1/datatypes/Asn1Optional.java20
-rw-r--r--src/org/uic/barcode/asn1/datatypes/Asn1SequenceOf.java70
-rw-r--r--src/org/uic/barcode/asn1/datatypes/Asn1String.java17
-rw-r--r--src/org/uic/barcode/asn1/datatypes/Asn1VarSizeBitstring.java58
-rw-r--r--src/org/uic/barcode/asn1/datatypes/Bitstring.java16
-rw-r--r--src/org/uic/barcode/asn1/datatypes/CharacterRestriction.java13
-rw-r--r--src/org/uic/barcode/asn1/datatypes/Choice.java12
-rw-r--r--src/org/uic/barcode/asn1/datatypes/DefaultAlphabet.java8
-rw-r--r--src/org/uic/barcode/asn1/datatypes/FieldOrder.java13
-rw-r--r--src/org/uic/barcode/asn1/datatypes/FixedSize.java12
-rw-r--r--src/org/uic/barcode/asn1/datatypes/HasExtensionMarker.java12
-rw-r--r--src/org/uic/barcode/asn1/datatypes/IntMinValue.java13
-rw-r--r--src/org/uic/barcode/asn1/datatypes/IntRange.java14
-rw-r--r--src/org/uic/barcode/asn1/datatypes/IsExtension.java12
-rw-r--r--src/org/uic/barcode/asn1/datatypes/NoAsn1Field.java10
-rw-r--r--src/org/uic/barcode/asn1/datatypes/Optional.java96
-rw-r--r--src/org/uic/barcode/asn1/datatypes/RestrictedString.java13
-rw-r--r--src/org/uic/barcode/asn1/datatypes/Sequence.java9
-rw-r--r--src/org/uic/barcode/asn1/datatypes/SizeRange.java14
-rw-r--r--src/org/uic/barcode/asn1/datatypes/package-info.java7
-rw-r--r--src/org/uic/barcode/asn1/datatypesimpl/OctetString.java46
-rw-r--r--src/org/uic/barcode/asn1/datatypesimpl/SequenceOfLong.java27
-rw-r--r--src/org/uic/barcode/asn1/datatypesimpl/SequenceOfStringIA5.java13
-rw-r--r--src/org/uic/barcode/asn1/datatypesimpl/SequenceOfStringUTF8.java13
-rw-r--r--src/org/uic/barcode/asn1/datatypesimpl/SequenceOfUnrestrictedLong.java30
-rw-r--r--src/org/uic/barcode/asn1/test/TestSequenceOfLong.java24
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeBooleanTest.java82
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeChoiceExtensionTest.java92
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeChoiceTest.java74
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeEnumExtensionTest.java146
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeEnumTest.java126
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeExtensionFieldOrderTest.java100
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeFieldOrderTest.java66
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeIntegerConstrainedTest.java71
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeIntegerExtensionTest.java89
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeIntegerSmallTest.java130
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeIntegerTest.java64
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeObjectIdentifierTest.java76
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeOctetStringTest.java80
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeOptionalSequenceExtensionTest.java118
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeRestrictedIntegerTest.java62
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeSequenceExtensionTest.java93
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeSequenceOfIntegerTest.java72
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeSequenceOfRestrictedIntegerTest.java77
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeSequenceOfStringListTest.java78
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeSequenceOfStringTest.java76
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeSequenceOfUtf8StringTest.java96
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeStringDefaultTest.java76
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeStringLengthTest.java151
-rw-r--r--src/org/uic/barcode/asn1/test/UperEncodeStringTest.java110
-rw-r--r--src/org/uic/barcode/asn1/uper/AnnotationStore.java31
-rw-r--r--src/org/uic/barcode/asn1/uper/Asn1EncodingException.java18
-rw-r--r--src/org/uic/barcode/asn1/uper/BigIntCoder.java97
-rw-r--r--src/org/uic/barcode/asn1/uper/BitBuffer.java32
-rw-r--r--src/org/uic/barcode/asn1/uper/BitStringCoder.java165
-rw-r--r--src/org/uic/barcode/asn1/uper/BooleanCoder.java35
-rw-r--r--src/org/uic/barcode/asn1/uper/ByteBitBuffer.java271
-rw-r--r--src/org/uic/barcode/asn1/uper/ByteCoder.java34
-rw-r--r--src/org/uic/barcode/asn1/uper/ChoiceCoder.java161
-rw-r--r--src/org/uic/barcode/asn1/uper/Decoder.java10
-rw-r--r--src/org/uic/barcode/asn1/uper/Document2.txt34
-rw-r--r--src/org/uic/barcode/asn1/uper/Encoder.java8
-rw-r--r--src/org/uic/barcode/asn1/uper/EnumCoder.java156
-rw-r--r--src/org/uic/barcode/asn1/uper/IntCoder.java267
-rw-r--r--src/org/uic/barcode/asn1/uper/ObjectIdentifierCoder.java175
-rw-r--r--src/org/uic/barcode/asn1/uper/SeqOfCoder.java156
-rw-r--r--src/org/uic/barcode/asn1/uper/SeqOfFixedSize.java18
-rw-r--r--src/org/uic/barcode/asn1/uper/SequenceCoder.java269
-rw-r--r--src/org/uic/barcode/asn1/uper/SimpleTypeResolver.java515
-rw-r--r--src/org/uic/barcode/asn1/uper/StringCoder.java341
-rw-r--r--src/org/uic/barcode/asn1/uper/UperEncoder.java720
77 files changed, 6517 insertions, 0 deletions
diff --git a/src/org/uic/barcode/asn1/datatypes/Alphabet.java b/src/org/uic/barcode/asn1/datatypes/Alphabet.java
new file mode 100644
index 0000000..2b153ae
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/Alphabet.java
@@ -0,0 +1,20 @@
+package org.uic.barcode.asn1.datatypes;
+
+/**
+ * Alphabet class for Restricted Strings.
+ *
+ * Use {@link AlphabetBuilder} for convenient construction of restriction alphabets.
+ */
+public abstract class Alphabet {
+
+ private final String chars;
+
+ protected Alphabet(String chars) {
+ this.chars = chars;
+ }
+
+ public final String chars() {
+ return chars;
+ }
+
+}
diff --git a/src/org/uic/barcode/asn1/datatypes/AlphabetBuilder.java b/src/org/uic/barcode/asn1/datatypes/AlphabetBuilder.java
new file mode 100644
index 0000000..b768897
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/AlphabetBuilder.java
@@ -0,0 +1,32 @@
+package org.uic.barcode.asn1.datatypes;
+
+
+public class AlphabetBuilder {
+
+ private final StringBuilder sb = new StringBuilder();
+
+ public AlphabetBuilder() {}
+
+ public String chars() {
+ return sb.toString();
+ }
+
+ public AlphabetBuilder withRange(char from, char to) {
+ for (char c = from; c <= to; c++) {
+ sb.append(c);
+ }
+ return this;
+ }
+
+ public AlphabetBuilder withChars(String str) {
+ sb.append(str);
+ return this;
+ }
+
+ public AlphabetBuilder withChars(Character... chars) {
+ for (char c : chars) {
+ sb.append(c);
+ }
+ return this;
+ }
+}
diff --git a/src/org/uic/barcode/asn1/datatypes/Asn1AnonymousType.java b/src/org/uic/barcode/asn1/datatypes/Asn1AnonymousType.java
new file mode 100644
index 0000000..b1b0499
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/Asn1AnonymousType.java
@@ -0,0 +1,15 @@
+package org.uic.barcode.asn1.datatypes;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+/**
+ * This annotation indicates that the class is not present in the original ASN.1 declaration.
+ * This happens when SEQUENCE members have restrictions (ranges, alphabets etc).
+ *
+ * This annotation plays no role in the UPER encoding.
+ *
+ */
+@Target({ElementType.TYPE})
+public @interface Asn1AnonymousType {
+
+}
diff --git a/src/org/uic/barcode/asn1/datatypes/Asn1BigInteger.java b/src/org/uic/barcode/asn1/datatypes/Asn1BigInteger.java
new file mode 100644
index 0000000..4adca22
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/Asn1BigInteger.java
@@ -0,0 +1,72 @@
+package org.uic.barcode.asn1.datatypes;
+
+import java.math.BigInteger;
+
+//outdated: use BigInteger
+public class Asn1BigInteger {
+
+ private final BigInteger value;
+
+ public Asn1BigInteger(final BigInteger value) {
+ this.value = value;
+ }
+
+ @Override public String toString() {
+ return "" + value;
+ }
+
+ public BigInteger value() { return value; }
+
+ public Long longValue() {
+ return value.longValue();
+ }
+
+ public Integer intValue() {
+ return value.intValue();
+ }
+
+ public Asn1BigInteger(Long num) {
+ this.value = BigInteger.valueOf(num);
+ }
+
+ public Asn1BigInteger(long num) {
+ this.value = BigInteger.valueOf(num);
+ }
+
+ public Asn1BigInteger(Integer num) {
+ this.value = BigInteger.valueOf(num);
+ }
+
+ public Asn1BigInteger(int num) {
+ this.value = BigInteger.valueOf(num);
+ }
+
+ public static Long toLong(Asn1BigInteger object) {
+ if (object == null) return null;
+ return object.longValue();
+ }
+
+ public static Asn1BigInteger toAsn1(Long object) {
+ if (object == null) return null;
+ return new Asn1BigInteger(object);
+ }
+
+ public static Asn1BigInteger toAsn1(Integer object) {
+ if (object == null) return null;
+ return new Asn1BigInteger(object);
+ }
+
+ public Long toLong(){
+ if (this.value != null) {
+ return this.value.longValue();
+ }
+ return null;
+ }
+
+ public BigInteger toBigInteger(){
+ return value;
+ }
+
+
+
+}
diff --git a/src/org/uic/barcode/asn1/datatypes/Asn1Default.java b/src/org/uic/barcode/asn1/datatypes/Asn1Default.java
new file mode 100644
index 0000000..bf5cfff
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/Asn1Default.java
@@ -0,0 +1,12 @@
+package org.uic.barcode.asn1.datatypes;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Asn1Default {
+ String value();
+}
diff --git a/src/org/uic/barcode/asn1/datatypes/Asn1Integer.java b/src/org/uic/barcode/asn1/datatypes/Asn1Integer.java
new file mode 100644
index 0000000..e12f8ec
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/Asn1Integer.java
@@ -0,0 +1,56 @@
+package org.uic.barcode.asn1.datatypes;
+
+
+
+//outdated: use BigInteger
+public class Asn1Integer {
+
+ public long value;
+
+ public Asn1Integer() {}
+ public Asn1Integer(long value) {
+ this.value = value;
+ }
+
+ public Long value() { return value; }
+
+ @Override public String toString() {
+ return "" + value;
+ }
+
+ public Long longObject () {
+ return new Long(value());
+ }
+
+ public Asn1Integer(Long num) {
+ this.value = num;
+ }
+
+
+ public Asn1Integer(Integer num) {
+ this.value = num;
+ }
+
+ public Asn1Integer(int num) {
+ this.value = num;
+ }
+
+ public static Long toLong(Asn1Integer object) {
+ if (object == null) return null;
+ return object.value();
+ }
+
+
+ public static Asn1Integer toAsn1(Long object) {
+ if (object == null) return null;
+ return new Asn1Integer(object);
+ }
+
+
+
+
+
+
+
+
+}
diff --git a/src/org/uic/barcode/asn1/datatypes/Asn1Optional.java b/src/org/uic/barcode/asn1/datatypes/Asn1Optional.java
new file mode 100644
index 0000000..71946ec
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/Asn1Optional.java
@@ -0,0 +1,20 @@
+package org.uic.barcode.asn1.datatypes;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that the field is OPTIONAL in ASN.1. Implemented as null. Equivalent to @Nullable.
+ *
+ * Using Optional<T> would require Manifests to capture generics (like in Gson).
+ *
+ */
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Asn1Optional {
+
+}
diff --git a/src/org/uic/barcode/asn1/datatypes/Asn1SequenceOf.java b/src/org/uic/barcode/asn1/datatypes/Asn1SequenceOf.java
new file mode 100644
index 0000000..4924b50
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/Asn1SequenceOf.java
@@ -0,0 +1,70 @@
+package org.uic.barcode.asn1.datatypes;
+
+import java.lang.reflect.ParameterizedType;
+import java.util.*;
+
+import org.uic.barcode.logger.Logger;
+import org.uic.barcode.logger.LoggerFactory;
+
+
+/**
+ * Class to represent ASN.1 construct "SEQUENCE OF".
+ * <p/>
+ * Extending classes should specify concrete types for T, generic collections can't be decoded (yet?).
+ * <p/>
+ * Usage example:
+ * <PRE>
+ * <code>
+ * {@literal @}Sequence
+ * public class Person {
+ * {@literal @}IntRange(minValue=0, maxValue=100, hasExtensionMarker=true)
+ * int age;
+ * Children children;
+ * }
+ * public class Children extends {@code Asn1SequenceOf<ChildInformation> } {
+ * public Children() { super(); }
+ * public Children({@code Collection<ChildInformation>} coll) { super(coll); }
+ * }
+ * </code>
+ * </PRE>
+ *
+ * <p/>
+ * Actually, UPER decoder and encoder consider anything that extends {@code List<T>} as a SEQUENCE OF.
+ *
+ *
+ * @param <T> type of elements contained.
+ */
+public abstract class Asn1SequenceOf<T> extends AbstractList<T> {
+ private final static Logger logger = LoggerFactory.getLogger("asnLogger");
+
+ private final List<T> bakingList;
+
+ @Override public T get(int index) { return bakingList.get(index); }
+ @Override public int size() { return bakingList.size(); }
+ @Override public boolean add (T e){ return bakingList.add(e);}
+
+ public Asn1SequenceOf() { this(new ArrayList<T>()); }
+ public Asn1SequenceOf(Collection<T> coll) {
+ logger.debug(String.format("Instantiating Sequence Of %s with %s",
+ ((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0],
+ coll));
+ bakingList = new ArrayList<>(coll);
+ }
+
+
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+ Asn1SequenceOf<?> that = (Asn1SequenceOf<?>) o;
+ return Objects.equals(bakingList, that.bakingList);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), bakingList);
+ }
+}
diff --git a/src/org/uic/barcode/asn1/datatypes/Asn1String.java b/src/org/uic/barcode/asn1/datatypes/Asn1String.java
new file mode 100644
index 0000000..fb80b92
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/Asn1String.java
@@ -0,0 +1,17 @@
+package org.uic.barcode.asn1.datatypes;
+
+public class Asn1String {
+
+ private String value;
+
+ public Asn1String() { this(""); }
+
+ public Asn1String(String value) {
+ this.value = value;
+ }
+
+ @Override public String toString() { return value; }
+
+ public String value() { return value; }
+
+}
diff --git a/src/org/uic/barcode/asn1/datatypes/Asn1VarSizeBitstring.java b/src/org/uic/barcode/asn1/datatypes/Asn1VarSizeBitstring.java
new file mode 100644
index 0000000..c07f7f0
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/Asn1VarSizeBitstring.java
@@ -0,0 +1,58 @@
+package org.uic.barcode.asn1.datatypes;
+
+import java.util.AbstractList;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.Objects;
+
+/**
+ * Convenience class for Bitstrings of variable size.
+ * For UPER, {@code List<Boolean>} works just as well.
+ */
+public class Asn1VarSizeBitstring extends AbstractList<Boolean> {
+
+ private final BitSet backing;
+
+ @Override public Boolean get(int index) {
+ return backing.get(index);
+ }
+
+ @Override public int size() {
+ return backing.length();
+ }
+
+ public Asn1VarSizeBitstring(Collection<Boolean> coll) {
+ backing = new BitSet();
+ int bitIndex = 0;
+ for (Boolean b : coll) {
+ backing.set(bitIndex, b);
+ bitIndex++;
+ }
+ }
+
+ public Asn1VarSizeBitstring(BitSet bitset) {
+ backing = (BitSet) bitset.clone();
+ }
+
+ protected void setBit(int bitIndex, boolean value) {
+ backing.set(bitIndex, value);
+ }
+
+ public boolean getBit(int bitIndex) {
+ return backing.get(bitIndex);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+ Asn1VarSizeBitstring booleen = (Asn1VarSizeBitstring) o;
+ return Objects.equals(backing, booleen.backing);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), backing);
+ }
+}
diff --git a/src/org/uic/barcode/asn1/datatypes/Bitstring.java b/src/org/uic/barcode/asn1/datatypes/Bitstring.java
new file mode 100644
index 0000000..1543f64
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/Bitstring.java
@@ -0,0 +1,16 @@
+package org.uic.barcode.asn1.datatypes;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** This annotation is used for bitstrings.
+ * In UPER, a SEQUENCE OF Booleans would look exactly as bitstring, so this annotation can be
+ * omitted for {@code List<Boolean>}.
+ */
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Bitstring {
+
+}
diff --git a/src/org/uic/barcode/asn1/datatypes/CharacterRestriction.java b/src/org/uic/barcode/asn1/datatypes/CharacterRestriction.java
new file mode 100644
index 0000000..e74c436
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/CharacterRestriction.java
@@ -0,0 +1,13 @@
+package org.uic.barcode.asn1.datatypes;
+
+public enum CharacterRestriction {
+ NumericString,
+ PrintableString,
+ VisibleString,
+ ISO646String,
+ IA5String,
+ BMPString,
+ UniversalString,
+ UTF8String,
+ ObjectIdentifier;
+}
diff --git a/src/org/uic/barcode/asn1/datatypes/Choice.java b/src/org/uic/barcode/asn1/datatypes/Choice.java
new file mode 100644
index 0000000..01a0034
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/Choice.java
@@ -0,0 +1,12 @@
+package org.uic.barcode.asn1.datatypes;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Choice {
+
+}
diff --git a/src/org/uic/barcode/asn1/datatypes/DefaultAlphabet.java b/src/org/uic/barcode/asn1/datatypes/DefaultAlphabet.java
new file mode 100644
index 0000000..62d13f4
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/DefaultAlphabet.java
@@ -0,0 +1,8 @@
+package org.uic.barcode.asn1.datatypes;
+
+public class DefaultAlphabet extends Alphabet {
+
+ public DefaultAlphabet() {
+ super("");
+ }
+}
diff --git a/src/org/uic/barcode/asn1/datatypes/FieldOrder.java b/src/org/uic/barcode/asn1/datatypes/FieldOrder.java
new file mode 100644
index 0000000..b8c378f
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/FieldOrder.java
@@ -0,0 +1,13 @@
+package org.uic.barcode.asn1.datatypes;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface FieldOrder {
+ int order() default -1;
+}
+
diff --git a/src/org/uic/barcode/asn1/datatypes/FixedSize.java b/src/org/uic/barcode/asn1/datatypes/FixedSize.java
new file mode 100644
index 0000000..4c17e60
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/FixedSize.java
@@ -0,0 +1,12 @@
+package org.uic.barcode.asn1.datatypes;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface FixedSize {
+ int value();
+}
diff --git a/src/org/uic/barcode/asn1/datatypes/HasExtensionMarker.java b/src/org/uic/barcode/asn1/datatypes/HasExtensionMarker.java
new file mode 100644
index 0000000..b8945fc
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/HasExtensionMarker.java
@@ -0,0 +1,12 @@
+package org.uic.barcode.asn1.datatypes;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface HasExtensionMarker {
+
+}
diff --git a/src/org/uic/barcode/asn1/datatypes/IntMinValue.java b/src/org/uic/barcode/asn1/datatypes/IntMinValue.java
new file mode 100644
index 0000000..e045287
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/IntMinValue.java
@@ -0,0 +1,13 @@
+package org.uic.barcode.asn1.datatypes;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface IntMinValue {
+ long minValue();
+ boolean hasExtensionMarker() default false;
+}
diff --git a/src/org/uic/barcode/asn1/datatypes/IntRange.java b/src/org/uic/barcode/asn1/datatypes/IntRange.java
new file mode 100644
index 0000000..08fc1fb
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/IntRange.java
@@ -0,0 +1,14 @@
+package org.uic.barcode.asn1.datatypes;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface IntRange {
+ long minValue();
+ long maxValue();
+ boolean hasExtensionMarker() default false;
+}
diff --git a/src/org/uic/barcode/asn1/datatypes/IsExtension.java b/src/org/uic/barcode/asn1/datatypes/IsExtension.java
new file mode 100644
index 0000000..8aacd32
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/IsExtension.java
@@ -0,0 +1,12 @@
+package org.uic.barcode.asn1.datatypes;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface IsExtension {
+
+}
diff --git a/src/org/uic/barcode/asn1/datatypes/NoAsn1Field.java b/src/org/uic/barcode/asn1/datatypes/NoAsn1Field.java
new file mode 100644
index 0000000..0fb7d2c
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/NoAsn1Field.java
@@ -0,0 +1,10 @@
+package org.uic.barcode.asn1.datatypes;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface NoAsn1Field {} \ No newline at end of file
diff --git a/src/org/uic/barcode/asn1/datatypes/Optional.java b/src/org/uic/barcode/asn1/datatypes/Optional.java
new file mode 100644
index 0000000..757ba29
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/Optional.java
@@ -0,0 +1,96 @@
+package org.uic.barcode.asn1.datatypes;
+
+import java.util.NoSuchElementException;
+import java.util.Objects;
+
+/** Represents optional values.
+ *
+ * Should be replaced by java.util.Optional from Java 8, when project moves to Java 8.
+ *
+ * @param <T> type of contained elements */
+public class Optional<T> {
+
+ private final T element;
+ private final boolean isPresent;
+
+ private Optional(T element, boolean isPresent) {
+ this.element = element;
+ this.isPresent = isPresent;
+ }
+
+ /** @return true if the Option contains a value */
+ public boolean isPresent() {
+ return isPresent;
+ }
+
+ /** @return the element if the option is not empty
+ * @throws java.util.NoSuchElementException if the option is empty */
+ public T get() {
+ if (isPresent) {
+ return element;
+ } else {
+ throw new NoSuchElementException("None.get");
+ }
+ }
+
+ /** @return the value, if present, otherwise return {@code other}
+ * @param other the value to be returned if there is no value present */
+ public T orElse(T other) {
+ return isPresent() ? get() : other;
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this Optional. The
+ * other object is considered equal if:
+ * <ul>
+ * <li>it is also an {@code Optional} and;
+ * <li>both instances have no value present or;
+ * <li>the present values are "equal to" each other via {@code equals()}.
+ * </ul>
+ *
+ * @param obj an object to be tested for equality
+ * @return {code true} if the other object is "equal to" this object
+ * otherwise {@code false}
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (!(obj instanceof Optional)) {
+ return false;
+ }
+
+ Optional<?> other = (Optional<?>) obj;
+ return Objects.equals(element, other.element);
+ }
+
+ /**
+ * Returns the hash code value of the present value, if any, or 0 (zero) if
+ * no value is present.
+ *
+ * @return hash code value of the present value or 0 if no value is present
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(element);
+ }
+
+ /** Returns an Option containing the value.
+ *
+ * @param <A> the type of the value
+ * @param element contained value
+ * @return a new Option that contains the value */
+ public static <A> Optional<A> of(final A element) {
+ return new Optional<A>(element, true);
+ }
+
+ /** Returns an empty option.
+ *
+ * @param <A>
+ * @return an empty Option */
+ public static <A> Optional<A> empty() {
+ return new Optional<A>(null, false);
+ }
+}
diff --git a/src/org/uic/barcode/asn1/datatypes/RestrictedString.java b/src/org/uic/barcode/asn1/datatypes/RestrictedString.java
new file mode 100644
index 0000000..7539aed
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/RestrictedString.java
@@ -0,0 +1,13 @@
+package org.uic.barcode.asn1.datatypes;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface RestrictedString {
+ CharacterRestriction value();
+ Class<? extends Alphabet> alphabet() default DefaultAlphabet.class;
+}
diff --git a/src/org/uic/barcode/asn1/datatypes/Sequence.java b/src/org/uic/barcode/asn1/datatypes/Sequence.java
new file mode 100644
index 0000000..31163d7
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/Sequence.java
@@ -0,0 +1,9 @@
+package org.uic.barcode.asn1.datatypes;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+//@Target({ElementType.ANNOTATION_TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Sequence {
+
+}
diff --git a/src/org/uic/barcode/asn1/datatypes/SizeRange.java b/src/org/uic/barcode/asn1/datatypes/SizeRange.java
new file mode 100644
index 0000000..7005d47
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/SizeRange.java
@@ -0,0 +1,14 @@
+package org.uic.barcode.asn1.datatypes;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SizeRange {
+ int minValue();
+ int maxValue();
+ boolean hasExtensionMarker() default false;
+}
diff --git a/src/org/uic/barcode/asn1/datatypes/package-info.java b/src/org/uic/barcode/asn1/datatypes/package-info.java
new file mode 100644
index 0000000..aaa134e
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypes/package-info.java
@@ -0,0 +1,7 @@
+/** Annotations to create Java classes that correspond to ASN.1 specifications.
+ *
+ * Some annotations (e.g. {@link SizeRange}, {@link FixedSize}, {@link IntRange},{@link IntMaxValue}
+ * {@link RestrictedString}) are Type-only annotations and sometime require creating extra classes,
+ * they can be extended to work as Field annotations too, but this will require modifications to the
+ * Encoder. */
+package org.uic.barcode.asn1.datatypes;
diff --git a/src/org/uic/barcode/asn1/datatypesimpl/OctetString.java b/src/org/uic/barcode/asn1/datatypesimpl/OctetString.java
new file mode 100644
index 0000000..69346a2
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypesimpl/OctetString.java
@@ -0,0 +1,46 @@
+package org.uic.barcode.asn1.datatypesimpl;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.uic.barcode.asn1.datatypes.Asn1SequenceOf;
+
+/*
+ * Sequence of Asn1Integer for restricted integers
+ *
+ *
+ */
+public class OctetString extends Asn1SequenceOf<Byte> {
+ public OctetString() { super(); }
+ public OctetString(Collection<Byte> coll) { super(coll); }
+
+ public OctetString(List<Byte> numbers) {
+ super();
+ this.addAll(numbers);
+ }
+
+ public static OctetString getSequence(List<Byte> numList) {
+ if (numList == null || numList.isEmpty()) return null;
+ return new OctetString(numList);
+ }
+
+
+ public byte[] toByteArray () {
+
+ byte[] bytes= new byte[this.size()];
+
+ for (int i = 0; i < this.size(); i++){
+ bytes[i] = this.get(i);
+ }
+
+ return bytes;
+ }
+
+ public OctetString(byte[] bytes){
+ super();
+ for (int i= 0;i < bytes.length; i++){
+ this.add(bytes[i]);
+ }
+ }
+
+}
diff --git a/src/org/uic/barcode/asn1/datatypesimpl/SequenceOfLong.java b/src/org/uic/barcode/asn1/datatypesimpl/SequenceOfLong.java
new file mode 100644
index 0000000..32ae07f
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypesimpl/SequenceOfLong.java
@@ -0,0 +1,27 @@
+package org.uic.barcode.asn1.datatypesimpl;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.uic.barcode.asn1.datatypes.Asn1SequenceOf;
+
+/*
+ * Sequence of Asn1Integer for restricted integers
+ *
+ *
+ */
+public class SequenceOfLong extends Asn1SequenceOf<Long> {
+ public SequenceOfLong() { super(); }
+ public SequenceOfLong(Collection<Long> coll) { super(coll); }
+
+ public SequenceOfLong(List<Long> numbers) {
+ super();
+ this.addAll(numbers);
+ }
+
+ public static SequenceOfLong getSequence(List<Long> numList) {
+ if (numList == null || numList.isEmpty()) return null;
+ return new SequenceOfLong(numList);
+ }
+
+}
diff --git a/src/org/uic/barcode/asn1/datatypesimpl/SequenceOfStringIA5.java b/src/org/uic/barcode/asn1/datatypesimpl/SequenceOfStringIA5.java
new file mode 100644
index 0000000..1abcdcb
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypesimpl/SequenceOfStringIA5.java
@@ -0,0 +1,13 @@
+package org.uic.barcode.asn1.datatypesimpl;
+
+import java.util.Collection;
+
+import org.uic.barcode.asn1.datatypes.Asn1SequenceOf;
+import org.uic.barcode.asn1.datatypes.CharacterRestriction;
+import org.uic.barcode.asn1.datatypes.RestrictedString;
+
+@RestrictedString(CharacterRestriction.IA5String)
+public class SequenceOfStringIA5 extends Asn1SequenceOf<String> {
+ public SequenceOfStringIA5() { super(); }
+ public SequenceOfStringIA5(Collection<String> coll) { super(coll); }
+}
diff --git a/src/org/uic/barcode/asn1/datatypesimpl/SequenceOfStringUTF8.java b/src/org/uic/barcode/asn1/datatypesimpl/SequenceOfStringUTF8.java
new file mode 100644
index 0000000..4e98cdd
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypesimpl/SequenceOfStringUTF8.java
@@ -0,0 +1,13 @@
+package org.uic.barcode.asn1.datatypesimpl;
+
+import java.util.Collection;
+
+import org.uic.barcode.asn1.datatypes.Asn1SequenceOf;
+import org.uic.barcode.asn1.datatypes.CharacterRestriction;
+import org.uic.barcode.asn1.datatypes.RestrictedString;
+
+@RestrictedString(CharacterRestriction.UTF8String)
+public class SequenceOfStringUTF8 extends Asn1SequenceOf<String> {
+ public SequenceOfStringUTF8() { super(); }
+ public SequenceOfStringUTF8(Collection<String> coll) { super(coll); }
+}
diff --git a/src/org/uic/barcode/asn1/datatypesimpl/SequenceOfUnrestrictedLong.java b/src/org/uic/barcode/asn1/datatypesimpl/SequenceOfUnrestrictedLong.java
new file mode 100644
index 0000000..6af7352
--- /dev/null
+++ b/src/org/uic/barcode/asn1/datatypesimpl/SequenceOfUnrestrictedLong.java
@@ -0,0 +1,30 @@
+package org.uic.barcode.asn1.datatypesimpl;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.uic.barcode.asn1.datatypes.Asn1SequenceOf;
+
+/*
+ * Sequence of Asn1Integer for restricted integers
+ *
+ */
+public class SequenceOfUnrestrictedLong extends Asn1SequenceOf<Long> {
+ public SequenceOfUnrestrictedLong() { super(); }
+ public SequenceOfUnrestrictedLong(Collection<Long> coll) { super(coll); }
+
+
+ public SequenceOfUnrestrictedLong(List<Long> numbers) {
+ super();
+ for (Long number: numbers){
+ this.add(new Long(number));
+ }
+ }
+
+
+ public static SequenceOfUnrestrictedLong getSequence(List<Long> numList) {
+ if (numList == null || numList.isEmpty()) return null;
+ return new SequenceOfUnrestrictedLong(numList);
+ }
+
+}
diff --git a/src/org/uic/barcode/asn1/test/TestSequenceOfLong.java b/src/org/uic/barcode/asn1/test/TestSequenceOfLong.java
new file mode 100644
index 0000000..61af5fa
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/TestSequenceOfLong.java
@@ -0,0 +1,24 @@
+package org.uic.barcode.asn1.test;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.uic.barcode.asn1.datatypes.Asn1SequenceOf;
+
+public class TestSequenceOfLong extends Asn1SequenceOf<Long> {
+ public TestSequenceOfLong() { super(); }
+ public TestSequenceOfLong(Collection<Long> coll) { super(coll); }
+
+
+ public TestSequenceOfLong(List<Long> numbers) {
+ super();
+ for (Long number: numbers){
+ this.add(new Long(number));
+ }
+ }
+
+ public static TestSequenceOfLong getSequence(List<Long> numList) {
+ if (numList == null || numList.isEmpty()) return null;
+ return new TestSequenceOfLong(numList);
+ }
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeBooleanTest.java b/src/org/uic/barcode/asn1/test/UperEncodeBooleanTest.java
new file mode 100644
index 0000000..b5e73b0
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeBooleanTest.java
@@ -0,0 +1,82 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.uic.barcode.asn1.datatypes.Asn1Optional;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.Sequence;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+
+public class UperEncodeBooleanTest {
+
+ /**
+ * Example from the Standard on UPER.
+ <pre>
+ TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ value BOOLEAN OPTIONAL,
+}
+ </pre>
+ */
+ @Sequence
+ public static class TestRecord {
+
+ @FieldOrder(order = 0)
+ @Asn1Optional() Boolean value;
+
+ public TestRecord() {
+ this(false);
+ }
+
+ public TestRecord(Boolean value) {
+ this.value = value;
+ }
+ }
+
+
+ @Test public void testTrue() throws IllegalArgumentException, IllegalAccessException {
+ TestRecord record = new TestRecord(new Boolean(true));
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("C0",hex);
+ }
+
+ @Test public void testFalse() throws IllegalArgumentException, IllegalAccessException {
+ TestRecord record = new TestRecord(new Boolean(false));
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("80",hex);
+ }
+
+ @Test public void testDecodeTrue() throws IllegalArgumentException, IllegalAccessException {
+ TestRecord record = new TestRecord(new Boolean(true));
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("C0",hex);
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.value,record.value);
+
+ }
+
+ @Test public void testDecodeFalse() throws IllegalArgumentException, IllegalAccessException {
+ TestRecord record = new TestRecord(new Boolean(false));
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("80",hex);
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.value,record.value);
+ }
+
+
+
+
+
+
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeChoiceExtensionTest.java b/src/org/uic/barcode/asn1/test/UperEncodeChoiceExtensionTest.java
new file mode 100644
index 0000000..3833ff2
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeChoiceExtensionTest.java
@@ -0,0 +1,92 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.uic.barcode.asn1.datatypes.Asn1Optional;
+import org.uic.barcode.asn1.datatypes.CharacterRestriction;
+import org.uic.barcode.asn1.datatypes.Choice;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.HasExtensionMarker;
+import org.uic.barcode.asn1.datatypes.IsExtension;
+import org.uic.barcode.asn1.datatypes.RestrictedString;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+
+public class UperEncodeChoiceExtensionTest {
+
+ /** Example for extended sequence
+ TestRecord ::= [APPLICATION 0] CHOICE {
+ value1 IA5String
+ ,...
+ ,value2 IA5String
+ }
+
+ value TestRecord ::= value2: "extension"
+ */
+ @Choice
+ @HasExtensionMarker
+ public static class TestRecordExtended {
+
+ @FieldOrder(order = 0)
+ @RestrictedString(CharacterRestriction.IA5String)
+ String value1 = null;
+
+ @FieldOrder(order = 1)
+ @IsExtension
+ @RestrictedString(CharacterRestriction.IA5String)
+ String value2 = "extension";
+
+ public TestRecordExtended() { }
+ }
+
+ /** Example for extended sequence
+ TestRecord ::= [APPLICATION 0] CHOICE {
+ value1 IA5String,
+ ,...
+ }
+ */
+ @Choice
+ @HasExtensionMarker
+ public static class TestRecord {
+
+ @RestrictedString(CharacterRestriction.IA5String)
+ @Asn1Optional() String value1 = "regular";
+
+ public TestRecord() { }
+ }
+
+
+ @Test public void testEncode() throws IllegalArgumentException, IllegalAccessException {
+ TestRecordExtended record = new TestRecordExtended();
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("800909CBE3A65DDCF4EFDC",hex);
+ }
+
+ @Test public void testDecodeExtended() throws IllegalArgumentException, IllegalAccessException {
+ TestRecordExtended record = new TestRecordExtended();
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("800909CBE3A65DDCF4EFDC",hex);
+ TestRecordExtended result = UperEncoder.decode(encoded, TestRecordExtended.class);
+ assertEquals(result.value2,record.value2);
+ }
+
+ @Test public void testDecode() throws IllegalArgumentException, IllegalAccessException {
+ TestRecordExtended record = new TestRecordExtended();
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("800909CBE3A65DDCF4EFDC",hex);
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assert(result == null);
+ }
+
+
+
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeChoiceTest.java b/src/org/uic/barcode/asn1/test/UperEncodeChoiceTest.java
new file mode 100644
index 0000000..69b92d1
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeChoiceTest.java
@@ -0,0 +1,74 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.uic.barcode.asn1.datatypes.CharacterRestriction;
+import org.uic.barcode.asn1.datatypes.Choice;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.RestrictedString;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+
+public class UperEncodeChoiceTest {
+
+ /**
+ * Example from the Standard on UPER.
+ <pre>
+ TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ value EnumType DEFAULT value2,
+ }
+
+ EnumType ::= ENUMERATED {
+ value1 (0),
+ value2 (1)
+ ,...
+ }
+ </pre>
+ */
+ @Choice
+ public static class TestRecord {
+
+ @FieldOrder(order = 0)
+ @RestrictedString(CharacterRestriction.UTF8String)
+ String valueUtf8;
+
+ @FieldOrder(order = 1)
+ @RestrictedString(CharacterRestriction.IA5String)
+ String valueIA5;
+
+ public TestRecord() {
+ }
+
+ public TestRecord(String utf8, String ia5) {
+ this.valueUtf8 = utf8;
+ this.valueIA5 = ia5;
+ }
+ }
+
+ @Test public void testEncode() throws IllegalArgumentException, IllegalAccessException {
+ TestRecord record = new TestRecord(null, "Meier");
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("82CDCBA72F20",hex);
+ }
+
+ @Test public void testDecode() throws IllegalArgumentException, IllegalAccessException {
+ TestRecord record = new TestRecord(null, "Meier");
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("82CDCBA72F20",hex);
+
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(null,record.valueUtf8);
+ assertEquals(result.valueIA5,record.valueIA5);
+ }
+
+
+
+
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeEnumExtensionTest.java b/src/org/uic/barcode/asn1/test/UperEncodeEnumExtensionTest.java
new file mode 100644
index 0000000..a2ecbe1
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeEnumExtensionTest.java
@@ -0,0 +1,146 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.uic.barcode.asn1.datatypes.Asn1Optional;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.HasExtensionMarker;
+import org.uic.barcode.asn1.datatypes.IsExtension;
+import org.uic.barcode.asn1.datatypes.Sequence;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+
+public class UperEncodeEnumExtensionTest {
+
+ /*** Example from the Standard on UPER.
+ <pre>
+ World-Schema DEFINITIONS AUTOMATIC TAGS ::=
+ BEGIN
+ TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ value EnumType DEFAULT value2
+ }
+
+ EnumType ::= ENUMERATED {
+ value1 (0),
+ value2 (1)
+ ,...
+ }
+ END
+ </pre>
+ */
+ @Sequence
+ public static class TestRecord {
+
+ @FieldOrder(order = 0)
+ @Asn1Optional EnumType value = EnumType.value1;
+ public TestRecord() {}
+ public void setValue(EnumType value) {
+ this.value = value;
+ }
+ }
+
+
+ /*** Example from the Standard on UPER.
+ TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ value EnumType DEFAULT value2,
+ }
+
+ EnumType ::= ENUMERATED {
+ value1 (0),
+ value2 (1)
+ ,...
+ value3 (2)
+ }
+ */
+ @Sequence
+ public static class TestRecordExtended {
+
+ @Asn1Optional EnumTypeExtended value = EnumTypeExtended.value3;
+
+ public TestRecordExtended() {}
+
+ public void setValue(EnumTypeExtended value) {
+ this.value = value;
+ }
+
+
+ }
+
+ @HasExtensionMarker
+ public enum EnumType {
+ value1("value1"),
+ value2("value2");
+
+ public String text;
+
+ EnumType(String text) {
+ this.text = text;
+ }
+
+ public String toString(){
+ return text;
+ }
+ }
+
+
+ @HasExtensionMarker
+ public enum EnumTypeExtended {
+ value1("value1"),
+ value2("value2"),
+
+ @IsExtension
+ value3("value3");
+
+ public String text;
+
+ EnumTypeExtended(String text) {
+ this.text = text;
+ }
+
+ public String toString(){
+ return text;
+ }
+ }
+
+
+
+ @Test public void testExtension() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecordExtended record = new TestRecordExtended();
+ record.setValue(EnumTypeExtended.value3);
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("Enum value3: data hex: %s", hex));
+ assertEquals("C000", hex);
+ }
+
+ @Test public void testExtensionDecoding() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecordExtended record = new TestRecordExtended();
+ record.setValue(EnumTypeExtended.value3);
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("Enum value3: data hex: %s", hex));
+ assertEquals("C000", hex);
+
+ TestRecordExtended result = UperEncoder.decode(encoded, TestRecordExtended.class);
+ assertEquals(result.value,EnumTypeExtended.value3);
+ }
+
+ @Test public void testUnknownExtensionDecoding() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecordExtended record = new TestRecordExtended();
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("Enum value3: data hex: %s", hex));
+ assertEquals("C000", hex);
+
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assert(result.value == null);
+ }
+
+
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeEnumTest.java b/src/org/uic/barcode/asn1/test/UperEncodeEnumTest.java
new file mode 100644
index 0000000..d1d2d82
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeEnumTest.java
@@ -0,0 +1,126 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.uic.barcode.asn1.datatypes.Asn1Default;
+import org.uic.barcode.asn1.datatypes.Asn1Optional;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.Sequence;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+
+public class UperEncodeEnumTest {
+
+ /**
+ * Example from the Standard on UPER.
+ <pre>
+ TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ value EnumType DEFAULT value2,
+ }
+
+ EnumType ::= ENUMERATED {
+ value1 (0),
+ value2 (1)
+ ,...
+ }
+ </pre>
+ */
+ @Sequence
+ public static class TestRecord {
+
+ @FieldOrder(order = 0)
+ @Asn1Default(value="value2")
+ @Asn1Optional EnumType value = EnumType.value2;
+
+
+ public TestRecord() {}
+
+ public TestRecord(EnumType value) {
+ this.value = value;
+ }
+ }
+
+ public enum EnumType {
+ value1("value1"),
+ value2("value2"),
+ value3("value3"),
+ value4("value4"),
+ value5("value5"),
+ value6("value6"),
+ value7("value7"),
+ value8("value8"),
+ value9("value9"),
+ value10("value10"),
+ value11("value11"),
+ value12("value12"),
+ value13("value13"),
+ value14("value14"),
+ value15("value15"),
+ value16("value16"),
+ value17("value17"),
+ value18("value18"),
+ value19("value19"),
+ value20("value20"),
+ value21("value21"),
+ value22("value22");
+
+
+ public String text;
+
+ EnumType(String text) {
+ this.text = text;
+ }
+
+ public String toString(){
+ return text;
+ }
+ }
+
+
+
+ @Test public void testNonDefaultValue() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord(EnumType.value4);
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("Enum value4: data hex: %s", hex));
+ assertEquals("8C", hex);
+ }
+
+ @Test public void testDefaultValue() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord(EnumType.value2);
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("Enum value2: data hex: %s", hex));
+ assertEquals("00", UperEncoder.hexStringFromBytes(encoded));
+ }
+
+ @Test public void testDecodeNonDefaultValue() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord(EnumType.value4);
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("Enum value4: data hex: %s", hex));
+ assertEquals("8C", hex);
+
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.value,EnumType.value4);
+ }
+
+ @Test public void testDecodeDefaultValue() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord(EnumType.value2);
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("Enum value2: data hex: %s", hex));
+ assertEquals("00", UperEncoder.hexStringFromBytes(encoded));
+
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.value,EnumType.value2);
+ }
+
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeExtensionFieldOrderTest.java b/src/org/uic/barcode/asn1/test/UperEncodeExtensionFieldOrderTest.java
new file mode 100644
index 0000000..ff97c27
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeExtensionFieldOrderTest.java
@@ -0,0 +1,100 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.uic.barcode.asn1.datatypes.Asn1BigInteger;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.HasExtensionMarker;
+import org.uic.barcode.asn1.datatypes.IsExtension;
+import org.uic.barcode.asn1.datatypes.Sequence;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+
+public class UperEncodeExtensionFieldOrderTest {
+
+ /**
+ * Example from the Standard on UPER.
+ <pre>
+ TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ number1 INTEGER,
+ ...,
+ number2 INTEGER,
+ number3 INTEGER
+ }
+
+ value TestRecord ::= {
+ value1 12345678909999899,
+ value2 5555555555,
+ value3 32001
+ }
+
+Encoding to the file 'data.uper' using PER UNALIGNED encoding rule...
+TestRecord SEQUENCE [root fieldcount (not encoded) = 1]
+ value1 INTEGER [length = 7.0]
+ 12345678909999899
+ value2 INTEGER [length = 5.0]
+ 5555555555
+ value3 INTEGER [length = 2.0]
+ 32001
+Total encoded length = 20.2
+Encoded successfully in 21 bytes:
+8395EE2A 2EF8858D 81C18140 52C8C338 C0C09F40 40
+
+
+ </pre>
+ */
+ @Sequence
+ @HasExtensionMarker
+ public static class TestRecord {
+
+ @FieldOrder(order = 0)
+ Asn1BigInteger value1;
+
+ @FieldOrder(order = 1)
+ @IsExtension
+ Asn1BigInteger value2;
+
+ @FieldOrder(order = 2)
+ @IsExtension
+ Asn1BigInteger value3;
+
+ public TestRecord() {
+ value1 = new Asn1BigInteger(12345678909999899L);
+ value2 = new Asn1BigInteger(5555555555L);
+ value3 = new Asn1BigInteger(32001L);
+ }
+
+
+ }
+
+
+ @Test public void test() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord();
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("8395EE2A2EF8858D81C1814052C8C338C0C09F4040",hex);
+
+
+ }
+
+ @Test public void testDecode() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord();
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("8395EE2A2EF8858D81C1814052C8C338C0C09F4040",hex);
+
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.value1.longValue(),record.value1.longValue());
+ assertEquals(result.value2.longValue(),record.value2.longValue());
+ assertEquals(result.value3.longValue(),record.value3.longValue());
+
+ }
+
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeFieldOrderTest.java b/src/org/uic/barcode/asn1/test/UperEncodeFieldOrderTest.java
new file mode 100644
index 0000000..5a23f24
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeFieldOrderTest.java
@@ -0,0 +1,66 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.uic.barcode.asn1.datatypes.Asn1Optional;
+import org.uic.barcode.asn1.datatypes.CharacterRestriction;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.RestrictedString;
+import org.uic.barcode.asn1.datatypes.Sequence;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+
+public class UperEncodeFieldOrderTest {
+
+ /**
+ * Example from the Standard on UPER.
+ <pre>
+ World-Schema DEFINITIONS AUTOMATIC TAGS ::=
+ BEGIN
+ TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ testString1 UTF8String OPTIONAL,
+ testString2 IA5String OPTIONAL
+ }
+ END
+ </pre>
+ */
+ @Sequence
+ public static class TestRecord {
+
+ @FieldOrder(order = 1)
+ @RestrictedString(CharacterRestriction.IA5String)
+ @Asn1Optional() String string2;
+
+ @FieldOrder(order = 0)
+ @RestrictedString(CharacterRestriction.UTF8String)
+ @Asn1Optional() String string1;
+
+
+ public TestRecord() {
+ }
+
+ public TestRecord(String utf8, String ia5) {
+ this.string1 = utf8;
+ this.string2 = ia5;
+ }
+ }
+
+
+ @Test public void test() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord("String1", "String2");
+ byte[] encoded = UperEncoder.encode(record);
+
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.string1,"String1");
+ assertEquals(result.string2,"String2");
+ }
+
+
+
+
+
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeIntegerConstrainedTest.java b/src/org/uic/barcode/asn1/test/UperEncodeIntegerConstrainedTest.java
new file mode 100644
index 0000000..575597f
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeIntegerConstrainedTest.java
@@ -0,0 +1,71 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.IntRange;
+import org.uic.barcode.asn1.datatypes.Sequence;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+
+public class UperEncodeIntegerConstrainedTest {
+
+ /**
+ * Example from the Standard on UPER.
+ <pre>
+ TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ number1 INTEGER (1..999),
+ number2 INTEGER (0..999),
+ number3 INTEGER (63..999)
+ }
+ </pre>
+ */
+ @Sequence
+ public static class TestRecord {
+
+ @FieldOrder(order = 0)
+ @IntRange(minValue=1, maxValue=999)
+ public Long value1;
+
+ @FieldOrder(order = 1)
+ @IntRange(minValue=0, maxValue=999)
+ public Long value2;
+
+ @FieldOrder(order = 2)
+ @IntRange(minValue=63, maxValue=999)
+ public Long value3;
+
+
+ public TestRecord() {
+ this(new Long(63L));
+ }
+
+ public TestRecord(Long num) {
+ value1 = num;
+ value2 = num;
+ value3 = num;
+ }
+ }
+
+
+
+ @Test public void test() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord(63L);
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("0F83F000",hex);
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.value1.longValue(),record.value1.longValue());
+ assertEquals(result.value2.longValue(),record.value2.longValue());
+ assertEquals(result.value3.longValue(),record.value3.longValue());
+
+ }
+
+
+
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeIntegerExtensionTest.java b/src/org/uic/barcode/asn1/test/UperEncodeIntegerExtensionTest.java
new file mode 100644
index 0000000..d0acd20
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeIntegerExtensionTest.java
@@ -0,0 +1,89 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.uic.barcode.asn1.datatypes.Asn1BigInteger;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.HasExtensionMarker;
+import org.uic.barcode.asn1.datatypes.IsExtension;
+import org.uic.barcode.asn1.datatypes.Sequence;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+
+public class UperEncodeIntegerExtensionTest {
+
+ /**
+ * Example from the Standard on UPER.
+ <pre>
+ TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ number1 INTEGER,
+ ...,
+ number2 INTEGER,
+ number3 INTEGER
+ }
+
+ value TestRecord ::= {
+ value1 12345678909999899,
+ value2 5555555555,
+ value3 32001
+ }
+
+Encoding to the file 'data.uper' using PER UNALIGNED encoding rule...
+TestRecord SEQUENCE [root fieldcount (not encoded) = 1]
+ value1 INTEGER [length = 7.0]
+ 12345678909999899
+ value2 INTEGER [length = 5.0]
+ 5555555555
+ value3 INTEGER [length = 2.0]
+ 32001
+Total encoded length = 20.2
+Encoded successfully in 21 bytes:
+8395EE2A 2EF8858D 81C18140 52C8C338 C0C09F40 40
+
+
+ </pre>
+ */
+ @Sequence
+ @HasExtensionMarker
+ public static class TestRecord {
+
+ @FieldOrder(order = 0)
+ Asn1BigInteger value1;
+
+ @FieldOrder(order = 2)
+ @IsExtension
+ Asn1BigInteger value3;
+
+ @FieldOrder(order = 1)
+ @IsExtension
+ Asn1BigInteger value2;
+
+
+ public TestRecord() {
+ value1 = new Asn1BigInteger(12345678909999899L);
+ value2 = new Asn1BigInteger(5555555555L);
+ value3 = new Asn1BigInteger(32001L);
+ }
+
+
+ }
+
+
+ @Test public void test() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord();
+ byte[] encoded = UperEncoder.encode(record);
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.value1.longValue(),record.value1.longValue());
+ assertEquals(result.value2.longValue(),record.value2.longValue());
+ assertEquals(result.value3.longValue(),record.value3.longValue());
+
+
+ }
+
+
+
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeIntegerSmallTest.java b/src/org/uic/barcode/asn1/test/UperEncodeIntegerSmallTest.java
new file mode 100644
index 0000000..5ada54d
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeIntegerSmallTest.java
@@ -0,0 +1,130 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.Sequence;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+
+public class UperEncodeIntegerSmallTest {
+
+ /**
+ * Example from the Standard on UPER.
+ <pre>
+ TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ number1 INTEGER,
+ number2 INTEGER
+ }
+ </pre>
+ */
+ @Sequence
+ public static class TestRecord {
+
+ @FieldOrder(order = 0)
+ public Long value1;
+
+ @FieldOrder(order = 1)
+ public Integer value2;
+
+ public TestRecord() {
+ this(new Long(12345678909999899L));
+ }
+
+ public TestRecord(Long num) {
+ value1 = num;
+ value2 = Integer.valueOf(num.intValue());
+ }
+ }
+
+
+
+ @Test public void test1() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord(1L);
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("01010101",hex);
+
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.value1.longValue(),record.value1.longValue());
+ assertEquals(result.value2.longValue(),record.value2.longValue());
+
+ }
+
+ @Test public void test16() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord(16L);
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("01100110",hex);
+
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.value1.longValue(),record.value1.longValue());
+ assertEquals(result.value2.longValue(),record.value2.longValue());
+
+ }
+
+
+ @Test public void test63() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord(63L);
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("013F013F",hex);
+
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.value1.longValue(),record.value1.longValue());
+ assertEquals(result.value2.longValue(),record.value2.longValue());
+
+ }
+
+ @Test public void test64() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord(64L);
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("01400140",hex);
+
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.value1.longValue(),record.value1.longValue());
+ assertEquals(result.value2.longValue(),record.value2.longValue());
+
+ }
+
+ @Test public void test127() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord(127L);
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("017F017F",hex);
+
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.value1.longValue(),record.value1.longValue());
+ assertEquals(result.value2.longValue(),record.value2.longValue());
+
+ }
+
+ @Test public void test128() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord(128L);
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("020080020080",hex);
+
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.value1.longValue(),record.value1.longValue());
+ assertEquals(result.value2.longValue(),record.value2.longValue());
+
+ }
+
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeIntegerTest.java b/src/org/uic/barcode/asn1/test/UperEncodeIntegerTest.java
new file mode 100644
index 0000000..d57fd00
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeIntegerTest.java
@@ -0,0 +1,64 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.uic.barcode.asn1.datatypes.Asn1BigInteger;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.Sequence;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+
+public class UperEncodeIntegerTest {
+
+ /**
+ * Example from the Standard on UPER.
+ <pre>
+ TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ number INTEGER,
+ }
+ </pre>
+ */
+ @Sequence
+ public static class TestRecord {
+
+ @FieldOrder(order = 0)
+ Asn1BigInteger value;
+
+ public TestRecord() {
+ this(new Long(12345678909999899L));
+ }
+
+ public TestRecord(Long num) {
+ value = new Asn1BigInteger(num);
+ }
+ }
+
+
+
+ @Test public void test() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord(12345678909999899L);
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("072BDC545DF10B1B",hex);
+
+ }
+
+ @Test public void testDecode() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord(12345678909999899L);
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("072BDC545DF10B1B",hex);
+
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.value.longValue(),record.value.longValue());
+
+ }
+
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeObjectIdentifierTest.java b/src/org/uic/barcode/asn1/test/UperEncodeObjectIdentifierTest.java
new file mode 100644
index 0000000..edf3348
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeObjectIdentifierTest.java
@@ -0,0 +1,76 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+import java.util.logging.Level;
+
+import org.junit.jupiter.api.Test;
+import org.uic.barcode.asn1.datatypes.Asn1Optional;
+import org.uic.barcode.asn1.datatypes.CharacterRestriction;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.RestrictedString;
+import org.uic.barcode.asn1.datatypes.Sequence;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+class UperEncodeObjectIdentifierTest {
+
+
+ /**
+ * Example from the Standard on UPER.
+ <pre>
+ TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ value1 OBJECT IDENTIFIER,
+ value2 OBJECT IDENTIFIER,
+ value3 OBJECT IDENTIFIER
+ }
+
+ value TestRecord ::= {
+ value1 2.16.840.1.101.3.4.3.1,
+ value2 2.16.840.1.101.3.4.3.2,
+ value3 1.2.840.10045.3.1.7
+ }
+ </pre>
+ */
+
+ @Sequence
+ public static class TestRecord {
+
+ @FieldOrder(order = 0)
+ @RestrictedString(CharacterRestriction.ObjectIdentifier)
+ String value1 = "2.16.840.1.101.3.4.3.1"; //DSA SHA224
+
+ @FieldOrder(order = 1)
+ @RestrictedString(CharacterRestriction.ObjectIdentifier)
+ String value2 = "2.16.840.1.101.3.4.3.2"; //DSA SHA248
+
+ @FieldOrder(order = 2)
+ @RestrictedString(CharacterRestriction.ObjectIdentifier)
+ String value3 = "1.2.840.10045.3.1.7"; //ECC
+
+ public TestRecord() {}
+
+ }
+
+ @Test
+ public void testEncode() throws IllegalArgumentException, IllegalAccessException {
+ TestRecord record = new TestRecord();
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("0960864801650304030109608648016503040302082A8648CE3D030107",hex);
+ }
+
+ @Test
+ public void testDecode() throws IllegalArgumentException, IllegalAccessException {
+ TestRecord record = new TestRecord();
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("0960864801650304030109608648016503040302082A8648CE3D030107",hex);
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.value1,record.value1);
+ assertEquals(result.value2,record.value2);
+ assertEquals(result.value3,record.value3);
+ }
+
+
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeOctetStringTest.java b/src/org/uic/barcode/asn1/test/UperEncodeOctetStringTest.java
new file mode 100644
index 0000000..af2a42e
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeOctetStringTest.java
@@ -0,0 +1,80 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.Sequence;
+import org.uic.barcode.asn1.datatypesimpl.OctetString;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+
+public class UperEncodeOctetStringTest {
+
+ /**
+ * Example from the Standard on UPER.
+ <pre>
+ World-Schema DEFINITIONS AUTOMATIC TAGS ::=
+ BEGIN
+ TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ value OCTET STRING
+ }
+ END
+
+ value TestRecord ::= { value '83DA'H }
+
+ Encoding to the file 'data.uper' using PER UNALIGNED encoding rule...
+ TestRecord SEQUENCE [fieldcount (not encoded) = 1]
+ value OCTET STRING [length = 2.0]
+ 0x83da
+ Total encoded length = 3.0
+ Encoded successfully in 3 bytes:
+ 0283DA
+
+ </pre>
+ */
+ @Sequence
+ public static class TestRecord {
+
+ @FieldOrder(order = 0)
+ OctetString value;
+
+ public TestRecord() {
+ value = new OctetString();
+ value.add(hexToByte("83"));
+ value.add(hexToByte("DA"));
+ }
+ }
+
+
+ @Test public void test() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord();
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("0283DA",hex);
+
+ }
+
+ @Test public void testDecode() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord();
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("0283DA",hex);
+
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.value,record.value);
+
+ }
+
+ public static byte hexToByte(String s){
+ return (byte) ((Character.digit(s.charAt(0), 16) << 4)
+ + Character.digit(s.charAt(1), 16));
+ }
+
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeOptionalSequenceExtensionTest.java b/src/org/uic/barcode/asn1/test/UperEncodeOptionalSequenceExtensionTest.java
new file mode 100644
index 0000000..55e6026
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeOptionalSequenceExtensionTest.java
@@ -0,0 +1,118 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.uic.barcode.asn1.datatypes.Asn1Optional;
+import org.uic.barcode.asn1.datatypes.CharacterRestriction;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.HasExtensionMarker;
+import org.uic.barcode.asn1.datatypes.IsExtension;
+import org.uic.barcode.asn1.datatypes.RestrictedString;
+import org.uic.barcode.asn1.datatypes.Sequence;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+
+public class UperEncodeOptionalSequenceExtensionTest {
+
+ /** Example for extended sequence including extension
+ World-Schema DEFINITIONS AUTOMATIC TAGS ::=
+ BEGIN
+ TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ value1 IA5String,
+ ...,
+ value2 IA5String OPTIONAL
+ }
+ END
+ */
+ @Sequence
+ @HasExtensionMarker
+ public static class TestRecordExtended {
+
+ @FieldOrder(order = 0)
+ @RestrictedString(CharacterRestriction.IA5String)
+ String value1;
+
+ @FieldOrder(order = 1)
+ @IsExtension
+ @RestrictedString(CharacterRestriction.IA5String)
+ @Asn1Optional() String value2;
+
+ public TestRecordExtended() { }
+
+ public void setValue1(String value1) {
+ this.value1 = value1;
+ }
+
+ public void setValue2(String value2) {
+ this.value2 = value2;
+ }
+
+
+
+ }
+
+ /** Example for extended sequence
+ TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ value1 IA5String,
+ ,...
+ }
+ */
+ @Sequence
+ @HasExtensionMarker
+ public static class TestRecord {
+
+ @RestrictedString(CharacterRestriction.IA5String)
+ String value1 = "regular";
+ public TestRecord() { }
+ }
+
+
+ @Test public void testEncode() throws IllegalArgumentException, IllegalAccessException {
+ TestRecordExtended record = new TestRecordExtended();
+ record.setValue1("regular");
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("03F2CB9FAECC3C80",hex);
+ }
+
+ @Test public void testEncodeWithoutOptionalElement() throws IllegalArgumentException, IllegalAccessException {
+ TestRecordExtended record = new TestRecordExtended();
+ record.setValue1("regular");
+ record.setValue2("extension");
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("83F2CB9FAECC3C80424272F8E997773D3BF700",hex);
+ }
+
+ @Test public void testDecodeExtended() throws IllegalArgumentException, IllegalAccessException {
+ TestRecordExtended record = new TestRecordExtended();
+ record.setValue1("regular");
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("03F2CB9FAECC3C80",hex);
+ TestRecordExtended result = UperEncoder.decode(encoded, TestRecordExtended.class);
+ assertEquals(result.value1,record.value1);
+ }
+
+ @Test public void testDecode() throws IllegalArgumentException, IllegalAccessException {
+ TestRecordExtended record = new TestRecordExtended();
+ record.setValue1("regular");
+ record.setValue2("extension");
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("83F2CB9FAECC3C80424272F8E997773D3BF700",hex);
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.value1,record.value1);
+ }
+
+
+
+
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeRestrictedIntegerTest.java b/src/org/uic/barcode/asn1/test/UperEncodeRestrictedIntegerTest.java
new file mode 100644
index 0000000..bc82621
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeRestrictedIntegerTest.java
@@ -0,0 +1,62 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.IntRange;
+import org.uic.barcode.asn1.datatypes.Sequence;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+
+public class UperEncodeRestrictedIntegerTest {
+
+ /**
+ * Example from the Standard on UPER.
+ <pre>
+TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ number INTEGER(32000..63000),
+}
+ </pre>
+ */
+ @Sequence
+ public static class TestRecord {
+
+ @FieldOrder(order = 0)
+ @IntRange(maxValue = 63000, minValue = 33000)
+ Long value;
+
+ public TestRecord() {
+ this(new Long(33005));
+ }
+
+ public TestRecord(Long num) {
+ value = num;
+ }
+ }
+
+
+ @Test public void test() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord(33005L);
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("000A",hex);
+ }
+
+ @Test public void testDecode() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord(33005L);
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("000A",hex);
+
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.value,record.value);
+ }
+
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeSequenceExtensionTest.java b/src/org/uic/barcode/asn1/test/UperEncodeSequenceExtensionTest.java
new file mode 100644
index 0000000..29d203b
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeSequenceExtensionTest.java
@@ -0,0 +1,93 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.uic.barcode.asn1.datatypes.Asn1Optional;
+import org.uic.barcode.asn1.datatypes.CharacterRestriction;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.HasExtensionMarker;
+import org.uic.barcode.asn1.datatypes.IsExtension;
+import org.uic.barcode.asn1.datatypes.RestrictedString;
+import org.uic.barcode.asn1.datatypes.Sequence;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+
+public class UperEncodeSequenceExtensionTest {
+
+ /** Example for extended sequence including extension
+ TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ value1 IA5String,
+ ,...
+ value2 IA5String
+ }
+ */
+ @Sequence
+ @HasExtensionMarker
+ public static class TestRecordExtended {
+
+ @FieldOrder(order = 0)
+ @RestrictedString(CharacterRestriction.IA5String)
+ @Asn1Optional() String value1 = "regular";
+
+ @FieldOrder(order = 1)
+ @IsExtension
+ @RestrictedString(CharacterRestriction.IA5String)
+ @Asn1Optional() String value2 = "extension";
+
+
+ public TestRecordExtended() { }
+ }
+
+ /** Example for extended sequence
+ TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ value1 IA5String,
+ ,...
+ }
+ */
+ @Sequence
+ @HasExtensionMarker
+ public static class TestRecord {
+
+ @RestrictedString(CharacterRestriction.IA5String)
+ @Asn1Optional() String value1 = "regular";
+
+ public TestRecord() { }
+ }
+
+
+ @Test public void testEncode() throws IllegalArgumentException, IllegalAccessException {
+ TestRecordExtended record = new TestRecordExtended();
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("C1F965CFD7661E402121397C74CBBB9E9DFB80",hex);
+ }
+
+ @Test public void testDecodeExtended() throws IllegalArgumentException, IllegalAccessException {
+ TestRecordExtended record = new TestRecordExtended();
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("C1F965CFD7661E402121397C74CBBB9E9DFB80",hex);
+ TestRecordExtended result = UperEncoder.decode(encoded, TestRecordExtended.class);
+ assertEquals(result.value1,record.value1);
+ assertEquals(result.value2,record.value2);
+ }
+
+ @Test public void testDecode() throws IllegalArgumentException, IllegalAccessException {
+ TestRecordExtended record = new TestRecordExtended();
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("C1F965CFD7661E402121397C74CBBB9E9DFB80",hex);
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.value1,record.value1);
+ }
+
+
+
+
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeSequenceOfIntegerTest.java b/src/org/uic/barcode/asn1/test/UperEncodeSequenceOfIntegerTest.java
new file mode 100644
index 0000000..9194dca
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeSequenceOfIntegerTest.java
@@ -0,0 +1,72 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.Sequence;
+import org.uic.barcode.asn1.datatypesimpl.SequenceOfUnrestrictedLong;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+
+public class UperEncodeSequenceOfIntegerTest {
+
+ /**
+ * Example from the Standard on UPER.
+ <pre>
+TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ number INTEGER,
+}
+ </pre>
+ */
+ @Sequence
+ public static class TestRecord {
+
+ @FieldOrder(order = 0)
+ SequenceOfUnrestrictedLong numbers;
+
+ public TestRecord() {
+ }
+
+ public TestRecord(List<Long> nums) {
+ numbers = new SequenceOfUnrestrictedLong(nums);
+ }
+ }
+
+
+ @Test public void test() throws IllegalArgumentException, IllegalAccessException {
+
+ ArrayList<Long> nums = new ArrayList<Long>();
+ nums.add(new Long(12345678909999899L));
+ nums.add(new Long(12345678909999899L));
+ TestRecord record = new TestRecord(nums);
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", UperEncoder.hexStringFromBytes(encoded)));
+ assertEquals("02072BDC545DF10B1B072BDC545DF10B1B",hex);
+
+ }
+
+ @Test public void testDecode() throws IllegalArgumentException, IllegalAccessException {
+
+ ArrayList<Long> nums = new ArrayList<Long>();
+ nums.add(new Long(12345678909999899L));
+ nums.add(new Long(12345678909999899L));
+ TestRecord record = new TestRecord(nums);
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", UperEncoder.hexStringFromBytes(encoded)));
+ assertEquals("02072BDC545DF10B1B072BDC545DF10B1B",hex);
+
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.numbers.get(0).longValue(),record.numbers.get(0).longValue());
+ assertEquals(result.numbers.get(1).longValue(),record.numbers.get(1).longValue());
+
+
+ }
+
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeSequenceOfRestrictedIntegerTest.java b/src/org/uic/barcode/asn1/test/UperEncodeSequenceOfRestrictedIntegerTest.java
new file mode 100644
index 0000000..d1834d0
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeSequenceOfRestrictedIntegerTest.java
@@ -0,0 +1,77 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.IntRange;
+import org.uic.barcode.asn1.datatypes.Sequence;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+
+public class UperEncodeSequenceOfRestrictedIntegerTest {
+
+ /**
+ * Example from the Standard on UPER.
+ <pre>
+TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ numbers SEQUENCE OF INTEGER(0..9999999),
+}
+ </pre>
+ */
+ @Sequence
+ public static class TestRecord {
+
+ @FieldOrder(order = 0)
+ @IntRange(minValue=9500000,maxValue=99900001)
+ TestSequenceOfLong numbers = null;;
+
+ public TestRecord() {
+ }
+
+ public void addNumber(Long longValue){
+ if (numbers == null) {
+ numbers = new TestSequenceOfLong();
+ }
+ numbers.add(longValue);
+ }
+ }
+
+
+ @Test public void test() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord();
+
+ record.addNumber(new Long(9500001L));
+ record.addNumber(new Long(9699999L));
+
+
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", UperEncoder.hexStringFromBytes(encoded)));
+ assertEquals("02000000200C34FC",hex);
+ }
+
+ @Test public void testDecode() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord();
+
+ record.addNumber(new Long(9500001L));
+ record.addNumber(new Long(9699999L));
+
+
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", UperEncoder.hexStringFromBytes(encoded)));
+ assertEquals("02000000200C34FC",hex);
+
+
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.numbers.get(0).longValue(),record.numbers.get(0).longValue());
+ assertEquals(result.numbers.get(1).longValue(),record.numbers.get(1).longValue());
+ }
+
+
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeSequenceOfStringListTest.java b/src/org/uic/barcode/asn1/test/UperEncodeSequenceOfStringListTest.java
new file mode 100644
index 0000000..49350cc
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeSequenceOfStringListTest.java
@@ -0,0 +1,78 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.uic.barcode.asn1.datatypes.CharacterRestriction;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.RestrictedString;
+import org.uic.barcode.asn1.datatypes.Sequence;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+
+public class UperEncodeSequenceOfStringListTest {
+
+ /**
+ * Example from the Standard on UPER.
+ <pre>
+ TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ strings SEQUENCE OF IA5String,
+ }
+ </pre>
+ */
+ @Sequence
+ public static class TestRecord {
+
+ @FieldOrder(order = 0)
+ @RestrictedString(CharacterRestriction.IA5String)
+ ArrayList<String> strings = new ArrayList<String>();
+
+ public TestRecord() {
+ }
+
+ public List<String> getStrings() {
+ return strings;
+ }
+
+
+ }
+
+
+ @Test public void test() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord();
+ record.getStrings().add("test1");
+ record.getStrings().add("test2");
+ record.getStrings().add("test3");
+
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", UperEncoder.hexStringFromBytes(encoded)));
+ assertEquals("0305E9979F4620BD32F3E8C817A65E7D1980",hex);
+ }
+
+ @Test public void testDecode() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord();
+ record.getStrings().add("test1");
+ record.getStrings().add("test2");
+ record.getStrings().add("test3");
+
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", UperEncoder.hexStringFromBytes(encoded)));
+ assertEquals("0305E9979F4620BD32F3E8C817A65E7D1980",hex);
+
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class, null);
+ assert(result.getStrings().contains("test1"));
+ assert(result.getStrings().contains("test2"));
+ assert(result.getStrings().contains("test3"));
+
+
+ }
+
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeSequenceOfStringTest.java b/src/org/uic/barcode/asn1/test/UperEncodeSequenceOfStringTest.java
new file mode 100644
index 0000000..f8eccc6
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeSequenceOfStringTest.java
@@ -0,0 +1,76 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.uic.barcode.asn1.datatypes.CharacterRestriction;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.RestrictedString;
+import org.uic.barcode.asn1.datatypes.Sequence;
+import org.uic.barcode.asn1.datatypesimpl.SequenceOfStringIA5;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+
+public class UperEncodeSequenceOfStringTest {
+
+ /**
+ * Example from the Standard on UPER.
+ <pre>
+ TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ strings SEQUENCE OF IA5String,
+ }
+ </pre>
+ */
+ @Sequence
+ public static class TestRecord {
+
+ @FieldOrder(order = 0)
+ SequenceOfStringIA5 strings = new SequenceOfStringIA5();
+
+ public TestRecord() {
+ }
+
+ public SequenceOfStringIA5 getStrings() {
+ return strings;
+ }
+
+
+ }
+
+
+ @Test public void test() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord();
+ record.getStrings().add("test1");
+ record.getStrings().add("test2");
+ record.getStrings().add("test3");
+
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", UperEncoder.hexStringFromBytes(encoded)));
+ assertEquals("0305E9979F4620BD32F3E8C817A65E7D1980",hex);
+ }
+
+ @Test public void testDecode() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord();
+ record.getStrings().add("test1");
+ record.getStrings().add("test2");
+ record.getStrings().add("test3");
+
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", UperEncoder.hexStringFromBytes(encoded)));
+ assertEquals("0305E9979F4620BD32F3E8C817A65E7D1980",hex);
+
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assert(result.getStrings().contains("test1"));
+ assert(result.getStrings().contains("test2"));
+ assert(result.getStrings().contains("test3"));
+
+
+ }
+
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeSequenceOfUtf8StringTest.java b/src/org/uic/barcode/asn1/test/UperEncodeSequenceOfUtf8StringTest.java
new file mode 100644
index 0000000..ab8fa2a
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeSequenceOfUtf8StringTest.java
@@ -0,0 +1,96 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.Sequence;
+import org.uic.barcode.asn1.datatypesimpl.SequenceOfStringUTF8;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+
+public class UperEncodeSequenceOfUtf8StringTest {
+
+ /**
+ * Example from the Standard on UPER.
+ <pre>
+ World-Schema DEFINITIONS AUTOMATIC TAGS ::=
+ BEGIN
+ TestRecord ::= SEQUENCE {
+ strings SEQUENCE OF UTF8String
+ }
+ END
+
+ value TestRecord ::= {
+ strings {"test1" , "test2" , "test3" }
+ }
+
+
+Encoding to the file 'data.uper' using PER UNALIGNED encoding rule...
+TestRecord SEQUENCE [fieldcount (not encoded) = 1]
+ strings SEQUENCE OF [count = 3]
+ UTF8String [length = 5.0]
+ 0x7465737431
+ UTF8String [length = 5.0]
+ 0x7465737432
+ UTF8String [length = 5.0]
+ 0x7465737433
+Total encoded length = 19.0
+Encoded successfully in 19 bytes:
+03057465 73743105 74657374 32057465 737433
+
+ </pre>
+ */
+ @Sequence
+ public static class TestRecord {
+
+ @FieldOrder(order = 0)
+ SequenceOfStringUTF8 strings = new SequenceOfStringUTF8();
+
+ public TestRecord() {
+ }
+
+ public SequenceOfStringUTF8 getStrings() {
+ return strings;
+ }
+
+
+ }
+
+
+ @Test public void test() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord();
+ record.getStrings().add("test1");
+ record.getStrings().add("test2");
+ record.getStrings().add("test3");
+
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", UperEncoder.hexStringFromBytes(encoded)));
+ assertEquals("03057465737431057465737432057465737433",hex);
+ }
+
+ @Test public void testDecode() throws IllegalArgumentException, IllegalAccessException {
+
+ TestRecord record = new TestRecord();
+ record.getStrings().add("test1");
+ record.getStrings().add("test2");
+ record.getStrings().add("test3");
+
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", UperEncoder.hexStringFromBytes(encoded)));
+ assertEquals("03057465737431057465737432057465737433",hex);
+
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assert(result.getStrings().contains("test1"));
+ assert(result.getStrings().contains("test2"));
+ assert(result.getStrings().contains("test3"));
+
+
+ }
+
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeStringDefaultTest.java b/src/org/uic/barcode/asn1/test/UperEncodeStringDefaultTest.java
new file mode 100644
index 0000000..d8e5029
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeStringDefaultTest.java
@@ -0,0 +1,76 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.uic.barcode.asn1.datatypes.Asn1Default;
+import org.uic.barcode.asn1.datatypes.Asn1Optional;
+import org.uic.barcode.asn1.datatypes.CharacterRestriction;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.RestrictedString;
+import org.uic.barcode.asn1.datatypes.Sequence;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+
+public class UperEncodeStringDefaultTest {
+
+ /**
+ * Example from the Standard on UPER.
+ <pre>
+ TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ testString1 UTF8String OPTIONAL,
+ testString2 IA5String DEFAULT("TestString")
+ }
+ </pre>
+ */
+ @Sequence
+ public static class TestRecord {
+
+ @FieldOrder(order = 0)
+ @RestrictedString(CharacterRestriction.UTF8String)
+ @Asn1Optional() String valueUtf8;
+
+ @FieldOrder(order = 1)
+ @RestrictedString(CharacterRestriction.IA5String)
+ @Asn1Default(value="testString") String valueIA5;
+
+ public TestRecord() {
+ }
+
+ public TestRecord(String utf8, String ia5) {
+ this.valueUtf8 = utf8;
+ this.valueIA5 = ia5;
+ }
+ }
+
+
+ @Test public void testEncode() throws IllegalArgumentException, IllegalAccessException {
+ TestRecord record = new TestRecord("Müller", "testString");
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("81D370EF1B1B195C80",hex);
+ }
+
+ @Test public void testEncodeDefault() throws IllegalArgumentException, IllegalAccessException {
+ TestRecord record = new TestRecord("Müller", "testString");
+ byte[] encoded = UperEncoder.encode(record);
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals(result.valueIA5,"testString");
+ }
+
+ @Test public void testEncodeDefault2() throws IllegalArgumentException, IllegalAccessException {
+ TestRecord record = new TestRecord("Müller", null);
+ byte[] encoded = UperEncoder.encode(record);
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals(result.valueIA5,"testString");
+ }
+
+
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeStringLengthTest.java b/src/org/uic/barcode/asn1/test/UperEncodeStringLengthTest.java
new file mode 100644
index 0000000..9dd4389
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeStringLengthTest.java
@@ -0,0 +1,151 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.uic.barcode.asn1.datatypes.Asn1Optional;
+import org.uic.barcode.asn1.datatypes.CharacterRestriction;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.RestrictedString;
+import org.uic.barcode.asn1.datatypes.Sequence;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+
+public class UperEncodeStringLengthTest {
+
+ /**
+ * Example from the Standard on UPER.
+ <pre>
+ World-Schema DEFINITIONS AUTOMATIC TAGS ::=
+ BEGIN
+ TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ testString1 UTF8String OPTIONAL
+ }
+ END
+
+ value TestRecord ::= {
+ testString1 "A"
+ }
+
+ </pre>
+ */
+
+
+
+ @Sequence
+ public static class TestRecord {
+
+ @FieldOrder(order = 0)
+ @RestrictedString(CharacterRestriction.UTF8String)
+ @Asn1Optional() String valueUtf8;
+
+ public TestRecord() {
+ }
+
+ public TestRecord(String utf8) {
+ this.valueUtf8 = utf8;
+ }
+ }
+
+
+ @Test public void testEncode1() throws IllegalArgumentException, IllegalAccessException {
+ TestRecord record = new TestRecord("A");
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("80A080",hex);
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(record.valueUtf8,result.valueUtf8);
+ }
+
+
+ @Test public void testEncode63() throws IllegalArgumentException, IllegalAccessException {
+ TestRecord record = new TestRecord("123456789012345678901234567890123456789012345678901234567890123");
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("9F9899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C9818991980",hex);
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(record.valueUtf8,result.valueUtf8);
+ }
+
+ @Test public void testEncode64() throws IllegalArgumentException, IllegalAccessException {
+ TestRecord record = new TestRecord("1234567890123456789012345678901234567890123456789012345678901234");
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("A01899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A00",hex);
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(record.valueUtf8,result.valueUtf8);
+ }
+
+ @Test public void testEncode65() throws IllegalArgumentException, IllegalAccessException {
+ TestRecord record = new TestRecord("12345678901234567890123456789012345678901234567890123456789012345");
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("A09899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A80",hex);
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(record.valueUtf8,result.valueUtf8);
+ }
+
+ @Test public void testEncode126() throws IllegalArgumentException, IllegalAccessException {
+ TestRecord record = new TestRecord("123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456");
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("BF1899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C9818" +
+ "99199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C98189919" +
+ "9A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A" +
+ "9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B00",hex);
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(record.valueUtf8,result.valueUtf8);
+ }
+
+
+ @Test public void testEncode127() throws IllegalArgumentException, IllegalAccessException {
+ TestRecord record = new TestRecord("1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567");
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("BF9899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C9818" +
+ "99199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C98189919" +
+ "9A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A" +
+ "9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B" +
+ "80",hex);
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(record.valueUtf8,result.valueUtf8);
+ }
+
+ @Test public void testEncode128() throws IllegalArgumentException, IllegalAccessException {
+ TestRecord record = new TestRecord("12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678");
+
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("C0401899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C98" +
+ "1899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C981899" +
+ "199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A" +
+ "1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B" +
+ "1B9C00",hex);
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(record.valueUtf8,result.valueUtf8);
+ }
+
+ @Test public void testEncode129() throws IllegalArgumentException, IllegalAccessException {
+ TestRecord record = new TestRecord("123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789");
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("C0409899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C98" +
+ "1899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C981899" +
+ "199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A" +
+ "1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B1B9C1C981899199A1A9B" +
+ "1B9C1C80",hex);
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(record.valueUtf8,result.valueUtf8);
+ }
+
+}
diff --git a/src/org/uic/barcode/asn1/test/UperEncodeStringTest.java b/src/org/uic/barcode/asn1/test/UperEncodeStringTest.java
new file mode 100644
index 0000000..5f4edc8
--- /dev/null
+++ b/src/org/uic/barcode/asn1/test/UperEncodeStringTest.java
@@ -0,0 +1,110 @@
+package org.uic.barcode.asn1.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.logging.Level;
+
+import org.junit.Test;
+import org.uic.barcode.asn1.datatypes.Asn1Optional;
+import org.uic.barcode.asn1.datatypes.CharacterRestriction;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.RestrictedString;
+import org.uic.barcode.asn1.datatypes.Sequence;
+import org.uic.barcode.asn1.uper.UperEncoder;
+
+
+public class UperEncodeStringTest {
+
+ /**
+ * Example from the Standard on UPER.
+ <pre>
+ World-Schema DEFINITIONS AUTOMATIC TAGS ::=
+ BEGIN
+ TestRecord ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ testString1 UTF8String OPTIONAL,
+ testString2 IA5String OPTIONAL
+ }
+ END
+ </pre>
+ */
+ @Sequence
+ public static class TestRecord {
+
+ @FieldOrder(order = 0)
+ @RestrictedString(CharacterRestriction.UTF8String)
+ @Asn1Optional() String valueUtf8;
+
+ @FieldOrder(order = 1)
+ @RestrictedString(CharacterRestriction.IA5String)
+ @Asn1Optional() String valueIA5;
+
+ public TestRecord() {
+ }
+
+ public TestRecord(String utf8, String ia5) {
+ this.valueUtf8 = utf8;
+ this.valueIA5 = ia5;
+ }
+ }
+
+
+ @Test public void testEncode() throws IllegalArgumentException, IllegalAccessException {
+
+ //Teststring: AêñüC
+ String original = new String("A" + "\u00ea" + "\u00f1" + "\u00fc" + "C");
+
+ TestRecord record = new TestRecord(original, "Meier");
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("C21070EAB0EC70EF10C166E5D39790",hex);
+
+ }
+
+ @Test public void testEncodeUtf8() throws IllegalArgumentException, IllegalAccessException {
+
+ //"你好�"
+ String original = new String("\u00e4" + "\u00bd" + "\u00a0" + "\u00e5" + "\u00a5" + "\u00bd" + "\u00e5" + "\u0090" + "\u0097");
+ TestRecord record = new TestRecord(original, "Meier");
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("C4B0E930AF70A830E970A970AF70E970A430A5C166E5D39790",hex);
+ }
+
+
+
+ @Test public void testDecode() throws IllegalArgumentException, IllegalAccessException {
+
+ //Teststring: AêñüC
+ String original = new String("A" + "\u00ea" + "\u00f1" + "\u00fc" + "C");
+
+ TestRecord record = new TestRecord(original, "Meier");
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("C21070EAB0EC70EF10C166E5D39790",hex);
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.valueUtf8,record.valueUtf8);
+ assertEquals(result.valueIA5,record.valueIA5);
+ }
+
+
+ @Test public void testDecodeUtf8() throws IllegalArgumentException, IllegalAccessException {
+ //"你好�"
+ String original = new String("\u00e4" + "\u00bd" + "\u00a0" + "\u00e5" + "\u00a5" + "\u00bd" + "\u00e5" + "\u0090" + "\u0097");
+ TestRecord record = new TestRecord(original, "Meier");
+ byte[] encoded = UperEncoder.encode(record);
+ String hex = UperEncoder.hexStringFromBytes(encoded);
+ UperEncoder.logger.log(Level.FINEST,String.format("data hex: %s", hex));
+ assertEquals("C4B0E930AF70A830E970A970AF70E970A430A5C166E5D39790",hex);
+ TestRecord result = UperEncoder.decode(encoded, TestRecord.class);
+ assertEquals(result.valueUtf8,record.valueUtf8);
+ assertEquals(result.valueIA5,record.valueIA5);
+ }
+
+
+
+
+
+}
diff --git a/src/org/uic/barcode/asn1/uper/AnnotationStore.java b/src/org/uic/barcode/asn1/uper/AnnotationStore.java
new file mode 100644
index 0000000..6a23a75
--- /dev/null
+++ b/src/org/uic/barcode/asn1/uper/AnnotationStore.java
@@ -0,0 +1,31 @@
+package org.uic.barcode.asn1.uper;
+
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+class AnnotationStore {
+
+ private Map<Class<? extends Annotation>, Annotation> annotations = new HashMap<>();
+
+ public AnnotationStore(Annotation[] classAnnot, Annotation[] fieldAnnot) {
+ for (Annotation a : classAnnot) {
+ annotations.put(a.annotationType(), a);
+ }
+ for (Annotation a : fieldAnnot) {
+ annotations.put(a.annotationType(), a);
+ }
+ }
+
+ public <T extends Annotation> T getAnnotation(Class<T> classOfT) {
+ @SuppressWarnings("unchecked")
+ // Annotations were added with value T for key classOfT.
+ T result = (T) annotations.get(classOfT);
+ return result;
+ }
+
+ public Collection<Annotation> getAnnotations() {
+ return annotations.values();
+ }
+} \ No newline at end of file
diff --git a/src/org/uic/barcode/asn1/uper/Asn1EncodingException.java b/src/org/uic/barcode/asn1/uper/Asn1EncodingException.java
new file mode 100644
index 0000000..ae14cd0
--- /dev/null
+++ b/src/org/uic/barcode/asn1/uper/Asn1EncodingException.java
@@ -0,0 +1,18 @@
+package org.uic.barcode.asn1.uper;
+
+public class Asn1EncodingException extends Exception {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -8719453936776248228L;
+
+ public Asn1EncodingException(String message) {
+ super(message);
+ }
+
+ public Asn1EncodingException(String extraMessage, Asn1EncodingException cause) {
+ super(extraMessage + cause.getMessage(), cause);
+ }
+
+}
diff --git a/src/org/uic/barcode/asn1/uper/BigIntCoder.java b/src/org/uic/barcode/asn1/uper/BigIntCoder.java
new file mode 100644
index 0000000..94e4b05
--- /dev/null
+++ b/src/org/uic/barcode/asn1/uper/BigIntCoder.java
@@ -0,0 +1,97 @@
+package org.uic.barcode.asn1.uper;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.math.BigInteger;
+
+import org.uic.barcode.asn1.datatypes.Asn1BigInteger;
+import org.uic.barcode.asn1.datatypes.Asn1Default;
+import org.uic.barcode.asn1.datatypes.IntRange;
+
+class BigIntCoder implements Encoder, Decoder {
+
+ @Override public <T> boolean canDecode(Class<T> classOfT, Annotation[] extraAnnotations) {
+ return Asn1BigInteger.class.isAssignableFrom(classOfT);
+ }
+
+ @Override public <T> T decode(BitBuffer bitbuffer,
+ Class<T> classOfT, Field f,
+ Annotation[] extraAnnotations) {
+ AnnotationStore annotations = new AnnotationStore(classOfT.getAnnotations(),
+ extraAnnotations);
+
+ String pos = String.format("%d.%d", bitbuffer.position()/8 , bitbuffer.position() % 8);
+ UperEncoder.logger.debug(String.format("Position %s BIG INT",pos));
+ IntRange intRange = annotations.getAnnotation(IntRange.class);
+ if (intRange != null && intRange.maxValue() > 0) {
+ throw new UnsupportedOperationException("Big int with upper range is not supported yet");
+ }
+
+ int lengthInOctets = (int) UperEncoder.decodeLengthDeterminant(bitbuffer);
+ BitBuffer valueBits = ByteBitBuffer.allocate(lengthInOctets * 8);
+ for (int i = 0; i < lengthInOctets * 8; i++) {
+ valueBits.put(bitbuffer.get());
+ }
+ valueBits.flip();
+ BigInteger resultValue = new BigInteger(+1, valueBits.array());
+ UperEncoder.logger.debug(String.format("big int Decoded as %s", resultValue));
+
+
+ //CG support for int range
+ if (intRange != null){
+ resultValue.add(BigInteger.valueOf(intRange.minValue()));
+ }
+
+
+ return UperEncoder.instantiate(classOfT, resultValue);
+ }
+
+ @Override public <T> boolean canEncode(T obj, Annotation[] extraAnnotations) {
+ return obj instanceof Asn1BigInteger;
+ }
+
+ @Override public <T> void encode(BitBuffer bitbuffer, T obj, Annotation[] extraAnnotations) throws Asn1EncodingException {
+ Class<?> type = obj.getClass();
+ AnnotationStore annotations = new AnnotationStore(type.getAnnotations(), extraAnnotations);
+ IntRange range = annotations.getAnnotation(IntRange.class);
+
+ //CG implementation with lower range limit added
+ BigInteger bint = ((Asn1BigInteger) obj).toBigInteger();
+ if (range != null) {
+ throw new UnsupportedOperationException("Asn1 BigInteger with range is not supported");
+ }
+ byte[] array = bint.toByteArray();
+ int lengthInOctets = array.length;
+ int position1 = bitbuffer.position();
+ try {
+ UperEncoder.encodeLengthDeterminant(bitbuffer, lengthInOctets);
+ } catch (Asn1EncodingException e) {
+ throw new Asn1EncodingException(" length determinant of " + type.getName(), e);
+ }
+ int position2 = bitbuffer.position();
+ for (byte b : array) {
+ bitbuffer.putByte(b);
+ }
+ UperEncoder.logger.debug(String.format("Big Int(%s): len %s, val %s", obj,
+ bitbuffer.toBooleanString(position1, position2 - position1),
+ bitbuffer.toBooleanStringFromPosition(position2)));
+ return;
+ }
+
+ @Override
+ public <T> T getDefault(Class<T> classOfT, Annotation[] extraAnnotations) {
+ AnnotationStore annotations = new AnnotationStore(classOfT.getAnnotations(), extraAnnotations);
+ Asn1Default defaultAnnotation = annotations.getAnnotation(Asn1Default.class);
+ if (defaultAnnotation == null) return null;
+ //check whether the class has a constructor for numeric types
+ String valueString = defaultAnnotation.value();
+ long value = Long.parseLong(valueString);
+ UperEncoder.logger.debug(String.format("Default INTEGER: %d",value ));
+
+ @SuppressWarnings("unchecked")
+ T t = (T) new Asn1BigInteger(value);
+ return t;
+
+ }
+
+} \ No newline at end of file
diff --git a/src/org/uic/barcode/asn1/uper/BitBuffer.java b/src/org/uic/barcode/asn1/uper/BitBuffer.java
new file mode 100644
index 0000000..bba0de7
--- /dev/null
+++ b/src/org/uic/barcode/asn1/uper/BitBuffer.java
@@ -0,0 +1,32 @@
+package org.uic.barcode.asn1.uper;
+
+/**
+ * An interface for convenient storage of bits, similar to Java's ByteBuffer.
+ *
+ * This interface and its implementation are very useful for UPER, since UPER operates on bits
+ * regardless of byte boundaries.
+ *
+ */
+public interface BitBuffer {
+ boolean get();
+ boolean get(int index);
+ BitBuffer put(boolean element);
+ BitBuffer put(int index, boolean element);
+ int limit();
+ int capacity();
+ int position();
+ int remaining();
+ BitBuffer flip();
+ String toBooleanString(int startIndex, int length);
+ String toBooleanStringFromPosition(int startIndex);
+ byte[] array();
+ BitBuffer putByte(byte element);
+ byte getByte();
+ void putInteger(int index, int length,int number);
+ void putChar6String(int index, int length, String s);
+ int getInteger(int index, int length);
+ String getChar6String(int position, int length);
+ void putChar5String(int index, int length, String s);
+ String getChar5String(int inxed, int length);
+ BitBuffer putByteArray(int index, byte[] data);
+}
diff --git a/src/org/uic/barcode/asn1/uper/BitStringCoder.java b/src/org/uic/barcode/asn1/uper/BitStringCoder.java
new file mode 100644
index 0000000..ba1692c
--- /dev/null
+++ b/src/org/uic/barcode/asn1/uper/BitStringCoder.java
@@ -0,0 +1,165 @@
+package org.uic.barcode.asn1.uper;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.uic.barcode.asn1.datatypes.Asn1VarSizeBitstring;
+import org.uic.barcode.asn1.datatypes.Bitstring;
+import org.uic.barcode.asn1.datatypes.FixedSize;
+import org.uic.barcode.asn1.datatypes.SizeRange;
+import org.uic.barcode.asn1.uper.UperEncoder.Asn1ContainerFieldSorter;
+
+class BitStringCoder implements Decoder, Encoder {
+
+ @Override public <T> boolean canEncode(T obj, Annotation[] extraAnnotations) {
+ Class<?> type = obj.getClass();
+ AnnotationStore annotations = new AnnotationStore(type.getAnnotations(),
+ extraAnnotations);
+ return annotations.getAnnotation(Bitstring.class) != null;
+ }
+
+ @Override public <T> void encode(BitBuffer bitbuffer, T obj, Annotation[] extraAnnotations) throws Asn1EncodingException {
+ Class<?> type = obj.getClass();
+ AnnotationStore annotations = new AnnotationStore(type.getAnnotations(),
+ extraAnnotations);
+ if (!(obj instanceof Asn1VarSizeBitstring)) {
+ if (UperEncoder.hasExtensionMarker(annotations)) {
+ throw new UnsupportedOperationException(
+ "Bitstring with extensions is not implemented yet");
+ }
+ FixedSize size = type.getAnnotation(FixedSize.class);
+ int position = bitbuffer.position();
+ if (size != null) {
+ Asn1ContainerFieldSorter sorter = new Asn1ContainerFieldSorter(type);
+ if (sorter.ordinaryFields.size() != size.value()) { throw new AssertionError(
+ "Declared size (" + size.value() +
+ ") and number of fields (" + sorter.ordinaryFields.size() +
+ ") do not match!"); }
+ for (Field f : sorter.ordinaryFields) {
+ try {
+ bitbuffer.put(f.getBoolean(obj));
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ throw new IllegalArgumentException("can't encode" + obj, e);
+ }
+ }
+ UperEncoder.logger.debug(String.format("BITSTRING %s, encoded as <%s>", obj.getClass().getName(),
+ bitbuffer.toBooleanStringFromPosition(position)));
+ return;
+ } else {
+ throw new UnsupportedOperationException(
+ "Bitstrings of variable size are not implemented yet");
+ }
+ } else if (obj instanceof Asn1VarSizeBitstring) {
+ int position = bitbuffer.position();
+ if (UperEncoder.hasExtensionMarker(annotations)) { throw new UnsupportedOperationException(
+ "Bitstring with extensions is not implemented yet"); }
+ Asn1VarSizeBitstring bitstring = (Asn1VarSizeBitstring) obj;
+ FixedSize fixedSize = annotations.getAnnotation(FixedSize.class);
+ SizeRange sizeRange = annotations.getAnnotation(SizeRange.class);
+ if (fixedSize != null) {
+ for (int i = 0; i < fixedSize.value(); i++) {
+ bitbuffer.put(bitstring.getBit(i));
+ }
+ UperEncoder.logger.debug(String.format("BITSTRING %s: %s", obj.getClass().getName(),
+ bitbuffer.toBooleanStringFromPosition(position)));
+ return;
+ } else if (sizeRange != null) {
+ int position1 = bitbuffer.position();
+ UperEncoder.encodeConstrainedInt(bitbuffer, bitstring.size(), sizeRange.minValue(),
+ sizeRange.maxValue());
+ int position2 = bitbuffer.position();
+ for (int i = 0; i < bitstring.size(); i++) {
+ bitbuffer.put(bitstring.getBit(i));
+ }
+ UperEncoder.logger.debug(String.format("BITSTRING %s size %s: %S", obj.getClass().getName(),
+ bitbuffer.toBooleanString(position1, position2 - position1),
+ bitbuffer.toBooleanStringFromPosition(position2)));
+ return;
+ } else {
+ throw new IllegalArgumentException("Both SizeRange and FixedSize are null");
+ }
+ }
+ }
+
+ @Override public <T> boolean canDecode(Class<T> classOfT, Annotation[] extraAnnotations) {
+ AnnotationStore annotations = new AnnotationStore(classOfT.getAnnotations(),
+ extraAnnotations);
+ return annotations.getAnnotation(Bitstring.class) != null;
+ }
+
+ @Override public <T> T decode(BitBuffer bitbuffer,
+ Class<T> classOfT, Field field,
+ Annotation[] extraAnnotations) {
+ AnnotationStore annotations = new AnnotationStore(classOfT.getAnnotations(),
+ extraAnnotations);
+ if (!Asn1VarSizeBitstring.class.isAssignableFrom(classOfT)) {
+ UperEncoder.logger.debug("Bitlist(fixed-size, all-named)");
+ FixedSize fixedSize = annotations.getAnnotation(FixedSize.class);
+ if (fixedSize == null) { throw new UnsupportedOperationException(
+ "bitstrings of non-fixed size that do not extend Asn1VarSizeBitstring are not supported yet");
+ }
+ Asn1ContainerFieldSorter sorter = new Asn1ContainerFieldSorter(classOfT);
+ if (fixedSize.value() != sorter.ordinaryFields.size()) { throw new IllegalArgumentException(
+ "Fixed size annotation " + fixedSize.value()
+ + " does not match the number of fields "
+ + sorter.ordinaryFields.size() + " in " + classOfT.getName()); }
+ if (UperEncoder.hasExtensionMarker(annotations)) {
+ boolean extensionPresent = bitbuffer.get();
+ if (extensionPresent) { throw new UnsupportedOperationException(
+ "extensions in fixed-size bitlist are not supported yet"); }
+ }
+ T result = UperEncoder.instantiate(classOfT);
+ for (Field f : sorter.ordinaryFields) {
+ boolean value = bitbuffer.get();
+ UperEncoder.logger.debug(String.format("Field %s set to %s", f.getName(), value));
+ try {
+ f.set(result, value);
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ throw new IllegalArgumentException("can't decode " + classOfT, e);
+ }
+ }
+ return result;
+ } else {
+ UperEncoder.logger.debug("Bitlist(var-size)");
+ FixedSize fixedSize = annotations.getAnnotation(FixedSize.class);
+ SizeRange sizeRange = annotations.getAnnotation(SizeRange.class);
+ // We use reflection here to access protected method of Asn1VarSizeBitstring.
+ // Alternative would be to mandate BitSet constructors for all subclasses of
+ // Asn1VarSizeBitstring.
+ Method setBitMethod;
+ try {
+ setBitMethod = Asn1VarSizeBitstring.class.getDeclaredMethod("setBit", int.class,
+ boolean.class);
+ setBitMethod.setAccessible(true);
+ } catch (SecurityException | NoSuchMethodException e) {
+ throw new AssertionError("Can't find/access setBit " + e);
+ }
+ Long size = (fixedSize != null) ? fixedSize.value() :
+ (sizeRange != null) ? UperEncoder.decodeConstrainedInt(bitbuffer,
+ UperEncoder.intRangeFromSizeRange(sizeRange)) :
+ badSize(classOfT);
+ T result = UperEncoder.instantiate(classOfT);
+ for (int i = 0; i < size; i++) {
+ try {
+ setBitMethod.invoke(result, i, bitbuffer.get());
+ } catch (IllegalArgumentException | InvocationTargetException | IllegalAccessException e) {
+ throw new IllegalArgumentException("Can't invoke setBit", e);
+ }
+ }
+ return result;
+ }
+ }
+
+ /** This function only throws an exception, to be used in ternary (a?b:c) expression. */
+ static <T> Long badSize(Class<T> classOfT) {
+ throw new IllegalArgumentException("both size range and fixed size are null for "
+ + classOfT.getName());
+ }
+
+ @Override
+ public <T> T getDefault(Class<T> classOfT, Annotation[] extraAnnotations) {
+ throw new IllegalArgumentException("Default Sequence not yet implemented");
+ }
+} \ No newline at end of file
diff --git a/src/org/uic/barcode/asn1/uper/BooleanCoder.java b/src/org/uic/barcode/asn1/uper/BooleanCoder.java
new file mode 100644
index 0000000..3bd7a38
--- /dev/null
+++ b/src/org/uic/barcode/asn1/uper/BooleanCoder.java
@@ -0,0 +1,35 @@
+package org.uic.barcode.asn1.uper;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+
+class BooleanCoder implements Decoder, Encoder {
+
+ @Override public <T> boolean canEncode(T obj, Annotation[] extraAnnotations) {
+ return obj instanceof Boolean;
+ }
+
+ @Override public <T> void encode(BitBuffer bitbuffer, T obj, Annotation[] extraAnnotations) {
+ UperEncoder.logger.debug(String.format("BOOLEAN %s", obj));
+ bitbuffer.put((Boolean) obj);
+ }
+
+ @Override public <T> boolean canDecode(Class<T> classOfT, Annotation[] extraAnnotations) {
+ return Boolean.class.isAssignableFrom(classOfT)
+ || boolean.class.isAssignableFrom(classOfT);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public <T> T decode(BitBuffer bitbuffer,
+ Class<T> classOfT, Field field,
+ Annotation[] extraAnnotations) {
+ Boolean result = new Boolean(bitbuffer.get());
+ UperEncoder.logger.debug(String.format("BOOL: decoded as %s",result));
+ return (T) result;
+ }
+
+ @Override
+ public <T> T getDefault(Class<T> classOfT, Annotation[] extraAnnotations) {
+ throw new IllegalArgumentException("Default Boolean not yet implemented");
+ }
+} \ No newline at end of file
diff --git a/src/org/uic/barcode/asn1/uper/ByteBitBuffer.java b/src/org/uic/barcode/asn1/uper/ByteBitBuffer.java
new file mode 100644
index 0000000..3ed3eed
--- /dev/null
+++ b/src/org/uic/barcode/asn1/uper/ByteBitBuffer.java
@@ -0,0 +1,271 @@
+package org.uic.barcode.asn1.uper;
+
+
+
+public class ByteBitBuffer implements BitBuffer {
+
+ byte[] bytes;
+ byte[] mask = new byte[] {
+ (byte) 0b1000_0000,
+ 0b0100_0000,
+ 0b0010_0000,
+ 0b0001_0000,
+ 0b0000_1000,
+ 0b0000_0100,
+ 0b0000_0010,
+ 0b0000_0001,
+ };
+
+ boolean isFinite;
+
+ int mark;
+ int position;
+ int limit;
+
+
+ @Override public boolean get(int index) {
+ if (index < 0) {
+ throw new IndexOutOfBoundsException("Index " + index + " is less than 0");
+ } else if (index >= limit) {
+ throw new IndexOutOfBoundsException("Index " + index + " violates the limit " + limit);
+ }
+ boolean result = (bytes[index / 8] & mask[index % 8]) != 0;
+ return result;
+ }
+
+ @Override public boolean get() {
+ boolean result = get(position);
+ position++;
+ return result;
+ }
+
+ private void grow() {
+ byte[] newbytes = new byte[2 * bytes.length];
+ System.arraycopy(bytes, 0, newbytes, 0, bytes.length);
+ bytes = newbytes;
+ }
+
+ @Override public BitBuffer put(int index, boolean element) {
+ if (bytes.length <= index / 8) {
+ if (isFinite) { throw new IndexOutOfBoundsException(); }
+ else { grow(); }
+ }
+ if (element) {
+ bytes[index / 8] |= mask[index % 8];
+ } else {
+ bytes[index / 8] &= ~mask[index % 8];
+ }
+ return this;
+ }
+
+ @Override public BitBuffer put(boolean element) {
+ put(position, element);
+ position++;
+ limit = limit < position ? position : limit; // TODO: should it be here?
+ return this;
+ }
+
+ @Override public BitBuffer putByte(byte element) {
+ for (int i = 0; i < 8; i++) {
+ put((element & mask[i]) != 0);
+ }
+ return this;
+ }
+
+ @Override public BitBuffer putByteArray(int index, byte[] data) {
+
+ for (int l = 0; l < data.length;l++) {
+ for (int i = 0; i < 8; i++) {
+ put((data[l] & mask[i]) != 0);
+ }
+ }
+ return this;
+ }
+
+
+ @Override public byte getByte() {
+ byte result = 0;
+ for (int i = 0; i < 8; i++) {
+ result |= (get() ? 1 : 0) << (7 - i);
+ }
+ return result;
+ }
+
+ @Override public int limit() {
+ return limit;
+ }
+
+ @Override public String toBooleanString(int startIndex, int length) {
+ StringBuilder sb = new StringBuilder(length);
+ for (int i = startIndex; i < startIndex + length; i++) {
+ sb.append(get(i) ? "1" : "0");
+ }
+ return sb.toString();
+ }
+
+ @Override public int capacity() {
+ return isFinite ? bytes.length * 8 : Integer.MAX_VALUE;
+ }
+
+ @Override public int position() {
+ return position;
+ }
+
+ @Override public int remaining() {
+ return limit - position;
+ }
+
+ public ByteBitBuffer(byte[] backingArray) {
+ this.bytes = backingArray;
+ this.isFinite = true;
+ }
+
+ private ByteBitBuffer(int initialCapacity) {
+ this.bytes = new byte[initialCapacity];
+ this.isFinite = false;
+ }
+
+ public static ByteBitBuffer allocate(int lengthInBits) {
+ return new ByteBitBuffer(new byte[(lengthInBits + 7) / 8]);
+ }
+
+ public static ByteBitBuffer createInfinite() {
+ return new ByteBitBuffer(64);
+ }
+
+ @Override public BitBuffer flip() {
+ limit = position;
+ position = 0;
+ return this;
+ }
+
+ @Override public String toBooleanStringFromPosition(int startIndex) {
+ return toBooleanString(startIndex, position-startIndex);
+ }
+
+ @Override public byte[] array() {
+ return bytes;
+ }
+
+ @Override
+ public void putInteger(int position, int length,int number) {
+ String s = Integer.toBinaryString(number);
+ if (s.length() > length) {
+ //value is to large
+ return;
+ }
+
+ for (int i = 0;i < length;i++){
+ int index = position + i;
+ this.put(index,false);
+ }
+
+
+ int startIndex = position + length - s.length();
+ for (int i = 0;i < s.length();i++){
+ /*
+ * i = max --> index = position + length - 1
+ * i = 0 --> index = position +
+ */
+ int index = startIndex + i;
+ if (s.charAt(i) == '1') {
+ this.put(index, true );
+ } else {
+ this.put(index, false);
+ }
+ }
+
+ }
+
+ @Override
+ public void putChar5String(int position, int length, String s) {
+
+ String upperCaseString = s.toUpperCase();
+ int offset = 0;
+ for (int i = 0; i < s.length() ; i++) {
+ char character = upperCaseString.charAt(i);
+ int intValue = (int) character - 32;
+ if (intValue > -1 && intValue < 64) {
+ this.putInteger(position + offset,5, intValue);
+ offset = offset + 5;
+ } else {
+ this.putInteger(position + offset,5,0);
+ position = position + 5;
+ }
+ }
+ }
+
+ @Override
+ public void putChar6String(int position, int length, String s) {
+
+ String upperCaseString = s.toUpperCase();
+ int offset = 0;
+ for (int i = 0; i < s.length() ; i++) {
+ char character = upperCaseString.charAt(i);
+ int intValue = (int) character - 32;
+ if (intValue > -1 && intValue < 64) {
+ this.putInteger(position + offset,6, intValue);
+ offset = offset + 6;
+ } else {
+ this.putInteger(position + offset,6,0);
+ position = position + 6;
+ }
+ }
+ }
+
+ @Override
+ public int getInteger(int position, int length) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0;i < length;i++){
+ if (this.get(position + i)) {
+ sb.append("1");
+ } else {
+ sb.append("0");
+ }
+ }
+ return Integer.parseInt(sb.toString(), 2);
+ }
+
+ @Override
+ public String getChar6String(int position, int length) {
+
+ StringBuilder stringBuilder = new StringBuilder();
+
+ int chars = length / 6;
+
+ for (int i = 0; i < chars; i++) {
+ int newPosition = position + i * 6;
+
+ int x = this.getInteger(newPosition, 6);
+ x = x + 32;
+
+ char c = (char) x;
+ stringBuilder.append(c);
+
+ }
+
+ return stringBuilder.toString().trim();
+ }
+
+ @Override
+ public String getChar5String(int position, int length) {
+
+ StringBuilder stringBuilder = new StringBuilder();
+
+ int chars = length / 5;
+
+ for (int i = 0; i < chars; i++) {
+ int newPosition = position + i * 5;
+
+ int x = getInteger(newPosition, 5);
+ x = x + 42;
+
+ char c = (char) x;
+ stringBuilder.append(c);
+
+ }
+
+ return stringBuilder.toString().trim();
+ }
+
+}
diff --git a/src/org/uic/barcode/asn1/uper/ByteCoder.java b/src/org/uic/barcode/asn1/uper/ByteCoder.java
new file mode 100644
index 0000000..f26a598
--- /dev/null
+++ b/src/org/uic/barcode/asn1/uper/ByteCoder.java
@@ -0,0 +1,34 @@
+package org.uic.barcode.asn1.uper;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+
+class ByteCoder implements Decoder, Encoder {
+
+ @Override public <T> boolean canEncode(T obj, Annotation[] extraAnnotations) {
+ return obj instanceof Byte;
+ }
+
+ @Override public <T> void encode(BitBuffer bitbuffer, T obj, Annotation[] extraAnnotations) throws Asn1EncodingException {
+ UperEncoder.encodeConstrainedInt(bitbuffer, ((Byte) obj).byteValue() & 0xff, 0, 255);
+ UperEncoder.logger.debug(String.format("BYTE %s", ((Byte) obj).byteValue()));
+ }
+
+ @Override public <T> boolean canDecode(Class<T> classOfT, Annotation[] extraAnnotations) {
+ return Byte.class.isAssignableFrom(classOfT) || byte.class.isAssignableFrom(classOfT);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public <T> T decode(BitBuffer bitbuffer,
+ Class<T> classOfT, Field field,
+ Annotation[] extraAnnotations) {
+ UperEncoder.logger.debug("BYTE");
+ return (T) new Byte((byte) UperEncoder.decodeConstrainedInt(bitbuffer, UperEncoder.newRange(0, 255, false)));
+ }
+
+ @Override
+ public <T> T getDefault(Class<T> classOfT, Annotation[] extraAnnotations) {
+ throw new IllegalArgumentException("Default Byte not yet implemented");
+ }
+
+} \ No newline at end of file
diff --git a/src/org/uic/barcode/asn1/uper/ChoiceCoder.java b/src/org/uic/barcode/asn1/uper/ChoiceCoder.java
new file mode 100644
index 0000000..d17a813
--- /dev/null
+++ b/src/org/uic/barcode/asn1/uper/ChoiceCoder.java
@@ -0,0 +1,161 @@
+package org.uic.barcode.asn1.uper;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+
+import org.uic.barcode.asn1.datatypes.Choice;
+import org.uic.barcode.asn1.uper.UperEncoder.Asn1ContainerFieldSorter;
+
+class ChoiceCoder implements Decoder, Encoder {
+
+ @Override public <T> boolean canEncode(T obj, Annotation[] extraAnnotations) {
+ Class<?> type = obj.getClass();
+ AnnotationStore annotations = new AnnotationStore(type.getAnnotations(),
+ extraAnnotations);
+ return annotations.getAnnotation(Choice.class) != null;
+ }
+
+ @Override public <T> void encode(BitBuffer bitbuffer, T obj, Annotation[] extraAnnotations) throws Asn1EncodingException {
+ Class<?> type = obj.getClass();
+ AnnotationStore annotations = new AnnotationStore(type.getAnnotations(),extraAnnotations);
+ UperEncoder.logger.debug("CHOICE");
+ int nonNullIndex = 0;
+ Field nonNullField = null;
+ Object nonNullFieldValue = null;
+ int currentIndex = 0;
+ Asn1ContainerFieldSorter sorter = new Asn1ContainerFieldSorter(type);
+ try {
+ for (Field f : sorter.ordinaryFields) {
+ if (f.get(obj) != null) {
+ nonNullIndex = currentIndex;
+ nonNullFieldValue = f.get(obj);
+ nonNullField = f;
+ break;
+ }
+ currentIndex++;
+ }
+ if (nonNullFieldValue != null) {
+ if (UperEncoder.hasExtensionMarker(annotations)) {
+ boolean extensionBit = false;
+ UperEncoder.logger.debug(String.format("with extension marker, set to %s", extensionBit));
+ bitbuffer.put(extensionBit);
+ }
+ if (sorter.ordinaryFields.size() > 1) { // Encode index only if more than one.
+ UperEncoder.logger.debug(String.format("with chosen element indexed %d", nonNullIndex));
+ UperEncoder.encodeConstrainedInt(bitbuffer, nonNullIndex, 0,
+ sorter.ordinaryFields.size() - 1);
+ }
+ UperEncoder.encode2(bitbuffer, nonNullFieldValue, nonNullField.getAnnotations());
+ return;
+ } else if (UperEncoder.hasExtensionMarker(annotations)) {
+ //CG encoding of extension fields
+ currentIndex = 0;
+ for (Field f : sorter.extensionFields) {
+ if (f.get(obj) != null) {
+ nonNullIndex = currentIndex;
+ nonNullFieldValue = f.get(obj);
+ nonNullField = f;
+ break;
+ }
+ currentIndex++;
+ }
+ if (nonNullField == null) {
+ UperEncoder.logger.debug(String.format("without choice of extension"));
+ return;
+ }
+ boolean extensionBit = true;
+ UperEncoder.logger.debug(String.format("with extension marker, set to <%s>", extensionBit));
+ bitbuffer.put(extensionBit);
+
+ //CG encode extension values
+ //Always encode index of the element
+ UperEncoder.logger.debug(String.format("with chosen extension element indexed %d", nonNullIndex));
+
+ //encode small integer even with value 0
+ UperEncoder.encodeSmallInt(bitbuffer, nonNullIndex);
+
+ //Encode as open field
+ UperEncoder.encodeAsOpenType(bitbuffer, nonNullFieldValue, nonNullField.getAnnotations());
+ return;
+ } else {
+ throw new IllegalArgumentException("Not Extension and All ordinary fields of Choice are null");
+ }
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ throw new IllegalArgumentException("can't encode " + obj, e);
+ } catch (Asn1EncodingException e) {
+ throw new Asn1EncodingException("." + type.getName(), e);
+ }
+ }
+
+ @Override public <T> boolean canDecode(Class<T> classOfT, Annotation[] extraAnnotations) {
+ AnnotationStore annotations = new AnnotationStore(classOfT.getAnnotations(),
+ extraAnnotations);
+ return annotations.getAnnotation(Choice.class) != null;
+ }
+
+ @Override public <T> T decode(BitBuffer bitbuffer,
+ Class<T> classOfT, Field field1,
+ Annotation[] extraAnnotations) {
+ AnnotationStore annotations = new AnnotationStore(classOfT.getAnnotations(),extraAnnotations);
+ UperEncoder.logger.debug(String.format("CHOICE: %s", classOfT.getName()));
+ T result = UperEncoder.instantiate(classOfT);
+ Asn1ContainerFieldSorter sorter = new Asn1ContainerFieldSorter(classOfT);
+
+ // Reset all fields, since default constructor initializes one.
+ for (Field f : sorter.allFields) {
+ try {
+ f.set(result, null);
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ throw new IllegalArgumentException("can't decode " + classOfT, e);
+ }
+ }
+ if (UperEncoder.hasExtensionMarker(annotations)) {
+ UperEncoder.logger.debug("with extension marker");
+ boolean extensionPresent = bitbuffer.get();
+ if (extensionPresent) {
+ //CG extension support added
+ int i = (int) UperEncoder.decodeSmallInt(bitbuffer);
+ UperEncoder.logger.debug(String.format("extension with index %d is present",i));
+ Field field = sorter.extensionFields.size() > i ? sorter.extensionFields.get(i) : null;
+ Class<?> classOfElement = field != null ? field.getType() : null;
+ if (field != null) {
+ try {
+ Object decodedValue = UperEncoder.decodeAsOpenType(bitbuffer, classOfElement,field, field.getAnnotations());
+ if (field != null) {
+ field.set(result, decodedValue);
+ }
+ return result;
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ throw new IllegalArgumentException("can't decode " + classOfT, e);
+ }
+ } else {
+ //CG skip the unknown extension element
+ UperEncoder.decodeSkipUnknownElement(bitbuffer, classOfT.getSimpleName());
+ return null;
+ }
+ //throw new UnsupportedOperationException("choice extension is not implemented yet");
+ } else {
+ UperEncoder.logger.debug(String.format("no extension present"));
+ //no extension is present
+ //We already consumed the bit, keep processing as if there were no extension.
+ }
+ }
+ int index = (int) UperEncoder.decodeConstrainedInt(bitbuffer,
+ UperEncoder.newRange(0, sorter.ordinaryFields.size() - 1, false));
+ Field f = sorter.ordinaryFields.get(index);
+ UperEncoder.logger.debug(String.format("CHOICE: selected %s", f.getName()));
+ Object fieldValue = UperEncoder.decodeAny(bitbuffer, f.getType(),f, f.getAnnotations());
+ try {
+ f.set(result, fieldValue);
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ throw new IllegalArgumentException("can't decode " + classOfT, e);
+ }
+ return result;
+ }
+
+ @Override
+ public <T> T getDefault(Class<T> classOfT, Annotation[] extraAnnotations) {
+ throw new IllegalArgumentException("Default Choice not yet implemented");
+ }
+
+} \ No newline at end of file
diff --git a/src/org/uic/barcode/asn1/uper/Decoder.java b/src/org/uic/barcode/asn1/uper/Decoder.java
new file mode 100644
index 0000000..947a752
--- /dev/null
+++ b/src/org/uic/barcode/asn1/uper/Decoder.java
@@ -0,0 +1,10 @@
+package org.uic.barcode.asn1.uper;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+
+public interface Decoder {
+ <T> boolean canDecode(Class<T> classOfT, Annotation[] extraAnnotations);
+ <T> T decode(BitBuffer bitbuffer, Class<T> classOfT,Field f, Annotation[] extraAnnotations);
+ <T> T getDefault(Class<T> classOfT, Annotation[] extraAnnotations);
+}
diff --git a/src/org/uic/barcode/asn1/uper/Document2.txt b/src/org/uic/barcode/asn1/uper/Document2.txt
new file mode 100644
index 0000000..176ec23
--- /dev/null
+++ b/src/org/uic/barcode/asn1/uper/Document2.txt
@@ -0,0 +1,34 @@
+ if (restrictionAnnotation.value() == CharacterRestriction.UTF8String) {
+ // UTF8 length
+ BitBuffer stringbuffer = ByteBitBuffer.createInfinite();
+
+ //char array replaced - begin
+ byte[] stringasbytearray = string.getBytes(StandardCharsets.UTF_8);
+
+ for (byte b: stringasbytearray){
+ UperEncoder.encodeConstrainedInt(stringbuffer, byte & 0xff, 0, 255);
+ }
+ //char array replaced - end
+
+ stringbuffer.flip();
+ if (stringbuffer.limit() % 8 != 0) {
+ throw new AssertionError("utf8 encoding resulted not in multiple of 8 bits");
+ }
+ int numOctets = (stringbuffer.limit() + 7) / 8; // Actually +7 is not needed here,
+ // since we already checked with %8.
+ int position1 = bitbuffer.position();
+ UperEncoder.encodeLengthDeterminant(bitbuffer, numOctets);
+ UperEncoder.logger.debug(String.format("UTF8String %s, length %d octets, encoded as %s", string, numOctets, bitbuffer.toBooleanStringFromPosition(position1)));
+ int position2 = bitbuffer.position();
+ for (int i = 0; i < stringbuffer.limit(); i++) {
+ bitbuffer.put(stringbuffer.get());
+ }
+ UperEncoder.logger.debug(String.format("UTF8String %s, encoded length %d octets, value bits: %s", string, numOctets, bitbuffer.toBooleanStringFromPosition(position2)));
+ return;
+
+
+
+
+
+
+new String(bytearray, StandardCharsets.UTF_8)); \ No newline at end of file
diff --git a/src/org/uic/barcode/asn1/uper/Encoder.java b/src/org/uic/barcode/asn1/uper/Encoder.java
new file mode 100644
index 0000000..1b3688f
--- /dev/null
+++ b/src/org/uic/barcode/asn1/uper/Encoder.java
@@ -0,0 +1,8 @@
+package org.uic.barcode.asn1.uper;
+
+import java.lang.annotation.Annotation;
+
+public interface Encoder {
+ <T> boolean canEncode(T obj, Annotation[] extraAnnotations);
+ <T> void encode(BitBuffer bitbuffer, T obj, Annotation[] extraAnnotations) throws Asn1EncodingException;
+}
diff --git a/src/org/uic/barcode/asn1/uper/EnumCoder.java b/src/org/uic/barcode/asn1/uper/EnumCoder.java
new file mode 100644
index 0000000..5d78bc7
--- /dev/null
+++ b/src/org/uic/barcode/asn1/uper/EnumCoder.java
@@ -0,0 +1,156 @@
+package org.uic.barcode.asn1.uper;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.uic.barcode.asn1.datatypes.Asn1Default;
+import org.uic.barcode.asn1.datatypes.IsExtension;
+
+class EnumCoder implements Decoder, Encoder {
+
+ @Override public <T> boolean canEncode(T obj, Annotation[] extraAnnotations) {
+ Class<?> type = obj.getClass();
+ return type.isEnum();
+ }
+
+ @Override public <T> void encode(BitBuffer bitbuffer, T obj, Annotation[] extraAnnotations) throws Asn1EncodingException {
+ Class<?> type = obj.getClass();
+ AnnotationStore annotations = new AnnotationStore(type.getAnnotations(), extraAnnotations);
+ String pos = String.format("%d.%d", bitbuffer.position()/8 , bitbuffer.position() % 8);
+ UperEncoder.logger.debug(String.format("Position %s ENUM",pos));
+ try {
+ int position = bitbuffer.position();
+
+ List<?> values = Arrays.asList(type.getEnumConstants());
+ int enumIndex = values.indexOf(obj);
+
+ if (!UperEncoder.hasExtensionMarker(annotations)) {
+ UperEncoder.logger.debug(String.format("enum without extension: index %d value %s, encoding index...", enumIndex,obj.toString()));
+ UperEncoder.encodeConstrainedInt(bitbuffer, enumIndex, 0, values.size() - 1);
+ return;
+ } else {
+ List<Object> valuesWithinExtensionRoot = new ArrayList<>();
+ List<Object> valuesOutsideExtensionRoot = new ArrayList<>();
+ for (Object c : type.getEnumConstants()) {
+ String field = c.toString();
+ boolean isExtension = false;
+ try {
+ isExtension = type.getField(field).isAnnotationPresent(IsExtension.class);
+ } catch (NoSuchFieldException e) {
+ throw new IllegalArgumentException("Illegal value for enum field " , e);
+ } catch (SecurityException e) {
+ throw new IllegalArgumentException("Illegal access restriction for enum field " , e);
+ }
+
+ if (!isExtension) {
+ valuesWithinExtensionRoot.add(c);
+ } else {
+ valuesOutsideExtensionRoot.add(c);
+ }
+ }
+
+ if (valuesWithinExtensionRoot.contains(obj)) {
+ UperEncoder.logger.debug(String.format("Extension indicator set to false"));
+ bitbuffer.put(false);
+ int index = valuesWithinExtensionRoot.indexOf(obj);
+ UperEncoder.encodeConstrainedInt(bitbuffer, index, 0, valuesWithinExtensionRoot.size() - 1);
+ UperEncoder.logger.debug(String.format("ENUM with extension: index %d value %s, encoded as root value <%s>", index, obj.toString(),
+ bitbuffer.toBooleanStringFromPosition(position)));
+ return;
+ } else {
+ //CG encode the index in the extension list as small integer
+ UperEncoder.logger.debug(String.format("Extension indicator set to true"));
+ bitbuffer.put(true);
+ int index = valuesOutsideExtensionRoot.indexOf(obj);
+
+ UperEncoder.encodeSmallInt(bitbuffer, index);
+ UperEncoder.logger.debug(String.format("ENUM with extension: index %d value %s, encoded as extension <%s>", index, obj.toString(),
+ bitbuffer.toBooleanStringFromPosition(position)));
+ }
+ }
+ } catch (Asn1EncodingException e) {
+ throw new Asn1EncodingException(type.getName(), e);
+ }
+ }
+
+ @Override public <T> boolean canDecode(Class<T> classOfT, Annotation[] extraAnnotations) {
+ return classOfT.isEnum();
+ }
+
+ @Override public <T> T decode(BitBuffer bitbuffer,
+ Class<T> classOfT, Field field,
+ Annotation[] extraAnnotations) {
+ AnnotationStore annotations = new AnnotationStore(classOfT.getAnnotations(), extraAnnotations);
+ UperEncoder.logger.debug("ENUM");
+ boolean extensionPresent = false;
+ if (UperEncoder.hasExtensionMarker(annotations)) {
+ extensionPresent = bitbuffer.get();
+ UperEncoder.logger.debug(String.format("with extension marker, %s" , extensionPresent ? "present" : "absent"));
+ }
+ T[] enumValues = classOfT.getEnumConstants();
+
+ int rootValues = 0;
+
+ boolean isExtension = false;
+ for (Object c : enumValues) {
+ String value = c.toString();
+ try {
+ isExtension = classOfT.getField(value).isAnnotationPresent(IsExtension.class);
+ } catch (NoSuchFieldException e) {
+ throw new IllegalArgumentException("Illegal value for extension field " , e);
+ } catch (SecurityException e) {
+ throw new IllegalArgumentException("Illegal value for extension field " , e);
+ }
+
+ if (!isExtension) rootValues++;
+ }
+
+ //
+ int index = 0;
+ if (!extensionPresent){
+ //root element
+ index = (int) UperEncoder.decodeConstrainedInt(bitbuffer,
+ UperEncoder.newRange(0, rootValues - 1, false));
+ } else {
+ //extension element, decode as small int without restriction
+ index = (int) UperEncoder.decodeSmallInt(bitbuffer);
+ //the encoded index is an index within the extensions list only
+ index = index + rootValues;
+ }
+
+ if (index > enumValues.length - 1 && extensionPresent) {
+ //this is an unknown extension
+ UperEncoder.logger.debug(String.format("Enum contains unknown extendion index %d" , index));
+ return null;
+ }
+ if (index > enumValues.length - 1 && !extensionPresent) {
+ //this should not happen
+ throw new IllegalArgumentException(
+ "decoded enum index " + index + " is larger then number of elements (0.."
+ + enumValues.length + ") in " + classOfT.getName());
+ }
+ T value = enumValues[index];
+ UperEncoder.logger.debug(String.format("Enum decoded as %s" , value.toString()));
+ return value;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> T getDefault(Class<T> classOfT, Annotation[] extraAnnotations) {
+ AnnotationStore annotations = new AnnotationStore(classOfT.getAnnotations(), extraAnnotations);
+ Asn1Default defaultAnnotation = annotations.getAnnotation(Asn1Default.class);
+ if (defaultAnnotation == null) return null;
+
+ for (Object c : classOfT.getEnumConstants()) {
+ if (c.toString().equals(defaultAnnotation.value())) {
+ return (T) c;
+ }
+ }
+
+ return null;
+ }
+
+} \ No newline at end of file
diff --git a/src/org/uic/barcode/asn1/uper/IntCoder.java b/src/org/uic/barcode/asn1/uper/IntCoder.java
new file mode 100644
index 0000000..87b561b
--- /dev/null
+++ b/src/org/uic/barcode/asn1/uper/IntCoder.java
@@ -0,0 +1,267 @@
+package org.uic.barcode.asn1.uper;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.math.BigInteger;
+
+import org.uic.barcode.asn1.datatypes.Asn1BigInteger;
+import org.uic.barcode.asn1.datatypes.Asn1Default;
+import org.uic.barcode.asn1.datatypes.Asn1Integer;
+import org.uic.barcode.asn1.datatypes.IntMinValue;
+import org.uic.barcode.asn1.datatypes.IntRange;
+
+
+class IntCoder implements Encoder, Decoder {
+
+
+
+ @Override public <T> boolean canDecode(Class<T> classOfT, Annotation[] extraAnnotations) {
+ return classOfT == Asn1Integer.class ||
+ classOfT == Asn1BigInteger.class||
+ classOfT == BigInteger.class ||
+ classOfT == Long.class ||
+ classOfT == Integer.class ||
+ classOfT == Short.class ;
+ }
+
+
+ @Override public <T> T decode(BitBuffer bitbuffer,
+ Class<T> classOfT, Field field,
+ Annotation[] extraAnnotations) {
+ AnnotationStore annotations = new AnnotationStore(classOfT.getAnnotations(),extraAnnotations);
+ String pos = String.format("Position: %d.%d", bitbuffer.position()/8 , bitbuffer.position() % 8);
+ UperEncoder.logger.debug(String.format("%s: INTEGER",pos));
+ IntRange intRange = annotations.getAnnotation(IntRange.class);
+ IntMinValue minValue = annotations.getAnnotation(IntMinValue.class);
+
+
+ if (intRange == null) {
+ return decodeUnconstrainedInteger(bitbuffer, classOfT, extraAnnotations, minValue);
+ }
+ UperEncoder.logger.debug(String.format("Integer, range %d..%d", intRange.minValue(), intRange.maxValue()));
+ return decodeConstrainedInteger(bitbuffer, classOfT, intRange, extraAnnotations);
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T> T decodeConstrainedInteger(BitBuffer bitbuffer, Class<T> classOfT, IntRange intRange, Annotation[] extraAnnotations) {
+
+ long value = UperEncoder.decodeConstrainedInt(bitbuffer, intRange);
+ UperEncoder.logger.debug(String.format("decoded as %d", value));
+
+ try {
+ if (classOfT == Asn1BigInteger.class) {
+ return ((T) new Asn1BigInteger(value));
+ } else if (classOfT == Asn1Integer.class) {
+ return (T) new Asn1Integer(value);
+ } else if (classOfT == BigInteger.class) {
+ return (T) BigInteger.valueOf(value);
+ } else if (classOfT == Long.class) {
+ return (T) Long.valueOf(value);
+ } else if (classOfT == Integer.class) {
+ return (T) Integer.valueOf(Long.valueOf(value).intValue());
+ } else if (classOfT == Short.class) {
+ return (T) Short.valueOf(Long.valueOf(value).shortValue());
+ }
+ } catch (Exception e) {
+ throw new IllegalArgumentException("size too small " + classOfT.getName() + ": " + e);
+ }
+
+ return null;
+
+
+ }
+
+ @Override public <T> boolean canEncode(T obj, Annotation[] extraAnnotations) {
+ return obj instanceof Asn1Integer ||
+ obj instanceof Asn1BigInteger ||
+ obj instanceof BigInteger ||
+ obj instanceof Long ||
+ obj instanceof Integer ||
+ obj instanceof Short;
+ }
+
+ @Override public <T> void encode(BitBuffer bitbuffer, T obj, Annotation[] extraAnnotations) throws Asn1EncodingException {
+ Class<?> type = obj.getClass();
+ AnnotationStore annotations = new AnnotationStore(type.getAnnotations(), extraAnnotations);
+ IntRange range = annotations.getAnnotation(IntRange.class);
+ IntMinValue minValue = annotations.getAnnotation(IntMinValue.class);
+ int position = bitbuffer.position();
+
+ //get value
+ if (range == null) {
+
+ try {
+ encodeUnconstrainedInteger(bitbuffer, obj, extraAnnotations,minValue);
+ } catch (Asn1EncodingException e) {
+ throw new Asn1EncodingException(" " + type.getSimpleName(), e);
+ } catch (Exception e1){
+ throw new Asn1EncodingException(" " + type.getSimpleName() + " - " + e1.getLocalizedMessage());
+ }
+ UperEncoder.logger.debug(String.format("INT(%s): %s", obj, bitbuffer.toBooleanStringFromPosition(position)));
+
+
+ } else {
+
+ try {
+
+ long value = 0;
+ if (obj instanceof BigInteger) {
+ try {
+ value = ((BigInteger) obj).longValue();
+ } catch (Exception e) {
+
+ UperEncoder.logger.debug("constrained BigInteger is too big for constrained int");
+ throw new Asn1EncodingException("constrained BigInteger is too big for constrained int" + type.getSimpleName());
+ }
+ } if (obj instanceof Asn1BigInteger) {
+ try {
+ value = ((Asn1BigInteger) obj).longValue();
+ } catch (Exception e) {
+
+ UperEncoder.logger.debug("constrained Asn1BigInteger is too big for constrained int");
+ throw new Asn1EncodingException("constrained Asn1BigInteger is too big for constrained int" + type.getSimpleName());
+ }
+ } if (obj instanceof Asn1Integer) {
+ try {
+ value = Asn1Integer.toLong((Asn1Integer) obj);
+ } catch (Exception e) {
+
+ UperEncoder.logger.debug("constrained BigInteger is too big for constrained int");
+ throw new Asn1EncodingException("constrained BigInteger is too big for constrained int" + type.getSimpleName());
+ }
+ } else if (obj instanceof Long) {
+ value = ((Long) obj).longValue();
+ } else if (obj instanceof Integer) {
+ value = ((Integer) obj).longValue();
+ } else if (obj instanceof Short) {
+ value = ((Short) obj).longValue();
+ }
+
+ UperEncoder.encodeConstrainedInt(bitbuffer, value, range.minValue(), range.maxValue(), range.hasExtensionMarker());
+ } catch (Asn1EncodingException e) {
+ throw new Asn1EncodingException(" " + type.getSimpleName(), e);
+ } catch (Exception e1){
+ throw new Asn1EncodingException(" " + type.getSimpleName() + " - " + e1.getLocalizedMessage());
+ }
+ UperEncoder.logger.debug(String.format("INT(%s): %s", obj, bitbuffer.toBooleanStringFromPosition(position)));
+ }
+ return;
+ }
+
+ private <T> void encodeUnconstrainedInteger(BitBuffer bitbuffer, Object obj, Annotation[] annotations, IntMinValue minValue) throws Asn1EncodingException {
+
+
+ BigInteger bint = null;
+ try {
+ if (obj instanceof BigInteger) {
+ bint = (BigInteger) obj;
+ } else if (obj instanceof Asn1BigInteger) {
+ bint = BigInteger.valueOf(((Asn1BigInteger) obj).longValue());
+ } else if (obj instanceof Asn1Integer) {
+ bint = BigInteger.valueOf(((Asn1Integer) obj).value());
+ } else if (obj instanceof Long) {
+ bint = BigInteger.valueOf(((Long) obj).longValue());
+ } else if (obj instanceof Integer) {
+ bint = BigInteger.valueOf(((Integer) obj).longValue());
+ } else if (obj instanceof Short) {
+ bint = BigInteger.valueOf(((Short) obj).longValue());
+ }
+ } catch (Exception e1){
+ throw new Asn1EncodingException(" " + obj.getClass().getSimpleName() + " - " + e1.getLocalizedMessage());
+ }
+
+
+ if (minValue != null) {
+ bint.subtract(BigInteger.valueOf(minValue.minValue()));
+ }
+
+ byte[] array = bint.toByteArray();
+ int lengthInOctets = array.length;
+ int position1 = bitbuffer.position();
+ try {
+ UperEncoder.encodeLengthDeterminant(bitbuffer, lengthInOctets);
+ } catch (Asn1EncodingException e) {
+ throw new Asn1EncodingException(" length determinant of INTEGER", e);
+ }
+ int position2 = bitbuffer.position();
+ for (byte b : array) {
+ bitbuffer.putByte(b);
+ }
+ UperEncoder.logger.debug(String.format("INTEGER Int(%s): len %s, val %s", bint.toString(),
+ bitbuffer.toBooleanString(position1, position2 - position1),
+ bitbuffer.toBooleanStringFromPosition(position2)));
+ return;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T decodeUnconstrainedInteger(BitBuffer bitbuffer, Class<T> classOfT, Annotation[] extraAnnotations,IntMinValue minValue) {
+ AnnotationStore annotations = new AnnotationStore(classOfT.getAnnotations(), extraAnnotations);
+
+ String pos = String.format("%d.%d", bitbuffer.position()/8 , bitbuffer.position() % 8);
+ UperEncoder.logger.debug(String.format("Position %s BIG INT",pos));
+ IntRange intRange = annotations.getAnnotation(IntRange.class);
+ if (intRange != null && intRange.maxValue() > 0) {
+ throw new UnsupportedOperationException("Big int with upper range is not supported yet");
+ }
+
+ int lengthInOctets = (int) UperEncoder.decodeLengthDeterminant(bitbuffer);
+ BitBuffer valueBits = ByteBitBuffer.allocate(lengthInOctets * 8);
+ for (int i = 0; i < lengthInOctets * 8; i++) {
+ valueBits.put(bitbuffer.get());
+ }
+ valueBits.flip();
+ BigInteger resultValue = new BigInteger(+1, valueBits.array());
+ if (minValue != null) {
+ resultValue.add(BigInteger.valueOf(minValue.minValue()));
+ }
+
+ UperEncoder.logger.debug(String.format("INTEGER Decoded as %s", resultValue));
+
+ try {
+ if (classOfT == Asn1BigInteger.class) {
+ return (T) new Asn1BigInteger(resultValue);
+ } else if (classOfT == BigInteger.class) {
+ return (T) resultValue;
+ } else if (classOfT == Long.class) {
+ return (T) Long.valueOf(resultValue.longValueExact());
+ } else if (classOfT == Integer.class) {
+ return (T) Integer.valueOf(resultValue.intValueExact());
+ } else if (classOfT == Short.class) {
+ return (T) Short.valueOf(resultValue.shortValueExact());
+ }
+ } catch (Exception e){
+ UperEncoder.logger.debug(String.format("INTEGER Decoded as %s is too big for data type", resultValue));
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> T getDefault(Class<T> classOfT, Annotation[] extraAnnotations) {
+ AnnotationStore annotations = new AnnotationStore(classOfT.getAnnotations(), extraAnnotations);
+ Asn1Default defaultAnnotation = annotations.getAnnotation(Asn1Default.class);
+ if (defaultAnnotation == null) return null;
+ //check whether the class has a constructor for numeric types
+ String valueString = defaultAnnotation.value();
+ long value = Long.parseLong(valueString);
+
+ try {
+ if (classOfT == Asn1BigInteger.class) {
+ return ((T) new Asn1BigInteger(value));
+ } else if (classOfT == BigInteger.class) {
+ return (T) BigInteger.valueOf(value);
+ } else if (classOfT == Long.class) {
+ return (T) Long.valueOf(value);
+ } else if (classOfT == Integer.class) {
+ return (T) Integer.valueOf(Long.valueOf(value).intValue());
+ } else if (classOfT == Short.class) {
+ return (T) Short.valueOf(Long.valueOf(value).shortValue());
+ }
+ } catch (Exception e) {
+ throw new IllegalArgumentException("size too small " + classOfT.getName() + ": " + e);
+ }
+
+ return null;
+ }
+
+} \ No newline at end of file
diff --git a/src/org/uic/barcode/asn1/uper/ObjectIdentifierCoder.java b/src/org/uic/barcode/asn1/uper/ObjectIdentifierCoder.java
new file mode 100644
index 0000000..2835e10
--- /dev/null
+++ b/src/org/uic/barcode/asn1/uper/ObjectIdentifierCoder.java
@@ -0,0 +1,175 @@
+package org.uic.barcode.asn1.uper;
+
+import java.io.ByteArrayOutputStream;
+import java.math.BigInteger;
+
+public class ObjectIdentifierCoder {
+
+
+/*
+ OID encoding for dummies :) :
+
+ each OID component is encoded to one or more bytes (octets)
+
+ OID encoding is just a concatenation of these OID component encodings
+
+ first two components are encoded in a special way (see below)
+
+ if OID component binary value has less than 7 bits, the encoding is just a single octet,
+ holding the component value (note, most significant bit, leftmost, will always be 0)
+ otherwise, if it has 8 and more bits, the value is "spread" into multiple octets - split the
+ binary representation into 7 bit chunks (from right), left-pad the first one with zeroes if needed,
+ and form octets from these septets by adding most significant (left) bit 1, except from the last
+ chunk, which will have bit 0 there.
+
+ first two components (X.Y) are encoded like it is a single component with a value 40*X + Y
+
+ This is a rewording of ITU-T recommendation X.690, chapter 8.19
+
+*/
+
+ /*
+ *
+The first octet has value 40 * value1 + value2. (This is unambiguous, since value1 is limited to values 0, 1, and 2; value2 is limited to the range 0 to 39 when value1 is 0 or 1; and, according to X.208, n is always at least 2.)
+
+The following octets, if any, encode value3, ..., valuen.
+Each value is encoded base 128, most significant digit first, with as few digits as possible, and the most significant bit of each octet except the last in the value's encoding set to "1."
+
+Example: The first octet of the BER encoding of RSA Data Security, Inc.'s object identifier is 40 * 1 + 2 = 42 = 2a16. The encoding of 840 = 6 * 128 + 4816 is 86 48 and the encoding of 113549 = 6 * 1282 + 7716 * 128 + d16 is 86 f7 0d. This leads to the following BER encoding:
+
+06 06 2a 86 48 86 f7 0d
+ */
+
+ private static final Long LONG_LIMIT = (Long.MAX_VALUE >> 7) - 0x7f;
+
+
+ /*
+ * adaptation of the bouncy castle implementation available at bouncy castle under APACHE 2.0 license
+ */
+ public static String decodeObjectId(byte[] bytes) {
+
+ StringBuffer objId = new StringBuffer();
+ long value = 0;
+ BigInteger bigValue = null;
+ boolean first = true;
+
+ for (int i = 0; i != bytes.length; i++) {
+
+ int b = bytes[i] & 0xff;
+
+ if (value <= LONG_LIMIT) {
+ value += (b & 0x7f);
+ if ((b & 0x80) == 0) { // end of number reached
+
+ if (first) {
+ if (value < 40) {
+ objId.append('0');
+ } else if (value < 80) {
+ objId.append('1');
+ value -= 40;
+ } else {
+ objId.append('2');
+ value -= 80;
+ }
+ first = false;
+ }
+
+ objId.append('.');
+ objId.append(value);
+ value = 0;
+ } else {
+ value <<= 7;
+ }
+ } else {
+ if (bigValue == null) {
+ bigValue = BigInteger.valueOf(value);
+ }
+ bigValue = bigValue.or(BigInteger.valueOf(b & 0x7f));
+ if ((b & 0x80) == 0) {
+ if (first) {
+ objId.append('2');
+ bigValue = bigValue.subtract(BigInteger.valueOf(80));
+ first = false;
+ }
+ objId.append('.');
+ objId.append(bigValue);
+ bigValue = null;
+ value = 0;
+ } else {
+ bigValue = bigValue.shiftLeft(7);
+ }
+ }
+ }
+
+ return objId.toString();
+
+ }
+
+
+ public static byte[] encodeObjectId(String oids) {
+
+ String[] components = oids.split("\\.");
+
+ if (components.length < 2) throw new AssertionError("Object Identifier Format error (" + oids + ")");
+
+ try {
+ int first = Integer.parseInt(components[0]) * 40;
+
+ ByteArrayOutputStream aOut = new ByteArrayOutputStream();
+
+
+ if (components[1].length() <= 18) {
+ writeField(aOut, first + Long.parseLong(components[1]));
+ } else {
+ writeField(aOut, new BigInteger(components[1]).add(BigInteger.valueOf(first)));
+ }
+
+ for (int i = 2; i < components.length; i++) {
+
+ if (components[i].length() <= 18) {
+ writeField(aOut, Long.parseLong(components[i]));
+ } else {
+ writeField(aOut, new BigInteger(components[i]));
+ }
+ }
+
+ return aOut.toByteArray();
+
+ } catch (NumberFormatException e) {
+ throw new AssertionError("Object Identifier Format error (" + oids + ")");
+ }
+ }
+
+
+ private static void writeField(ByteArrayOutputStream out, long fieldValue)
+ {
+ byte[] result = new byte[9];
+ int pos = 8;
+ result[pos] = (byte)((int)fieldValue & 0x7f);
+ while (fieldValue >= (1L << 7)) {
+ fieldValue >>= 7;
+ result[--pos] = (byte)((int)fieldValue & 0x7f | 0x80);
+ }
+ out.write(result, pos, 9 - pos);
+ }
+
+ private static void writeField(ByteArrayOutputStream out, BigInteger fieldValue)
+ {
+ int byteCount = (fieldValue.bitLength() + 6) / 7;
+ if (byteCount == 0) {
+ out.write(0);
+ } else {
+ BigInteger tmpValue = fieldValue;
+ byte[] tmp = new byte[byteCount];
+ for (int i = byteCount - 1; i >= 0; i--) {
+ tmp[i] = (byte)((tmpValue.intValue() & 0x7f) | 0x80);
+ tmpValue = tmpValue.shiftRight(7);
+ }
+ tmp[byteCount - 1] &= 0x7f;
+ out.write(tmp, 0, tmp.length);
+ }
+ }
+
+
+
+}
diff --git a/src/org/uic/barcode/asn1/uper/SeqOfCoder.java b/src/org/uic/barcode/asn1/uper/SeqOfCoder.java
new file mode 100644
index 0000000..a7ae7ba
--- /dev/null
+++ b/src/org/uic/barcode/asn1/uper/SeqOfCoder.java
@@ -0,0 +1,156 @@
+package org.uic.barcode.asn1.uper;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.uic.barcode.asn1.datatypes.FixedSize;
+import org.uic.barcode.asn1.datatypes.SizeRange;
+import org.uic.barcode.asn1.uper.SimpleTypeResolver.Unknown;
+
+
+class SeqOfCoder implements Decoder, Encoder {
+
+ @Override public <T> boolean canEncode(T obj, Annotation[] extraAnnotations) {
+ return obj instanceof List<?>;
+ }
+
+ @Override public <T> void encode(BitBuffer bitbuffer, T obj, Annotation[] extraAnnotations) throws Asn1EncodingException {
+ Class<?> type = obj.getClass();
+ UperEncoder.logger.debug(String.format("SEQUENCE OF %s",obj.getClass().getName()));
+ AnnotationStore annotations = new AnnotationStore(type.getAnnotations(), extraAnnotations);
+ List<?> list = (List<?>) obj;
+
+ final FixedSize fixedSize = annotations.getAnnotation(FixedSize.class);
+
+ //CG pass annotations too each field encoding
+ Annotation[] annotationArray = new Annotation[] {};
+ if (annotations != null & annotations.getAnnotations() != null && !annotations.getAnnotations().isEmpty()) {
+ ArrayList<Annotation> fieldAnnotations = new ArrayList<Annotation>();
+ fieldAnnotations.addAll(annotations.getAnnotations());
+ annotationArray = new Annotation[fieldAnnotations.size()];
+ for (int i = 0; i< fieldAnnotations.size();i++){
+ annotationArray[i] = fieldAnnotations.get(i);
+ }
+ }
+
+ SizeRange sizeRange = annotations.getAnnotation(SizeRange.class);
+ if (fixedSize != null)
+ sizeRange = new SizeRange() {
+ @Override public Class<? extends Annotation> annotationType() { return SizeRange.class; }
+ @Override public int minValue() { return fixedSize.value(); }
+ @Override public int maxValue() { return fixedSize.value(); }
+ @Override public boolean hasExtensionMarker() { return false; }
+ };
+ if (sizeRange == null) {
+ int position1 = bitbuffer.position();
+ try {
+ UperEncoder.encodeLengthDeterminant(bitbuffer, list.size());
+ } catch (Asn1EncodingException e) {
+ throw new Asn1EncodingException(" number of elements ", e);
+ }
+ UperEncoder.logger.debug(String.format("unbound size %d, encoded as %s", list.size(),
+ bitbuffer.toBooleanStringFromPosition(position1)));
+ UperEncoder.logger.debug(String.format(" all elems of Seq Of: %s", list ));
+ for (Object elem : list) {
+ try {
+ UperEncoder.encode2(bitbuffer, elem, annotationArray);
+ } catch (Asn1EncodingException e) {
+ throw new Asn1EncodingException(" element " + elem.toString(), e);
+ }
+ }
+ return;
+ }
+ boolean outsideOfRange = list.size() < sizeRange.minValue()
+ || sizeRange.maxValue() < list.size();
+ if (outsideOfRange && !sizeRange.hasExtensionMarker()) { throw new IllegalArgumentException(
+ "Out-of-range size for " + obj.getClass() + ", expected " +
+ sizeRange.minValue() + ".." + sizeRange.maxValue() + ", got "
+ + list.size()); }
+ if (sizeRange.hasExtensionMarker()) {
+ bitbuffer.put(outsideOfRange);
+ UperEncoder.logger.debug(String.format("With Extension Marker, %s of range (%d <= %d <= %d)",
+ (outsideOfRange ? "outside" : "inside"), sizeRange.minValue(), list.size(),
+ sizeRange.maxValue()));
+ if (outsideOfRange) { throw new UnsupportedOperationException(
+ "Sequence-of size range extensions are not implemented yet, range " +
+ sizeRange.minValue() + ".." + sizeRange.maxValue()
+ + ", requested size " + list.size()); }
+ }
+ UperEncoder.logger.debug(String.format("seq-of of constrained size %d, encoding size...", list.size()));
+ UperEncoder.encodeConstrainedInt(bitbuffer, list.size(), sizeRange.minValue(), sizeRange.maxValue());
+ UperEncoder.logger.debug(String.format(" all elems of Seq Of: %s", list));
+ for (Object elem : list) {
+ UperEncoder.encode2(bitbuffer, elem, new Annotation[] {});
+ }
+ }
+
+ @Override public <T> boolean canDecode(Class<T> classOfT, Annotation[] extraAnnotations) {
+ return List.class.isAssignableFrom(classOfT);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public <T> T decode(BitBuffer bitbuffer,
+ Class<T> classOfT,Field field,
+ Annotation[] extraAnnotations) {
+ AnnotationStore annotations = new AnnotationStore(classOfT.getAnnotations(),
+ extraAnnotations);
+ UperEncoder.logger.debug(String.format("SEQUENCE OF for %s", classOfT));
+ FixedSize fixedSize = annotations.getAnnotation(FixedSize.class);
+ SizeRange sizeRange = annotations.getAnnotation(SizeRange.class);
+
+ //CG pass annotations from the sequence to each element encoding
+ Annotation[] annotationArray = new Annotation[] {};
+
+ if (annotations != null && annotations.getAnnotations() != null && !annotations.getAnnotations().isEmpty()){
+ annotationArray = new Annotation[annotations.getAnnotations().size()];
+ Iterator<Annotation> it = annotations.getAnnotations().iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ annotationArray[i] = it.next();
+ i++;
+ }
+ }
+
+
+ long size =
+ (fixedSize != null) ? fixedSize.value() :
+ (sizeRange != null) ? UperEncoder.decodeConstrainedInt(bitbuffer, UperEncoder.intRangeFromSizeRange(sizeRange)) :
+ UperEncoder.decodeLengthDeterminant(bitbuffer);
+ Collection<Object> coll = new ArrayList<Object>((int) size);
+
+ Class<?> classOfElements;
+ Class<?>[] typeArgs = SimpleTypeResolver.resolveRawArguments(List.class, classOfT);
+ classOfElements = typeArgs[0];
+ if (classOfElements == null || classOfElements == Unknown.class) {
+ try {
+ ParameterizedType elementType = (ParameterizedType) field.getGenericType();
+ classOfElements = (Class<?>) elementType.getActualTypeArguments()[0];
+ } catch (SecurityException e) {
+ throw new IllegalArgumentException("Can't resolve type of elements for " + classOfT.getName());
+ }
+ }
+ for (int i = 0; i < size; i++) {
+ coll.add(UperEncoder.decodeAny(bitbuffer, classOfElements,field, annotationArray));
+ }
+
+ T result = null;
+ try {
+ result = UperEncoder.instantiate(classOfT, coll);
+ } catch (Exception e) {
+ result = (T) coll;
+ }
+ return result;
+
+ }
+
+
+ @Override
+ public <T> T getDefault(Class<T> classOfT, Annotation[] extraAnnotations) {
+ throw new IllegalArgumentException("Default Sequence not yet implemented");
+ }
+} \ No newline at end of file
diff --git a/src/org/uic/barcode/asn1/uper/SeqOfFixedSize.java b/src/org/uic/barcode/asn1/uper/SeqOfFixedSize.java
new file mode 100644
index 0000000..5d75e72
--- /dev/null
+++ b/src/org/uic/barcode/asn1/uper/SeqOfFixedSize.java
@@ -0,0 +1,18 @@
+package org.uic.barcode.asn1.uper;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.uic.barcode.asn1.datatypes.Asn1SequenceOf;
+import org.uic.barcode.asn1.datatypes.FixedSize;
+
+
+public class SeqOfFixedSize {
+ @FixedSize(3)
+ public static class Bar extends Asn1SequenceOf<Byte> {
+ public Bar(Byte... coll) { this(Arrays.asList(coll)); }
+ public Bar(Collection<Byte> coll) { super(coll); }
+ }
+
+
+}
diff --git a/src/org/uic/barcode/asn1/uper/SequenceCoder.java b/src/org/uic/barcode/asn1/uper/SequenceCoder.java
new file mode 100644
index 0000000..5e8386e
--- /dev/null
+++ b/src/org/uic/barcode/asn1/uper/SequenceCoder.java
@@ -0,0 +1,269 @@
+package org.uic.barcode.asn1.uper;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.util.ArrayDeque;
+import java.util.Deque;
+
+import org.uic.barcode.asn1.datatypes.Asn1Default;
+import org.uic.barcode.asn1.datatypes.Asn1SequenceOf;
+import org.uic.barcode.asn1.datatypes.Sequence;
+import org.uic.barcode.asn1.uper.UperEncoder.Asn1ContainerFieldSorter;
+
+class SequenceCoder implements Decoder, Encoder {
+
+ @Override public <T> boolean canEncode(T obj, Annotation[] extraAnnotations) {
+ Class<?> type = obj.getClass();
+ AnnotationStore annotations = new AnnotationStore(type.getAnnotations(), extraAnnotations);
+
+ return annotations.getAnnotation(Sequence.class) != null;
+ }
+
+ @Override public <T> void encode(BitBuffer bitbuffer, T obj, Annotation[] extraAnnotations) throws Asn1EncodingException {
+ Class<?> type = obj.getClass();
+ AnnotationStore annotations = new AnnotationStore(type.getAnnotations(), extraAnnotations);
+ String pos = String.format("%d.%d", bitbuffer.position()/8 , bitbuffer.position() % 8);
+ UperEncoder.logger.debug(String.format("Position %s: SEQUENCE %s", pos, type.getSimpleName()));
+
+ Asn1ContainerFieldSorter sorter = new Asn1ContainerFieldSorter(type);
+ try {
+ if (UperEncoder.hasExtensionMarker(annotations)) {
+ boolean extensionsPresent = !sorter.extensionFields.isEmpty()
+ && UperEncoder.hasNonNullExtensions(obj, sorter);
+ UperEncoder.logger.debug(String.format("with extension marker, %s extensions, extensionBit: <%s>",
+ extensionsPresent ? "with" : "without", extensionsPresent));
+ bitbuffer.put(extensionsPresent);
+ }
+ // Bitmask for optional fields.
+ for (Field f : sorter.optionalOrdinaryFields) {
+
+ boolean fieldPresent = isPresent(f, f.get(obj));
+
+ UperEncoder.logger.debug(String.format("with optional field %s %s, presence encoded as bit <%s>",
+ f.getName(), fieldPresent ? "present" : "absent", fieldPresent));
+
+ bitbuffer.put(fieldPresent); // null means the field is absent.
+ }
+
+ // All ordinary fields (fields within extension root).
+ for (Field f : sorter.ordinaryFields) {
+ //CG do not include default values
+ if (UperEncoder.isMandatory(f) || isPresent(f,f.get(obj))) {
+
+ pos = String.format("Position: %d.%d", bitbuffer.position()/8 , bitbuffer.position() % 8);
+ UperEncoder.logger.debug(String.format("%s: Field %s", pos, f.getName()));
+ try {
+ UperEncoder.encode2(bitbuffer, f.get(obj), f.getAnnotations());
+ } catch (Asn1EncodingException e) {
+ throw new Asn1EncodingException("." + f.getName(), e);
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("Illegal value for field " + f.getName(), e);
+ }
+ }
+ }
+ // Extension fields.
+ if (UperEncoder.hasExtensionMarker(annotations)
+ && !sorter.extensionFields.isEmpty()
+ && UperEncoder.hasNonNullExtensions(obj, sorter)) {
+ // Total extensions count.
+ int numExtensions = sorter.extensionFields.size();
+ UperEncoder.logger.debug(String.format("continuing sequence : %d extension(s) are present, encoding length determinant for them...", numExtensions));
+ UperEncoder.encodeLengthOfBitmask(bitbuffer, numExtensions);
+ // Bitmask for present extensions.
+ for (Field f : sorter.extensionFields) {
+ boolean fieldIsPresent = isPresent(f,f.get(obj));
+
+ UperEncoder.logger.debug(String.format("Extension %s is %s, presence encoded as <%s>", f.getName(),
+ fieldIsPresent ? "present" : "absent", fieldIsPresent ? "1" : "0"));
+
+ bitbuffer.put(fieldIsPresent);
+ }
+ // Values of extensions themselves.
+ for (Field f : sorter.extensionFields) {
+ //CG do not encode default values
+ if (UperEncoder.isMandatory(f) || isPresent(f,f.get(obj))) {
+ UperEncoder.logger.debug(String.format("Encoding extension field %s", f.getName()));
+ try {
+ UperEncoder.encodeAsOpenType(bitbuffer, f.get(obj), f.getAnnotations());
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("Illegal value for extension field " + f.getName(), e);
+ }
+ }
+ }
+ }
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ throw new IllegalArgumentException("can't encode " + obj, e);
+ }
+ sorter.revertAccess();
+ }
+
+ @SuppressWarnings("unchecked")
+ protected <T> boolean isPresent(Field f, Object fieldObject){
+
+ if (fieldObject == null) return false;
+
+ boolean fieldPresent = fieldObject != null;
+
+ if (fieldObject instanceof Asn1SequenceOf) {
+ if (((Asn1SequenceOf<T>)fieldObject).size() == 0){
+ //CG do not encode optional empty sequences
+ fieldPresent = false;
+ }
+ }
+
+ if (fieldObject instanceof String) {
+ if (((String)fieldObject).length() == 0){
+ //CG do not encode optional empty sequences
+ fieldPresent = false;
+ }
+ }
+
+ //CG DEFAULT VALUES
+ if (fieldPresent && UperEncoder.isDefault(f,fieldObject)) {
+ UperEncoder.logger.debug(String.format("Field %s has default value", f.getName()));
+ fieldPresent = false;
+ }
+ //CG No ASN1
+ if (UperEncoder.isNotAsn1(f)) {
+ fieldPresent = false;
+ }
+
+ return fieldPresent;
+
+ }
+
+ @Override public <T> boolean canDecode(Class<T> classOfT, Annotation[] extraAnnotations) {
+ AnnotationStore annotations = new AnnotationStore(classOfT.getAnnotations(),
+ extraAnnotations);
+ return annotations.getAnnotation(Sequence.class) != null;
+ }
+
+ @Override public <T> T decode(BitBuffer bitbuffer,
+ Class<T> classOfT,Field f1,
+ Annotation[] extraAnnotations) {
+ UperEncoder.logger.debug(String.format("decode SEQUENCE %s",classOfT.getSimpleName()));
+ AnnotationStore annotations = new AnnotationStore(classOfT.getAnnotations(),extraAnnotations);
+ T result = UperEncoder.instantiate(classOfT);
+ Asn1ContainerFieldSorter sorter = new Asn1ContainerFieldSorter(classOfT);
+ boolean hasExtensionMarker = UperEncoder.hasExtensionMarker(annotations);
+ boolean extensionPresent = false;
+ if (hasExtensionMarker) {
+ extensionPresent = bitbuffer.get();
+ UperEncoder.logger.debug(String.format("with extension marker, extension %s", extensionPresent ? "present!" : "absent"));
+ }
+ // Bitmask for optional fields.
+ Deque<Boolean> optionalFieldsMask = new ArrayDeque<>(sorter.optionalOrdinaryFields.size());
+ for (Field f : sorter.optionalOrdinaryFields) {
+ optionalFieldsMask.add(bitbuffer.get());
+ UperEncoder.logger.debug(String.format("with optional field %s %s" , f.getName() , optionalFieldsMask.getLast() ? "present" : "absent"));
+ }
+ // All ordinary fields (fields within extension root).
+
+ for (Field f : sorter.ordinaryFields) {
+ if (!UperEncoder.isTestInstrumentation(f) && (UperEncoder.isMandatory(f)
+ ||
+ (UperEncoder.isOptional(f) && optionalFieldsMask.pop()))) {
+ UperEncoder.logger.debug(String.format("Field : %s", f.getName()));
+ try {
+ f.set(result, UperEncoder.decodeAny(bitbuffer,f.getType(),f, f.getAnnotations()));
+ } catch (IllegalAccessException e) {
+ throw new IllegalArgumentException("can't access 'set method' for field " + f + " of class " + classOfT + " " + e, e);
+ }
+ } else {
+ //CG might have a default value
+ if (f.getAnnotation(Asn1Default.class) != null) {
+ try {
+ UperEncoder.logger.debug(String.format(String.format("Retrieve default for element : %s",f.getName())));
+ f.set(result,UperEncoder.getDefault(f.getType(),f.getAnnotations()));
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("can't decode " + classOfT, e);
+ } catch (IllegalAccessException e) {
+ throw new IllegalArgumentException("can't decode " + classOfT, e);
+ }
+ }
+ }
+ }
+ if (!hasExtensionMarker) {
+ //done
+ sorter.revertAccess();
+ return result;
+ }
+
+ // Possible extensions
+ int numExtensions = 0;
+ if (UperEncoder.hasExtensionMarker(annotations)){
+ if (extensionPresent) {
+ // Number of extensions.
+ numExtensions = (int) UperEncoder.decodeLengthOfBitmask(bitbuffer);
+ UperEncoder.logger.debug(String.format("sequence has %d extension(s)", numExtensions));
+ // Bitmask for extensions.
+ boolean[] bitmaskValueIsPresent = new boolean[numExtensions];
+ for (int i = 0; i < numExtensions; i++) {
+ bitmaskValueIsPresent[i] = bitbuffer.get();
+ UperEncoder.logger.debug(String.format("extension %s is %s", i, bitmaskValueIsPresent[i] ? "present" : "absent"));
+ }
+ // Values.
+ UperEncoder.logger.debug("decoding extensions values...");
+ for (int i = 0; i < numExtensions; i++) {
+ UperEncoder.logger.debug(String.format("sequence extension %s %s", i, bitmaskValueIsPresent[i] ? "present" : "absent"));
+ if (bitmaskValueIsPresent[i]) {
+ UperEncoder.logger.debug(String.format("decoding extension %d...", i));
+ Field field = sorter.extensionFields.size() > i ? sorter.extensionFields.get(i) : null;
+ Class<?> classOfElement = field != null ? field.getType() : null;
+ if (field != null) {
+ try {
+ Object decodedValue = UperEncoder.decodeAsOpenType(bitbuffer, classOfElement,field, field.getAnnotations());
+ if (field != null) {
+ field.set(result, decodedValue);
+ }
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ throw new IllegalArgumentException("can't decode " + classOfT, e);
+ }
+ } else {
+ //CG skip the unknown extension element
+ UperEncoder.decodeSkipUnknownElement(bitbuffer, classOfT.getSimpleName());
+ }
+ } else {
+ //CG the absent extension filed might have a default value
+ Field field = sorter.extensionFields.size() > i ? sorter.extensionFields.get(i) : null;
+ Class<?> classOfElement = field != null ? field.getType() : null;
+ if (field != null && field.getAnnotation(Asn1Default.class) != null) {
+ try {
+ field.set(result,UperEncoder.getDefault(classOfElement,field.getAnnotations()));
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("can't decode " + classOfElement.getSimpleName(), e);
+ } catch (IllegalAccessException e) {
+ throw new IllegalArgumentException("can't decode " + classOfElement.getSimpleName(), e);
+ }
+ UperEncoder.logger.debug(String.format("Default set for %s", field.getName()));
+ }
+ }
+ }//end of loop on present extension fields
+ } else {
+ //CG there is an extension marker but the extension is not present
+ // then there sill might be an element with a default value
+ for (Field field : sorter.extensionFields) {
+ if ( numExtensions <= sorter.extensionFields.indexOf(field)) {
+ if (field.getAnnotation(Asn1Default.class) != null) {
+ Class<?> classOfElement = field != null ? field.getType() : null;
+ try {
+ field.set(result,UperEncoder.getDefault(classOfElement, field.getAnnotations()));
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("can't decode default" + classOfElement.getSimpleName(), e);
+ } catch (IllegalAccessException e) {
+ throw new IllegalArgumentException("can't decode default" + classOfElement.getSimpleName(), e);
+ }
+ }
+ }
+ }
+ } // end of extension handling
+ }
+ sorter.revertAccess();
+ return result;
+ }
+
+ @Override
+ public <T> T getDefault(Class<T> classOfT, Annotation[] annotations) {
+ throw new IllegalArgumentException("Default Sequence not yet implemented");
+ }
+} \ No newline at end of file
diff --git a/src/org/uic/barcode/asn1/uper/SimpleTypeResolver.java b/src/org/uic/barcode/asn1/uper/SimpleTypeResolver.java
new file mode 100644
index 0000000..9444e44
--- /dev/null
+++ b/src/org/uic/barcode/asn1/uper/SimpleTypeResolver.java
@@ -0,0 +1,515 @@
+package org.uic.barcode.asn1.uper;
+
+/*
+ * Copyright 2002-2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+
+/**
+ * Enhanced type resolution utilities.
+ *
+ * @author Jonathan Halterman
+ */
+public final class SimpleTypeResolver {
+ /** Cache of type variable/argument pairs */
+ private static final Map<Class<?>, Reference<Map<TypeVariable<?>, Type>>> TYPE_VARIABLE_CACHE = Collections
+ .synchronizedMap(new WeakHashMap<Class<?>, Reference<Map<TypeVariable<?>, Type>>>());
+ private static volatile boolean CACHE_ENABLED = true;
+ private static boolean RESOLVES_LAMBDAS;
+ private static Method GET_CONSTANT_POOL;
+ private static Method GET_CONSTANT_POOL_SIZE;
+ private static Method GET_CONSTANT_POOL_METHOD_AT;
+ private static final Map<String, Method> OBJECT_METHODS = new HashMap<String, Method>();
+ private static final Map<Class<?>, Class<?>> PRIMITIVE_WRAPPERS;
+ private static final Double JAVA_VERSION;
+
+ static {
+ JAVA_VERSION = Double.parseDouble(System.getProperty("java.specification.version", "0"));
+
+ try {
+
+ GET_CONSTANT_POOL = Class.class.getDeclaredMethod("getConstantPool");
+ String constantPoolName = JAVA_VERSION < 9 ? "sun.reflect.ConstantPool" : "jdk.internal.reflect.ConstantPool";
+ Class<?> constantPoolClass = Class.forName(constantPoolName);
+ GET_CONSTANT_POOL_SIZE = constantPoolClass.getDeclaredMethod("getSize");
+ GET_CONSTANT_POOL_METHOD_AT = constantPoolClass.getDeclaredMethod("getMethodAt", int.class);
+
+ // setting the methods as accessible
+ // additional checks - make sure we get a result when invoking the Class::getConstantPool and
+ // ConstantPool::getSize on a class
+ Object constantPool = GET_CONSTANT_POOL.invoke(Object.class);
+ GET_CONSTANT_POOL_SIZE.invoke(constantPool);
+
+ for (Method method : Object.class.getDeclaredMethods())
+ OBJECT_METHODS.put(method.getName(), method);
+
+ RESOLVES_LAMBDAS = true;
+ } catch (Exception ignore) {
+ }
+
+ Map<Class<?>, Class<?>> types = new HashMap<Class<?>, Class<?>>();
+ types.put(boolean.class, Boolean.class);
+ types.put(byte.class, Byte.class);
+ types.put(char.class, Character.class);
+ types.put(double.class, Double.class);
+ types.put(float.class, Float.class);
+ types.put(int.class, Integer.class);
+ types.put(long.class, Long.class);
+ types.put(short.class, Short.class);
+ types.put(void.class, Void.class);
+ PRIMITIVE_WRAPPERS = Collections.unmodifiableMap(types);
+ }
+
+ /** An unknown type. */
+ public static final class Unknown {
+ private Unknown() {
+ }
+ }
+
+ /**
+ * Enables the internal caching of resolved TypeVariables.
+ */
+ public static void enableCache() {
+ CACHE_ENABLED = true;
+ }
+
+ /**
+ * Disables the internal caching of resolved TypeVariables.
+ */
+ public static void disableCache() {
+ TYPE_VARIABLE_CACHE.clear();
+ CACHE_ENABLED = false;
+ }
+
+ /**
+ * Returns the raw class representing the argument for the {@code type} using type variable information from the
+ * {@code subType}. If no arguments can be resolved then {@code Unknown.class} is returned.
+ *
+ * @param type to resolve argument for
+ * @param subType to extract type variable information from
+ * @return argument for {@code type} else {@link Unknown}.class if no type arguments are declared
+ * @throws IllegalArgumentException if more or less than one argument is resolved for the {@code type}
+ */
+ public static <T, S extends T> Class<?> resolveRawArgument(Class<T> type, Class<S> subType) {
+ return resolveRawArgument(resolveGenericType(type, subType), subType);
+ }
+
+ /**
+ * Returns the raw class representing the argument for the {@code genericType} using type variable information from
+ * the {@code subType}. If {@code genericType} is an instance of class, then {@code genericType} is returned. If no
+ * arguments can be resolved then {@code Unknown.class} is returned.
+ *
+ * @param genericType to resolve argument for
+ * @param subType to extract type variable information from
+ * @return argument for {@code genericType} else {@link Unknown}.class if no type arguments are declared
+ * @throws IllegalArgumentException if more or less than one argument is resolved for the {@code genericType}
+ */
+ public static Class<?> resolveRawArgument(Type genericType, Class<?> subType) {
+ Class<?>[] arguments = resolveRawArguments(genericType, subType);
+ if (arguments == null)
+ return Unknown.class;
+
+ if (arguments.length != 1)
+ throw new IllegalArgumentException(
+ "Expected 1 argument for generic type " + genericType + " but found " + arguments.length);
+
+ return arguments[0];
+ }
+
+ /**
+ * Returns an array of raw classes representing arguments for the {@code type} using type variable information from
+ * the {@code subType}. Arguments for {@code type} that cannot be resolved are returned as {@code Unknown.class}. If
+ * no arguments can be resolved then {@code null} is returned.
+ *
+ * @param type to resolve arguments for
+ * @param subType to extract type variable information from
+ * @return array of raw classes representing arguments for the {@code type} else {@code null} if no type arguments are
+ * declared
+ */
+ public static <T, S extends T> Class<?>[] resolveRawArguments(Class<T> type, Class<S> subType) {
+ return resolveRawArguments(resolveGenericType(type, subType), subType);
+ }
+
+ /**
+ * Returns an array of raw classes representing arguments for the {@code genericType} using type variable information
+ * from the {@code subType}. Arguments for {@code genericType} that cannot be resolved are returned as
+ * {@code Unknown.class}. If no arguments can be resolved then {@code null} is returned.
+ *
+ * @param genericType to resolve arguments for
+ * @param subType to extract type variable information from
+ * @return array of raw classes representing arguments for the {@code genericType} else {@code null} if no type
+ * arguments are declared
+ */
+ public static Class<?>[] resolveRawArguments(Type genericType, Class<?> subType) {
+ Class<?>[] result = null;
+ Class<?> functionalInterface = null;
+
+ // Handle lambdas
+ if (RESOLVES_LAMBDAS && subType.isSynthetic()) {
+ Class<?> fi = genericType instanceof ParameterizedType
+ && ((ParameterizedType) genericType).getRawType() instanceof Class
+ ? (Class<?>) ((ParameterizedType) genericType).getRawType()
+ : genericType instanceof Class ? (Class<?>) genericType : null;
+ if (fi != null && fi.isInterface())
+ functionalInterface = fi;
+ }
+
+ if (genericType instanceof ParameterizedType) {
+ ParameterizedType paramType = (ParameterizedType) genericType;
+ Type[] arguments = paramType.getActualTypeArguments();
+ result = new Class[arguments.length];
+ for (int i = 0; i < arguments.length; i++)
+ result[i] = resolveRawClass(arguments[i], subType, functionalInterface);
+ } else if (genericType instanceof TypeVariable) {
+ result = new Class[1];
+ result[0] = resolveRawClass(genericType, subType, functionalInterface);
+ } else if (genericType instanceof Class) {
+ TypeVariable<?>[] typeParams = ((Class<?>) genericType).getTypeParameters();
+ result = new Class[typeParams.length];
+ for (int i = 0; i < typeParams.length; i++)
+ result[i] = resolveRawClass(typeParams[i], subType, functionalInterface);
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns the generic {@code type} using type variable information from the {@code subType} else {@code null} if the
+ * generic type cannot be resolved.
+ *
+ * @param type to resolve generic type for
+ * @param subType to extract type variable information from
+ * @return generic {@code type} else {@code null} if it cannot be resolved
+ */
+ public static Type resolveGenericType(Class<?> type, Type subType) {
+ Class<?> rawType;
+ if (subType instanceof ParameterizedType)
+ rawType = (Class<?>) ((ParameterizedType) subType).getRawType();
+ else
+ rawType = (Class<?>) subType;
+
+ if (type.equals(rawType))
+ return subType;
+
+ Type result;
+ if (type.isInterface()) {
+ for (Type superInterface : rawType.getGenericInterfaces())
+ if (superInterface != null && !superInterface.equals(Object.class))
+ if ((result = resolveGenericType(type, superInterface)) != null)
+ return result;
+ }
+
+ Type superClass = rawType.getGenericSuperclass();
+ if (superClass != null && !superClass.equals(Object.class))
+ if ((result = resolveGenericType(type, superClass)) != null)
+ return result;
+
+ return null;
+ }
+
+ /**
+ * Resolves the raw class for the {@code genericType}, using the type variable information from the {@code subType}
+ * else {@link Unknown} if the raw class cannot be resolved.
+ *
+ * @param genericType to resolve raw class for
+ * @param subType to extract type variable information from
+ * @return raw class for the {@code genericType} else {@link Unknown} if it cannot be resolved
+ */
+ public static Class<?> resolveRawClass(Type genericType, Class<?> subType) {
+ return resolveRawClass(genericType, subType, null);
+ }
+
+ private static Class<?> resolveRawClass(Type genericType, Class<?> subType, Class<?> functionalInterface) {
+ if (genericType instanceof Class) {
+ return (Class<?>) genericType;
+ } else if (genericType instanceof ParameterizedType) {
+ return resolveRawClass(((ParameterizedType) genericType).getRawType(), subType, functionalInterface);
+ } else if (genericType instanceof GenericArrayType) {
+ GenericArrayType arrayType = (GenericArrayType) genericType;
+ Class<?> component = resolveRawClass(arrayType.getGenericComponentType(), subType, functionalInterface);
+ return Array.newInstance(component, 0).getClass();
+ } else if (genericType instanceof TypeVariable) {
+ TypeVariable<?> variable = (TypeVariable<?>) genericType;
+ genericType = getTypeVariableMap(subType, functionalInterface).get(variable);
+ genericType = genericType == null ? resolveBound(variable)
+ : resolveRawClass(genericType, subType, functionalInterface);
+ }
+
+ return genericType instanceof Class ? (Class<?>) genericType : Unknown.class;
+ }
+
+ private static Map<TypeVariable<?>, Type> getTypeVariableMap(final Class<?> targetType,
+ Class<?> functionalInterface) {
+ Reference<Map<TypeVariable<?>, Type>> ref = TYPE_VARIABLE_CACHE.get(targetType);
+ Map<TypeVariable<?>, Type> map = ref != null ? ref.get() : null;
+
+ if (map == null) {
+ map = new HashMap<TypeVariable<?>, Type>();
+
+ // Populate lambdas
+ if (functionalInterface != null)
+ populateLambdaArgs(functionalInterface, targetType, map);
+
+ // Populate interfaces
+ populateSuperTypeArgs(targetType.getGenericInterfaces(), map, functionalInterface != null);
+
+ // Populate super classes and interfaces
+ Type genericType = targetType.getGenericSuperclass();
+ Class<?> type = targetType.getSuperclass();
+ while (type != null && !Object.class.equals(type)) {
+ if (genericType instanceof ParameterizedType)
+ populateTypeArgs((ParameterizedType) genericType, map, false);
+ populateSuperTypeArgs(type.getGenericInterfaces(), map, false);
+
+ genericType = type.getGenericSuperclass();
+ type = type.getSuperclass();
+ }
+
+ // Populate enclosing classes
+ type = targetType;
+ while (type.isMemberClass()) {
+ genericType = type.getGenericSuperclass();
+ if (genericType instanceof ParameterizedType)
+ populateTypeArgs((ParameterizedType) genericType, map, functionalInterface != null);
+
+ type = type.getEnclosingClass();
+ }
+
+ if (CACHE_ENABLED)
+ TYPE_VARIABLE_CACHE.put(targetType, new WeakReference<Map<TypeVariable<?>, Type>>(map));
+ }
+
+ return map;
+ }
+
+ /**
+ * Populates the {@code map} with with variable/argument pairs for the given {@code types}.
+ */
+ private static void populateSuperTypeArgs(final Type[] types, final Map<TypeVariable<?>, Type> map,
+ boolean depthFirst) {
+ for (Type type : types) {
+ if (type instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) type;
+ if (!depthFirst)
+ populateTypeArgs(parameterizedType, map, depthFirst);
+ Type rawType = parameterizedType.getRawType();
+ if (rawType instanceof Class)
+ populateSuperTypeArgs(((Class<?>) rawType).getGenericInterfaces(), map, depthFirst);
+ if (depthFirst)
+ populateTypeArgs(parameterizedType, map, depthFirst);
+ } else if (type instanceof Class) {
+ populateSuperTypeArgs(((Class<?>) type).getGenericInterfaces(), map, depthFirst);
+ }
+ }
+ }
+
+ /**
+ * Populates the {@code map} with variable/argument pairs for the given {@code type}.
+ */
+ private static void populateTypeArgs(ParameterizedType type, Map<TypeVariable<?>, Type> map, boolean depthFirst) {
+ if (type.getRawType() instanceof Class) {
+ TypeVariable<?>[] typeVariables = ((Class<?>) type.getRawType()).getTypeParameters();
+ Type[] typeArguments = type.getActualTypeArguments();
+
+ if (type.getOwnerType() != null) {
+ Type owner = type.getOwnerType();
+ if (owner instanceof ParameterizedType)
+ populateTypeArgs((ParameterizedType) owner, map, depthFirst);
+ }
+
+ for (int i = 0; i < typeArguments.length; i++) {
+ TypeVariable<?> variable = typeVariables[i];
+ Type typeArgument = typeArguments[i];
+
+ if (typeArgument instanceof Class) {
+ map.put(variable, typeArgument);
+ } else if (typeArgument instanceof GenericArrayType) {
+ map.put(variable, typeArgument);
+ } else if (typeArgument instanceof ParameterizedType) {
+ map.put(variable, typeArgument);
+ } else if (typeArgument instanceof TypeVariable) {
+ TypeVariable<?> typeVariableArgument = (TypeVariable<?>) typeArgument;
+ if (depthFirst) {
+ Type existingType = map.get(variable);
+ if (existingType != null) {
+ map.put(typeVariableArgument, existingType);
+ continue;
+ }
+ }
+
+ Type resolvedType = map.get(typeVariableArgument);
+ if (resolvedType == null)
+ resolvedType = resolveBound(typeVariableArgument);
+ map.put(variable, resolvedType);
+ }
+ }
+ }
+ }
+
+ /**
+ * Resolves the first bound for the {@code typeVariable}, returning {@code Unknown.class} if none can be resolved.
+ */
+ public static Type resolveBound(TypeVariable<?> typeVariable) {
+ Type[] bounds = typeVariable.getBounds();
+ if (bounds.length == 0)
+ return Unknown.class;
+
+ Type bound = bounds[0];
+ if (bound instanceof TypeVariable)
+ bound = resolveBound((TypeVariable<?>) bound);
+
+ return bound == Object.class ? Unknown.class : bound;
+ }
+
+ /**
+ * Populates the {@code map} with variable/argument pairs for the {@code functionalInterface}.
+ */
+ private static void populateLambdaArgs(Class<?> functionalInterface, final Class<?> lambdaType,
+ Map<TypeVariable<?>, Type> map) {
+ if (RESOLVES_LAMBDAS) {
+ // Find SAM
+ for (Method m : functionalInterface.getMethods()) {
+ if (!isDefaultMethod(m) && !Modifier.isStatic(m.getModifiers()) && !m.isBridge()) {
+ // Skip methods that override Object.class
+ Method objectMethod = OBJECT_METHODS.get(m.getName());
+ if (objectMethod != null && Arrays.equals(m.getTypeParameters(), objectMethod.getTypeParameters()))
+ continue;
+
+ // Get functional interface's type params
+ Type returnTypeVar = m.getGenericReturnType();
+ Type[] paramTypeVars = m.getGenericParameterTypes();
+
+ Member member = getMemberRef(lambdaType);
+ if (member == null)
+ return;
+
+ // Populate return type argument
+ if (returnTypeVar instanceof TypeVariable) {
+ Class<?> returnType = member instanceof Method ? ((Method) member).getReturnType()
+ : ((Constructor<?>) member).getDeclaringClass();
+ returnType = wrapPrimitives(returnType);
+ if (!returnType.equals(Void.class))
+ map.put((TypeVariable<?>) returnTypeVar, returnType);
+ }
+
+ Class<?>[] arguments = member instanceof Method ? ((Method) member).getParameterTypes()
+ : ((Constructor<?>) member).getParameterTypes();
+
+ // Populate object type from arbitrary object method reference
+ int paramOffset = 0;
+ if (paramTypeVars.length > 0 && paramTypeVars[0] instanceof TypeVariable
+ && paramTypeVars.length == arguments.length + 1) {
+ Class<?> instanceType = member.getDeclaringClass();
+ map.put((TypeVariable<?>) paramTypeVars[0], instanceType);
+ paramOffset = 1;
+ }
+
+ // Handle additional arguments that are captured from the lambda's enclosing scope
+ int argOffset = 0;
+ if (paramTypeVars.length < arguments.length) {
+ argOffset = arguments.length - paramTypeVars.length;
+ }
+
+ // Populate type arguments
+ for (int i = 0; i + argOffset < arguments.length; i++) {
+ if (paramTypeVars[i] instanceof TypeVariable)
+ map.put((TypeVariable<?>) paramTypeVars[i + paramOffset], wrapPrimitives(arguments[i + argOffset]));
+ }
+
+ return;
+ }
+ }
+ }
+ }
+
+ private static boolean isDefaultMethod(Method m) {
+ //CG
+ return false;
+ //return JAVA_VERSION >= 1.8 && m.isDefault();
+ }
+
+ private static Member getMemberRef(Class<?> type) {
+ Object constantPool;
+ try {
+ constantPool = GET_CONSTANT_POOL.invoke(type);
+ } catch (Exception ignore) {
+ return null;
+ }
+
+ Member result = null;
+ for (int i = getConstantPoolSize(constantPool) - 1; i >= 0; i--) {
+ Member member = getConstantPoolMethodAt(constantPool, i);
+ // Skip SerializedLambda constructors and members of the "type" class
+ if (member == null
+ || (member instanceof Constructor
+ && member.getDeclaringClass().getName().equals("java.lang.invoke.SerializedLambda"))
+ || member.getDeclaringClass().isAssignableFrom(type))
+ continue;
+
+ result = member;
+
+ // Return if not valueOf method
+ if (!(member instanceof Method) || !isAutoBoxingMethod((Method) member))
+ break;
+ }
+
+ return result;
+ }
+
+ private static boolean isAutoBoxingMethod(Method method) {
+ Class<?>[] parameters = method.getParameterTypes();
+ return method.getName().equals("valueOf") && parameters.length == 1 && parameters[0].isPrimitive()
+ && wrapPrimitives(parameters[0]).equals(method.getDeclaringClass());
+ }
+
+ private static Class<?> wrapPrimitives(Class<?> clazz) {
+ return clazz.isPrimitive() ? PRIMITIVE_WRAPPERS.get(clazz) : clazz;
+ }
+
+ private static int getConstantPoolSize(Object constantPool) {
+ try {
+ return (Integer) GET_CONSTANT_POOL_SIZE.invoke(constantPool);
+ } catch (Exception ignore) {
+ return 0;
+ }
+ }
+
+ private static Member getConstantPoolMethodAt(Object constantPool, int i) {
+ try {
+ return (Member) GET_CONSTANT_POOL_METHOD_AT.invoke(constantPool, i);
+ } catch (Exception ignore) {
+ return null;
+ }
+ }
+}
+
diff --git a/src/org/uic/barcode/asn1/uper/StringCoder.java b/src/org/uic/barcode/asn1/uper/StringCoder.java
new file mode 100644
index 0000000..fe06e3d
--- /dev/null
+++ b/src/org/uic/barcode/asn1/uper/StringCoder.java
@@ -0,0 +1,341 @@
+package org.uic.barcode.asn1.uper;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.uic.barcode.asn1.datatypes.Asn1Default;
+import org.uic.barcode.asn1.datatypes.Asn1String;
+import org.uic.barcode.asn1.datatypes.CharacterRestriction;
+import org.uic.barcode.asn1.datatypes.DefaultAlphabet;
+import org.uic.barcode.asn1.datatypes.FixedSize;
+import org.uic.barcode.asn1.datatypes.RestrictedString;
+import org.uic.barcode.asn1.datatypes.SizeRange;
+import org.uic.barcode.logger.Logger;
+import org.uic.barcode.logger.LoggerFactory;
+
+
+class StringCoder implements Decoder, Encoder {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger("asnLogger");
+
+ @Override public <T> boolean canEncode(T obj, Annotation[] extraAnnotations) {
+ return obj instanceof String || obj instanceof Asn1String;
+ }
+
+ @Override public <T> void encode(BitBuffer bitbuffer, T obj, Annotation[] extraAnnotations) throws Asn1EncodingException {
+ String pos = String.format("Position: %d.%d", bitbuffer.position()/8 , bitbuffer.position() % 8);
+ UperEncoder.logger.debug(String.format("%s: encode STRING %s of type %s", pos, obj, obj.getClass().getName()));
+ Class<?> type = obj.getClass();
+ AnnotationStore annotations = new AnnotationStore(type.getAnnotations(), extraAnnotations);
+ String string = (obj instanceof String) ? ((String) obj) : ((Asn1String) obj).value();
+ RestrictedString restrictionAnnotation = annotations.getAnnotation(RestrictedString.class);
+ if (restrictionAnnotation == null) {
+ throw new UnsupportedOperationException("Unrestricted character strings are not supported yet. All annotations: " + Arrays.asList(type.getAnnotations()));
+ }
+
+ FixedSize fixedSize = annotations.getAnnotation(FixedSize.class);
+ SizeRange sizeRange = annotations.getAnnotation(SizeRange.class);
+ if (fixedSize != null && fixedSize.value() != string.length()) {
+ throw new IllegalArgumentException(
+ "Bad string length, expected " + fixedSize.value() + ", got " + string.length());
+ }
+ if (sizeRange != null
+ && !sizeRange.hasExtensionMarker()
+ && (string.length() < sizeRange.minValue() || sizeRange.maxValue() < string
+ .length())) { throw new IllegalArgumentException(
+ "Bad string length, expected " + sizeRange.minValue() + ".."
+ + sizeRange.maxValue() + ", got " + string.length()); }
+
+ if (restrictionAnnotation.value() == CharacterRestriction.ObjectIdentifier) {
+
+ byte[] oidb = ObjectIdentifierCoder.encodeObjectId(string);
+
+ BitBuffer stringbuffer = ByteBitBuffer.createInfinite();
+
+ for (byte b: oidb){
+ UperEncoder.encodeConstrainedInt(stringbuffer, b & 0xff, 0, 255);
+ }
+ //-for (char c : string.toCharArray()) {
+ //- encodeChar(stringbuffer, c, restrictionAnnotation);
+ //-}
+ //char array replaced - end
+
+ stringbuffer.flip();
+ if (stringbuffer.limit() % 8 != 0) {
+ throw new AssertionError("encoding resulted not in multiple of 8 bits");
+ }
+ int numOctets = (stringbuffer.limit() + 7) / 8; // Actually +7 is not needed here,
+ // since we already checked with %8.
+ int position1 = bitbuffer.position();
+ UperEncoder.encodeLengthDeterminant(bitbuffer, numOctets);
+ UperEncoder.logger.debug(String.format("ObjectIdentifier %s, length %d octets, encoded as %s", string, numOctets, bitbuffer.toBooleanStringFromPosition(position1)));
+ int position2 = bitbuffer.position();
+ for (int i = 0; i < stringbuffer.limit(); i++) {
+ bitbuffer.put(stringbuffer.get());
+ }
+ UperEncoder.logger.debug(String.format("UTF8String %s, encoded length %d octets, value bits: %s", string, numOctets, bitbuffer.toBooleanStringFromPosition(position2)));
+ return;
+ } else if (restrictionAnnotation.value() == CharacterRestriction.UTF8String) {
+ // UTF8 length
+ BitBuffer stringbuffer = ByteBitBuffer.createInfinite();
+
+ //char array replaced - begin
+ byte[] stringasbytearray = string.getBytes(StandardCharsets.UTF_8);
+
+ for (byte b: stringasbytearray){
+ UperEncoder.encodeConstrainedInt(stringbuffer, b & 0xff, 0, 255);
+ }
+ //-for (char c : string.toCharArray()) {
+ //- encodeChar(stringbuffer, c, restrictionAnnotation);
+ //-}
+ //char array replaced - end
+
+ stringbuffer.flip();
+ if (stringbuffer.limit() % 8 != 0) {
+ throw new AssertionError("utf8 encoding resulted not in multiple of 8 bits");
+ }
+ int numOctets = (stringbuffer.limit() + 7) / 8; // Actually +7 is not needed here,
+ // since we already checked with %8.
+ int position1 = bitbuffer.position();
+ UperEncoder.encodeLengthDeterminant(bitbuffer, numOctets);
+ UperEncoder.logger.debug(String.format("UTF8String %s, length %d octets, encoded as %s", string, numOctets, bitbuffer.toBooleanStringFromPosition(position1)));
+ int position2 = bitbuffer.position();
+ for (int i = 0; i < stringbuffer.limit(); i++) {
+ bitbuffer.put(stringbuffer.get());
+ }
+ UperEncoder.logger.debug(String.format("UTF8String %s, encoded length %d octets, value bits: %s", string, numOctets, bitbuffer.toBooleanStringFromPosition(position2)));
+ return;
+ } else if (fixedSize != null) {
+ if (fixedSize.value() != string.length()) { throw new IllegalArgumentException(
+ "String length does not match constraints"); }
+ int position = bitbuffer.position();
+ for (int i = 0; i < fixedSize.value(); i++) {
+ encodeChar(bitbuffer, string.charAt(i), restrictionAnnotation);
+ }
+ UperEncoder.logger.debug(String.format("string encoded as <%s>", bitbuffer.toBooleanStringFromPosition(position)));
+ return;
+ } else if (sizeRange != null) {
+ UperEncoder.logger.debug("string length");
+ int position1 = bitbuffer.position();
+ UperEncoder.encodeConstrainedInt(bitbuffer, string.length(), sizeRange.minValue(),sizeRange.maxValue(), sizeRange.hasExtensionMarker());
+ int position2 = bitbuffer.position();
+ UperEncoder.logger.debug("string content");
+ for (int i = 0; i < string.length(); i++) {
+ encodeChar(bitbuffer, string.charAt(i), restrictionAnnotation);
+ }
+ UperEncoder.logger.debug(String.format("STRING %s size %d: %s", obj.getClass().getName(), bitbuffer.toBooleanString(position1, position2 - position1),bitbuffer.toBooleanStringFromPosition(position2)));
+ return;
+ } else {
+ int position1 = bitbuffer.position();
+ UperEncoder.encodeLengthDeterminant(bitbuffer, string.length());
+ int position2 = bitbuffer.position();
+ for (int i = 0; i < string.length(); i++) {
+ encodeChar(bitbuffer, string.charAt(i), restrictionAnnotation);
+ }
+ UperEncoder.logger.debug(String.format("STRING %s size %s: %s", obj.getClass().getName(), bitbuffer.toBooleanString(position1, position2 - position1),bitbuffer.toBooleanStringFromPosition(position2)));
+ return;
+ }
+ }
+
+ @Override public <T> boolean canDecode(Class<T> classOfT, Annotation[] extraAnnotations) {
+ return String.class.isAssignableFrom(classOfT) || Asn1String.class.isAssignableFrom(classOfT);
+ }
+
+ @Override public <T> T decode(BitBuffer bitbuffer,
+ Class<T> classOfT, Field field,
+ Annotation[] extraAnnotations) {
+ UperEncoder.logger.debug("decode String");
+ AnnotationStore annotations = new AnnotationStore(classOfT.getAnnotations(), extraAnnotations);
+ RestrictedString restrictionAnnotation = annotations.getAnnotation(RestrictedString.class);
+ if (restrictionAnnotation == null) {
+ throw new UnsupportedOperationException(
+ "Unrestricted character strings are not supported yet. All annotations: " + Arrays.asList(classOfT.getAnnotations()));
+ }
+ if (restrictionAnnotation.value() == CharacterRestriction.ObjectIdentifier) {
+ //decode object identifier
+ Long numOctets = UperEncoder.decodeLengthDeterminant(bitbuffer);
+ List<Boolean> content = new ArrayList<Boolean>();
+ for (int i = 0; i < numOctets * 8; i++) {
+ content.add(bitbuffer.get());
+ }
+ byte[] contentBytes = UperEncoder.bytesFromCollection(content);
+ UperEncoder.logger.debug(String.format("Content bytes (hex): %s", UperEncoder.hexStringFromBytes(contentBytes)));
+ String resultStr = ObjectIdentifierCoder.decodeObjectId(contentBytes);
+ UperEncoder.logger.debug(String.format("Object Identifier: %s", resultStr));
+ T result = UperEncoder.instantiate(classOfT, resultStr);
+ return result;
+ } else if (restrictionAnnotation.value() == CharacterRestriction.UTF8String) {
+ Long numOctets = UperEncoder.decodeLengthDeterminant(bitbuffer);
+ List<Boolean> content = new ArrayList<Boolean>();
+ for (int i = 0; i < numOctets * 8; i++) {
+ content.add(bitbuffer.get());
+ }
+ byte[] contentBytes = UperEncoder.bytesFromCollection(content);
+ UperEncoder.logger.debug(String.format("Content bytes (hex): %s", UperEncoder.hexStringFromBytes(contentBytes)));
+ String resultStr = StandardCharsets.UTF_8.decode(ByteBuffer.wrap(contentBytes)).toString();
+ UperEncoder.logger.debug(String.format("Decoded as %s", resultStr));
+ T result = UperEncoder.instantiate(classOfT, resultStr);
+ return result;
+ } else {
+ FixedSize fixedSize = annotations.getAnnotation(FixedSize.class);
+ SizeRange sizeRange = annotations.getAnnotation(SizeRange.class);
+ long numChars = (fixedSize != null) ? fixedSize.value() :
+ (sizeRange != null) ? UperEncoder.decodeConstrainedInt(bitbuffer,
+ UperEncoder.intRangeFromSizeRange(sizeRange)) :
+ UperEncoder.decodeLengthDeterminant(bitbuffer);
+ UperEncoder.logger.debug(String.format("known-multiplier string, numchars: %d", numChars));
+ StringBuilder stringBuilder = new StringBuilder((int) numChars);
+ for (int c = 0; c < numChars; c++) {
+ stringBuilder.append(decodeRestrictedChar(bitbuffer, restrictionAnnotation));
+ }
+ String resultStr = stringBuilder.toString();
+ UperEncoder.logger.debug(String.format("Decoded as %s", resultStr));
+ T result = UperEncoder.instantiate(classOfT, resultStr);
+ return result;
+ }
+ }
+
+ private static void encodeChar(BitBuffer bitbuffer, char c, RestrictedString restriction) throws Asn1EncodingException {
+ UperEncoder.logger.debug(String.format("char %s", c));
+ switch (restriction.value()) {
+ case IA5String:
+ if (restriction.alphabet() != DefaultAlphabet.class) {
+ throw new UnsupportedOperationException("alphabet for IA5String is not supported yet.");
+ }
+ UperEncoder.encodeConstrainedInt(
+ bitbuffer,
+ StandardCharsets.US_ASCII.encode(CharBuffer.wrap(new char[] { c })).get() & 0xff,
+ 0,
+ 127);
+ return;
+ case UTF8String:
+ if (restriction.alphabet() != DefaultAlphabet.class) {
+ throw new UnsupportedOperationException("alphabet for UTF8 is not supported yet.");
+ }
+ ByteBuffer buffer = StandardCharsets.UTF_8.encode(CharBuffer.wrap(new char[] { c }));
+ for (int i = 0; i < buffer.limit(); i++) {
+ UperEncoder.encodeConstrainedInt(bitbuffer, buffer.get() & 0xff, 0, 255);
+ }
+ return;
+ case VisibleString:
+ case ISO646String:
+ if (restriction.alphabet() != DefaultAlphabet.class) {
+ char[] chars;
+ try {
+ chars = UperEncoder.instantiate(restriction.alphabet()).chars().toCharArray();
+ } catch (IllegalArgumentException e) {
+ LOGGER.info("Uninstantinatable alphabet ", e);
+ throw new IllegalArgumentException("Uninstantinatable alphabet" + restriction.alphabet().getName());
+ }
+ if (BigInteger.valueOf(chars.length - 1).bitLength() < BigInteger.valueOf(126)
+ .bitLength()) {
+ Arrays.sort(chars);
+ String strAlphabet = new String(chars);
+ int index = strAlphabet.indexOf(c);
+ if (index < 0) { throw new IllegalArgumentException("can't find character " + c + " in alphabet " + strAlphabet); }
+ UperEncoder.encodeConstrainedInt(
+ bitbuffer,
+ index,
+ 0,
+ chars.length - 1);
+ return;
+ } else {
+ UperEncoder.encodeConstrainedInt(
+ bitbuffer,
+ StandardCharsets.US_ASCII.encode(CharBuffer.wrap(new char[] { c }))
+ .get() & 0xff,
+ 0,
+ 126);
+ return;
+ }
+ } else {
+ UperEncoder.encodeConstrainedInt(
+ bitbuffer,
+ StandardCharsets.US_ASCII.encode(CharBuffer.wrap(new char[] { c }))
+ .get() & 0xff,
+ 0,
+ 126);
+ return;
+ }
+ default:
+ throw new UnsupportedOperationException("String type " + restriction
+ + " is not supported yet");
+ }
+ }
+
+ private static String decodeRestrictedChar(BitBuffer bitqueue,
+ RestrictedString restrictionAnnotation) {
+ switch (restrictionAnnotation.value()) {
+ case IA5String: {
+ if (restrictionAnnotation.alphabet() != DefaultAlphabet.class) {
+ throw new UnsupportedOperationException(
+ "alphabet for IA5String is not supported yet.");
+ }
+ byte charByte = (byte) UperEncoder.decodeConstrainedInt(bitqueue, UperEncoder.newRange(0, 127, false));
+ byte[] bytes = new byte[] { charByte };
+ String result = StandardCharsets.US_ASCII.decode(ByteBuffer.wrap(bytes)).toString();
+ if (result.length() != 1) {
+ throw new AssertionError("decoded more than one char (" + result + ")");
+ }
+ return result;
+ }
+ case VisibleString:
+ case ISO646String: {
+ if (restrictionAnnotation.alphabet() != DefaultAlphabet.class) {
+ char[] chars;
+ try {
+ chars = UperEncoder.instantiate(restrictionAnnotation.alphabet()).chars().toCharArray();
+ } catch (IllegalArgumentException e) {
+ LOGGER.info("Uninstantinatable alphabet ", e);
+ throw new IllegalArgumentException("Uninstantinatable alphabet " + restrictionAnnotation.alphabet().getName());
+ }
+ if (BigInteger.valueOf(chars.length - 1).bitLength() < BigInteger.valueOf(126)
+ .bitLength()) {
+ Arrays.sort(chars);
+ int index = (byte) UperEncoder.decodeConstrainedInt(bitqueue, UperEncoder.newRange(0, chars.length - 1, false));
+ String strAlphabet = new String(chars);
+ char c = strAlphabet.charAt(index);
+ String result = new String("" + c);
+ return result;
+ } else { // Encode normally
+ byte charByte = (byte) UperEncoder.decodeConstrainedInt(bitqueue, UperEncoder.newRange(0, 126, false));
+ byte[] bytes = new byte[] { charByte };
+ String result = StandardCharsets.US_ASCII.decode(ByteBuffer.wrap(bytes)).toString();
+ if (result.length() != 1) { throw new AssertionError(
+ "decoded more than one char (" + result + ")");
+ }
+ return result;
+ }
+ } else { // Encode normally
+ byte charByte = (byte) UperEncoder.decodeConstrainedInt(bitqueue, UperEncoder.newRange(0, 126, false));
+ byte[] bytes = new byte[] { charByte };
+ String result = StandardCharsets.US_ASCII.decode(ByteBuffer.wrap(bytes)).toString();
+ if (result.length() != 1) {
+ throw new AssertionError("decoded more than one char (" + result + ")");
+ }
+ return result;
+ }
+ }
+ default:
+ throw new UnsupportedOperationException("String type " + restrictionAnnotation + " is not supported yet");
+
+ }
+ }
+
+ @Override
+ public <T> T getDefault(Class<T> classOfT, Annotation[] extraAnnotations) {
+ AnnotationStore annotations = new AnnotationStore(classOfT.getAnnotations(), extraAnnotations);
+ Asn1Default defaultAnnotation = annotations.getAnnotation(Asn1Default.class);
+ if (defaultAnnotation == null) return null;
+ T result = UperEncoder.instantiate(classOfT, defaultAnnotation.value());
+ return result;
+ }
+
+} \ No newline at end of file
diff --git a/src/org/uic/barcode/asn1/uper/UperEncoder.java b/src/org/uic/barcode/asn1/uper/UperEncoder.java
new file mode 100644
index 0000000..a5ef5c5
--- /dev/null
+++ b/src/org/uic/barcode/asn1/uper/UperEncoder.java
@@ -0,0 +1,720 @@
+package org.uic.barcode.asn1.uper;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.uic.barcode.asn1.datatypes.Asn1Default;
+import org.uic.barcode.asn1.datatypes.Asn1Optional;
+import org.uic.barcode.asn1.datatypes.FieldOrder;
+import org.uic.barcode.asn1.datatypes.HasExtensionMarker;
+import org.uic.barcode.asn1.datatypes.IntRange;
+import org.uic.barcode.asn1.datatypes.IsExtension;
+import org.uic.barcode.asn1.datatypes.NoAsn1Field;
+import org.uic.barcode.asn1.datatypes.SizeRange;
+import org.uic.barcode.logger.Logger;
+import org.uic.barcode.logger.LoggerFactory;
+
+
+
+/** A "quick-and-dirty" implementation of ASN.1 encoder for UPER (Unaligned Packed Encoding Rules).
+ *
+ * @see ITU-T Recommendation <a
+ * href="http://www.itu.int/ITU-T/recommendations/rec.aspx?rec=x.691">X.691</a>
+ *
+ * TODO: Cover the rest of (useful) ASN.1 datatypes and PER-visible constraints,
+ * write unit tests for them. Clean-up, do more refactoring.
+ **/
+public final class UperEncoder {
+ public final static Logger logger = LoggerFactory.getLogger("asnLogger");
+
+ private final static int NUM_16K = 16384;
+ @SuppressWarnings("unused")
+ private final static int NUM_32K = 32768;
+ @SuppressWarnings("unused")
+ private final static int NUM_48K = 49152;
+ @SuppressWarnings("unused")
+ private final static int NUM_64K = 65536;
+
+ private UperEncoder(){}
+
+ public static <T> byte[] encode(T obj)
+ throws IllegalArgumentException, UnsupportedOperationException {
+ try {
+ BitBuffer bitbuffer = ByteBitBuffer.createInfinite();
+ encode2(bitbuffer, obj, new Annotation[] {});
+ bitbuffer.flip();
+ byte[] result = Arrays.copyOf(bitbuffer.array(), (bitbuffer.limit() + 7) / 8);
+ return result;
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("Can't encode " + obj.getClass().getName() + ": " + e, e);
+ } catch (Asn1EncodingException e) {
+ throw new IllegalArgumentException("Can't encode " + obj.getClass().getName() + ":" + e.getMessage(), e);
+ }
+ }
+
+ public static <T> T decode(byte[] bytes, Class<T> classOfT) throws IllegalArgumentException,
+ UnsupportedOperationException {
+ BitBuffer bitQueue = bitBufferFromBinaryString(binaryStringFromBytes(bytes));
+ T result = decodeAny(bitQueue, classOfT,null, new Annotation[] {});
+ if (bitQueue.remaining() > 7) {
+ throw new IllegalArgumentException("Can't fully decode "
+ + classOfT.getName() + ", got (" + result.getClass().getName() + "): " + result
+ + "; remaining " + bitQueue.remaining() + " bits: " + bitQueue);
+ }
+ return result;
+ }
+
+ public static <T> T decode(byte[] bytes, Class<T> classOfT, Field f) throws IllegalArgumentException,
+ UnsupportedOperationException {
+ BitBuffer bitQueue = bitBufferFromBinaryString(binaryStringFromBytes(bytes));
+ T result = decodeAny(bitQueue, classOfT, f, new Annotation[] {});
+ if (bitQueue.remaining() > 7) {
+ throw new IllegalArgumentException("Can't fully decode "
+ + classOfT.getName() + ", got (" + result.getClass().getName() + "): " + result
+ + "; remaining " + bitQueue.remaining() + " bits: " + bitQueue);
+ }
+ return result;
+ }
+
+
+ static <T> void encode2(BitBuffer bitbuffer, T obj, Annotation[] extraAnnotations) throws Asn1EncodingException {
+ for (Encoder e : encoders) {
+ if (e.canEncode(obj, extraAnnotations)) {
+ e.encode(bitbuffer, obj, extraAnnotations);
+ return;
+ }
+ }
+ logger.debug(String.format("Can't find encoder for %s",obj.getClass().getSimpleName()));
+
+ throw new IllegalArgumentException("Can't find encoder for " + obj.getClass().getName()
+ + " with extra annotations " + Arrays.asList(extraAnnotations));
+ }
+
+ static <T> T decodeAny(BitBuffer bitbuffer,Class<T> classOfT, Field f, Annotation[] extraAnnotations) {
+
+ logger.debug(String.format(String.format("Decoding classOfT : %s",classOfT.getCanonicalName())));
+
+ for (Decoder e : decoders) {
+ if (e.canDecode(classOfT, extraAnnotations)) {
+ return e.decode(bitbuffer, classOfT,f, extraAnnotations);
+ }
+ }
+
+ logger.debug(String.format("Can't find decoder for %s",classOfT.getSimpleName()));
+
+ throw new IllegalArgumentException("Can't find decoder for " + classOfT.getName()
+ + " with extra annotations " + Arrays.asList(extraAnnotations));
+ }
+
+ static <T> T getDefault(Class<T> classOfT, Annotation[] annotations) {
+ AnnotationStore annots = new AnnotationStore(classOfT.getAnnotations(), annotations);
+ Asn1Default defaultAnnotation = annots.getAnnotation(Asn1Default.class);
+
+ if (defaultAnnotation == null){
+ return null;
+ }
+
+ Annotation[] defaultAnnots = new Annotation[] {defaultAnnotation};
+
+ for (Decoder e : decoders) {
+ if (e.canDecode(classOfT, defaultAnnots)) {
+ return e.getDefault(classOfT, defaultAnnots);
+ }
+ }
+ logger.debug(String.format("Can't find decoder for %s",classOfT.getSimpleName()));
+
+ throw new IllegalArgumentException("Can't find default for " + classOfT.getName()
+ + " with extra annotations " + defaultAnnotation.toString());
+ }
+
+ static IntRange newRange(
+ final long minValue,
+ final long maxValue,
+ final boolean hasExtensionMarker) {
+
+ return new IntRange() {
+ @Override public Class<? extends Annotation> annotationType() {
+ return IntRange.class;
+ }
+ @Override public long minValue() { return minValue; }
+ @Override public long maxValue() { return maxValue; }
+ @Override public boolean hasExtensionMarker() { return hasExtensionMarker; }
+ };
+ }
+
+ static IntRange intRangeFromSizeRange(SizeRange sizeRange) {
+ return newRange(sizeRange.minValue(), sizeRange.maxValue(), sizeRange.hasExtensionMarker());
+ }
+
+ private static List<Encoder> encoders = new ArrayList<>();
+ private static List<Decoder> decoders = new ArrayList<>();
+
+ static {
+ encoders.add(new IntCoder());
+ //encoders.add(new BigIntCoder());
+ encoders.add(new ByteCoder());
+ encoders.add(new BooleanCoder());
+ encoders.add(new SequenceCoder());
+ encoders.add(new ChoiceCoder());
+ encoders.add(new EnumCoder());
+ encoders.add(new BitStringCoder());
+ encoders.add(new SeqOfCoder());
+ encoders.add(new StringCoder());
+
+ decoders.add(new IntCoder());
+ //decoders.add(new BigIntCoder());
+ decoders.add(new ByteCoder());
+ decoders.add(new BooleanCoder());
+ decoders.add(new SequenceCoder());
+ decoders.add(new ChoiceCoder());
+ decoders.add(new EnumCoder());
+ decoders.add(new BitStringCoder());
+ decoders.add(new SeqOfCoder());
+ decoders.add(new StringCoder());
+
+ }
+
+
+ static <T> void encodeAsOpenType(
+ BitBuffer bitbuffer, T obj, Annotation[] extraAnnotations)
+ throws IllegalArgumentException, IllegalAccessException, Asn1EncodingException {
+ logger.debug(String.format("OPEN TYPE for {%s}. Encoding preceedes length determinant" ,obj != null ? obj.getClass().getSimpleName() : "null"));
+ BitBuffer tmpbuffer = ByteBitBuffer.createInfinite();
+ encode2(tmpbuffer, obj, extraAnnotations);
+ int numBytes = (tmpbuffer.position() + 7) / 8;
+ logger.debug(String.format("Encoding open type length determinant (%d) for %s (will be inserted before the open type content)", numBytes, obj != null ? obj.getClass().getName() : "null" ));
+ try {
+ encodeLengthDeterminant(bitbuffer, numBytes);
+ } catch (Asn1EncodingException e) {
+ throw new Asn1EncodingException(" length of open type ", e);
+ }
+ tmpbuffer.flip();
+ for (int i = 0; i < tmpbuffer.limit(); i++) {
+ bitbuffer.put(tmpbuffer.get());
+ }
+ //CG padding bits to fill the byte: Open Types are wrapped in an OCTET STRING
+ int paddingBits = numBytes*8 - tmpbuffer.limit();
+ for (int i = 0; i < paddingBits; i++) {
+ bitbuffer.put(false);
+ }
+
+ }
+
+ static <T> T decodeAsOpenType(BitBuffer bitbuffer, Class<T> classOfT,Field f, Annotation[] extraAnnotations) {
+ logger.debug(String.format("OPEN TYPE for %s. Encoding preceedes length determinant", classOfT != null ? classOfT.getName() : "null"));
+ long numBytes = decodeLengthDeterminant(bitbuffer);
+ BitBuffer openTypeBitBuffer = ByteBitBuffer.allocate((int)numBytes * 8);
+ for (int i = 0; i < numBytes * 8; i++) {
+ openTypeBitBuffer.put(bitbuffer.get());
+ }
+ openTypeBitBuffer.flip();
+ if (classOfT != null) {
+ T result = decodeAny(openTypeBitBuffer, classOfT, f, extraAnnotations);
+ // Assert that padding bits are all 0.
+ logger.debug(String.format("open type had padding bits"));
+ for (int i = 0; i < openTypeBitBuffer.remaining(); i++) {
+ boolean paddingBit = openTypeBitBuffer.get();
+ logger.debug(String.format("padding bit %d was <%s>", i, paddingBit ? "1" : "0"));
+ if (paddingBit) {
+ throw new IllegalArgumentException("non-zero padding bit " + i + " for open type " + classOfT.getSimpleName()); }
+ }
+ return result;
+ } else {
+ return null;
+ }
+ }
+
+ /*
+ * skip an unknown extension element
+ * - decode length
+ * - skip the bytes according to the length
+ */
+ static void decodeSkipUnknownElement(BitBuffer bitbuffer, String name) {
+ logger.debug(String.format("Skip unknown extension in %s. Encoding preceedes length determinant", name));
+ long numBytes = decodeLengthDeterminant(bitbuffer);
+ for (int i = 0; i < numBytes * 8; i++) {
+ bitbuffer.get();
+ }
+ logger.debug(String.format(String.format("Skiped %d bytes", numBytes)));
+ }
+
+ static <T> boolean hasNonNullExtensions(
+ T obj, Asn1ContainerFieldSorter sorter)
+ throws IllegalArgumentException, IllegalAccessException {
+ for (Field f : sorter.extensionFields) {
+ //CG elements with default value will not be not included
+ if (f.get(obj) != null && !isDefault(f,f.get(obj)) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static <T> Constructor<T> findConsturctor(Class<T> classOfT, Object... parameters) {
+ @SuppressWarnings("unchecked")
+ Constructor<T>[] declaredConstructors = (Constructor<T>[]) classOfT
+ .getDeclaredConstructors();
+ for (Constructor<T> c : declaredConstructors) {
+ Class<?>[] parameterTypes = c.getParameterTypes();
+ if (parameterTypes.length == parameters.length) {
+ boolean constructorIsOk = true;
+ for (int i = 0; i < parameters.length; i++) {
+ if (!parameterTypes[i].isAssignableFrom(parameters[i].getClass())) {
+ constructorIsOk = false;
+ break;
+ }
+ }
+ if (constructorIsOk) { return c; }
+ }
+ }
+ Class<?>[] parameterTypes = new Class<?>[parameters.length];
+ for (int i = 0; i < parameters.length; i++) {
+ parameterTypes[i] = parameters[i].getClass();
+ }
+ throw new IllegalArgumentException("Can't get the " + parameters.length +
+ "-argument constructor for parameter(s) "
+ + Arrays.asList(parameters) +
+ " of type(s) " + Arrays.asList(parameterTypes) + " for class "
+ + classOfT.getName() + " (" + classOfT.getClass().getName() + " or " + Arrays.asList(classOfT.getClasses()) + ")" +
+ ", all constructors: " + Arrays.asList(classOfT.getDeclaredConstructors()));
+ }
+
+ /** Instantiate a given class T using given parameters. */
+ static <T> T instantiate(Class<T> classOfT, Object... parameters) {
+ Class<?>[] parameterTypes = new Class<?>[parameters.length];
+ for (int i = 0; i < parameters.length; i++) {
+ parameterTypes[i] = parameters[i].getClass();
+ }
+ Constructor<T> constructor = findConsturctor(classOfT, parameters);
+ boolean constructorIsAccessible = constructor.isAccessible();
+ constructor.setAccessible(true);
+ T result;
+ try {
+ result = constructor.newInstance(parameters);
+ } catch (IllegalArgumentException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
+ throw new IllegalArgumentException("Can't instantiate " + classOfT.getName(), e);
+ }
+ constructor.setAccessible(constructorIsAccessible);
+ return result;
+ }
+
+ static long decodeConstrainedInt(BitBuffer bitqueue, IntRange intRange) {
+ long lowerBound = intRange.minValue();
+ long upperBound = intRange.maxValue();
+ boolean hasExtensionMarker = intRange.hasExtensionMarker();
+ if (upperBound < lowerBound) {
+ throw new IllegalArgumentException("Lower bound " + lowerBound + " is larger that upper bound " + upperBound);
+ }
+ if (hasExtensionMarker) {
+ boolean extensionIsActive = bitqueue.get();
+ if (extensionIsActive) {
+ //in extensions are encoded as uncontraint integers, thius an Asn1BigInteger type should be used(a lower range bound might be applied).
+ throw new UnsupportedOperationException("int extension are not supported yet");
+ }
+ }
+ final Long range = upperBound - lowerBound + 1;
+ if (range == 1) {
+ return lowerBound;
+ }
+ int bitlength = BigInteger.valueOf(range - 1).bitLength();
+ logger.debug(String.format("This int will require %d bits, available %d" , bitlength, bitqueue.remaining()));
+ BitBuffer relevantBits = ByteBitBuffer.allocate( ((bitlength + 7) / 8) * 8); // Full bytes.
+ int numPaddingBits = (8 - (bitlength % 8)) % 8; // Leading padding 0-bits.
+ for (int i = 0; i < numPaddingBits; i++) {
+ relevantBits.put(false);
+ }
+ for (int i = 0; i < bitlength; i++) {
+ relevantBits.put(bitqueue.get());
+ }
+ relevantBits.flip();
+ final BigInteger big = new BigInteger(+1, relevantBits.array());
+ final Long result = lowerBound + big.longValue();
+ logger.debug(String.format("bits %s decoded as %d plus lower bound %d give %d",
+ relevantBits.toBooleanStringFromPosition(0), big.longValue(), lowerBound, result));
+ if ((result < intRange.minValue() || intRange.maxValue() < result)
+ && !intRange.hasExtensionMarker()) {
+ throw new AssertionError("Decoded value "
+ + result + " is outside of range (" + intRange.minValue() + ".."
+ + intRange.maxValue() + ")");
+ }
+ return result;
+ }
+
+
+ //CG Begin
+ static boolean isDefault(Field f, Object obj) {
+
+ if (f.getAnnotation(Asn1Default.class) != null) {
+ String value = f.getAnnotation(Asn1Default.class).value();
+ if (obj.toString().equals(value)){
+ return true;
+ }
+ }
+ return false;
+
+ }
+
+
+ public static boolean isNotAsn1(Field f) {
+ return (f.getAnnotation(NoAsn1Field.class) != null);
+ }
+ //CG End
+
+
+ static boolean hasExtensionMarker(AnnotationStore annotations) {
+ return annotations.getAnnotation(HasExtensionMarker.class) != null;
+ }
+
+ private static boolean isExtension(Field f) {
+ return f.getAnnotation(IsExtension.class) != null;
+ }
+
+ static boolean isMandatory(Field f) {
+ return !isOptional(f);
+ }
+
+ static boolean isOptional(Field f) {
+ //CG elements with default value are treated as optional as they are not encoded in case of the default value
+ if (f.getAnnotation(Asn1Optional.class) != null ||
+ f.getAnnotation(Asn1Default.class) != null) {
+ return true;
+ }
+ return false;
+ }
+
+ static class Asn1ContainerFieldSorter {
+ /** "Outside extension root" */
+ List<Field> extensionFields = new ArrayList<>();
+ List<Field> optionalExtensionFields = new ArrayList<>();
+ List<Field> mandatoryExtensionField = new ArrayList<>();
+ /** "Within extension root" */
+ List<Field> ordinaryFields = new ArrayList<>();
+ List<Field> mandatoryOrdinaryFields = new ArrayList<>();
+ List<Field> optionalOrdinaryFields = new ArrayList<>();
+ List<Field> allFields = new ArrayList<>(); // Excluding test instrumentation.
+
+ Map<Field, Boolean> originalAccess = new HashMap<>();
+
+ Asn1ContainerFieldSorter(Class<?> type) {
+
+ /*
+ *
+ * sorting of the fields added to compensate the error
+ * in the java SDK on android where getDeclaredFields does
+ * not return the fields in the order of the class definition
+ *
+ */
+ List<Field> fields = Arrays.asList(type.getDeclaredFields());
+ Collections.sort(fields, new Comparator<Field>() {
+ @Override
+ public int compare(Field o1, Field o2) {
+ FieldOrder ao1 = o1.getAnnotation(FieldOrder.class);
+ FieldOrder ao2 = o2.getAnnotation(FieldOrder.class);
+ int order1 = ao1 == null ? Integer.MAX_VALUE : ao1.order();
+ int order2 = ao2 == null ? Integer.MAX_VALUE : ao2.order();
+ if (order1 == Integer.MAX_VALUE || order2 == Integer.MAX_VALUE || order1 < 0 || order2 < 0 || order1 == order2) {
+ logger.debug(String.format("field order error for %s",type.getSimpleName()));
+ }
+ return Integer.compare(order1, order2);
+ }
+ });
+
+
+ for (Field f : fields) {
+ if (isTestInstrumentation(f) || isNonAsn1Field(f) ) {
+ continue;
+ }
+ originalAccess.put(f, f.isAccessible());
+ f.setAccessible(true);
+ if (isExtension(f)) {
+ extensionFields.add(f);
+ if (isOptional(f)) {
+ optionalExtensionFields.add(f);
+ } else {
+ mandatoryExtensionField.add(f);
+ }
+ }
+ else {
+ ordinaryFields.add(f);
+ }
+ allFields.add(f);
+ }
+ for (Field f : ordinaryFields) {
+ if (isMandatory(f)) {
+ mandatoryOrdinaryFields.add(f);
+ } else {
+ optionalOrdinaryFields.add(f);
+ }
+ }
+ }
+
+ public void revertAccess() {
+ for (Entry<Field, Boolean> entry : originalAccess.entrySet()) {
+ entry.getKey().setAccessible(entry.getValue());
+ }
+ }
+ }
+
+ static boolean isTestInstrumentation(Field f) {
+ return f.getName().startsWith("$");
+ }
+
+ static boolean isNonAsn1Field(Field f) {
+ if (f.getAnnotation(NoAsn1Field.class) != null) {
+ return true;
+ }
+ return false;
+ }
+
+ static void encodeLengthOfBitmask(BitBuffer bitbuffer, int n) throws Asn1EncodingException {
+ try {
+ if (n <= 63) {
+ logger.debug(String.format("normally small length of bitmask, length %d <= 63 indicated as bit <0>", n));
+ bitbuffer.put(false);
+ encodeConstrainedInt(bitbuffer, n, 1, 63);
+ return;
+ } else {
+ logger.debug(String.format("normally small length of bitmask, length %s > 63 indicated as bit <1>", n));
+ bitbuffer.put(true);
+ encodeLengthDeterminant(bitbuffer, n);
+ return;
+ }
+ } catch (Asn1EncodingException e) {
+ throw new Asn1EncodingException(" length of bitmask ", e);
+ }
+ }
+
+ static void encodeSmallInt(BitBuffer bitbuffer, int n) throws Asn1EncodingException {
+ try {
+ if (n <= 63) {
+ logger.debug(String.format("normally small length of bitmask, length %d <= 63 indicated as bit <0>", n));
+ bitbuffer.put(false);
+ encodeConstrainedInt(bitbuffer, n, 0, 63);
+ return;
+ } else {
+ logger.debug(String.format("normally small length of bitmask, length %s > 63 indicated as bit <1>", n));
+ bitbuffer.put(true);
+ encodeLengthDeterminant(bitbuffer, n);
+ return;
+ }
+ } catch (Asn1EncodingException e) {
+ throw new Asn1EncodingException(" length of bitmask ", e);
+ }
+ }
+
+ static void encodeLengthDeterminant(BitBuffer bitbuffer, int n) throws Asn1EncodingException {
+ try {
+ int position = bitbuffer.position();
+ if (n < 128) {
+ bitbuffer.put(false);
+ encodeConstrainedInt(bitbuffer, n, 0, 127);
+ logger.debug(String.format("Length determinant %d, encoded as <%s>", n, bitbuffer.toBooleanStringFromPosition(position)));
+ if (bitbuffer.position() - position != 8) {
+ throw new AssertionError("length determinant encoded not as 8 bits");
+ }
+ return;
+ } else if (n < NUM_16K) {
+ bitbuffer.put(true);
+ bitbuffer.put(false);
+ encodeConstrainedInt(bitbuffer, n, 0, NUM_16K - 1);
+ logger.debug(String.format("Length determinant %d, encoded as 2bits+14bits: <%s>", n,bitbuffer.toBooleanStringFromPosition(position)));
+ if (bitbuffer.position() - position != 16) {
+ throw new AssertionError("length determinant encoded not as 16 bits");
+ }
+ return;
+ } else {
+ throw new UnsupportedOperationException("Length greater than 16K is not supported yet.");
+ }
+ } catch (Asn1EncodingException e) {
+ throw new Asn1EncodingException(" length determinant ", e);
+ }
+
+ }
+
+ static long decodeLengthOfBitmask(BitBuffer bitbuffer) {
+ logger.debug("decoding length of bitmask");
+ boolean isGreaterThan63 = bitbuffer.get();
+ logger.debug(String.format("length determinant extension preamble size flag: preamble size > 63 is %s", isGreaterThan63));
+ if (!isGreaterThan63) {
+ Long result = decodeConstrainedInt(bitbuffer, newRange(1, 63, false));
+ logger.debug(String.format("normally small length of bitmask, length <= 63, decoded as %d", result));
+ return result;
+ } else {
+ logger.debug(String.format("normally small length of bitmask, length > 63, decoding as ordinary length determinant..."));
+ return decodeLengthDeterminant(bitbuffer);
+ }
+ }
+
+ static long decodeSmallInt(BitBuffer bitbuffer) {
+ logger.debug("decoding small int");
+ boolean isGreaterThan63 = bitbuffer.get();
+ logger.debug(String.format("length determinant extension preamble size flag: preamble size > 63 is %s", isGreaterThan63));
+ if (!isGreaterThan63) {
+ Long result = decodeConstrainedInt(bitbuffer, newRange(0, 63, false));
+ logger.debug(String.format("normally small length of bitmask, length <= 63, decoded as %d", result));
+ return result;
+ } else {
+ logger.debug(String.format("normally small length of bitmask, length > 63, decoding as ordinary length determinant..."));
+ return decodeLengthDeterminant(bitbuffer);
+ }
+ }
+
+ static long decodeLengthDeterminant(BitBuffer bitbuffer) {
+ boolean bit8 = bitbuffer.get();
+ if (!bit8) { // then value is less than 128
+ Long result = decodeConstrainedInt(bitbuffer, newRange(0, 127, false));
+ logger.debug(String.format("length determinant, decoded as %d", result));
+ return result;
+ } else {
+ boolean bit7 = bitbuffer.get();
+ if (!bit7) { // then value is less than 16K
+ Long result = decodeConstrainedInt(bitbuffer, newRange(0, NUM_16K - 1, false));
+ logger.debug(String.format("length determinant, decoded as %d", result));
+ return result;
+ } else { // "Large" n
+ logger.debug("lengthes longer than 16K are not supported yet.");
+ throw new UnsupportedOperationException("lengthes longer than 16K are not supported yet.");
+ }
+ }
+
+ }
+
+ static void encodeConstrainedInt(
+ final BitBuffer bitbuffer,
+ final long value,
+ final long lowerBound,
+ final long upperBound) throws Asn1EncodingException {
+ encodeConstrainedInt(bitbuffer, value, lowerBound, upperBound, false);
+ }
+
+ static void encodeConstrainedInt(
+ final BitBuffer bitbuffer,
+ final long value,
+ final long lowerBound,
+ final long upperBound,
+ final boolean hasExtensionMarker
+ ) throws Asn1EncodingException {
+ if (upperBound < lowerBound) {
+ throw new IllegalArgumentException("Lower bound "
+ + lowerBound + " is larger than upper bound " + upperBound);
+ }
+ if (!hasExtensionMarker && (value < lowerBound || value > upperBound)) {
+ throw new Asn1EncodingException(
+ " Value " + value + " is outside of fixed range " +
+ lowerBound + ".." + upperBound);
+ }
+ final Long range = upperBound - lowerBound + 1;
+ final int position = bitbuffer.position();
+ if (hasExtensionMarker) {
+ boolean outsideOfRange = value < lowerBound || value > upperBound;
+ logger.debug(String.format("constrained int with extension marker, %s extension range",outsideOfRange ? "outside" : "within", outsideOfRange ? "1" : "0"));
+ bitbuffer.put(outsideOfRange);
+ if (outsideOfRange) {
+ throw new UnsupportedOperationException(
+ "INT extensions are not supported yet");
+ }
+ }
+ if (range == 1) {
+ logger.debug("constrained int of empty range, resulting in empty encoding <>");
+ return;
+ }
+ final BigInteger big = BigInteger.valueOf(value - lowerBound);
+ final int numPaddingBits = BigInteger.valueOf(range - 1).bitLength() - big.bitLength();
+ for (int i = 0; i < numPaddingBits; i++) {
+ bitbuffer.put(false);
+ }
+ for (int i = big.bitLength() - 1; i >= 0; i--) {
+ bitbuffer.put(big.testBit(i));
+ }
+ logger.debug(String.format("constrained int %d encoded as <%s>", value, bitbuffer.toBooleanStringFromPosition(position)));
+ return;
+ }
+
+ public static byte[] bytesFromCollection(List<Boolean> bitlist) {
+ int sizeBytes = (bitlist.size() + 7) / 8;
+ byte[] result = new byte[sizeBytes];
+ int byteId = 0;
+ byte bitId = 7;
+ logger.debug(String.format("byte: < %s >", bitlist));
+ for (Boolean b : bitlist) {
+ //logger.debug(String.format("bitId: %s, byteId: %s, value: %s", bitId, byteId, b));
+ result[byteId] |= (b ? 1 : 0) << bitId;
+ bitId--;
+ if (bitId < 0) {
+ bitId = 7;
+ byteId++;
+ }
+ }
+ int nZeros = sizeBytes * 8 - bitlist.size();
+ String zeros = nZeros > 0 ? String.format("%0" + nZeros + "d", 0) : "";
+ logger.debug(String.format("Padding bits (%d): <%s>", nZeros, zeros));
+ return result;
+ }
+
+ final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
+
+ public static String hexStringFromBytes(byte[] bytes) {
+ char[] hexChars = new char[bytes.length * 2];
+ for (int j = 0; j < bytes.length; j++) {
+ int v = bytes[j] & 0xFF;
+ hexChars[j * 2] = hexArray[v >>> 4];
+ hexChars[j * 2 + 1] = hexArray[v & 0x0F];
+ }
+ return new String(hexChars);
+ }
+
+ public static byte[] bytesFromHexString(String s) {
+ int len = s.length();
+ byte[] data = new byte[len / 2];
+ for (int i = 0; i < len; i += 2) {
+ data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ + Character.digit(s.charAt(i + 1), 16));
+ }
+ return data;
+ }
+
+ public static String binaryStringFromBytes(byte[] bytes) {
+ StringBuilder sb = new StringBuilder(bytes.length * Byte.SIZE);
+ for (int i = 0; i < Byte.SIZE * bytes.length; i++)
+ sb.append((bytes[i / Byte.SIZE] << i % Byte.SIZE & 0x80) == 0 ? '0' : '1');
+ return sb.toString();
+ }
+
+ public static byte[] bytesFromBinaryString(String s) {
+ int len = s.length();
+ byte[] result = new byte[(len + Byte.SIZE - 1) / Byte.SIZE];
+ char c;
+ for (int i = 0; i < len; i++)
+ if ((c = s.charAt(i)) == '1') result[i / Byte.SIZE] = (byte) (result[i / Byte.SIZE] | (0x80 >>> (i % Byte.SIZE)));
+ else if (c != '0')
+ throw new IllegalArgumentException();
+ return result;
+ }
+
+ private static BitBuffer bitBufferFromBinaryString(String s) {
+ ByteBitBuffer result = ByteBitBuffer.allocate(s.length());
+ for (int i = 0; i < s.length(); i++) {
+ if (s.charAt(i) != '1' && s.charAt(i) != '0') {
+ throw new IllegalArgumentException("bad character in 'binary' string " + s.charAt(i));
+ }
+ result.put(s.charAt(i) == '1');
+ }
+ result.flip();
+ return result;
+ }
+
+
+
+
+}