package net.gcdc.asn1.uper;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import net.gcdc.asn1.datatypes.Choice;
import net.gcdc.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("with extension marker, set to {}", extensionBit);
bitbuffer.put(extensionBit);
}
if (sorter.ordinaryFields.size() > 1) { // Encode index only if more than one.
UperEncoder.logger.debug("with chosen element indexed {}", nonNullIndex);
UperEncoder.encodeConstrainedInt(bitbuffer, nonNullIndex, 0,
sorter.ordinaryFields.size() - 1);
}
UperEncoder.encode2(bitbuffer, nonNullFieldValue, nonNullField.getAnnotations());
return;
} else if (UperEncoder.hasExtensionMarker(annotations)) {
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) { throw new IllegalArgumentException(
"All fields of Choice are null"); }
boolean extensionBit = true;
UperEncoder.logger.debug("with extension marker, set to <{}>", extensionBit);
bitbuffer.put(extensionBit);
throw new UnsupportedOperationException(
"Choice extension is not implemented yet");
} 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,
Annotation[] extraAnnotations) {
AnnotationStore annotations = new AnnotationStore(classOfT.getAnnotations(),
extraAnnotations);
UperEncoder.logger.debug("CHOICE");
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) {
throw new UnsupportedOperationException(
"choice extension is not implemented yet");
} else {
// 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);
Object fieldValue = UperEncoder.decode2(bitbuffer, f.getType(), f.getAnnotations());
try {
f.set(result, fieldValue);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new IllegalArgumentException("can't decode " + classOfT, e);
}
return result;
}
}