summaryrefslogblamecommitdiffstats
path: root/src/net/gcdc/asn1/uper/BitStringCoder.java
blob: e60c68e1e28ef6ee73aa71996babc09e5805df1c (plain) (tree)




































































































































































                                                                                                                              
package net.gcdc.asn1.uper;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import net.gcdc.asn1.datatypes.Asn1VarSizeBitstring;
import net.gcdc.asn1.datatypes.Bitstring;
import net.gcdc.asn1.datatypes.FixedSize;
import net.gcdc.asn1.datatypes.SizeRange;
import net.gcdc.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");
	}
}