package net.gcdc.asn1.uper;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import net.gcdc.asn1.datatypes.Asn1Integer;
import net.gcdc.asn1.datatypes.IntRange;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class IntCoder implements Encoder, Decoder {
private static final Map<Class<?>, IntRange> DEFAULT_RANGE;
private static final Logger LOGGER = LoggerFactory.getLogger(IntCoder.class.getName());
static {
DEFAULT_RANGE = new HashMap<>();
DEFAULT_RANGE.put(short.class, UperEncoder.newRange(Short.MIN_VALUE, Short.MAX_VALUE, false));
DEFAULT_RANGE.put(Short.class, UperEncoder.newRange(Short.MIN_VALUE, Short.MAX_VALUE, false));
DEFAULT_RANGE.put(int.class, UperEncoder.newRange(Integer.MIN_VALUE, Integer.MAX_VALUE, false));
DEFAULT_RANGE.put(Integer.class, UperEncoder.newRange(Integer.MIN_VALUE, Integer.MAX_VALUE, false));
DEFAULT_RANGE.put(long.class, UperEncoder.newRange(Long.MIN_VALUE, Long.MAX_VALUE, false));
DEFAULT_RANGE.put(Long.class, UperEncoder.newRange(Long.MIN_VALUE, Long.MAX_VALUE, false));
// Byte is not part of this, since we treat byte as unsigned byte, while the rest we treat
// as it is.
// Asn1Integer have max range of Long. Bigger ranges require Asn1BigInteger.
DEFAULT_RANGE.put(Asn1Integer.class, UperEncoder.newRange(Long.MIN_VALUE, Long.MAX_VALUE, false));
}
@Override public <T> boolean canDecode(Class<T> classOfT, Annotation[] extraAnnotations) {
return Asn1Integer.class.isAssignableFrom(classOfT) |
Long.class.isAssignableFrom(classOfT) |
long.class.isAssignableFrom(classOfT) |
Integer.class.isAssignableFrom(classOfT) |
int.class.isAssignableFrom(classOfT) |
Short.class.isAssignableFrom(classOfT) |
short.class.isAssignableFrom(classOfT);
}
@Override public <T> T decode(BitBuffer bitbuffer,
Class<T> classOfT,
Annotation[] extraAnnotations) {
AnnotationStore annotations = new AnnotationStore(classOfT.getAnnotations(),
extraAnnotations);
UperEncoder.logger.debug("INTEGER");
IntRange intRange = annotations.getAnnotation(IntRange.class);
if (intRange == null) {
intRange = DEFAULT_RANGE.get(classOfT);
}
UperEncoder.logger.debug("Integer, range {}..{}", intRange.minValue(), intRange.maxValue());
long value = UperEncoder.decodeConstrainedInt(bitbuffer, intRange);
UperEncoder.logger.debug("decoded as {}", value);
Class<?>[] numericTypes = new Class<?>[] { long.class, int.class, short.class };
Constructor<T> constructor = null;
for (Class<?> t : numericTypes) {
try {
constructor = classOfT.getConstructor(t);
} catch (NoSuchMethodException e) {
// We expect exception here. Just ignore them and try next constructor.
// After the loop, check if any constructor was found.
} catch (SecurityException e) {
throw new IllegalArgumentException("can't access constructor of "
+ classOfT.getName() + ": " + e);
}
}
if (constructor == null) { throw new IllegalArgumentException(
"can't find any numeric constructor for " + classOfT.getName()
+ ", all constructors: " + Arrays.asList(classOfT.getConstructors())); }
try {
Class<?> typeOfConstructorArgument = constructor.getParameterTypes()[0];
if (typeOfConstructorArgument.isAssignableFrom(long.class)) {
return constructor.newInstance(value);
} else if (typeOfConstructorArgument.isAssignableFrom(int.class)) {
return constructor.newInstance((int) value);
} else if (typeOfConstructorArgument.isAssignableFrom(short.class)) {
return constructor.newInstance((short) value);
} else {
throw new IllegalArgumentException("unrecognized constructor argument "
+ typeOfConstructorArgument.getName());
}
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
| InstantiationException e1) {
throw new IllegalArgumentException("failed to invoke constructor of "
+ classOfT.getName() + ": " + e1);
}
}
@Override public <T> boolean canEncode(T obj, Annotation[] extraAnnotations) {
return obj instanceof Asn1Integer ||
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);
if (range == null) {
range = DEFAULT_RANGE.get(obj);
}
int position = bitbuffer.position();
try {
UperEncoder.encodeConstrainedInt(bitbuffer, ((Asn1Integer) obj).value(), range.minValue(),
range.maxValue(), range.hasExtensionMarker());
} catch (Asn1EncodingException e) {
throw new Asn1EncodingException(" " + type.getSimpleName(), e);
}
UperEncoder.logger.debug("INT({}): {}", obj, bitbuffer.toBooleanStringFromPosition(position));
return;
}
}