summaryrefslogtreecommitdiffstats
path: root/src/org/uic/barcode/asn1/uper
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/uic/barcode/asn1/uper')
-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
21 files changed, 3513 insertions, 0 deletions
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;
+ }
+
+
+
+
+}