All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.credibledoc.iso8583packer.ValueHolder Maven / Gradle / Ivy

There is a newer version: 1.0.51
Show newest version
package com.credibledoc.iso8583packer;

import com.credibledoc.iso8583packer.bcd.BcdBodyPacker;
import com.credibledoc.iso8583packer.bcd.BcdService;
import com.credibledoc.iso8583packer.bitmap.BitmapPacker;
import com.credibledoc.iso8583packer.body.BodyPacker;
import com.credibledoc.iso8583packer.dump.DumpService;
import com.credibledoc.iso8583packer.dump.Visualizer;
import com.credibledoc.iso8583packer.exception.PackerRuntimeException;
import com.credibledoc.iso8583packer.length.LengthPacker;
import com.credibledoc.iso8583packer.message.MsgField;
import com.credibledoc.iso8583packer.message.MsgFieldType;
import com.credibledoc.iso8583packer.message.MsgPair;
import com.credibledoc.iso8583packer.message.MsgValue;
import com.credibledoc.iso8583packer.navigator.Navigator;
import com.credibledoc.iso8583packer.navigator.NavigatorService;
import com.credibledoc.iso8583packer.offset.Offset;
import com.credibledoc.iso8583packer.tag.TagPacker;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.List;
import java.util.Objects;

