sirius.biz.i5.Transformer Maven / Gradle / Ivy
Show all versions of sirius-biz Show documentation
/*
* Made with all the love in the world
* by scireum in Remshalden, Germany
*
* Copyright by scireum GmbH
* http://www.scireum.de - [email protected]
*/
package sirius.biz.i5;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.ibm.as400.access.AS400Bin4;
import com.ibm.as400.access.AS400PackedDecimal;
import com.ibm.as400.access.AS400Text;
import com.ibm.as400.access.AS400ZonedDecimal;
import sirius.kernel.health.Exceptions;
import javax.annotation.Nonnull;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* Helps to transform byte oriented record (sent and received from the i5) into Java objects.
*
* This is achieved by placing {@link Transform} annotations on fields with appropriate AS400... types. These
* are scanned and automatically copied to or from the byte array.
*/
public class Transformer {
private Map transforms = Maps.newLinkedHashMap();
Transformer(Class> target) {
List result = Lists.newArrayList();
for (Field field : target.getDeclaredFields()) {
if (field.isAnnotationPresent(Transform.class)) {
field.setAccessible(true);
result.add(field);
}
}
result.sort(Comparator.comparingInt(f -> f.getAnnotation(Transform.class).position()));
for (Field field : result) {
transforms.put(field, field.getAnnotation(Transform.class));
}
}
/**
* Filles the given object from the given byte array.
*
* @param object the object to fill
* @param data the received byte array
*/
public void fromBytes(@Nonnull Object object, byte[] data) {
int offset = 0;
for (Map.Entry e : transforms.entrySet()) {
offset = transform(e, object, data, offset);
}
}
/**
* Fills the byte array from the given object.
*
* @param object the object to take data from
* @param destination the byte array to fill. Note that this has to have the correct length already.
*/
public void toBytes(@Nonnull Object object, byte[] destination) {
int offset = 0;
for (Map.Entry e : transforms.entrySet()) {
offset = serialize(e, object, destination, offset);
}
}
private int serialize(Entry e, Object object, byte[] data, int offset) {
Transform info = e.getValue();
Field field = e.getKey();
try {
Object value = field.get(object);
if (info.targetType() == AS400Text.class) {
AS400Text mapper = new AS400Text(info.length());
mapper.toBytes(value == null ? "" : value, data, offset);
return offset + mapper.getByteLength();
}
if (info.targetType() == AS400ZonedDecimal.class) {
AS400ZonedDecimal mapper = new AS400ZonedDecimal(info.length(), info.decimal());
mapper.toBytes(value == null ? 0d : ((BigDecimal) value).doubleValue(), data, offset);
return offset + mapper.getByteLength();
}
if (info.targetType() == Byte.class) {
if (value != null) {
//noinspection SuspiciousSystemArraycopy
System.arraycopy(value, 0, data, offset, info.length());
}
return offset + info.length();
}
throw Exceptions.handle()
.to(I5Connector.LOG)
.withSystemErrorMessage("Cannot transform a field from type: %s (%s.%s)",
info.targetType().getName(),
object.getClass().getName(),
field.getName())
.handle();
} catch (Exception ex) {
throw Exceptions.handle()
.to(I5Connector.LOG)
.error(ex)
.withSystemErrorMessage("Error while transforming '%s.%s' from %s: %s (%s)",
object.getClass().getName(),
field.getName(),
info.targetType().getName())
.handle();
}
}
private int transform(Entry e, Object object, byte[] data, int offset) {
Transform info = e.getValue();
Field field = e.getKey();
try {
if (info.targetType() == AS400Bin4.class) {
AS400Bin4 mapper = new AS400Bin4();
field.set(object, mapper.toObject(data, offset));
return offset + mapper.getByteLength();
}
if (info.targetType() == AS400Text.class) {
AS400Text mapper = new AS400Text(info.length());
field.set(object, ((String) mapper.toObject(data, offset)).trim());
return offset + mapper.getByteLength();
}
if (info.targetType() == AS400ZonedDecimal.class) {
AS400ZonedDecimal mapper = new AS400ZonedDecimal(info.length(), info.decimal());
field.set(object, mapper.toObject(data, offset));
return offset + mapper.getByteLength();
}
if (info.targetType() == AS400PackedDecimal.class) {
AS400PackedDecimal mapper = new AS400PackedDecimal(info.length(), info.decimal());
field.set(object, mapper.toObject(data, offset));
return offset + mapper.getByteLength();
}
if (info.targetType() == Byte.class) {
field.set(object, Arrays.copyOfRange(data, offset, info.length()));
return offset + info.length();
}
throw Exceptions.handle()
.to(I5Connector.LOG)
.withSystemErrorMessage("Cannot transform a field to type: %s (%s.%s)",
info.targetType().getName(),
object.getClass().getName(),
field.getName())
.handle();
} catch (Exception ex) {
throw Exceptions.handle()
.to(I5Connector.LOG)
.error(ex)
.withSystemErrorMessage("Error while transforming '%s.%s' to %s: %s (%s)",
object.getClass().getName(),
field.getName(),
info.targetType().getName())
.handle();
}
}
/**
* Creates a string representation of the given object by dumping all known fields.
*
* @param transformable the object to convert to a string
* @return a string representation of the given object
*/
protected String asString(@Nonnull Transformable transformable) {
StringBuilder sb = new StringBuilder();
for (Map.Entry e : transforms.entrySet()) {
try {
sb.append(e.getKey().getName());
sb.append(": ");
sb.append(e.getKey().get(transformable));
sb.append("\n");
} catch (Exception t) {
Exceptions.ignore(t);
}
}
return sb.toString();
}
}