From 3c8cacbb8db12f386514e1e6b62df90b34592dc1 Mon Sep 17 00:00:00 2001 From: chochos Date: Mon, 6 Dec 2010 18:42:02 +0000 Subject: [PATCH] Added new IsoTypes: BINARY, LLBIN and LLLBIN. Similar to ALPHA, LLVAR and LLLVAR but they handle binary values that get translated to byte arrays, directly if using binary coding, or hex-encoded if using ASCII. git-svn-id: https://j8583.svn.sourceforge.net/svnroot/j8583/j8583@63 db60a039-f72e-0410-b1d2-baa000846441 --- src/main/java/com/solab/iso8583/IsoType.java | 31 ++++++++- src/main/java/com/solab/iso8583/IsoValue.java | 59 ++++++++++++++++- .../java/com/solab/iso8583/util/HexCodec.java | 63 +++++++++++++++++++ src/test/java/j8583/TestHexCodec.java | 40 ++++++++++++ 4 files changed, 189 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/solab/iso8583/util/HexCodec.java create mode 100644 src/test/java/j8583/TestHexCodec.java diff --git a/src/main/java/com/solab/iso8583/IsoType.java b/src/main/java/com/solab/iso8583/IsoType.java index a66a5da..44b71c3 100644 --- a/src/main/java/com/solab/iso8583/IsoType.java +++ b/src/main/java/com/solab/iso8583/IsoType.java @@ -50,7 +50,13 @@ public enum IsoType { /** Time of day in format HHmmss */ TIME(false, 6), /** An amount, expressed in cents with a fixed length of 12. */ - AMOUNT(false, 12); + AMOUNT(false, 12), + /** Similar to ALPHA but holds byte arrays instead of strings. */ + BINARY(true, 0), + /** 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); private boolean needsLen; private int length; @@ -85,7 +91,7 @@ public enum IsoType { throw new IllegalArgumentException("Cannot format date as " + this); } - /** Formats the string to the given length (length is only useful if type is ALPHA). */ + /** Formats the string to the given length (length is only useful if type is ALPHA, NUMERIC or BINARY). */ public String format(String value, int length) { if (this == ALPHA) { if (value == null) { @@ -116,6 +122,23 @@ public enum IsoType { return new String(c); } else if (this == AMOUNT) { return IsoType.NUMERIC.format(new BigDecimal(value).movePointRight(2).intValue(), 12); + } else if (this == BINARY) { + + if (value == null) { + value = ""; + } + if (value.length() > length) { + return value.substring(0, length); + } + char[] c = new char[length]; + System.arraycopy(value.toCharArray(), 0, c, 0, value.length()); + for (int i = value.length(); i < c.length; i++) { + c[i] = '0'; + } + return new String(c); + + } else if (this == LLBIN || this == LLLBIN) { + return value; } throw new IllegalArgumentException("Cannot format String as " + this); } @@ -145,6 +168,8 @@ public enum IsoType { //No hay decimales asi que dejamos los dos ultimos digitos como 0 System.arraycopy(v.toCharArray(), 0, digits, 10 - v.length(), v.length()); return new String(digits); + } else if (this == BINARY || this == LLBIN || this == LLLBIN) { + //TODO } throw new IllegalArgumentException("Cannot format number as " + this); } @@ -158,6 +183,8 @@ public enum IsoType { return format(value.longValue(), length); } else if (this == ALPHA || this == LLVAR || this == LLLVAR) { return format(value.toString(), length); + } else if (this == BINARY || this == LLBIN || this == LLLBIN) { + //TODO } throw new IllegalArgumentException("Cannot format BigDecimal as " + this); } diff --git a/src/main/java/com/solab/iso8583/IsoValue.java b/src/main/java/com/solab/iso8583/IsoValue.java index 687d971..4145706 100644 --- a/src/main/java/com/solab/iso8583/IsoValue.java +++ b/src/main/java/com/solab/iso8583/IsoValue.java @@ -23,6 +23,8 @@ import java.io.OutputStream; import java.math.BigDecimal; import java.util.Date; +import com.solab.iso8583.util.HexCodec; + /** Represents a value that is stored in a field inside an ISO8583 message. * It can format the value when the message is generated. * Some values have a fixed length, other values require a length to be specified @@ -72,6 +74,22 @@ public class IsoValue implements Cloneable { } 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) { + if (custom == null) { + length = ((byte[])value).length; + } else { + //TODO special encoder, NO strings + String enc = custom.encodeField(value); + if (enc == null) { + enc = value == null ? "" : value.toString(); + } + length = enc.length(); + } + if (t == IsoType.LLBIN && length > 99) { + 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 { length = type.getLength(); } @@ -104,6 +122,16 @@ public class IsoValue implements Cloneable { } else if (t == IsoType.LLLVAR && length > 999) { throw new IllegalArgumentException("LLLVAR can only hold values up to 999 chars"); } + } else if (t == IsoType.LLBIN || t == IsoType.LLLBIN) { + if (len == 0) { + //TODO customfield binary! + length = custom == null ? ((byte[])val).length : custom.encodeField(value).length(); + } + if (t == IsoType.LLBIN && length > 99) { + 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"); + } } } @@ -148,6 +176,10 @@ public class IsoValue implements Cloneable { return encoder == null ? value.toString() : encoder.encodeField(value); } else if (value instanceof Date) { return type.format((Date)value); + } else if (type == IsoType.BINARY) { + return type.format(encoder == null ? HexCodec.hexEncode((byte[])value) : encoder.encodeField(value), length * 2); + } else if (type == IsoType.LLBIN || type == IsoType.LLLBIN) { + return encoder == null ? HexCodec.hexEncode((byte[])value) : encoder.encodeField(value); } return encoder == null ? value.toString() : encoder.encodeField(value); } @@ -204,6 +236,25 @@ public class IsoValue implements Cloneable { } outs.write((length % 10) + 48); } + } else if (type == IsoType.LLBIN || type == IsoType.LLLBIN) { + if (binary) { + if (type == IsoType.LLLBIN ) { + outs.write(length / 100); //00 to 09 automatically in BCD + } + //BCD encode the rest of the length + outs.write((((length % 100) / 10) << 4) | (length % 10)); + } else { + //write the length in ASCII + if (type == IsoType.LLLBIN) { + outs.write((length / 50) + 48); + } + if (length >= 10) { + outs.write((((length*2) % 100) / 10) + 48); + } else { + outs.write(48); + } + outs.write(((length*2) % 10) + 48); + } } else if (binary) { //numeric types in binary are coded like this byte[] buf = null; @@ -221,8 +272,12 @@ public class IsoValue implements Cloneable { return; } } - //Just write the value as text - outs.write(toString().getBytes()); + if (binary && (type == IsoType.BINARY || type == IsoType.LLBIN || type == IsoType.LLLBIN)) { + outs.write((byte[])value); + } else { + //Just write the value as text + outs.write(toString().getBytes()); + } } /** Encode the value as BCD and put it in the buffer. The buffer must be big enough diff --git a/src/main/java/com/solab/iso8583/util/HexCodec.java b/src/main/java/com/solab/iso8583/util/HexCodec.java new file mode 100644 index 0000000..077e69a --- /dev/null +++ b/src/main/java/com/solab/iso8583/util/HexCodec.java @@ -0,0 +1,63 @@ +package com.solab.iso8583.util; + +/** Utility class to perform HEX encoding/decoding of values. */ +public class HexCodec { + + static final char[] HEX = new char[]{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + public static String hexEncode(byte[] buffer) { + if (buffer.length == 0) { + return ""; + } + int holder = 0; + char[] chars = new char[buffer.length * 2]; + for (int i = 0; i < buffer.length; i++) { + holder = (buffer[i] & 0xf0) >> 4; + chars[i * 2] = HEX[holder]; + holder = buffer[i] & 0x0f; + chars[(i * 2) + 1] = HEX[holder]; + } + return new String(chars); + } + + public static byte[] hexDecode(String hex) { + //A null string returns an empty array + if (hex == null || hex.length() == 0) { + return new byte[0]; + } else if (hex.length() < 3) { + return new byte[]{ (byte)(Integer.parseInt(hex, 16) & 0xff) }; + } + //Adjust accordingly for odd-length strings + int count = hex.length(); + int nibble = 0; + if (count % 2 != 0) { + count++; + nibble = 1; + } + byte[] buf = new byte[count / 2]; + char c = 0; + int holder = 0; + int pos = 0; + for (int i = 0; i < buf.length; i++) { + for (int z = 0; z < 2; z++) { + c = hex.charAt(pos++); + if (c >= 'A' && c <= 'F') { + c -= 55; + } else if (c >= '0' && c <= '9') { + c -= 48; + } else if (c >= 'a' && c <= 'f') { + c -= 87; + } + if (nibble == 0) { + holder = c << 4; + } else { + holder |= c; + buf[i] = (byte)holder; + } + nibble = 1 - nibble; + } + } + return buf; + } + +} diff --git a/src/test/java/j8583/TestHexCodec.java b/src/test/java/j8583/TestHexCodec.java new file mode 100644 index 0000000..9ca2cfb --- /dev/null +++ b/src/test/java/j8583/TestHexCodec.java @@ -0,0 +1,40 @@ +package j8583; + +import junit.framework.Assert; + +import org.junit.Test; + +import com.solab.iso8583.util.HexCodec; + + +public class TestHexCodec { + + public void encodeDecode(String hex) { + byte[] buf = HexCodec.hexDecode(hex); + Assert.assertEquals((hex.length() / 2) + (hex.length() % 2), buf.length); + String reenc = HexCodec.hexEncode(buf); + if (reenc.startsWith("0") && !hex.startsWith("0")) { + Assert.assertEquals(reenc.substring(1), hex); + } else { + Assert.assertEquals(hex, reenc); + } + } + + @Test + public void testCodec() { + byte[] buf = HexCodec.hexDecode("A"); + Assert.assertEquals(0x0a, buf[0]); + encodeDecode("A"); + encodeDecode("0123456789ABCDEF"); + buf = HexCodec.hexDecode("0123456789ABCDEF"); + Assert.assertEquals(1, buf[0]); + Assert.assertEquals(0x23, buf[1]); + Assert.assertEquals(0x45, buf[2]); + Assert.assertEquals(0x67, buf[3]); + Assert.assertEquals(0x89, (buf[4] & 0xff)); + Assert.assertEquals(0xab, (buf[5] & 0xff)); + Assert.assertEquals(0xcd, (buf[6] & 0xff)); + Assert.assertEquals(0xef, (buf[7] & 0xff)); + } + +}