Implement #13
This commit is contained in:
@@ -51,6 +51,7 @@ public class IsoMessage {
|
||||
/** Flag to enforce secondary bitmap even if empty. */
|
||||
private boolean forceb2;
|
||||
private boolean binBitmap;
|
||||
private boolean forceStringEncoding;
|
||||
private String encoding = System.getProperty("file.encoding");
|
||||
|
||||
/** Creates a new empty message with no values set. */
|
||||
@@ -96,7 +97,15 @@ public class IsoMessage {
|
||||
return encoding;
|
||||
}
|
||||
|
||||
/** Sets the string to be sent as ISO header, that is, after the length header but before the message type.
|
||||
/** Specified whether the variable-length fields should encode their length
|
||||
* headers using string conversion with the proper character encoding. Default
|
||||
* is false, which is the old behavior (encoding as ASCII). This is only useful
|
||||
* for text format. */
|
||||
public void setForceStringEncoding(boolean flag) {
|
||||
forceStringEncoding = flag;
|
||||
}
|
||||
|
||||
/** Sets the string to be sent as ISO header, that is, after the length header but before the message type.
|
||||
* This is useful in case an application needs some custom data in the ISO header of each message (very rare). */
|
||||
public void setIsoHeader(String value) {
|
||||
isoHeader = value;
|
||||
@@ -385,7 +394,7 @@ public class IsoMessage {
|
||||
IsoValue<?> v = fields[i];
|
||||
if (v != null) {
|
||||
try {
|
||||
v.write(bout, binary);
|
||||
v.write(bout, binary, forceStringEncoding);
|
||||
} catch (IOException ex) {
|
||||
//should never happen, writing to a ByteArrayOutputStream
|
||||
}
|
||||
|
||||
@@ -245,48 +245,50 @@ public class IsoValue<T> implements Cloneable {
|
||||
return encoder;
|
||||
}
|
||||
|
||||
protected void writeLengthHeader(final int l, final OutputStream outs, final int digits,
|
||||
final boolean binary, final boolean forceStringEncoding)
|
||||
throws IOException {
|
||||
if (binary) {
|
||||
if (digits == 3) {
|
||||
outs.write(l / 100); //00 to 09 automatically in BCD
|
||||
}
|
||||
//BCD encode the rest of the length
|
||||
outs.write((((l % 100) / 10) << 4) | (l % 10));
|
||||
} else if (forceStringEncoding) {
|
||||
String lhead = Integer.toString(l);
|
||||
final int ldiff = digits - lhead.length();
|
||||
if (ldiff == 1) {
|
||||
lhead = '0' + lhead;
|
||||
} else if (ldiff == 2) {
|
||||
lhead = "00" + lhead;
|
||||
}
|
||||
outs.write(lhead.getBytes(encoding));
|
||||
} else {
|
||||
//write the length in ASCII
|
||||
if (digits == 3) {
|
||||
outs.write((l / 100) + 48);
|
||||
}
|
||||
if (l >= 10) {
|
||||
outs.write(((l % 100) / 10) + 48);
|
||||
} else {
|
||||
outs.write(48);
|
||||
}
|
||||
outs.write((l % 10) + 48);
|
||||
}
|
||||
}
|
||||
|
||||
/** 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 it's a variable length type.
|
||||
* @param outs The stream to which the value will be written.
|
||||
* @param binary Specifies whether the value should be written in binary or text format.
|
||||
* @param forceStringEncoding When using text format, force the encoding of length headers
|
||||
* 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) {
|
||||
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);
|
||||
}
|
||||
writeLengthHeader(length, outs, type == IsoType.LLLVAR ? 3 : 2, binary, forceStringEncoding);
|
||||
} 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 {
|
||||
int _l = length*2;
|
||||
//write the length in ASCII
|
||||
if (type == IsoType.LLLBIN) {
|
||||
outs.write((_l / 100) + 48);
|
||||
}
|
||||
if (_l >= 10) {
|
||||
outs.write(((_l % 100) / 10) + 48);
|
||||
} else {
|
||||
outs.write(48);
|
||||
}
|
||||
outs.write((_l % 10) + 48);
|
||||
}
|
||||
writeLengthHeader(binary ? length : length*2, outs, type == IsoType.LLLBIN ? 3 : 2, binary, forceStringEncoding);
|
||||
} else if (binary) {
|
||||
//numeric types in binary are coded like this
|
||||
byte[] buf = null;
|
||||
|
||||
@@ -94,13 +94,24 @@ public class MessageFactory<T extends IsoMessage> {
|
||||
throw new IllegalArgumentException("Cannot set null encoding.");
|
||||
}
|
||||
encoding = value;
|
||||
if (parseMap.size() > 0) {
|
||||
if (!parseMap.isEmpty()) {
|
||||
for (Map<Integer, FieldParseInfo> pt : parseMap.values()) {
|
||||
for (FieldParseInfo fpi : pt.values()) {
|
||||
fpi.setCharacterEncoding(encoding);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!typeTemplates.isEmpty()) {
|
||||
for (T tmpl : typeTemplates.values()) {
|
||||
tmpl.setCharacterEncoding(encoding);
|
||||
for (int i = 2 ; i<129; i++) {
|
||||
IsoValue<?> v = tmpl.getField(i);
|
||||
if (v != null) {
|
||||
v.setCharacterEncoding(encoding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the encoding used to parse ALPHA, LLVAR and LLLVAR fields. The default is the file.encoding
|
||||
@@ -143,12 +154,12 @@ public class MessageFactory<T extends IsoMessage> {
|
||||
}
|
||||
/** Returns a custom field encoder/decoder for the specified field number, if one is available. */
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> CustomField<T> getCustomField(int index) {
|
||||
public <F> CustomField<F> getCustomField(int index) {
|
||||
return customFields.get(index);
|
||||
}
|
||||
/** Returns a custom field encoder/decoder for the specified field number, if one is available. */
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> CustomField<T> getCustomField(Integer index) {
|
||||
public <F> CustomField<F> getCustomField(Integer index) {
|
||||
return customFields.get(index);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ public abstract class FieldParseInfo {
|
||||
protected IsoType type;
|
||||
protected int length;
|
||||
private String encoding = System.getProperty("file.encoding");
|
||||
protected boolean forceStringDecoding;
|
||||
|
||||
/** Creates a new instance that parses a value of the specified type, with the specified length.
|
||||
* The length is only useful for ALPHA and NUMERIC types.
|
||||
@@ -47,6 +48,13 @@ public abstract class FieldParseInfo {
|
||||
length = len;
|
||||
}
|
||||
|
||||
/** Specified whether length headers for variable-length fields in text mode should
|
||||
* be decoded using proper string conversion with the character encoding. Default is false,
|
||||
* which means use the old behavior of decoding as ASCII. */
|
||||
public void setForceStringDecoding(boolean flag) {
|
||||
forceStringDecoding = flag;
|
||||
}
|
||||
|
||||
public void setCharacterEncoding(String value) {
|
||||
encoding = value;
|
||||
}
|
||||
@@ -119,4 +127,14 @@ public abstract class FieldParseInfo {
|
||||
return fpi;
|
||||
}
|
||||
|
||||
protected int decodeLength(byte[] buf, int pos, int digits) throws UnsupportedEncodingException {
|
||||
if (forceStringDecoding) {
|
||||
return Integer.parseInt(new String(buf, pos, digits, encoding));
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
package com.solab.iso8583.parse;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.text.ParseException;
|
||||
|
||||
import com.solab.iso8583.CustomBinaryField;
|
||||
@@ -40,7 +41,7 @@ public class LlbinParseInfo extends FieldParseInfo {
|
||||
@Override
|
||||
public <T> IsoValue<?> parse(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<T> custom)
|
||||
throws ParseException {
|
||||
throws ParseException, UnsupportedEncodingException {
|
||||
if (pos < 0) {
|
||||
throw new ParseException(String.format("Invalid LLBIN field %d position %d",
|
||||
field, pos), pos);
|
||||
@@ -48,7 +49,7 @@ public class LlbinParseInfo extends FieldParseInfo {
|
||||
throw new ParseException(String.format("Insufficient LLBIN header field %d",
|
||||
field), pos);
|
||||
}
|
||||
length = ((buf[pos] - 48) * 10) + (buf[pos + 1] - 48);
|
||||
length = decodeLength(buf, pos, 2);
|
||||
if (length < 0) {
|
||||
throw new ParseException(String.format("Invalid LLBIN field %d length %d pos %d",
|
||||
field, length, pos), pos);
|
||||
|
||||
@@ -18,6 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
package com.solab.iso8583.parse;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.text.ParseException;
|
||||
|
||||
import com.solab.iso8583.CustomBinaryField;
|
||||
@@ -40,7 +41,7 @@ public class LllbinParseInfo extends FieldParseInfo {
|
||||
@Override
|
||||
public <T> IsoValue<?> parse(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<T> custom)
|
||||
throws ParseException {
|
||||
throws ParseException, UnsupportedEncodingException {
|
||||
if (pos < 0) {
|
||||
throw new ParseException(String.format("Invalid LLLBIN field %d pos %d",
|
||||
field, pos), pos);
|
||||
@@ -48,11 +49,7 @@ public class LllbinParseInfo extends FieldParseInfo {
|
||||
throw new ParseException(String.format("Insufficient LLLBIN header field %d",
|
||||
field), pos);
|
||||
}
|
||||
if (!(Character.isDigit(buf[pos]) && Character.isDigit(buf[pos+1]) && Character.isDigit(buf[pos+2]))) {
|
||||
throw new ParseException(String.format("Invalid LLLBIN length '%s' field %d pos %d",
|
||||
new String(buf, pos, 3), field, pos), pos);
|
||||
}
|
||||
final int l = ((buf[pos] - 48) * 100) + ((buf[pos + 1] - 48) * 10) + (buf[pos + 2] - 48);
|
||||
final int l = decodeLength(buf, pos, 3);
|
||||
if (l < 0) {
|
||||
throw new ParseException(String.format("Invalid LLLBIN length %d field %d pos %d",
|
||||
l, field, pos), pos);
|
||||
|
||||
@@ -46,11 +46,7 @@ public class LllvarParseInfo extends FieldParseInfo {
|
||||
throw new ParseException(String.format(
|
||||
"Insufficient data for LLLVAR header field %d pos %d", field, pos), pos);
|
||||
}
|
||||
if (!(Character.isDigit(buf[pos]) && Character.isDigit(buf[pos+1]) && Character.isDigit(buf[pos+2]))) {
|
||||
throw new ParseException(String.format("Invalid LLLVAR length '%s' field %d pos %d",
|
||||
new String(buf, pos, 3), field, pos), pos);
|
||||
}
|
||||
length = ((buf[pos] - 48) * 100) + ((buf[pos + 1] - 48) * 10) + (buf[pos + 2] - 48);
|
||||
length = decodeLength(buf, pos, 3);
|
||||
if (length < 0) {
|
||||
throw new ParseException(String.format("Invalid LLLVAR length %d field %d pos %d",
|
||||
length, field, pos), pos);
|
||||
|
||||
@@ -44,7 +44,7 @@ public class LlvarParseInfo extends FieldParseInfo {
|
||||
} else if (pos+2 > buf.length) {
|
||||
throw new ParseException(String.format("Insufficient data for LLVAR header, pos %d", pos), pos);
|
||||
}
|
||||
length = ((buf[pos] - 48) * 10) + (buf[pos + 1] - 48);
|
||||
length = decodeLength(buf, pos, 2);
|
||||
if (length < 0) {
|
||||
throw new ParseException(String.format(
|
||||
"Invalid LLVAR length %d, field %d pos %d", length, field, pos), pos);
|
||||
|
||||
Reference in New Issue
Block a user