This is the mavenized version of the project, and it's the one I'll be working with from now on.
git-svn-id: https://j8583.svn.sourceforge.net/svnroot/j8583/j8583@52 db60a039-f72e-0410-b1d2-baa000846441
This commit is contained in:
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
j8583 A Java implementation of the ISO8583 protocol
|
||||
Copyright (C) 2007 Enrique Zamudio Lopez
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
package com.solab.iso8583;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/** 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
|
||||
* so that the value can be padded to the specified length. LLVAR and LLLVAR
|
||||
* values do not need a length specification because the length is calculated
|
||||
* from the stored value.
|
||||
*
|
||||
* @author Enrique Zamudio
|
||||
*/
|
||||
public class IsoValue<T> implements Cloneable {
|
||||
|
||||
private IsoType type;
|
||||
private T value;
|
||||
private CustomField<T> encoder;
|
||||
private int length;
|
||||
|
||||
public IsoValue(IsoType t, T value) {
|
||||
this(t, value, null);
|
||||
}
|
||||
|
||||
/** Creates a new instance that stores the specified value as the specified type.
|
||||
* Useful for storing LLVAR or LLLVAR types, as well as fixed-length value types
|
||||
* like DATE10, DATE4, AMOUNT, etc.
|
||||
* @param t the ISO type.
|
||||
* @param value The value to be stored.
|
||||
* @param custom An optional CustomField to encode/decode a custom value.
|
||||
*/
|
||||
public IsoValue(IsoType t, T value, CustomField<T> custom) {
|
||||
if (t.needsLength()) {
|
||||
throw new IllegalArgumentException("Fixed-value types must use constructor that specifies length");
|
||||
}
|
||||
encoder = custom;
|
||||
type = t;
|
||||
this.value = value;
|
||||
if (type == IsoType.LLVAR || type == IsoType.LLLVAR) {
|
||||
if (custom == null) {
|
||||
length = value.toString().length();
|
||||
} else {
|
||||
String enc = custom.encodeField(value);
|
||||
if (enc == null) {
|
||||
enc = value == null ? "" : value.toString();
|
||||
}
|
||||
length = enc.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) {
|
||||
throw new IllegalArgumentException("LLLVAR can only hold values up to 999 chars");
|
||||
}
|
||||
} else {
|
||||
length = type.getLength();
|
||||
}
|
||||
}
|
||||
|
||||
public IsoValue(IsoType t, T val, int len) {
|
||||
this(t, val, len, null);
|
||||
}
|
||||
|
||||
/** Creates a new instance that stores the specified value as the specified type.
|
||||
* Useful for storing fixed-length value types.
|
||||
* @param t The ISO8583 type for this field.
|
||||
* @param val The value to store in the field.
|
||||
* @param len The length for the value.
|
||||
* @param custom An optional CustomField to encode/decode a custom value.
|
||||
*/
|
||||
public IsoValue(IsoType t, T val, int len, CustomField<T> custom) {
|
||||
type = t;
|
||||
value = val;
|
||||
length = len;
|
||||
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) {
|
||||
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) {
|
||||
throw new IllegalArgumentException("LLLVAR can only hold values up to 999 chars");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the ISO type to which the value must be formatted. */
|
||||
public IsoType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/** Returns the length of the stored value, of the length of the formatted value
|
||||
* in case of NUMERIC or ALPHA. It doesn't include the field length header in case
|
||||
* of LLVAR or LLLVAR. */
|
||||
public int getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
/** Returns the stored value without any conversion or formatting. */
|
||||
public T getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/** Returns the formatted value as a String. The formatting depends on the type of the
|
||||
* receiver. */
|
||||
public String toString() {
|
||||
if (value == null) {
|
||||
return "ISOValue<null>";
|
||||
}
|
||||
if (type == IsoType.NUMERIC || type == IsoType.AMOUNT) {
|
||||
if (type == IsoType.AMOUNT) {
|
||||
return type.format((BigDecimal)value, 12);
|
||||
} else if (value instanceof Number) {
|
||||
return type.format(((Number)value).longValue(), length);
|
||||
} else {
|
||||
return type.format(encoder == null ? value.toString() : encoder.encodeField(value), length);
|
||||
}
|
||||
} else if (type == IsoType.ALPHA) {
|
||||
return type.format(encoder == null ? value.toString() : encoder.encodeField(value), length);
|
||||
} else if (type == IsoType.LLLVAR || type == IsoType.LLLVAR) {
|
||||
return encoder == null ? value.toString() : encoder.encodeField(value);
|
||||
} else if (value instanceof Date) {
|
||||
return type.format((Date)value);
|
||||
}
|
||||
return encoder == null ? value.toString() : encoder.encodeField(value);
|
||||
}
|
||||
|
||||
/** Returns a copy of the receiver that references the same value object. */
|
||||
@SuppressWarnings("unchecked")
|
||||
public IsoValue<T> clone() {
|
||||
try {
|
||||
return (IsoValue<T>)super.clone();
|
||||
} catch (CloneNotSupportedException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true of the other object is also an IsoValue and has the same type and length,
|
||||
* and if other.getValue().equals(getValue()) returns true. */
|
||||
public boolean equals(Object other) {
|
||||
if (other == null || !(other instanceof IsoValue<?>)) {
|
||||
return false;
|
||||
}
|
||||
IsoValue<?> comp = (IsoValue<?>)other;
|
||||
return (comp.getType() == getType() && comp.getValue().equals(getValue()) && comp.getLength() == getLength());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return value == null ? 0 : toString().hashCode();
|
||||
}
|
||||
|
||||
/** Writes the formatted value to a stream, with the length header
|
||||
* if it's a variable length type. */
|
||||
public void write(OutputStream outs, boolean binary) throws IOException {
|
||||
if (type == IsoType.LLLVAR || type == IsoType.LLVAR) {
|
||||
if (binary) {
|
||||
if (type == IsoType.LLLVAR) {
|
||||
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.LLLVAR) {
|
||||
outs.write((length / 100) + 48);
|
||||
}
|
||||
if (length >= 10) {
|
||||
outs.write(((length % 100) / 10) + 48);
|
||||
} else {
|
||||
outs.write(48);
|
||||
}
|
||||
outs.write((length % 10) + 48);
|
||||
}
|
||||
} else if (binary) {
|
||||
//numeric types in binary are coded like this
|
||||
byte[] buf = null;
|
||||
if (type == IsoType.NUMERIC) {
|
||||
buf = new byte[(length / 2) + (length % 2)];
|
||||
} else if (type == IsoType.AMOUNT) {
|
||||
buf = new byte[6];
|
||||
} else if (type == IsoType.DATE10 || type == IsoType.DATE4 || type == IsoType.DATE_EXP || type == IsoType.TIME) {
|
||||
buf = new byte[length / 2];
|
||||
}
|
||||
//Encode in BCD if it's one of these types
|
||||
if (buf != null) {
|
||||
toBcd(toString(), buf);
|
||||
outs.write(buf);
|
||||
return;
|
||||
}
|
||||
}
|
||||
//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
|
||||
* to store the digits in the original value (half the length of the string). */
|
||||
private void toBcd(String value, byte[] buf) {
|
||||
int charpos = 0; //char where we start
|
||||
int bufpos = 0;
|
||||
if (value.length() % 2 == 1) {
|
||||
//for odd lengths we encode just the first digit in the first byte
|
||||
buf[0] = (byte)(value.charAt(0) - 48);
|
||||
charpos = 1;
|
||||
bufpos = 1;
|
||||
}
|
||||
//encode the rest of the string
|
||||
while (charpos < value.length()) {
|
||||
buf[bufpos] = (byte)(((value.charAt(charpos) - 48) << 4)
|
||||
| (value.charAt(charpos + 1) - 48));
|
||||
charpos += 2;
|
||||
bufpos++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user