diff --git a/src/main/java/com/solab/iso8583/IsoMessage.java b/src/main/java/com/solab/iso8583/IsoMessage.java index 0a6cf5a..062c0a7 100644 --- a/src/main/java/com/solab/iso8583/IsoMessage.java +++ b/src/main/java/com/solab/iso8583/IsoMessage.java @@ -22,11 +22,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; -import java.util.ArrayList; import java.util.BitSet; -import java.util.Collections; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; /** Represents an ISO8583 message. This is the core class of the framework. * Contains the bitmap which is modified as fields are added/removed. @@ -46,7 +42,7 @@ public class IsoMessage { /** Indicates if the message is binary-coded. */ private boolean binary; /** This is where the values are stored. */ - private Map> fields = new ConcurrentHashMap>(); + private IsoValue[] fields = new IsoValue[129]; /** Stores the optional ISO header. */ private String isoHeader; private int etx = -1; @@ -108,16 +104,13 @@ public class IsoMessage { * @param field The field number. 1 is the secondary bitmap and is not returned as such; * real fields go from 2 to 128. */ public Object getObjectValue(int field) { - IsoValue v = fields.get(field); - if (v == null) { - return null; - } - return v.getValue(); + IsoValue v = fields[field]; + return v == null ? null : v.getValue(); } /** Returns the IsoValue for the specified field. First real field is 2. */ public IsoValue getField(int field) { - return fields.get(field); + return fields[field]; } /** Stored the field in the specified index. The first field is the secondary bitmap and has index 1, @@ -126,11 +119,7 @@ public class IsoMessage { if (index < 2 || index > 128) { throw new IndexOutOfBoundsException("Field index must be between 2 and 128"); } - if (field == null) { - fields.remove(index); - } else { - fields.put(index, field); - } + fields[index] = field; } /** Sets the specified value in the specified field, creating an IsoValue internally. @@ -156,7 +145,7 @@ public class IsoMessage { throw new IndexOutOfBoundsException("Field index must be between 2 and 128"); } if (value == null) { - fields.remove(index); + fields[index] = null; } else { IsoValue v = null; if (t.needsLength()) { @@ -164,14 +153,14 @@ public class IsoMessage { } else { v = new IsoValue(t, value, encoder); } - fields.put(index, v); + fields[index] = v; } } /** Returns true is the message has a value in the specified field. * @param idx The field number. */ public boolean hasField(int idx) { - return fields.get(idx) != null; + return fields[idx] != null; } /** Writes a message to a stream, after writing the specified number of bytes indicating @@ -289,12 +278,11 @@ public class IsoMessage { } //Bitmap - ArrayList keys = new ArrayList(); - keys.addAll(fields.keySet()); - Collections.sort(keys); BitSet bs = new BitSet(forceb2 ? 128 : 64); - for (Integer i : keys) { - bs.set(i - 1); + for (int i = 2 ; i < 129; i++) { + if (fields[i] != null) { + bs.set(i - 1); + } } if (forceb2) { bs.set(0); @@ -338,12 +326,14 @@ public class IsoMessage { } //Fields - for (Integer i : keys) { - IsoValue v = fields.get(i); - try { - v.write(bout, binary); - } catch (IOException ex) { - //should never happen, writing to a ByteArrayOutputStream + for (int i = 2; i < 129; i++) { + IsoValue v = fields[i]; + if (v != null) { + try { + v.write(bout, binary); + } catch (IOException ex) { + //should never happen, writing to a ByteArrayOutputStream + } } } return bout.toByteArray(); diff --git a/src/main/java/com/solab/iso8583/IsoType.java b/src/main/java/com/solab/iso8583/IsoType.java index 133d5ff..a66a5da 100644 --- a/src/main/java/com/solab/iso8583/IsoType.java +++ b/src/main/java/com/solab/iso8583/IsoType.java @@ -114,6 +114,8 @@ public enum IsoType { } System.arraycopy(x, 0, c, lim, x.length); return new String(c); + } else if (this == AMOUNT) { + return IsoType.NUMERIC.format(new BigDecimal(value).movePointRight(2).intValue(), 12); } throw new IllegalArgumentException("Cannot format String as " + this); } diff --git a/src/main/java/com/solab/iso8583/IsoValue.java b/src/main/java/com/solab/iso8583/IsoValue.java index 96ef805..add7791 100644 --- a/src/main/java/com/solab/iso8583/IsoValue.java +++ b/src/main/java/com/solab/iso8583/IsoValue.java @@ -96,7 +96,9 @@ public class IsoValue implements Cloneable { if (length == 0 && t.needsLength()) { throw new IllegalArgumentException(String.format("Length must be greater than zero for type %s (value '%s')", t, val)); } else if (t == IsoType.LLVAR || t == IsoType.LLLVAR) { - length = custom == null ? val.toString().length() : custom.encodeField(value).length(); + if (len == 0) { + length = custom == null ? val.toString().length() : custom.encodeField(value).length(); + } if (t == IsoType.LLVAR && length > 99) { throw new IllegalArgumentException("LLVAR can only hold values up to 99 chars"); } else if (t == IsoType.LLLVAR && length > 999) { @@ -130,7 +132,11 @@ public class IsoValue implements Cloneable { } if (type == IsoType.NUMERIC || type == IsoType.AMOUNT) { if (type == IsoType.AMOUNT) { - return type.format((BigDecimal)value, 12); + if (value instanceof BigDecimal) { + return type.format((BigDecimal)value, 12); + } else { + return type.format(value.toString(), 12); + } } else if (value instanceof Number) { return type.format(((Number)value).longValue(), length); } else { diff --git a/src/main/java/com/solab/iso8583/MessageFactory.java b/src/main/java/com/solab/iso8583/MessageFactory.java index 4e8ec8f..c3bdc0d 100644 --- a/src/main/java/com/solab/iso8583/MessageFactory.java +++ b/src/main/java/com/solab/iso8583/MessageFactory.java @@ -313,25 +313,46 @@ public class MessageFactory { return m; } //Now we parse each field - for (Integer i : index) { - FieldParseInfo fpi = parseGuide.get(i); - if (bs.get(i - 1)) { - if (ignoreLast && pos >= buf.length && i == index.get(index.size() -1)) { - log.warn("Field {} is not really in the message even though it's in the bitmap", i); - bs.clear(i - 1); - } else { - IsoValue val = useBinary ? fpi.parseBinary(buf, pos, getCustomField(i)) : fpi.parse(buf, pos, getCustomField(i)); - m.setField(i, val); - if (useBinary && !(val.getType() == IsoType.ALPHA || val.getType() == IsoType.LLVAR - || val.getType() == IsoType.LLLVAR)) { - pos += (val.getLength() / 2) + (val.getLength() % 2); + if (useBinary) { + for (Integer i : index) { + FieldParseInfo fpi = parseGuide.get(i); + if (bs.get(i - 1)) { + if (ignoreLast && pos >= buf.length && i == index.get(index.size() -1)) { + log.warn("Field {} is not really in the message even though it's in the bitmap", i); + bs.clear(i - 1); } else { - pos += val.getLength(); + IsoValue val = fpi.parseBinary(buf, pos, getCustomField(i)); + m.setField(i, val); + if (!(val.getType() == IsoType.ALPHA || val.getType() == IsoType.LLVAR + || val.getType() == IsoType.LLLVAR)) { + pos += (val.getLength() / 2) + (val.getLength() % 2); + } else { + pos += val.getLength(); + } + if (val.getType() == IsoType.LLVAR) { + pos++; + } else if (val.getType() == IsoType.LLLVAR) { + pos += 2; + } } - if (val.getType() == IsoType.LLVAR) { - pos += useBinary ? 1 : 2; - } else if (val.getType() == IsoType.LLLVAR) { - pos += useBinary ? 2 : 3; + } + } + } else { + for (Integer i : index) { + FieldParseInfo fpi = parseGuide.get(i); + if (bs.get(i - 1)) { + if (ignoreLast && pos >= buf.length && i == index.get(index.size() -1)) { + log.warn("Field {} is not really in the message even though it's in the bitmap", i); + bs.clear(i - 1); + } else { + IsoValue val = fpi.parse(buf, pos, getCustomField(i)); + m.setField(i, val); + pos += val.getLength(); + if (val.getType() == IsoType.LLVAR) { + pos += 2; + } else if (val.getType() == IsoType.LLLVAR) { + pos += 3; + } } } } @@ -404,6 +425,14 @@ public class MessageFactory { return typeTemplates.get(type); } + public void freeze() { + typeTemplates = Collections.unmodifiableMap(typeTemplates); + parseMap = Collections.unmodifiableMap(parseMap); + parseOrder = Collections.unmodifiableMap(parseOrder); + isoHeaders = Collections.unmodifiableMap(isoHeaders); + customFields = Collections.unmodifiableMap(customFields); + } + /** Sets a map with the fields that are to be expected when parsing a certain type of * message. * @param type The message type. diff --git a/src/main/java/com/solab/iso8583/parse/FieldParseInfo.java b/src/main/java/com/solab/iso8583/parse/FieldParseInfo.java index 0326c4c..4233fd4 100644 --- a/src/main/java/com/solab/iso8583/parse/FieldParseInfo.java +++ b/src/main/java/com/solab/iso8583/parse/FieldParseInfo.java @@ -88,29 +88,33 @@ public class FieldParseInfo { throw new ParseException(String.format("Insufficient data for LLVAR field, pos %d", pos), pos); } if (custom == null) { - return new IsoValue(type, new String(buf, pos + 2, length), null); + return new IsoValue(type, new String(buf, pos + 2, length), length, null); } else { - IsoValue v = new IsoValue(type, custom.decodeField(new String(buf, pos + 2, length)), custom); + IsoValue v = new IsoValue(type, custom.decodeField(new String(buf, pos + 2, length)), length, custom); if (v.getValue() == null) { - return new IsoValue(type, new String(buf, pos + 2, length), null); + return new IsoValue(type, new String(buf, pos + 2, length), length, null); } return v; } } else if (type == IsoType.LLLVAR) { + if (!(Character.isDigit(buf[pos]) && Character.isDigit(buf[pos+1]) && Character.isDigit(buf[pos+2]))) { + throw new ParseException(String.format("Invalid LLLVAR length '%s' pos %d", new String(buf, pos, 3), pos), pos); + } length = ((buf[pos] - 48) * 100) + ((buf[pos + 1] - 48) * 10) + (buf[pos + 2] - 48); if (length < 0) { throw new ParseException(String.format("Invalid LLLVAR length %d pos %d", length, pos), pos); } - if (pos+3 > buf.length || length+pos+3 > buf.length) { + if (length+pos+3 > buf.length) { throw new ParseException(String.format("Insufficient data for LLLVAR field, pos %d", pos), pos); } + String _v = length == 0 ? "" : new String(buf, pos + 3, length); if (custom == null) { - return new IsoValue(type, new String(buf, pos + 3, length), null); + return new IsoValue(type, _v, length, null); } else { - IsoValue v = new IsoValue(type, custom.decodeField(new String(buf, pos + 3, length)), custom); + IsoValue v = new IsoValue(type, custom.decodeField(_v), length, custom); if (v.getValue() == null) { //problems decoding? return the string - return new IsoValue(type, new String(buf, pos + 3, length), null); + return new IsoValue(type, _v, length, null); } return v; } diff --git a/src/main/resources/PLACEHOLDER b/src/main/resources/PLACEHOLDER deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/solab/iso8583/parse/j8583.dtd b/src/main/resources/com/solab/iso8583/parse/j8583.dtd similarity index 100% rename from src/main/java/com/solab/iso8583/parse/j8583.dtd rename to src/main/resources/com/solab/iso8583/parse/j8583.dtd