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 java.util.List; 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; class BitStringCoder implements Decoder, Encoder { @Override public 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 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 = annotations.getAnnotation(FixedSize.class); int position = bitbuffer.position(); if (size != null) { if (!List.class.isAssignableFrom(type)) { throw new AssertionError("Field should be a list of booleans!"); } List list = (List)obj; if (list.size() != size.value()) { throw new AssertionError( "Declared size (" + size.value() + ") and number of fields (" + list.size() + ") do not match!"); } for (Boolean f : list) { try { bitbuffer.put(f); } catch (IllegalArgumentException 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 boolean canDecode(Class classOfT, Annotation[] extraAnnotations) { AnnotationStore annotations = new AnnotationStore(classOfT.getAnnotations(), extraAnnotations); return annotations.getAnnotation(Bitstring.class) != null; } @Override public T decode(BitBuffer bitbuffer, Class 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"); } 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); Method addBooleanMethod; try { addBooleanMethod = classOfT.getDeclaredMethod("add", Object.class); addBooleanMethod.setAccessible(true); } catch (SecurityException | NoSuchMethodException e) { throw new AssertionError("Can't find/access add " + e); } for (int i = 0; i < fixedSize.value(); i++) { try { addBooleanMethod.invoke(result, bitbuffer.get()); } catch (IllegalArgumentException | InvocationTargetException | IllegalAccessException e) { throw new IllegalArgumentException("Can't invoke add", 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 Long badSize(Class classOfT) { throw new IllegalArgumentException("both size range and fixed size are null for " + classOfT.getName()); } @Override public T getDefault(Class classOfT, Annotation[] extraAnnotations) { throw new IllegalArgumentException("Default Sequence not yet implemented"); } }