diff --git a/jpos/src/main/java/org/jpos/iso/ISOBinaryFieldPackager.java b/jpos/src/main/java/org/jpos/iso/ISOBinaryFieldPackager.java
index b2ed4f3f07..9c4f96ee21 100644
--- a/jpos/src/main/java/org/jpos/iso/ISOBinaryFieldPackager.java
+++ b/jpos/src/main/java/org/jpos/iso/ISOBinaryFieldPackager.java
@@ -18,6 +18,9 @@
package org.jpos.iso;
+import org.jpos.iso.packager.GenericPackagerParams;
+import org.xml.sax.Attributes;
+
import java.io.IOException;
import java.io.InputStream;
@@ -26,7 +29,7 @@
* @author joconnor
* @version $Revision$ $Date$
*/
-public class ISOBinaryFieldPackager extends ISOFieldPackager
+public class ISOBinaryFieldPackager extends ISOFieldPackager implements GenericPackagerParams
{
private BinaryInterpreter interpreter;
private Prefixer prefixer;
@@ -88,6 +91,22 @@ public void setPrefixer(Prefixer prefixer)
this.prefixer = prefixer;
}
+ /**
+ * Supports the {@code inclusive="true"} GenericPackager attribute for
+ * one- and two-byte binary length prefixes.
+ */
+ @Override
+ public void setGenericPackagerParams(Attributes atts) {
+ if (!Boolean.parseBoolean(atts.getValue("inclusive")))
+ return;
+ if (prefixer == BinaryPrefixer.B)
+ prefixer = SelfInclusiveBinaryPrefixer.B;
+ else if (prefixer == BinaryPrefixer.BB)
+ prefixer = SelfInclusiveBinaryPrefixer.BB;
+ else
+ throw new IllegalArgumentException("The inclusive attribute requires a binary length prefix");
+ }
+
public int getMaxPackedLength()
{
return prefixer.getPackedLength() + interpreter.getPackedLength(getLength());
diff --git a/jpos/src/main/java/org/jpos/iso/SelfInclusiveBinaryPrefixer.java b/jpos/src/main/java/org/jpos/iso/SelfInclusiveBinaryPrefixer.java
new file mode 100644
index 0000000000..556b8d11c7
--- /dev/null
+++ b/jpos/src/main/java/org/jpos/iso/SelfInclusiveBinaryPrefixer.java
@@ -0,0 +1,61 @@
+/*
+ * jPOS Project [http://jpos.org]
+ * Copyright (C) 2000-2026 jPOS Software SRL
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.jpos.iso;
+
+/**
+ * Binary prefixer whose encoded length includes the length prefix itself.
+ */
+public class SelfInclusiveBinaryPrefixer implements Prefixer {
+ /** A self-inclusive, one-byte binary length prefixer. */
+ public static final SelfInclusiveBinaryPrefixer B = new SelfInclusiveBinaryPrefixer(BinaryPrefixer.B);
+
+ /** A self-inclusive, two-byte binary length prefixer. */
+ public static final SelfInclusiveBinaryPrefixer BB = new SelfInclusiveBinaryPrefixer(BinaryPrefixer.BB);
+
+ private final BinaryPrefixer prefixer;
+
+ private SelfInclusiveBinaryPrefixer(BinaryPrefixer prefixer) {
+ this.prefixer = prefixer;
+ }
+
+ @Override
+ public void encodeLength(int length, byte[] b) throws ISOException {
+ int inclusiveLength = length + getPackedLength();
+ if (inclusiveLength > maxLength())
+ throw new ISOException("invalid len " + length + " for a self-inclusive binary prefix");
+ prefixer.encodeLength(inclusiveLength, b);
+ }
+
+ @Override
+ public int decodeLength(byte[] b, int offset) throws ISOException {
+ int inclusiveLength = prefixer.decodeLength(b, offset);
+ if (inclusiveLength < getPackedLength())
+ throw new ISOException("invalid self-inclusive binary prefix " + inclusiveLength);
+ return inclusiveLength - getPackedLength();
+ }
+
+ @Override
+ public int getPackedLength() {
+ return prefixer.getPackedLength();
+ }
+
+ private int maxLength() {
+ return getPackedLength() == 1 ? 0xFF : 0xFFFF;
+ }
+}
diff --git a/jpos/src/main/java/org/jpos/iso/packager/GenericPackager.java b/jpos/src/main/java/org/jpos/iso/packager/GenericPackager.java
index 6aef9a9124..61f4e5e149 100644
--- a/jpos/src/main/java/org/jpos/iso/packager/GenericPackager.java
+++ b/jpos/src/main/java/org/jpos/iso/packager/GenericPackager.java
@@ -481,6 +481,8 @@ else if (localName.equals("isofield"))
f.setLength(Integer.parseInt(size));
f.setPad(Boolean.parseBoolean(pad));
f.setTrim(Boolean.parseBoolean(trim));
+ if (f instanceof GenericPackagerParams)
+ ((GenericPackagerParams)f).setGenericPackagerParams(atts);
// Modified for using TaggedFieldPackager
if( f instanceof TaggedFieldPackager){
((TaggedFieldPackager)f).setToken( token );
diff --git a/jpos/src/main/resources/org/jpos/iso/packager/genericpackager.dtd b/jpos/src/main/resources/org/jpos/iso/packager/genericpackager.dtd
index 3029cfcf44..56187a0315 100644
--- a/jpos/src/main/resources/org/jpos/iso/packager/genericpackager.dtd
+++ b/jpos/src/main/resources/org/jpos/iso/packager/genericpackager.dtd
@@ -19,6 +19,7 @@
+
@@ -39,3 +40,4 @@
+
diff --git a/jpos/src/test/java/org/jpos/iso/SelfInclusiveBinaryPrefixerTest.java b/jpos/src/test/java/org/jpos/iso/SelfInclusiveBinaryPrefixerTest.java
new file mode 100644
index 0000000000..cdc85f28f9
--- /dev/null
+++ b/jpos/src/test/java/org/jpos/iso/SelfInclusiveBinaryPrefixerTest.java
@@ -0,0 +1,43 @@
+/*
+ * jPOS Project [http://jpos.org]
+ * Copyright (C) 2000-2026 jPOS Software SRL
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.jpos.iso;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class SelfInclusiveBinaryPrefixerTest {
+ @Test
+ void encodesAndDecodesOneByteLengths() throws Exception {
+ byte[] prefix = new byte[1];
+ SelfInclusiveBinaryPrefixer.B.encodeLength(10, prefix);
+
+ assertEquals(11, prefix[0] & 0xFF);
+ assertEquals(10, SelfInclusiveBinaryPrefixer.B.decodeLength(prefix, 0));
+ }
+
+ @Test
+ void encodesAndDecodesTwoByteLengths() throws Exception {
+ byte[] prefix = new byte[2];
+ SelfInclusiveBinaryPrefixer.BB.encodeLength(10, prefix);
+
+ assertEquals(12, (prefix[0] & 0xFF) * 256 + (prefix[1] & 0xFF));
+ assertEquals(10, SelfInclusiveBinaryPrefixer.BB.decodeLength(prefix, 0));
+ }
+}
diff --git a/jpos/src/test/java/org/jpos/iso/packager/GenericPackagerTest.java b/jpos/src/test/java/org/jpos/iso/packager/GenericPackagerTest.java
index 96eb24a7e3..6f8f1468ff 100644
--- a/jpos/src/test/java/org/jpos/iso/packager/GenericPackagerTest.java
+++ b/jpos/src/test/java/org/jpos/iso/packager/GenericPackagerTest.java
@@ -18,6 +18,7 @@
package org.jpos.iso.packager;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
@@ -30,6 +31,7 @@
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
+import java.nio.charset.StandardCharsets;
import java.util.EmptyStackException;
import org.jpos.core.Configuration;
@@ -39,6 +41,7 @@
import org.jpos.iso.IFA_AMOUNT;
import org.jpos.iso.IFA_BITMAP;
import org.jpos.iso.ISOException;
+import org.jpos.iso.ISOBinaryField;
import org.jpos.iso.ISOFieldPackager;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -51,6 +54,23 @@
public class GenericPackagerTest {
+ @Test
+ public void testInclusiveBinaryField() throws Exception {
+ String xml = ""
+ + ""
+ + ""
+ + "";
+ GenericPackager packager = new GenericPackager(new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)));
+
+ ISOFieldPackager fieldPackager = packager.getFieldPackager(126);
+ byte[] packed = fieldPackager.pack(new ISOBinaryField(126, new byte[] { 0x12, 0x34 }));
+ assertEquals(3, packed[0] & 0xFF);
+
+ ISOBinaryField unpacked = new ISOBinaryField(126);
+ assertEquals(3, fieldPackager.unpack(unpacked, packed, 0));
+ assertArrayEquals(new byte[] { 0x12, 0x34 }, (byte[]) unpacked.getValue());
+ }
+
@Test
public void testConstructor() throws Throwable {
GenericPackager genericPackager = new GenericPackager();