FixLengthImpl.java
package org.cyclopsgroup.caff.format;
import java.lang.reflect.AccessibleObject;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.cyclopsgroup.caff.conversion.AnnotatedConverter;
import org.cyclopsgroup.caff.conversion.Converter;
import org.cyclopsgroup.caff.ref.ValueReference;
import org.cyclopsgroup.caff.ref.ValueReferenceScanner;
/**
* Internal actual fix-length algorithm
*
* @author <a href="mailto:jiaqi@cyclopsgroup.org">Jiaqi Guo</a>
* @param <T> Type of bean to format/parse
*/
class FixLengthImpl<T> {
private class Slot {
private final Converter<Object> converter;
private final FixLengthField field;
private final char fill;
private final ValueReference<T> reference;
private Slot(FixLengthField field, Converter<Object> converter, ValueReference<T> reference) {
this.field = field;
this.converter = converter;
this.reference = reference;
this.fill = field.fill() == 0 ? type.fill() : field.fill();
}
}
private final Map<String, Slot> slots;
/** Message level meta data */
final FixLengthType type;
/**
* @param beanType type of bean to format/parse
*/
FixLengthImpl(Class<T> beanType) {
FixLengthType type = beanType.getAnnotation(FixLengthType.class);
if (type == null) {
throw new IllegalArgumentException(
"Type " + beanType + " isn't annotated with " + FixLengthType.class);
}
this.type = type;
final Map<String, Slot> slots = new HashMap<String, Slot>();
ValueReferenceScanner<T> scanner = new ValueReferenceScanner<T>(beanType);
scanner.scanForAnnotation(
FixLengthField.class,
new ValueReferenceScanner.Listener<T, FixLengthField>() {
@SuppressWarnings("unchecked")
@Override
public void handleReference(
ValueReference<T> reference, FixLengthField hint, AccessibleObject access) {
Slot slot =
new Slot(
hint,
new AnnotatedConverter<Object>((Class<Object>) reference.getType(), access),
reference);
slots.put(reference.getName(), slot);
}
});
this.slots = Collections.unmodifiableMap(slots);
}
/**
* @param bean Empty bean to populate
* @param input A line of input
*/
void populate(T bean, CharSequence input) {
for (Slot slot : slots.values()) {
if (!slot.reference.isWritable()) {
continue;
}
if (slot.field.start() >= input.length()) {
continue;
}
CharSequence content =
input.subSequence(
slot.field.start(),
Math.min(slot.field.start() + slot.field.length(), input.length()));
content = slot.field.align().trim(content, slot.fill);
Object value = slot.converter.fromCharacters(content);
slot.reference.writeValue(value, bean);
}
}
/**
* Print bean into given char array FIXME It's not completely working yet
*
* @param bean Bean to print
* @return Char array of result
*/
char[] print(T bean) {
char[] output = new char[type.length()];
Arrays.fill(output, type.fill());
for (Slot slot : slots.values()) {
if (!slot.reference.isReadable()) {
continue;
}
Object value = slot.reference.readValue(bean);
CharSequence content = slot.converter.toCharacters(value);
if (content.length() > slot.field.length()) {
content = slot.field.trim().trim(content, slot.field.length(), slot.field.align());
}
slot.field.align().fill(content, output, slot.field.start(), slot.field.length(), slot.fill);
}
return output;
}
}