/**
 * This builder helps to fill the existing {@link MsgField} definition with data. It contains
 * two state objects, the {@link #msgValue} and {@link #msgField}.
 * 

* The {@link MsgField} definition can be created by {@link FieldBuilder}. * * @author Kyrylo Semenko */ public class ValueHolder { static final int INITIAL_SIZE_100_BYTES = 100; static final String PARTIAL_DUMP = "\nPartial dump:\n"; static final String ROOT_MSG_FIELD = "\nRoot MsgField:\n"; static final String THE_MSG_FIELD = "The MsgField '"; /** * The builder state. Contains an object created from the {@link #msgField} template. This field will be a part * of object graph for packing and unpacking this graph from / to a byte array. You can show this graph by calling * the {@link com.credibledoc.iso8583packer.dump.DumpService#dumpMsgValue(MsgField, MsgValue, boolean)} method. */ protected MsgValue msgValue; /** * The builder state. Contains an object created by the {@link FieldBuilder} builder. It contains a graph of * {@link MsgField}s connected to each other. This graph defines rules of packing and unpacking of bytes from / to * a {@link #msgValue} object graph. You can show this graph by calling * the {@link com.credibledoc.iso8583packer.dump.DumpService#dumpMsgField(MsgField)} method. */ protected MsgField msgField; protected Navigator navigator; protected Visualizer visualizer; /** * Please do not create instances of this builder. It uses for internal purposes only, * see the {@link #newInstance(MsgField)} method. */ protected ValueHolder() { // empty } /** * Create a new instance of {@link MsgValue} from the {@link MsgField} definition for filling it with data. *

* Example of usage: *

     *     ValueHolder valueHolder = ValueHolder.newInstance(isoMsgField);
     * 
* * How to fill the data to the {@link FieldBuilder}? See the following example: *
     *     String mtiValue = "0200";
     *     valueHolder.jumpToChild(MTI_NAME).setValue(mtiValue);
     * 
* * @param definition existing definition created with {@link FieldBuilder}. * @return A new instance of the {@link ValueHolder} with {@link #msgValue} and {@link #msgField} in its context. */ public static ValueHolder newInstance(MsgField definition) { ValueHolder valueHolder = new ValueHolder(); valueHolder.createDefaultServices(); return valueHolder.setValueAndField(definition); } /** * Create a new instance of {@link MsgValue} from the {@link FieldBuilder#getCurrentField()} value. *

* See also the {@link #newInstance(MsgField)} method description. * * @param fieldBuilder the existing {@link FieldBuilder} with the {@link FieldBuilder#msgField} value. * @return A new instance of the {@link ValueHolder} with {@link #msgValue} and {@link #msgField} in its context. */ public static ValueHolder newInstance(FieldBuilder fieldBuilder) { return newInstance(fieldBuilder.getCurrentField()); } /** * Create instances of services used in the builder. The method may be overridden if needed. */ protected void createDefaultServices() { navigator = NavigatorService.getInstance(); visualizer = DumpService.getInstance(); visualizer.setNavigator(navigator); navigator.setVisualizer(visualizer); } /** * Set the instances of {@link MsgValue} and {@link MsgField} from the definition tho the {@link ValueHolder} instance. * * @param definition existing definition created with {@link FieldBuilder}. * @return A new instance of this {@link ValueHolder} with {@link #msgValue} and {@link #msgField} in its context. */ protected ValueHolder setValueAndField(MsgField definition) { this.msgField = definition; this.msgValue = navigator.newFromNameAndTag(definition); return this; } /** * Create a new instance of {@link ValueHolder}. * * @param msgValue will be set to the {@link ValueHolder#msgValue}. * @param msgField will be set to the {@link ValueHolder#msgField}. * @return The new created {@link ValueHolder} with the {@link #msgValue} and {@link #msgField} in its context. */ public static ValueHolder newInstance(MsgValue msgValue, MsgField msgField) { ValueHolder valueHolder = new ValueHolder(); valueHolder.createDefaultServices(); return valueHolder.setValueAndField(msgValue, msgField); } /** * Set the arguments to the current instance of {@link ValueHolder}. * * @param msgValue will be set to the {@link ValueHolder#msgValue}. * @param msgField will be set to the {@link ValueHolder#msgField}. * @return The current instance of {@link ValueHolder} with the {@link #msgValue} and {@link #msgField} in its context. */ protected ValueHolder setValueAndField(MsgValue msgValue, MsgField msgField) { try { this.msgValue = msgValue; this.msgField = msgField; if (this.msgValue.getName() == null) { this.msgValue.setName(msgField.getName()); } return this; } catch (Exception e) { MsgField rootMsgField = navigator.findRoot(msgField); throw new PackerRuntimeException("Exception in method get(msgValue, msgField): " + e.getMessage() + "\n" + "\nThe root MsgField:\n" + visualizer.dumpMsgField(rootMsgField) + "\nMsgValue:\n" + visualizer.dumpMsgValue(msgField, msgValue, true) + "\n", e); } } /** * Unpack the bytes started from offset to a new {@link MsgField} using its definition from the third argument. * @param bytes the source bytes. * @param offset the index where the field starts in the bytes. * @param msgField the definition of the {@link MsgField} structure. * @return The unpacked {@link MsgValue}. */ public static MsgValue unpack(byte[] bytes, int offset, MsgField msgField) { ValueHolder valueHolder = newInstance(msgField); return valueHolder.unpackMsgField(bytes, offset); } /** * Unpack the bytes started from offset to the {@link #msgField} in the context. * @param bytes the source bytes. * @param offset the index where the field starts in the bytes. * @return The unpacked {@link MsgValue}. */ protected MsgValue unpackMsgField(byte[] bytes, int offset) { MsgValue newMsgValue = navigator.newFromNameAndTag(msgField); try { Offset offsetObject = new Offset(); offsetObject.setValue(offset); MsgPair msgPair = new MsgPair(msgField, newMsgValue); unpackFieldRecursively(bytes, offsetObject, msgPair); msgValue = msgPair.getMsgValue(); msgField = msgPair.getMsgField(); return newMsgValue; } catch (Exception e) { String dump = PARTIAL_DUMP + visualizer.dumpMsgValue(msgField, newMsgValue, true) + ROOT_MSG_FIELD + visualizer.dumpMsgField(navigator.findRoot(msgField)); throw new PackerRuntimeException("Cannot unpack field: " + navigator.generatePath(newMsgValue) + dump, e); } } /** * @param bytes the source bytes. * @param offset the index where the field starts in the bytes. * @param msgPair the definition of the {@link MsgField} structure and the field values. */ protected void unpackFieldRecursively(byte[] bytes, Offset offset, MsgPair msgPair) { navigator.validateSameNamesAndTags(msgPair); Integer rawDataLength; MsgFieldType msgFieldType = msgPair.getMsgField().getType(); if (MsgFieldType.MSG == msgFieldType) { if (isValType(msgPair.getMsgField().getChildren())) { rawDataLength = sumChildrenLen(msgPair.getMsgField().getChildren()); } else { rawDataLength = bytes.length - offset.getValue(); } } else if (msgFieldType == MsgFieldType.BIT_SET) { rawDataLength = unpackBitSet(bytes, offset, msgPair); } else if (MsgFieldType.VAL == msgFieldType) { rawDataLength = unpackFixedLengthType(bytes, offset, msgPair); } else { rawDataLength = unpackOtherTypes(bytes, offset, msgPair); } if (msgPair.getMsgField().getChildren() == null) { // set value, but for leaves (outer children) only. setValueToLeaf(bytes, offset, msgPair, rawDataLength); } else { unpackChildren(bytes, offset, msgPair, rawDataLength); return; } offset.add(rawDataLength); } private int sumChildrenLen(List children) { int result = 0; for (MsgField child : children) { result = result + child.getLen(); } return result; } private boolean isValType(List children) { for (MsgField child : children) { if (child.getType() != MsgFieldType.VAL) { return false; } } return true; } protected Integer unpackOtherTypes(byte[] bytes, Offset offset, MsgPair msgPair) { Integer rawDataLength = null; TagPacker tagPacker = navigator.getTagPacker(msgPair.getMsgField()); int tagPackedLength = tagPacker == null ? 0 : tagPacker.getPackedLength(); boolean lengthFirst = MsgFieldType.getLengthFirstTypes().contains(msgPair.getMsgField().getType()); Object tag = null; if (lengthFirst && MsgFieldType.isLengthType(msgPair.getMsgField())) { rawDataLength = unpackLength(bytes, offset, msgPair) - tagPackedLength; } if (MsgFieldType.getTaggedTypes().contains(msgPair.getMsgField().getType())) { tag = unpackTag(bytes, offset, msgPair); } boolean tagUnpackedButIsDifferent = tag != null && !Objects.equals(tag, msgPair.getMsgField().getTag()); if (tagUnpackedButIsDifferent) { replaceWithSibling(msgPair, tag); } if (MsgFieldType.getTaggedTypes().contains(msgPair.getMsgField().getType())) { unpackTagBytes(bytes, offset, msgPair, tagPackedLength, tag); } if (!lengthFirst && MsgFieldType.isLengthType(msgPair.getMsgField())) { rawDataLength = unpackLength(bytes, offset, msgPair); } if (msgPair.getMsgField().getLen() != null) { rawDataLength = msgPair.getMsgField().getLen(); } if (rawDataLength == null) { throw new PackerRuntimeException("Cannot find rawDataLength of the msgField with path '" + navigator.getPathRecursively(msgPair.getMsgField()) + "'"); } // unpack field body unpackBodyBytes(bytes, offset, msgPair, rawDataLength); return rawDataLength; } protected Integer unpackFixedLengthType(byte[] bytes, Offset offset, MsgPair msgPair) { Integer rawDataLength; MsgField currentMsgField = msgPair.getMsgField(); rawDataLength = currentMsgField.getLen(); if (rawDataLength == null) { rawDataLength = bytes.length - offset.getValue(); } int remaining = bytes.length - offset.getValue(); if (rawDataLength > remaining) { throw new PackerRuntimeException("Cannot unpack bytes because the remaining data length '" + remaining + "' of the byte array is less than defined 'len' value '" + rawDataLength + "' of the MsgField with path '" + navigator.getPathRecursively(currentMsgField) + "'."); } unpackBodyBytes(bytes, offset, msgPair, rawDataLength); return rawDataLength; } protected LengthPacker getLengthPackerFromParentOrSelfOrThrowException(MsgField msgField) { boolean parentContainsChildrenLengthPacker = msgField.getParent() != null && msgField.getParent().getChildrenLengthPacker() != null; if (parentContainsChildrenLengthPacker) { return msgField.getParent().getChildrenLengthPacker(); } if (msgField.getLengthPacker() == null) { throw new PackerRuntimeException("Property lengthPacker is not defined. Please define it by calling " + "the .defineHeaderLengthPacker() method. " + "Current MsgField: " + navigator.generatePath(msgField)); } return msgField.getLengthPacker(); } protected void unpackBodyBytes(byte[] bytes, Offset offset, MsgPair msgPair, Integer rawDataLength) { try { byte[] rawData = new byte[rawDataLength]; System.arraycopy(bytes, offset.getValue(), rawData, 0, rawData.length); msgPair.getMsgValue().setBodyBytes(rawData); } catch (Exception e) { String path = navigator.getPathRecursively(msgPair.getMsgField()); throw new PackerRuntimeException("Current MsgField: '" + path + "', source bytes length: '" + bytes.length + "', offset: '" + offset.getValue() + "', rawDataLength: '" + rawDataLength + "'", e); } } protected void unpackChildren(byte[] bytes, Offset offset, MsgPair msgPair, Integer rawDataLength) { int offsetWithChildren = offset.getValue() + rawDataLength; if (rawDataLength > 0) { msgPair.getMsgValue().setChildren(new ArrayList<>()); } int childNum = 0; while (offset.getValue() < offsetWithChildren) { List children = msgPair.getMsgField().getChildren(); MsgField msgFieldFirstChild = children.get(childNum); if (children.size() > childNum + 1) { childNum++; } MsgValue msgValueChild = navigator.newFromNameAndTag(msgFieldFirstChild); msgPair.getMsgValue().getChildren().add(msgValueChild); msgValueChild.setParent(msgPair.getMsgValue()); MsgPair msgPairChild = new MsgPair(msgFieldFirstChild, msgValueChild); unpackFieldRecursively(bytes, offset, msgPairChild); } if (offset.getValue() != offsetWithChildren) { throw new PackerRuntimeException("Expected end of children is '" + offsetWithChildren + "' but current offset is '" + offset.getValue() + "'. These values should be equal."); } } protected void unpackTagBytes(byte[] bytes, Offset offset, MsgPair msgPair, Integer fieldTagLength, Object tag) { byte[] tagBytes = new byte[fieldTagLength]; System.arraycopy(bytes, offset.getValue(), tagBytes, 0, tagBytes.length); offset.add(fieldTagLength); msgPair.getMsgValue().setTagBytes(tagBytes); msgPair.getMsgValue().setTag(tag); } protected Object unpackTag(byte[] bytes, Offset offset, MsgPair msgPair) { Object tag; TagPacker tagPacker = navigator.getTagPacker(msgPair.getMsgField()); if (tagPacker == null) { tag = msgPair.getMsgField().getTag(); } else { tag = tagPacker.unpack(bytes, offset.getValue()); } return tag; } protected int unpackBitSet(byte[] bytes, Offset offset, MsgPair msgPair) { List fieldNums = getFieldNumsFromBitSet(bytes, offset, msgPair); for (int nextFieldNum : fieldNums) { MsgField msgFieldChild = findChildByFieldNum(msgPair.getMsgField(), nextFieldNum); MsgValue msgValueChild = navigator.newFromNameAndTag(msgFieldChild); List children = msgPair.getMsgValue().getChildren(); if (children == null) { children = new ArrayList<>(); msgPair.getMsgValue().setChildren(children); } children.add(msgValueChild); msgValueChild.setParent(msgPair.getMsgValue()); MsgPair msgPairChild = new MsgPair(msgFieldChild, msgValueChild); unpackFieldRecursively(bytes, offset, msgPairChild); } return 0; } protected int unpackLength(byte[] bytes, Offset offset, MsgPair msgPair) { LengthPacker lengthPacker = getLengthPackerFromParentOrSelfOrThrowException(msgPair.getMsgField()); int lenLength = lengthPacker.calculateLenLength(bytes, offset.getValue()); byte[] lengthBytes = new byte[lenLength]; System.arraycopy(bytes, offset.getValue(), lengthBytes, 0, lengthBytes.length); msgPair.getMsgValue().setLengthBytes(lengthBytes); int rawDataLength = lengthPacker.unpack(bytes, offset.getValue()); offset.add(lenLength); return rawDataLength; } protected void setValueToLeaf(byte[] bytes, Offset offset, MsgPair msgPair, int bodyBytesLength) { BodyPacker bodyPacker = msgPair.getMsgField().getBodyPacker(); if (bodyPacker == null) { String path = navigator.getPathRecursively(msgPair.getMsgField()); throw new PackerRuntimeException("BodyPacker not found for MsgField with path '" + path + "'. " + "Please call the defineBodyPacker(...) method " + "of the FieldBuilder class, for example " + "MsgField subfield35 = FieldBuilder.builder(...).defineBodyPacker(...)."); } Object bodyValue = bodyPacker.unpack(bytes, offset.getValue(), bodyBytesLength); msgPair.getMsgValue().setBodyValue(bodyValue); } protected void replaceWithSibling(MsgPair msgPair, Object tag) { MsgPair result = new MsgPair(); MsgField msgFieldSibling = findSiblingByTag(tag, msgPair.getMsgField()); if (msgFieldSibling == null) { throwSiblingNotFound(msgPair, tag); } MsgValue oldMsgValue = msgPair.getMsgValue(); MsgValue parentMsgValue = oldMsgValue.getParent(); result.setMsgField(msgFieldSibling); assert msgFieldSibling != null; MsgValue newMsgValue = navigator.newFromNameAndTag(msgFieldSibling); if (parentMsgValue != null) { parentMsgValue.getChildren().remove(oldMsgValue); parentMsgValue.getChildren().add(newMsgValue); newMsgValue.setParent(parentMsgValue); } result.setMsgValue(newMsgValue); newMsgValue.setBitSet(oldMsgValue.getBitSet()); newMsgValue.setTagBytes(oldMsgValue.getTagBytes()); newMsgValue.setLengthBytes(oldMsgValue.getLengthBytes()); msgPair.setMsgField(result.getMsgField()); msgPair.setMsgValue(result.getMsgValue()); } protected List getFieldNumsFromBitSet(byte[] bytes, Offset offset, MsgPair msgPair) { // unpack BitmapPacker bitmapPacker = msgPair.getMsgField().getBitMapPacker(); if (bitmapPacker == null) { throw new PackerRuntimeException("Please call the defineHeaderBitmapPacker(...) " + "method for this field " + navigator.getPathRecursively(msgPair.getMsgValue())); } int consumed = bitmapPacker.unpack(msgPair.getMsgValue(), bytes, offset.getValue()); byte[] bitMapBytes = new byte[consumed]; System.arraycopy(bytes, offset.getValue(), bitMapBytes, 0, consumed); offset.add(consumed); msgPair.getMsgValue().setBodyBytes(bitMapBytes); return getFieldNumsAndValidateBitSet(msgPair); } protected MsgField findChildByFieldNum(MsgField msgField, int nextFieldNum) { for (MsgField child : msgField.getChildren()) { if (nextFieldNum == child.getFieldNum()) { return child; } } throw new PackerRuntimeException("Cannot find child with fieldNum '" + nextFieldNum + "' in the msgField " + navigator.getPathRecursively(msgField)); } protected List getFieldNumsAndValidateBitSet(MsgPair msgPair) { BitSet unpackedBitSet = msgPair.getMsgValue().getBitSet(); List fieldNums = new ArrayList<>(); int maxFieldNum = getMaxFieldNum(msgPair.getMsgField().getChildren(), msgPair.getMsgField()); // first bit in a bitmap is used as flag of additional bitmaps for (int nextFieldNum = 2; nextFieldNum <= maxFieldNum; nextFieldNum++) { MsgField childMsgField = navigator.findByFieldNum(msgPair.getMsgField().getChildren(), nextFieldNum); if (unpackedBitSet.get(nextFieldNum) && childMsgField == null) { String path = navigator.getPathRecursively(msgPair.getMsgField()); throw new PackerRuntimeException("Unpacked bitSet contains fieldNum '" + nextFieldNum + "', " + "but the MsgField with path '" + path + "' has no child with such fieldNum. " + "Please set the defineFieldNum(" + nextFieldNum + ") value " + "on one of the field '" + path + "' children."); } if (unpackedBitSet.get(nextFieldNum)) { fieldNums.add(nextFieldNum); } } return fieldNums; } protected void throwSiblingNotFound(MsgPair paramMsgPair, Object tag) { MsgField paramMsgField = paramMsgPair.getMsgField(); MsgValue paramMsgValue = paramMsgPair.getMsgValue(); String previousFieldCause = ""; MsgField parentMsgField = paramMsgField.getParent(); if (parentMsgField != null && parentMsgField.getLengthPacker() != null) { previousFieldCause = " \nNext cause is incorrect implementation of the " + parentMsgField.getLengthPacker().getClass().getSimpleName() + " class of the previous '" + navigator.getPathRecursively(parentMsgField) + "' field."; } String tagPackerClass = "null"; TagPacker tagPacker = navigator.getTagPacker(paramMsgField); if (tagPacker != null) { tagPackerClass = tagPacker.getClass().getSimpleName(); } throw new PackerRuntimeException("Cannot find the sibling with tag '" + tag + "' for the '" + navigator.getPathRecursively(paramMsgValue) + "' Field. Its parent '" + navigator.getPathRecursively(parentMsgField) + "' has no child with such tag. \nThere are few possible causes of this error. " + "\nFirst cause is incorrect implementation of " + tagPackerClass + ".unpack() method used for unpacking tag from bytes. " + "\nSecond cause is undefined child with tag '" + tag + "' and with parent '" + navigator.getPathRecursively(parentMsgField) + "' in the MsgField definition." + previousFieldCause + " \nNext cause can be the order of tag and " + "length subfields where some MsgFields have the first tag and the second length but other " + "MsgFields vise versa, for example F0F0F3 F9F3 F0, where F0F0F3 is the length 003, F9F3 is " + "the tag 93 and F0 is the body." + " \nNext cause is wrong place of actual context inside the rootMsgField, actual place is " + navigator.getPathRecursively(paramMsgField)); } protected int getMaxFieldNum(List children, MsgField parent) { int result = 0; for (MsgField nextMsgField : children) { Integer fieldNum = nextMsgField.getFieldNum(); if (fieldNum == null) { String path = navigator.getPathRecursively(nextMsgField); throw new PackerRuntimeException("The MsgField with path '" + path + "' " + "has no 'fieldNum' property defined. " + "Please call the defineFieldNum(...) value to the MsgField. " + "The value is mandatory because its parent has the '" + MsgFieldType.class.getSimpleName() + "." + parent.getType() + "' type."); } if (fieldNum > result) { result = fieldNum; } } return result; } protected MsgField findSiblingByTag(Object tag, MsgField msgField) { MsgField parent = msgField.getParent(); for (MsgField child : parent.getChildren()) { if (Objects.equals(child.getTag(), tag)) { return child; } } return null; } /** * Create a new instance of {@link ValueHolder} and set msgPair values to its * {@link #msgField} and {@link #msgValue} context objects. * @param msgPair contains {@link MsgField} and {@link MsgValue}. * @return The created instance. */ public static ValueHolder newInstance(MsgPair msgPair) { ValueHolder valueHolder = new ValueHolder(); valueHolder.createDefaultServices(); valueHolder.msgField = msgPair.getMsgField(); valueHolder.msgValue = msgPair.getMsgValue(); return valueHolder; } /** * Change the current {@link #msgField} and {@link #msgValue} to the new location in the {@link #msgField} graph. *

* The new location will be the child with given name from {@link #msgField} * and the first child of the {@link #msgValue} with the name. *

* If the current {@link #msgValue} doesn't have the child, it will be created. Else the found child will be set * tho the {@link #msgValue} field. * * @param childName the {@link #msgField}'s child. * @return The current instance of {@link ValueHolder}. */ public ValueHolder jumpToChild(String childName) { try { MsgField msgFieldChild = navigator.getChildOrThrowException(childName, msgField); MsgValue msgValueChild = navigator.findByName(msgValue.getChildren(), childName); if (msgValueChild == null) { msgValueChild = navigator.newFromNameAndTag(msgFieldChild); msgValueChild.setParent(msgValue); if (msgValue.getChildren() == null) { msgValue.setChildren(new ArrayList<>()); } msgValue.getChildren().add(msgValueChild); } this.msgValue = msgValueChild; this.msgField = msgFieldChild; return this; } catch (Exception e) { MsgField rootMsgField = navigator.findRoot(msgField); throw new PackerRuntimeException("Exception message: " + e.getMessage() + "\nCannot find a child." + ROOT_MSG_FIELD + visualizer.dumpMsgField(rootMsgField), e); } } /** * Change actual {@link #msgField} and {@link #msgValue} to their parents. * @return The current instance of {@link ValueHolder}. */ public ValueHolder jumpToParent() { if (msgField.getParent() == null) { throw new PackerRuntimeException("MsgField '" + navigator.getPathRecursively(msgField) + "' has no parent."); } if (msgValue.getParent() == null) { throw new PackerRuntimeException("MsgValue '" + navigator.getPathRecursively(msgValue) + "' has no parent."); } msgField = msgField.getParent(); msgValue = msgValue.getParent(); return this; } /** * Set the {@link MsgValue#setBodyValue(Object)} from the argument to the {@link #msgValue}. *

* Example of usage: *

     *     ValueHolder.newInstance(msgPair)
     *             .jumpToChild("child_name")
     *             .setValue("some_value");
     * 
* *

* Set the {@link MsgValue#setBodyBytes(byte[])} from the bodyValue to the field, see the * {@link #setBytes(Object)} method description. *

* Length and tag are not mandatory. * * @param bodyValue can be 'null' for d. * @return The current {@link ValueHolder} with the same {@link #msgValue} and {@link #msgField} in its context. */ public ValueHolder setValue(Object bodyValue) { if (msgField.getChildren() != null && !msgField.getChildren().isEmpty()) { throw new PackerRuntimeException("Cannot set bodyValue to fields with children. Values can only be set to " + "leaf fields. Field: " + navigator.getPathRecursively(msgField) + ", bodyValue: " + bodyValue); } if (bodyValue == null) { msgValue.setBodyBytes(null); msgValue.setLengthBytes(null); msgValue.setBodyValue(null); return this; } try { msgValue.setBodyValue(bodyValue); byte[] valueBytes = setBytes(bodyValue); setTagAndLenBytes(valueBytes, msgValue, msgField); return this; } catch (Exception e) { MsgValue rootMsgValue = navigator.findRoot(msgValue); MsgField rootMsgField = navigator.findRoot(msgField); MsgField appropriateMsgField = navigator.findByNameAndTagOrThrowException(rootMsgField, rootMsgValue); throw new PackerRuntimeException("Exception message: " + e.getMessage() + "\nCannot set bodyValue '" + bodyValue + "' to field '" + navigator.getPathRecursively(msgField) + "'" + "\nRoot MsgValue:\n" + visualizer.dumpMsgValue(appropriateMsgField, rootMsgValue, true) + "\nThe MsgField:\n" + visualizer.dumpMsgField(msgField) + ROOT_MSG_FIELD + visualizer.dumpMsgField(rootMsgField), e); } } protected byte[] setBytes(Object bodyValue) { BodyPacker bodyPacker = msgField.getBodyPacker(); if (bodyPacker == null) { throw new PackerRuntimeException("BodyPacker not found. Please call setBodyPacker(...) " + "method, for example " + "MsgField subfield35 = FieldBuilder.builder().BodyPacker(HexBodyPacker.getInstance())...\n" + "MsgField: " + navigator.getPathRecursively(msgField)); } byte[] bodyBytes; int bodyLength = bodyPacker.getPackedLength(bodyValue); Integer exactlyLength = msgField.getExactlyLength(); if (exactlyLength != null && bodyLength != exactlyLength) { throw new PackerRuntimeException(THE_MSG_FIELD + navigator.getPathRecursively(msgField) + "' contains the 'exactlyLength' definition with value '" + exactlyLength + "', but the bodyValue length '" + bodyLength + "' is not the same."); } Integer maxLen = msgField.getMaxLen(); if (maxLen != null && maxLen < bodyLength) { throw new PackerRuntimeException(THE_MSG_FIELD + navigator.getPathRecursively(msgField) + "' contains the 'maxLen' definition with value '" + maxLen + "', but its bodyValue length '" + bodyLength + "' is greater."); } Integer len = msgField.getLen(); if (len != null && bodyLength != len) { throw new PackerRuntimeException(THE_MSG_FIELD + navigator.getPathRecursively(msgField) + "' contains the 'len' definition with value '" + len + "', but its bodyValue length '" + bodyLength + "' is different."); } bodyBytes = new byte[bodyLength]; bodyPacker.pack(bodyValue, bodyBytes, 0); msgValue.setBodyBytes(bodyBytes); return bodyBytes; } /** * Set valueBytes to the msgValue * @param valueBytes source data * @param msgValue target object * @param msgField the field definition */ protected void setTagAndLenBytes(byte[] valueBytes, MsgValue msgValue, MsgField msgField) { MsgField msgFieldParent = msgField.getParent(); LengthPacker lengthPacker; Object tag = this.msgValue.getTag(); if (msgField.getType() == MsgFieldType.LEN_TAG_VAL) { // field parent contains lengthPacker, hence length precedes tag assert msgFieldParent != null; lengthPacker = msgFieldParent.getChildrenLengthPacker(); packTagBytes(msgField, msgValue, tag); int tagAndValueLength = valueBytes.length + msgValue.getTagBytes().length; byte[] lengthBytes = lengthPacker.pack(tagAndValueLength); msgValue.setLengthBytes(lengthBytes); } else { // field itself contains lengthPacker lengthPacker = msgField.getLengthPacker(); if (MsgFieldType.getTaggedTypes().contains(msgField.getType())) { packTagBytes(msgField, msgValue, tag); } if (lengthPacker != null) { byte[] lengthBytes = lengthPacker.pack(valueBytes.length); msgValue.setLengthBytes(lengthBytes); } } } protected void packTagBytes(MsgField msgField, MsgValue msgValue, Object tag) { TagPacker tagPacker = navigator.getTagPacker(msgField); if (tagPacker == null) { msgValue.setTagBytes(new byte[0]); return; } byte[] tagBytes = tagPacker.pack(tag); msgValue.setTagBytes(tagBytes); } /** * Change the current {@link #msgValue} and {@link #msgField} to the new location. If the field sibling is not found, * create a new one. * * @param siblingName the sibling name of the current {@link #msgValue} and {@link #msgField}. * @return The current builder instance with the new {@link #msgValue} and {@link #msgField}. */ public ValueHolder jumpToSibling(String siblingName) { try { MsgField msgFieldSibling = navigator.getSiblingOrThrowException(siblingName, msgField); MsgValue parentMsgValue = msgValue.getParent(); MsgValue siblingMsgValue = navigator.findByName(parentMsgValue.getChildren(), siblingName); if (siblingMsgValue == null) { siblingMsgValue = navigator.newFromNameAndTag(msgFieldSibling); siblingMsgValue.setParent(msgValue.getParent()); msgValue.getParent().getChildren().add(siblingMsgValue); sortFieldChildren(msgValue.getParent(), msgField.getParent()); } msgValue = siblingMsgValue; msgField = msgFieldSibling; return this; } catch (Exception e) { MsgValue rootMsgValue = navigator.findRoot(msgValue); MsgField rootMsgField = navigator.findRoot(msgField); throw new PackerRuntimeException("Exception: " + e.getMessage() + "\nCannot jumpToSibling '" + siblingName + "' of the message definition." + "\nMsgValue:\n" + visualizer.dumpMsgValue(rootMsgField, rootMsgValue, true) + "\nMsgField:\n" + visualizer.dumpMsgField(rootMsgField) + "\n", e); } } /** * @param msgValue to be sorted * @param msgField how to sort */ protected void sortFieldChildren(MsgValue msgValue, MsgField msgField) { List msgValueChildren = msgValue.getChildren(); List msgFieldChildren = msgField.getChildren(); List result = new ArrayList<>(msgValueChildren.size()); for (MsgField def : msgFieldChildren) { String name = def.getName(); MsgValue msgFieldChild = remove(name, msgValueChildren); while (msgFieldChild != null) { result.add(msgFieldChild); msgFieldChild = remove(name, msgValueChildren); } if (msgValueChildren.isEmpty()) { msgValue.setChildren(result); return; } } throw new PackerRuntimeException("Cannot find fields in MsgValue. Fields: " + msgValueChildren + ""); } protected MsgValue remove(String name, List msgValueChildren) { for (MsgValue child : msgValueChildren) { if (name.equals(child.getName())) { msgValueChildren.remove(child); return child; } } return null; } /** * @return The {@link #msgValue} value; */ public MsgValue getCurrentMsgValue() { return msgValue; } /** * @return The {@link #msgField} value; */ public MsgField getCurrentMsgField() { return msgField; } /** * Add subfields to the current {@link #msgValue}. Old children will be deleted. * * @param subfields new children * @return The current instance with {@link #msgValue} and {@link #msgField} in its context. */ public ValueHolder setChildren(List subfields) { for (MsgValue child : subfields) { child.setParent(msgValue); } msgValue.setChildren(subfields); return this; } /** * Navigate to the root {@link #msgValue} and {@link #msgField}. * @return The current instance of {@link ValueHolder} with root of {@link #msgValue} and corresponding node of * {@link #msgField} in its context. */ public ValueHolder jumpToRoot() { msgValue = navigator.findRoot(msgValue); msgField = navigator.findByNameAndTagOrThrowException(msgField, msgValue); return this; } /** * Pack this {@link #msgValue} to bytes for sending to Host. * @return Bytes created from the {@link #msgValue}. */ public byte[] pack() { try { ByteArrayOutputStream result = packRecursively(msgValue, msgField); return result.toByteArray(); } catch (Exception e) { MsgValue rootMsgValue = navigator.findRoot(msgValue); MsgField rootMsgField = navigator.findRoot(msgField); throw new PackerRuntimeException("Exception: " + e.getMessage() + "\n" + "Cannot pack field '" + navigator.getPathRecursively(msgValue) + "'" + PARTIAL_DUMP + visualizer.dumpMsgValue(rootMsgField, rootMsgValue, true) + "\nMsgField:\n" + visualizer.dumpMsgField(msgField) + "\n", e); } } protected ByteArrayOutputStream packRecursively(MsgValue msgValue, MsgField msgField) throws IOException { int maxLen = msgField.getMaxLen() != null ? msgField.getMaxLen() : INITIAL_SIZE_100_BYTES; ByteArrayOutputStream messageBytes = new ByteArrayOutputStream(maxLen); if (msgValue.getChildren() != null) { ByteArrayOutputStream childrenBytes = new ByteArrayOutputStream(maxLen); if (MsgFieldType.BIT_SET == msgField.getType()) { packBitmap(msgValue, msgField, messageBytes); } for (MsgValue nextMsgValue : msgValue.getChildren()) { MsgField msgFieldChild = navigator.findByName(msgField.getChildren(), nextMsgValue.getName()); ByteArrayOutputStream childArray = packRecursively(nextMsgValue, msgFieldChild); childArray.writeTo(childrenBytes); } byte[] bytes = childrenBytes.toByteArray(); if (bytes.length != 0) { packBodyBytesAndLengthAndTag(msgValue, msgField, messageBytes, bytes); } childrenBytes.writeTo(messageBytes); } else { if (msgValue.getBodyBytes() != null) { // Do not pack empty fields ByteArrayOutputStream headerAndValue = packHeaderAndValue(msgValue, msgField); headerAndValue.writeTo(messageBytes); } } return messageBytes; } protected void packBitmap(MsgValue msgValue, MsgField msgField, ByteArrayOutputStream messageBytes) throws IOException { BitmapPacker bitmapPacker = msgField.getBitMapPacker(); if (bitmapPacker == null) { throw new PackerRuntimeException("The value of '" + BitmapPacker.class.getSimpleName() + "' type is mandatory for '" + MsgFieldType.class.getSimpleName() + "' '" + MsgFieldType.BIT_SET + "' type. " + "Please call the defineBitmapPacker(...) method."); } BitSet bitSet = new BitSet(); for (MsgField nextMsgField : msgField.getChildren()) { if (navigator.findByFieldNum(msgValue.getChildren(), nextMsgField.getFieldNum()) != null) { bitSet.set(nextMsgField.getFieldNum()); } } msgValue.setBitSet(bitSet); byte[] bytes = bitmapPacker.pack(bitSet); msgValue.setBodyBytes(bytes); messageBytes.write(bytes); } protected void packBodyBytesAndLengthAndTag(MsgValue msgValue, MsgField msgField, ByteArrayOutputStream messageBytes, byte[] bytes) throws IOException { msgValue.setBodyBytes(bytes); LengthPacker lengthPacker; boolean lengthPrecedesTag = msgField.getType() == MsgFieldType.LEN_TAG_VAL; if (MsgFieldType.getTaggedTypes().contains(msgField.getType())) { setTagBytes(msgValue, msgField); } if (MsgFieldType.getLengthTypes().contains(msgField.getType())) { lengthPacker = getLengthPackerFromParentOrSelfOrThrowException(msgField); packLength(msgValue, lengthPacker, bytes, lengthPrecedesTag); } if (lengthPrecedesTag) { writeLengthBytesIfAllowed(msgValue, msgField, messageBytes); writeTagBytesIfAllowed(msgValue, msgField, messageBytes); } else { writeTagBytesIfAllowed(msgValue, msgField, messageBytes); writeLengthBytesIfAllowed(msgValue, msgField, messageBytes); } } protected void writeTagBytesIfAllowed(MsgValue msgValue, MsgField msgField, ByteArrayOutputStream messageBytes) throws IOException { if (MsgFieldType.getTaggedTypes().contains(msgField.getType())) { messageBytes.write(msgValue.getTagBytes()); } } protected void writeLengthBytesIfAllowed(MsgValue msgValue, MsgField msgField, ByteArrayOutputStream messageBytes) throws IOException { if (MsgFieldType.getLengthTypes().contains(msgField.getType())) { messageBytes.write(msgValue.getLengthBytes()); } } protected ByteArrayOutputStream packHeaderAndValue(MsgValue msgValue, MsgField msgField) throws IOException { if (msgValue.getBodyBytes() == null) { throw new PackerRuntimeException("Expected non-null MsgValue.bodyBytes but found 'null'. MsgValue path: '" + navigator.getPathRecursively(msgValue) + "'. Please set the value or delete the MsgField. " + "The cause of the exception probably in the setValue() method."); } int length = msgValue.getBodyBytes().length; ByteArrayOutputStream result; length += msgValue.getTagBytes() == null ? 0 : msgValue.getTagBytes().length; length += msgValue.getLengthBytes() == null ? 0 : msgValue.getLengthBytes().length; result = new ByteArrayOutputStream(length); boolean lengthPrecedesTag = msgField.getType() == MsgFieldType.LEN_TAG_VAL; if (lengthPrecedesTag) { writeLengthBytesIfExist(result, msgValue); writeTagBytes(result, msgValue); } else { writeTagBytes(result, msgValue); writeLengthBytesIfExist(result, msgValue); } if (msgValue.getBodyBytes() != null) { result.write(msgValue.getBodyBytes()); } return result; } protected void writeTagBytes(ByteArrayOutputStream result, MsgValue msgValue) throws IOException { if (msgValue.getTagBytes() != null) { result.write(msgValue.getTagBytes()); } } protected void writeLengthBytesIfExist(ByteArrayOutputStream result, MsgValue msgValue) throws IOException { if (msgValue.getLengthBytes() != null) { result.write(msgValue.getLengthBytes()); } } protected void packLength(MsgValue msgValue, LengthPacker lengthPacker, byte[] bytes, boolean lengthPrecedesTag) { int bytesLength; if (lengthPrecedesTag) { bytesLength = bytes.length + msgValue.getTagBytes().length; } else { bytesLength = bytes.length; } byte[] lengthBytes = lengthPacker.pack(bytesLength); msgValue.setLengthBytes(lengthBytes); } protected void setTagBytes(MsgValue msgValue, MsgField msgField) { Object tag = msgValue.getTag(); TagPacker tagPacker = navigator.getTagPacker(msgField); assert tagPacker != null; byte[] tagBytes = tagPacker.pack(tag); msgValue.setTagBytes(tagBytes); } /** * Create a copy from the current {@link #msgValue} and set it to this {@link #msgValue} context. * This sibling will have the same {@link MsgField#getName()} and {@link MsgField#getFieldNum()} as its sibling. *

* It is useful for creation of multiple repeated fields with the same name and/or fieldNum. * * @return The current actual {@link ValueHolder}. */ public ValueHolder cloneSibling() { MsgValue clone = navigator.newFromNameAndTag(msgField); clone.setParent(msgValue.getParent()); msgValue.getParent().getChildren().add(clone); msgValue = clone; return this; } /** * Create the new instance and set {@link #msgValue} and {@link #msgField} from the current instance. * * @return Created instance of {@link ValueHolder}. */ public ValueHolder copyValueHolder() { ValueHolder clone = new ValueHolder(); clone.createDefaultServices(); clone.msgValue = msgValue; clone.msgField = msgField; return clone; } /** * Call the {@link #unpack(byte[], int, MsgField)} method with offset 0 and with current {@link #msgField} from the * {@link ValueHolder} context. * * @param bytes will be set as first argument * @return The {@link MsgValue} unpacked from the bytes. */ public MsgValue unpack(byte[] bytes) { unpackMsgField(bytes, 0); return msgValue; } /** * @return Existing {@link #msgField} and {@link #msgValue} created previously by the {@link #newInstance(MsgField)} method. */ public MsgPair getCurrentPair() { return new MsgPair(this.msgField, this.msgValue); } public void validateData() { try { validateRecursively(msgField, msgValue); List msgFieldChildren = msgField.getChildren(); if (msgFieldChildren != null) { for (MsgField childMsgField : msgFieldChildren) { MsgValue childMsgValue = navigator.findByName(msgValue.getChildren(), childMsgField.getName()); if (childMsgValue != null) { validateRecursively(childMsgField, childMsgValue); } } } } catch (Exception e) { String dump = PARTIAL_DUMP + visualizer.dumpMsgValue(msgField, msgValue, true) + ROOT_MSG_FIELD + visualizer.dumpMsgField(navigator.findRoot(msgField)); throw new PackerRuntimeException("Validation failed: " + dump, e); } } protected void validateRecursively(MsgField msgField, MsgValue msgValue) { if (msgField.getBodyPacker() instanceof BcdBodyPacker) { BcdService.validateIsStringBcdNumber(msgValue); } } /** * @param type expected type of returned value * @param expected type of returned value * @return The {@link MsgValue#getBodyValue()} casted to T type */ public T getValue(Class type) { return type.cast(msgValue.getBodyValue()); } /** * @param navigator see the {@link #navigator} field description. */ public void setNavigator(Navigator navigator) { this.navigator = navigator; } /** * @param visualizer see the {@link #visualizer} field description. */ public void setVisualizer(Visualizer visualizer) { this.visualizer = visualizer; } /** * Change the actual {@link #msgValue} object graph place (location) to be the same as the {@link #msgField}, * see the {@link Navigator#synchronizeMessageValue(MsgField, MsgValue)} method description. * * @return The current {@link ValueHolder} instance with adjusted {@link #msgValue}. */ public ValueHolder adjust() { msgValue = this.navigator.synchronizeMessageValue(msgField, msgValue); return this; } /** * Change the actual object graph places (locations) of the {@link #msgField} and {@link #msgValue}. * * @param fieldNames the {@link MsgField#getName()}s of fields from the root of the {@link #msgField}. * @return The current {@link ValueHolder} instance with a new place (location) of the {@link #msgValue}. */ public ValueHolder jumpAbsolute(String ... fieldNames) { jumpToRoot(); if (!msgField.getName().equals(fieldNames[0])) { String msgFieldDump = visualizer.dumpMsgField(msgField); throw new PackerRuntimeException("MsgField with name '" + fieldNames[0] + "' is not a root field. " + "Expected the root field name '" + msgField.getName() + "'." + "\nMsgField:\n" + msgFieldDump); } for (int i = 1; i < fieldNames.length; i++) { String name = fieldNames[i]; if (name == null) { throw new PackerRuntimeException("MsgField name cannot be 'null'. " + "FieldNames: " + Arrays.toString(fieldNames)); } try { jumpToChild(name); } catch (Exception e) { throw new PackerRuntimeException("Cannot find MsgField with path '" + Arrays.toString(fieldNames) + "'", e); } } return this; } /** * Call the {@link #jumpAbsolute(String...)} method. * * @param fieldNames the {@link MsgField#getName()}s of fields from the root of the {@link #msgField}. * @return The current {@link ValueHolder} instance with a new place (location) of the {@link #msgValue}. */ public ValueHolder jumpAbsolute(List fieldNames) { return jumpAbsolute(fieldNames.toArray(new String[0])); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy