summaryrefslogblamecommitdiffstats
path: root/src/main/java/org/uic/barcode/asn1/uper/BitStringCoder.java
blob: 6f435c478382c3b60d30b08925084a2425685505 (plain) (tree)
1
2
3
4
5
6
7
8
                                  




                                                   
                      
 



                                                           


















                                                                                                                              
                                                                        

                                                






                                                                                         
                                                          



                                                                          
                         

                                                          

















































                                                                                                                 
                                                                    







                                                                                                                  





                                                                                    









                                                                                   
                     


                                                                                                           
                 
                         










































                                                                                                           
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 <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 = 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<Boolean> list = (List<Boolean>)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 <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, AsnExtractor extractor) {
        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 <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");
	}
}