package zutil.parser.binary; import java.lang.reflect.Field; import java.util.*; import zutil.ByteUtil; import zutil.ClassUtil; import zutil.converter.Converter; import zutil.parser.binary.BinaryStruct.*; /** * A class representing each field in a BinaryStruct. */ public class BinaryFieldData { private static final HashMap> cache = new HashMap<>(); private int index; private int length; private Field field; /* @VariableLengthBinaryField */ private BinaryFieldData lengthField; private int lengthMultiplier; /* @CustomBinaryField */ private BinaryFieldSerializer serializer; protected static List getStructFieldList(Class clazz){ if (!cache.containsKey(clazz)) { try { ArrayList list = new ArrayList<>(); for (Field field : clazz.getDeclaredFields()) { if (field.isAnnotationPresent(BinaryField.class) || field.isAnnotationPresent(CustomBinaryField.class) || field.isAnnotationPresent(VariableLengthBinaryField.class)) list.add(new BinaryFieldData(field)); } Collections.sort(list, new Comparator(){ @Override public int compare(BinaryFieldData o1, BinaryFieldData o2) { return o1.index - o2.index; } }); cache.put(clazz, list); } catch (Exception e) { throw new RuntimeException(e); } } return cache.get(clazz); } private BinaryFieldData(Field f) throws IllegalAccessException, InstantiationException, NoSuchFieldException { field = f; this.length = -1; this.lengthField = null; this.lengthMultiplier = 1; this.serializer = null; if (field.isAnnotationPresent(CustomBinaryField.class)){ CustomBinaryField fieldData = field.getAnnotation(CustomBinaryField.class); this.index = fieldData.index(); this.serializer = fieldData.serializer().newInstance(); } else if (field.isAnnotationPresent(VariableLengthBinaryField.class)) { VariableLengthBinaryField fieldData = field.getAnnotation(VariableLengthBinaryField.class); this.index = fieldData.index(); this.lengthMultiplier = fieldData.multiplier(); this.lengthField = new BinaryFieldData( field.getDeclaringClass().getDeclaredField(fieldData.lengthField())); if ( !ClassUtil.isNumber(lengthField.getType())) throw new IllegalArgumentException("Length variable for VariableLengthBinaryStruct needs to be of a number type."); } else { BinaryField fieldData = field.getAnnotation(BinaryField.class); this.index = fieldData.index(); this.length = fieldData.length(); } } public String getName(){ return field.getName(); } public Class getType(){ return field.getType(); } public void setByteValue(Object obj, byte[] data){ try { field.setAccessible(true); if (field.getType() == Boolean.class || field.getType() == boolean.class) field.set(obj, data[0] != 0); else if (field.getType() == Integer.class || field.getType() == int.class) field.set(obj, Converter.toInt(data)); else if (field.getType() == String.class) field.set(obj, new String(data)); else throw new UnsupportedOperationException("Unsupported BinaryStruct field class: "+ field.getClass()); } catch (IllegalAccessException e){ e.printStackTrace(); } } public void setValue(Object obj, Object value){ try { field.setAccessible(true); field.set(obj, value); } catch (IllegalAccessException e){ e.printStackTrace(); } } public byte[] getByteValue(Object obj){ try { field.setAccessible(true); if (field.getType() == Boolean.class || field.getType() == boolean.class) return ByteUtil.getBits( new byte[]{ (byte)(field.getBoolean(obj) ? 0x01 : 0x00)}, getBitLength(obj)); else if (field.getType() == Integer.class || field.getType() == int.class) return ByteUtil.getBits( Converter.toBytes(field.getInt(obj)), getBitLength(obj)); else if (field.getType() == String.class) return ByteUtil.getReverseByteOrder( ByteUtil.getBits( ((String)(field.get(obj))).getBytes(), getBitLength(obj))); else throw new UnsupportedOperationException("Unsupported BinaryStruct field type: "+ getType()); } catch (IllegalAccessException e){ e.printStackTrace(); } return null; } public Object getValue(Object obj){ try { field.setAccessible(true); return field.get(obj); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; } public int getBitLength(Object obj){ if(lengthField != null) return (int) lengthField.getValue(obj) * lengthMultiplier; return length; } public BinaryFieldSerializer getSerializer(){ return serializer; } }