This is the method I came up with a while ago. Not a best practice. I like object orientation (˘ω˘)
First, extract from InputStream to byte []. Please refer to here for how to take it out. Wrap it to create a ByteBuffer (hereafter buf).
byte[] bin = BinUtil.readAll(stream);
ByteBuffer buf = ByteBuffer.wrap(bin);
(long)buf.getInt()
There is no concept of sign in Java (˘ω˘).
(long)buf.getInt () & 0xffffffffL
--Define the data type interface.
You have to be able to get and get data from buf. Prepare compile and decompile. The overall data size length and offset (described later) are also required.
public interface DataTypeInterface<T> {
Integer getOffset();
void setOffset(Integer offset);
Integer getSize();
void setSize(Integer size);
void decompile(ByteBuffer buf);
void compile(ByteBuffer buf);
/**
*Get child elements
*
* @return child element
*/
T get();
/**
*Set child elements
*
* @param item Child element
*/
void set(T item);
}
A type whose internal value can be represented by a java primitive (such as String or Long).
public abstract class SingleDataType<T> implements DataTypeInterface<T>{
/**
*Internal value
*/
private T value;
/**
*Object start position in byte stream
*/
private Integer offset;
/**
*Object byte size
*/
private Integer size;
@Override
final public Integer getOffset(){
return this.offset;
}
@Override
final public void setOffset(Integer offset){
this.offset= offset;
}
@Override
final public void setSize(Integer size) {
this.size = size;
}
@Override
public Integer getSize() {
return this.size;
}
public T get() {
return this.value;
}
public void set(T value) {
this.value = value;
}
}
Only numbers.
abstract public class NumericDataType<T extends Number> extends SingleDataType<T> {
@Override
final public T get() {
return super.get();
}
@Override
public void set(T val) {
super.set(val);
}
abstract public void setNumber(Number val);
}
LONG Basically done long (゜ ∀ ゜)
public class LONG extends NumericDataType<Long> {
@Override
public void decompile(ByteBuffer buf) {
this.setOffset(buf.position());
this.set((long)buf.getInt());
}
@Override
public void compile(ByteBuffer buf) {
this.setOffset(buf.position());
buf.putInt(this.get().intValue());
}
public LONG(ByteBuffer buf) {
this.decompile(buf);
this.setSize(4);
}
public LONG(Long value) {
this.set(value);
this.setSize(4);
}
/**
*Validate and set the value
*
* @param value value
* @The range of throws IllegalArgumentException values is-If it is less than 2147483648L or more than 2147483647L
*/
@Override
public void set(Long value) throws IllegalArgumentException {
/*1*/
if ( value < -2147483648L || 2147483647L < value) {
throw new IllegalArgumentException("The range of values exceeds the maximum and minimum lengths");
}
/*2*/
super.set(value);
}
@Override
public void setNumber(Number val) {
this.set(val.longValue());
}
}
There is no concept of struct in Java (; ω;). I'll do something with annotations and reflection
@Retention(RetentionPolicy.RUNTIME)
public @interface PROPERTY {
int order();
}
Enumerate the fields by reflection, extract only those with the above annotations, sort them in order, and decompile in that order.
public abstract class CompositeDataType implements DataTypeInterface<DataTypeInterface>{
protected Integer offset;
@Override
final public Integer getOffset(){
return this.offset;
}
@Override
final public void setOffset(Integer offset){
this.offset= offset;
}
@Override
public Integer getSize(){
return this.OrderedProperties()
.entrySet()
.stream()
.mapToInt(item -> item.getValue().getSize())
.sum();
}
@Override
@Deprecated
public void setSize(Integer size) {
throw new RuntimeException("You cannot set the size of a composite object.");
}
@Override
public void decompile(ByteBuffer buf){
this.setOffset(buf.position());
for (Map.Entry<String, DataTypeInterface> item: this.OrderedProperties().entrySet()) {
try {
item.getValue().decompile(buf);
}catch (IllegalArgumentException e){
System.out.println("Key("+ item.getKey() +")Child elements of are skipped");
e.printStackTrace();
}
}
}
@Override
public void compile(ByteBuffer buf) {
this.setOffset(buf.position());
for (Map.Entry<String, DataTypeInterface> item: this.OrderedProperties().entrySet()) {
try {
item.getValue().compile(buf);
}catch (IllegalArgumentException e){
System.out.println("Key("+ item.getKey() +")Child elements of are skipped");
e.printStackTrace();
}
}
}
protected Map<String,DataTypeInterface> OrderedProperties() {
List<Field> fields=new ArrayList<>();
Class c= this.getClass() ;
while(c != null){
fields.addAll(java.util.Arrays.asList(c.getDeclaredFields()));
c = c.getSuperclass();
}
List<Field> paramfields = fields
.stream()
.filter(f -> f.getAnnotation(PROPERTY.class) != null)
.collect(Collectors.toList());
Collections.sort(paramfields, (Field o1, Field o2) -> {
PROPERTY or1 = o1.getAnnotation(PROPERTY.class);
PROPERTY or2 = o2.getAnnotation(PROPERTY.class);
if (or1 != null && or2 != null) {
return or1.order() - or2.order();
}
return o1.getName().compareTo(o2.getName());
}
);
Map<String, DataTypeInterface> m = new LinkedHashMap<>();
try {
for (Field f : paramfields) {
f.setAccessible(true);
m.put(f.getName(), (DataTypeInterface) f.get(this));
}
} catch (IllegalAccessException e) {
throw new RuntimeException("field("+ fields.toString() +"Cannot access");
}
return m;
}
@Override
public DataTypeInterface get() {
return null;
}
@Override
public void set(DataTypeInterface item) {
}
}
If you have any data structure, use it as follows.
public class Version extends CompositeDataType {
@PROPERTY(order = 1)
public LONG version = new LONG();
@PROPERTY(order = 2)
public LONG subVersion = new LONG();
}
You can reuse what you make.
public class Data1 extends CompositeDataType {
@PROPERTY(order = 1)
public Version version = new Version();
@PROPERTY(order = 2)
public LONG data1 = new LONG();
}
This is the basic one, and 2 does array data and offset data.
Recommended Posts