fix #36 LLLLVAR + LLLLBIN

This commit is contained in:
Enrique Zamudio
2015-02-19 19:40:03 -06:00
parent 576dc50b44
commit 00c046881b
9 changed files with 270 additions and 37 deletions
@@ -341,8 +341,7 @@ public class IsoMessage {
return bs;
}
/** This calls writeInternal(), allowing applications to get the byte buffer containing the
* message data, without the length header. */
/** Writes the message to a memory stream and returns a byte array with the result. */
public byte[] writeData() {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
if (isoHeader != null) {
@@ -461,6 +460,8 @@ public class IsoMessage {
sb.append(String.format("%02d", desc.length()));
} else if (v.getType() == IsoType.LLLBIN || v.getType() == IsoType.LLLVAR) {
sb.append(String.format("%03d", desc.length()));
} else if (v.getType() == IsoType.LLLLBIN || v.getType() == IsoType.LLLLVAR) {
sb.append(String.format("%04d", desc.length()));
}
sb.append(desc);
}
+5 -1
View File
@@ -56,7 +56,11 @@ public enum IsoType {
/** Similar to LLVAR but holds byte arrays instead of strings. */
LLBIN(false, 0),
/** Similar to LLLVAR but holds byte arrays instead of strings. */
LLLBIN(false, 0);
LLLBIN(false, 0),
/** variable length with 4-digit header length. */
LLLLVAR(false, 0),
/** variable length byte array with 4-digit header length. */
LLLLBIN(false, 0);
private boolean needsLen;
private int length;
+38 -15
View File
@@ -63,7 +63,7 @@ public class IsoValue<T> implements Cloneable {
encoder = custom;
type = t;
this.value = value;
if (type == IsoType.LLVAR || type == IsoType.LLLVAR) {
if (type == IsoType.LLVAR || type == IsoType.LLLVAR || type == IsoType.LLLLVAR) {
if (custom == null) {
length = value.toString().length();
} else {
@@ -77,8 +77,10 @@ public class IsoValue<T> implements Cloneable {
throw new IllegalArgumentException("LLVAR can only hold values up to 99 chars");
} else if (t == IsoType.LLLVAR && length > 999) {
throw new IllegalArgumentException("LLLVAR can only hold values up to 999 chars");
}
} else if (type == IsoType.LLBIN || type == IsoType.LLLBIN) {
} else if (t == IsoType.LLLLVAR && length > 9999) {
throw new IllegalArgumentException("LLLLVAR can only hold values up to 9999 chars");
}
} else if (type == IsoType.LLBIN || type == IsoType.LLLBIN || type == IsoType.LLLLBIN) {
if (custom == null) {
if (value instanceof byte[]) {
length = ((byte[])value).length;
@@ -98,6 +100,8 @@ public class IsoValue<T> implements Cloneable {
throw new IllegalArgumentException("LLBIN can only hold values up to 99 chars");
} else if (t == IsoType.LLLBIN && length > 999) {
throw new IllegalArgumentException("LLLBIN can only hold values up to 999 chars");
} else if (t == IsoType.LLLLBIN && length > 9999) {
throw new IllegalArgumentException("LLLLBIN can only hold values up to 9999 chars");
}
} else {
length = type.getLength();
@@ -122,7 +126,7 @@ public class IsoValue<T> implements Cloneable {
encoder = custom;
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) {
} else if (t == IsoType.LLVAR || t == IsoType.LLLVAR || t == IsoType.LLLLVAR) {
if (len == 0) {
length = custom == null ? val.toString().length() : custom.encodeField(value).length();
}
@@ -130,8 +134,10 @@ public class IsoValue<T> implements Cloneable {
throw new IllegalArgumentException("LLVAR can only hold values up to 99 chars");
} else if (t == IsoType.LLLVAR && length > 999) {
throw new IllegalArgumentException("LLLVAR can only hold values up to 999 chars");
} else if (t == IsoType.LLLLVAR && length > 9999) {
throw new IllegalArgumentException("LLLLVAR can only hold values up to 9999 chars");
}
} else if (t == IsoType.LLBIN || t == IsoType.LLLBIN) {
} else if (t == IsoType.LLBIN || t == IsoType.LLLBIN || t == IsoType.LLLLBIN) {
if (len == 0) {
if (custom == null) {
length = ((byte[])val).length;
@@ -146,6 +152,8 @@ public class IsoValue<T> implements Cloneable {
throw new IllegalArgumentException("LLBIN can only hold values up to 99 chars");
} else if (t == IsoType.LLLBIN && length > 999) {
throw new IllegalArgumentException("LLLBIN can only hold values up to 999 chars");
} else if (t == IsoType.LLLLBIN && length > 9999) {
throw new IllegalArgumentException("LLLLBIN can only hold values up to 9999 chars");
}
}
}
@@ -202,7 +210,7 @@ public class IsoValue<T> implements Cloneable {
}
} else if (type == IsoType.ALPHA) {
return type.format(encoder == null ? value.toString() : encoder.encodeField(value), length);
} else if (type == IsoType.LLVAR || type == IsoType.LLLVAR) {
} else if (type == IsoType.LLVAR || type == IsoType.LLLVAR || type == IsoType.LLLLVAR) {
return encoder == null ? value.toString() : encoder.encodeField(value);
} else if (value instanceof Date) {
return type.format((Date)value, tz);
@@ -213,7 +221,7 @@ public class IsoValue<T> implements Cloneable {
} else {
return type.format(encoder == null ? value.toString() : encoder.encodeField(value), length * 2);
}
} else if (type == IsoType.LLBIN || type == IsoType.LLLBIN) {
} else if (type == IsoType.LLBIN || type == IsoType.LLLBIN || type == IsoType.LLLLBIN) {
if (value instanceof byte[]) {
final byte[] _v = (byte[])value;
return encoder == null ? HexCodec.hexEncode(_v, 0, _v.length) : encoder.encodeField(value);
@@ -255,11 +263,21 @@ public class IsoValue<T> implements Cloneable {
return encoder;
}
protected void writeLengthHeader(final int l, final OutputStream outs, final int digits,
protected void writeLengthHeader(final int l, final OutputStream outs, final IsoType type,
final boolean binary, final boolean forceStringEncoding)
throws IOException {
final int digits;
if (type == IsoType.LLLLBIN || type == IsoType.LLLLVAR) {
digits = 4;
} else if (type == IsoType.LLLBIN || type == IsoType.LLLVAR) {
digits = 3;
} else {
digits = 2;
}
if (binary) {
if (digits == 3) {
if (digits == 4) {
outs.write((((l % 1000) / 100) << 4) | ((l % 1000)/100));
} else if (digits == 3) {
outs.write(l / 100); //00 to 09 automatically in BCD
}
//BCD encode the rest of the length
@@ -271,11 +289,16 @@ public class IsoValue<T> implements Cloneable {
lhead = '0' + lhead;
} else if (ldiff == 2) {
lhead = "00" + lhead;
} else if (ldiff == 3) {
lhead = "000" + lhead;
}
outs.write(encoding == null ? lhead.getBytes():lhead.getBytes(encoding));
} else {
//write the length in ASCII
if (digits == 3) {
if (digits == 4) {
outs.write((l/1000)+48);
outs.write(((l%1000)/100)+48);
} else if (digits == 3) {
outs.write((l / 100) + 48);
}
if (l >= 10) {
@@ -295,10 +318,10 @@ public class IsoValue<T> implements Cloneable {
* for variable-length fields to be done with the proper character encoding. When false,
* the length headers are encoded as ASCII; this used to be the only behavior. */
public void write(final OutputStream outs, final boolean binary, final boolean forceStringEncoding) throws IOException {
if (type == IsoType.LLLVAR || type == IsoType.LLVAR) {
writeLengthHeader(length, outs, type == IsoType.LLLVAR ? 3 : 2, binary, forceStringEncoding);
} else if (type == IsoType.LLBIN || type == IsoType.LLLBIN) {
writeLengthHeader(binary ? length : length*2, outs, type == IsoType.LLLBIN ? 3 : 2, binary, forceStringEncoding);
if (type == IsoType.LLLVAR || type == IsoType.LLVAR || type == IsoType.LLLLVAR) {
writeLengthHeader(length, outs, type, binary, forceStringEncoding);
} else if (type == IsoType.LLBIN || type == IsoType.LLLBIN || type == IsoType.LLLLBIN) {
writeLengthHeader(binary ? length : length*2, outs, type, binary, forceStringEncoding);
} else if (binary) {
//numeric types in binary are coded like this
byte[] buf = null;
@@ -316,7 +339,7 @@ public class IsoValue<T> implements Cloneable {
return;
}
}
if (binary && (type == IsoType.BINARY || type == IsoType.LLBIN || type == IsoType.LLLBIN)) {
if (binary && (type == IsoType.BINARY || type == IsoType.LLBIN || type == IsoType.LLLBIN || type == IsoType.LLLLBIN)) {
int missing = 0;
if (value instanceof byte[]) {
outs.write((byte[])value);
@@ -448,9 +448,10 @@ public class MessageFactory<T extends IsoMessage> {
}
if (val.getType() == IsoType.LLVAR || val.getType() == IsoType.LLBIN) {
pos++;
} else if (val.getType() == IsoType.LLLVAR || val.getType() == IsoType.LLLBIN) {
pos += 2;
}
} else if (val.getType() == IsoType.LLLVAR || val.getType() == IsoType.LLLBIN
|| val.getType() == IsoType.LLLLVAR || val.getType() == IsoType.LLLLBIN) {
pos += 2;
}
}
}
}
@@ -475,7 +476,9 @@ public class MessageFactory<T extends IsoMessage> {
pos += 2;
} else if (val.getType() == IsoType.LLLVAR || val.getType() == IsoType.LLLBIN) {
pos += 3;
}
} else if (val.getType() == IsoType.LLLLVAR || val.getType() == IsoType.LLLLBIN) {
pos += 4;
}
}
}
}
@@ -52,11 +52,11 @@ public abstract class AlphaNumericFieldParseInfo extends FieldParseInfo {
_v = new String(buf, pos, buf.length-pos, getCharacterEncoding()).substring(0, length);
}
if (custom == null) {
return new IsoValue<String>(type, _v, length, null);
return new IsoValue<>(type, _v, length, null);
} else {
T decoded = custom.decodeField(_v);
return decoded == null ? new IsoValue<String>(type, _v, length, null) :
new IsoValue<T>(type, decoded, length, custom);
return decoded == null ? new IsoValue<>(type, _v, length, null) :
new IsoValue<>(type, decoded, length, custom);
}
} catch (StringIndexOutOfBoundsException ex) {
throw new ParseException(String.format(
@@ -127,7 +127,11 @@ public abstract class FieldParseInfo {
fpi = new NumericParseInfo(len);
} else if (t == IsoType.TIME) {
fpi = new TimeParseInfo();
}
} else if (t == IsoType.LLLLVAR) {
fpi = new LlllvarParseInfo();
} else if (t == IsoType.LLLLBIN) {
fpi = new LlllbinParseInfo();
}
if (fpi == null) {
throw new IllegalArgumentException(String.format("Cannot parse type %s", t));
}
@@ -138,11 +142,19 @@ public abstract class FieldParseInfo {
protected int decodeLength(byte[] buf, int pos, int digits) throws UnsupportedEncodingException {
if (forceStringDecoding) {
return Integer.parseInt(new String(buf, pos, digits, encoding), 10);
} else if (digits == 3) {
return ((buf[pos] - 48) * 100) + ((buf[pos + 1] - 48) * 10) + (buf[pos + 2] - 48);
} else {
return ((buf[pos] - 48) * 10) + (buf[pos + 1] - 48);
switch(digits) {
case 2:
return ((buf[pos] - 48) * 10) + (buf[pos + 1] - 48);
case 3:
return ((buf[pos] - 48) * 100) + ((buf[pos + 1] - 48) * 10)
+ (buf[pos + 2] - 48);
case 4:
return ((buf[pos] - 48) * 1000) + ((buf[pos + 1] - 48) * 100)
+ ((buf[pos + 2] - 48) * 10) + (buf[pos + 3] - 48);
}
}
return -1;
}
}
@@ -1,10 +1,112 @@
package com.solab.iso8583.parse;
import com.solab.iso8583.CustomBinaryField;
import com.solab.iso8583.CustomField;
import com.solab.iso8583.IsoType;
import com.solab.iso8583.IsoValue;
import com.solab.iso8583.util.HexCodec;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
/**
* Blabla.
*
* @author Enrique Zamudio
* Date: 19/02/15 18:29
*/
public class LlllbinParseInfo {
public class LlllbinParseInfo extends FieldParseInfo {
public LlllbinParseInfo() {
super(IsoType.LLLLBIN, 0);
}
@Override
public <T> IsoValue<?> parse(final int field, final byte[] buf,
final int pos, final CustomField<T> custom)
throws ParseException, UnsupportedEncodingException {
if (pos < 0) {
throw new ParseException(String.format("Invalid LLLLBIN field %d pos %d",
field, pos), pos);
} else if (pos+4 > buf.length) {
throw new ParseException(String.format("Insufficient LLLLBIN header field %d",
field), pos);
}
final int l = decodeLength(buf, pos, 4);
if (l < 0) {
throw new ParseException(String.format("Invalid LLLLBIN length %d field %d pos %d",
l, field, pos), pos);
} else if (l+pos+4 > buf.length) {
throw new ParseException(String.format(
"Insufficient data for LLLLBIN field %d, pos %d", field, pos), pos);
}
byte[] binval = l == 0 ? new byte[0] : HexCodec.hexDecode(new String(buf, pos + 4, l));
if (custom == null) {
return new IsoValue<>(type, binval, binval.length, null);
} else if (custom instanceof CustomBinaryField) {
try {
T dec = ((CustomBinaryField<T>)custom).decodeBinaryField(
buf, pos + 4, l);
return dec == null ? new IsoValue<>(type, binval, binval.length, null) :
new IsoValue<>(type, dec, 0, custom);
} catch (IndexOutOfBoundsException ex) {
throw new ParseException(String.format(
"Insufficient data for LLLLBIN field %d, pos %d", field, pos), pos);
}
} else {
try {
T dec = custom.decodeField(
l == 0 ? "" : new String(buf, pos + 4, l));
return dec == null ? new IsoValue<>(type, binval, binval.length, null) :
new IsoValue<>(type, dec, l, custom);
} catch (IndexOutOfBoundsException ex) {
throw new ParseException(String.format(
"Insufficient data for LLLLBIN field %d, pos %d", field, pos), pos);
}
}
}
@Override
public <T> IsoValue<?> parseBinary(final int field, final byte[] buf,
final int pos, final CustomField<T> custom)
throws ParseException {
if (pos < 0) {
throw new ParseException(String.format("Invalid bin LLLLBIN field %d pos %d",
field, pos), pos);
} else if (pos+2 > buf.length) {
throw new ParseException(String.format("Insufficient LLLLBIN header field %d",
field), pos);
}
final int l = ((buf[pos] & 0xf0) * 1000) + ((buf[pos] & 0x0f) * 100)
+ (((buf[pos + 1] & 0xf0) >> 4) * 10) + (buf[pos + 1] & 0x0f);
if (l < 0) {
throw new ParseException(String.format("Invalid LLLLBIN length %d field %d pos %d",
l, field, pos), pos);
}
if (l+pos+2 > buf.length) {
throw new ParseException(String.format(
"Insufficient data for bin LLLLBIN field %d, pos %d requires %d, only %d available",
field, pos, l, buf.length-pos+1), pos);
}
byte[] _v = new byte[l];
System.arraycopy(buf, pos+2, _v, 0, l);
if (custom == null) {
return new IsoValue<>(type, _v, null);
} else if (custom instanceof CustomBinaryField) {
try {
T dec = ((CustomBinaryField<T>)custom).decodeBinaryField(
buf, pos + 2, l);
return dec == null ? new IsoValue<>(type, _v, _v.length, null) :
new IsoValue<T>(type, dec, l, custom);
} catch (IndexOutOfBoundsException ex) {
throw new ParseException(String.format(
"Insufficient data for LLLLBIN field %d, pos %d", field, pos), pos);
}
} else {
T dec = custom.decodeField(HexCodec.hexEncode(_v, 0, _v.length));
return dec == null ? new IsoValue<>(type, _v, null) :
new IsoValue<>(type, dec, custom);
}
}
}
@@ -1,10 +1,98 @@
package com.solab.iso8583.parse;
import com.solab.iso8583.CustomField;
import com.solab.iso8583.IsoType;
import com.solab.iso8583.IsoValue;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
/**
* Blabla.
*
* @author Enrique Zamudio
* Date: 19/02/15 18:30
*/
public class LlllvarParseInfo {
public class LlllvarParseInfo extends FieldParseInfo {
public LlllvarParseInfo() {
super(IsoType.LLLLVAR, 0);
}
@Override
public <T> IsoValue<?> parse(final int field, final byte[] buf,
final int pos, final CustomField<T> custom)
throws ParseException, UnsupportedEncodingException {
if (pos < 0) {
throw new ParseException(String.format(
"Invalid LLLLVAR field %d %d", field, pos), pos);
} else if (pos+4 > buf.length) {
throw new ParseException(String.format(
"Insufficient data for LLLLVAR header, pos %d", pos), pos);
}
final int len = decodeLength(buf, pos, 4);
if (len < 0) {
throw new ParseException(String.format(
"Invalid LLLLVAR length %d, field %d pos %d", len, field, pos), pos);
} else if (len+pos+4 > buf.length) {
throw new ParseException(String.format(
"Insufficient data for LLLLVAR field %d, pos %d", field, pos), pos);
}
String _v;
try {
_v = len == 0 ? "" : new String(buf, pos + 4, len, getCharacterEncoding());
} catch (IndexOutOfBoundsException ex) {
throw new ParseException(String.format(
"Insufficient data for LLLLVAR header, field %d pos %d", field, pos), pos);
}
//This is new: if the String's length is different from the specified
// length in the buffer, there are probably some extended characters.
// So we create a String from the rest of the buffer, and then cut it to
// the specified length.
if (_v.length() != len) {
_v = new String(buf, pos + 4, buf.length-pos-4,
getCharacterEncoding()).substring(0, len);
}
if (custom == null) {
return new IsoValue<>(type, _v, len, null);
} else {
T dec = custom.decodeField(_v);
return dec == null ? new IsoValue<>(type, _v, len, null) :
new IsoValue<>(type, dec, len, custom);
}
}
@Override
public <T> IsoValue<?> parseBinary(final int field, final byte[] buf,
final int pos, final CustomField<T> custom)
throws ParseException, UnsupportedEncodingException {
if (pos < 0) {
throw new ParseException(String.format("Invalid bin LLLLVAR field %d pos %d",
field, pos), pos);
} else if (pos+2 > buf.length) {
throw new ParseException(String.format(
"Insufficient data for bin LLLLVAR header, field %d pos %d",
field, pos), pos);
}
final int len = ((buf[pos] & 0xf0) * 1000) + ((buf[pos] & 0x0f) * 100)
+ (((buf[pos + 1] & 0xf0) >> 4) * 10) + (buf[pos + 1] & 0x0f);
if (len < 0) {
throw new ParseException(String.format(
"Invalid bin LLLLVAR length %d, field %d pos %d", len, field, pos), pos);
}
if (len+pos+2 > buf.length) {
throw new ParseException(String.format(
"Insufficient data for bin LLLLVAR field %d, pos %d", field, pos), pos);
}
if (custom == null) {
return new IsoValue<>(type, new String(buf, pos + 2, len,
getCharacterEncoding()), null);
} else {
T dec = custom.decodeField(new String(buf, pos + 2, len, getCharacterEncoding()));
return dec == null ? new IsoValue<>(type,
new String(buf, pos + 2, len, getCharacterEncoding()), null) :
new IsoValue<>(type, dec, custom);
}
}
}
@@ -70,11 +70,11 @@ public class LlvarParseInfo extends FieldParseInfo {
getCharacterEncoding()).substring(0, len);
}
if (custom == null) {
return new IsoValue<String>(type, _v, len, null);
return new IsoValue<>(type, _v, len, null);
} else {
T dec = custom.decodeField(_v);
return dec == null ? new IsoValue<String>(type, _v, len, null) :
new IsoValue<T>(type, dec, len, custom);
return dec == null ? new IsoValue<>(type, _v, len, null) :
new IsoValue<>(type, dec, len, custom);
}
}
@@ -100,13 +100,13 @@ public class LlvarParseInfo extends FieldParseInfo {
"Insufficient data for bin LLVAR field %d, pos %d", field, pos), pos);
}
if (custom == null) {
return new IsoValue<String>(type, new String(buf, pos + 1, len,
return new IsoValue<>(type, new String(buf, pos + 1, len,
getCharacterEncoding()), null);
} else {
T dec = custom.decodeField(new String(buf, pos + 1, len, getCharacterEncoding()));
return dec == null ? new IsoValue<String>(type,
return dec == null ? new IsoValue<>(type,
new String(buf, pos + 1, len, getCharacterEncoding()), null) :
new IsoValue<T>(type, dec, custom);
new IsoValue<>(type, dec, custom);
}
}