Oject-10438 Add Java EPC encoder and Saxon-HE extension function
Add three Java source files that enable SGTIN-96 EPC generation directly from within the DELIVERY.xsl transformation: - Gs1EpcEncoder.java: encodes SGTIN-96 (38-bit serial via MD5) and SGTIN-198 (140-bit ASCII serial); serial derived from MD5(DOCNUM+POSNR+counter) - Sgtin96Function.java: Saxon-HE ExtensionFunctionDefinition that registers gs1:encodeSgtin96FromIdoc() for use in XSLT - TransformAndEncode.java: Saxon s9api runner that registers the extension and executes DELIVERY.xsl in a single pass Also update .gitignore to track java/src/ while continuing to exclude compiled output in java/bin/.
This commit is contained in:
@@ -19,6 +19,9 @@ dist/
|
|||||||
# Package files
|
# Package files
|
||||||
*.jar
|
*.jar
|
||||||
|
|
||||||
|
# additional Libraries
|
||||||
|
libs/
|
||||||
|
|
||||||
# Maven
|
# Maven
|
||||||
target/
|
target/
|
||||||
dist/
|
dist/
|
||||||
@@ -47,7 +50,9 @@ Thumbs.db
|
|||||||
*.flv
|
*.flv
|
||||||
*.mov
|
*.mov
|
||||||
*.wmv
|
*.wmv
|
||||||
|
*.script
|
||||||
|
|
||||||
test0001.camel.yaml
|
test0001.camel.yaml
|
||||||
testing/
|
testing/
|
||||||
.continue/agents/new-config.yaml
|
.continue/agents/new-config.yaml
|
||||||
|
java/bin/
|
||||||
@@ -0,0 +1,336 @@
|
|||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
public class Gs1EpcEncoder {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
if (args.length > 0 && (args[0].equals("-h") || args[0].equals("--help") || args[0].equals("-?"))) {
|
||||||
|
printHelp();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example values — edit these to test different inputs
|
||||||
|
String gtin = "07333544290754"; // GTIN-14
|
||||||
|
String serial = "134539124202"; // Numeric for SGTIN-96, ASCII for SGTIN-198
|
||||||
|
int filter = 1; // EPC filter value (1 = POS item)
|
||||||
|
int companyPrefixDigits = 7; // GS1 company prefix length → partition 5
|
||||||
|
|
||||||
|
// Used only for the 96-idoc scheme
|
||||||
|
String idocNumber = "0000000096668203"; // 16-char SAP IDOC number
|
||||||
|
String segmentPos = "000042"; // 6-char IDOC segment position
|
||||||
|
|
||||||
|
// Pass a scheme as first argument; defaults to 198
|
||||||
|
String scheme = (args.length > 0) ? args[0] : "198";
|
||||||
|
|
||||||
|
String epcHex;
|
||||||
|
String fullMemory;
|
||||||
|
|
||||||
|
switch (scheme) {
|
||||||
|
case "96":
|
||||||
|
epcHex = encodeSgtin96(gtin, serial, filter, companyPrefixDigits);
|
||||||
|
fullMemory = buildFullMemory(epcHex, 96);
|
||||||
|
System.out.println("Scheme: SGTIN-96");
|
||||||
|
System.out.println("GTIN: " + gtin);
|
||||||
|
System.out.println("Serial: " + serial);
|
||||||
|
System.out.println("EPC (96-bit): " + epcHex.toUpperCase());
|
||||||
|
System.out.println("Full Tag Memory: " + fullMemory.toUpperCase());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "96-idoc":
|
||||||
|
epcHex = encodeSgtin96FromIdoc(gtin, idocNumber, segmentPos, filter, companyPrefixDigits);
|
||||||
|
fullMemory = buildFullMemory(epcHex, 96);
|
||||||
|
System.out.println("Scheme: SGTIN-96 (MD5 serial from IDOC)");
|
||||||
|
System.out.println("GTIN: " + gtin);
|
||||||
|
System.out.println("IDOC + Segment: " + idocNumber + segmentPos);
|
||||||
|
System.out.println("EPC (96-bit): " + epcHex.toUpperCase());
|
||||||
|
System.out.println("Full Tag Memory: " + fullMemory.toUpperCase());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "198":
|
||||||
|
epcHex = encodeSgtin198(gtin, serial, filter, companyPrefixDigits);
|
||||||
|
fullMemory = buildFullMemory(epcHex, 198);
|
||||||
|
System.out.println("Scheme: SGTIN-198");
|
||||||
|
System.out.println("GTIN: " + gtin);
|
||||||
|
System.out.println("Serial: " + serial);
|
||||||
|
System.out.println("EPC (198-bit): " + epcHex.toUpperCase());
|
||||||
|
System.out.println("Full Tag Memory: " + fullMemory.toUpperCase());
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
System.err.println("Unknown scheme: " + scheme);
|
||||||
|
System.err.println("Run with -h for usage.");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void printHelp() {
|
||||||
|
System.out.println("Gs1EpcEncoder — GS1 EPC tag encoder (SGTIN-96 / SGTIN-198)");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("USAGE");
|
||||||
|
System.out.println(" java Gs1EpcEncoder [scheme]");
|
||||||
|
System.out.println(" java Gs1EpcEncoder -h | --help | -?");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("SCHEMES");
|
||||||
|
System.out.println(" 198 SGTIN-198 (default)");
|
||||||
|
System.out.println(" 96-bit header/partition/prefix/item + 140-bit ASCII serial");
|
||||||
|
System.out.println(" Serial: up to 20 printable ASCII characters");
|
||||||
|
System.out.println(" Output: 50 hex chars (EPC) + 8 hex chars (CRC+PC) = 58 total");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println(" 96 SGTIN-96");
|
||||||
|
System.out.println(" 58-bit header/partition/prefix/item + 38-bit numeric serial");
|
||||||
|
System.out.println(" Serial: integer 0 – 274,877,906,943");
|
||||||
|
System.out.println(" Output: 24 hex chars (EPC) + 8 hex chars (CRC+PC) = 32 total");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println(" 96-idoc SGTIN-96 with MD5-derived serial");
|
||||||
|
System.out.println(" Concatenates IDOC number (16 chars) + segment position (6 chars),");
|
||||||
|
System.out.println(" MD5-hashes the result, and uses the top 38 bits as the serial.");
|
||||||
|
System.out.println(" Collision probability: 1 in 2^38 (~274 billion)");
|
||||||
|
System.out.println(" Output: same format as 96");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("PARAMETERS (set at top of main())");
|
||||||
|
System.out.println(" gtin GTIN-14 (14 digits)");
|
||||||
|
System.out.println(" digit 0 : indicator / packaging level");
|
||||||
|
System.out.println(" digits 1–N: GS1 company prefix (N = companyPrefixDigits)");
|
||||||
|
System.out.println(" digits N+1–12: item reference");
|
||||||
|
System.out.println(" digit 13 : check digit (excluded from EPC)");
|
||||||
|
System.out.println(" serial Serial number");
|
||||||
|
System.out.println(" scheme 198 : ASCII string, max 20 chars");
|
||||||
|
System.out.println(" scheme 96 : numeric string, max 274,877,906,943");
|
||||||
|
System.out.println(" filter EPC filter value (3 bits, 0–7)");
|
||||||
|
System.out.println(" 0 = all others 1 = POS item 2 = full case");
|
||||||
|
System.out.println(" 3 = reserved 4 = inner pack 5 = reserved");
|
||||||
|
System.out.println(" 6 = unit load 7 = component");
|
||||||
|
System.out.println(" companyPrefixDigits GS1 company prefix length (6–12)");
|
||||||
|
System.out.println(" determines the EPC partition value (0–6):");
|
||||||
|
System.out.println(" 12→0 11→1 10→2 9→3 8→4 7→5 6→6");
|
||||||
|
System.out.println(" idocNumber [96-idoc only] SAP IDOC number, 16 chars");
|
||||||
|
System.out.println(" e.g. 0000000096668203");
|
||||||
|
System.out.println(" segmentPos [96-idoc only] IDOC segment position, 6 chars");
|
||||||
|
System.out.println(" e.g. 000042");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("OUTPUT");
|
||||||
|
System.out.println(" EPC hex encoded EPC without CRC/PC prefix");
|
||||||
|
System.out.println(" Full Tag Memory CRC (4 hex) + PC word (4 hex) + EPC hex");
|
||||||
|
System.out.println(" CRC is a dummy 0000 (real CRC computed by reader)");
|
||||||
|
System.out.println(" PC word encodes EPC length in 16-bit words");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("EXAMPLES");
|
||||||
|
System.out.println(" java Gs1EpcEncoder 198");
|
||||||
|
System.out.println(" java Gs1EpcEncoder 96");
|
||||||
|
System.out.println(" java Gs1EpcEncoder 96-idoc");
|
||||||
|
System.out.println(" java Gs1EpcEncoder -h");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
// Encoders //
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
|
||||||
|
// Encode SGTIN-96 (96-bit EPC, numeric serial, max value 274,877,906,943)
|
||||||
|
public static String encodeSgtin96(String gtin, String serial, int filter, int prefixDigits) {
|
||||||
|
int partition = getPartition(prefixDigits);
|
||||||
|
int prefixBits = getPrefixBits(partition);
|
||||||
|
int itemBits = getItemBits(partition);
|
||||||
|
|
||||||
|
StringBuilder bits = new StringBuilder();
|
||||||
|
|
||||||
|
// 1. Header: 8 bits (SGTIN-96 = 0x30)
|
||||||
|
bits.append("00110000");
|
||||||
|
|
||||||
|
// 2. Filter + Partition: 3 bits each
|
||||||
|
bits.append(toBinary(filter, 3));
|
||||||
|
bits.append(toBinary(partition, 3));
|
||||||
|
|
||||||
|
// 3. Company prefix
|
||||||
|
String companyPrefix = gtin.substring(1, 1 + prefixDigits);
|
||||||
|
bits.append(toBinaryStringNumeric(companyPrefix, prefixBits));
|
||||||
|
|
||||||
|
// 4. Item reference (indicator digit + item digits, excluding check digit)
|
||||||
|
String itemRef = gtin.substring(0, 1) + gtin.substring(1 + prefixDigits, 13);
|
||||||
|
bits.append(toBinaryStringNumeric(itemRef, itemBits));
|
||||||
|
|
||||||
|
// 5. Serial number: 38 bits, numeric (not ASCII)
|
||||||
|
bits.append(toBinaryStringNumeric(serial, 38));
|
||||||
|
|
||||||
|
if (bits.length() != 96) {
|
||||||
|
throw new IllegalStateException("Bit length = " + bits.length() + ", expected 96");
|
||||||
|
}
|
||||||
|
|
||||||
|
return binaryToHexFixed(bits.toString(), 24); // 24 hex chars = 12 bytes = 96 bits
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode SGTIN-96 using a MD5-derived serial from an IDOC number + segment position.
|
||||||
|
// The 22-char combined input is hashed with MD5 (128 bits); the top 38 bits are
|
||||||
|
// used as the numeric serial, which fits the SGTIN-96 serial field (max 274,877,906,943).
|
||||||
|
public static String encodeSgtin96FromIdoc(String gtin, String idocNumber, String segmentPos, int filter, int prefixDigits) {
|
||||||
|
String serial = md5ToSerial38(idocNumber + segmentPos);
|
||||||
|
return encodeSgtin96(gtin, serial, filter, prefixDigits);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MD5-hash the input string and return the top 38 bits as a decimal string.
|
||||||
|
// 128-bit MD5 → shift right by 90 → 38 bits → fits SGTIN-96 serial field.
|
||||||
|
private static String md5ToSerial38(String input) {
|
||||||
|
try {
|
||||||
|
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||||
|
byte[] hash = md.digest(input.getBytes(StandardCharsets.UTF_8));
|
||||||
|
// Interpret all 16 bytes as an unsigned 128-bit integer, then take the top 38 bits
|
||||||
|
BigInteger full128 = new BigInteger(1, hash); // 1 = positive/unsigned
|
||||||
|
long serial38 = full128.shiftRight(128 - 38).longValue();
|
||||||
|
return Long.toString(serial38);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new IllegalStateException("MD5 not available", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode SGTIN-198 (198-bit EPC, 7-bit ASCII serial, max 20 characters)
|
||||||
|
public static String encodeSgtin198(String gtin, String serial, int filter, int prefixDigits) {
|
||||||
|
int partition = getPartition(prefixDigits);
|
||||||
|
int prefixBits = getPrefixBits(partition);
|
||||||
|
int itemBits = getItemBits(partition);
|
||||||
|
|
||||||
|
StringBuilder bits = new StringBuilder();
|
||||||
|
|
||||||
|
// 1. Header: 8 bits (SGTIN-198 = 0x36)
|
||||||
|
bits.append("00110110");
|
||||||
|
|
||||||
|
// 2. Filter + Partition: 3 bits each
|
||||||
|
bits.append(toBinary(filter, 3));
|
||||||
|
bits.append(toBinary(partition, 3));
|
||||||
|
|
||||||
|
// 3. Company prefix
|
||||||
|
String companyPrefix = gtin.substring(1, 1 + prefixDigits);
|
||||||
|
bits.append(toBinaryStringNumeric(companyPrefix, prefixBits));
|
||||||
|
|
||||||
|
// 4. Item reference (indicator digit + item digits, excluding check digit)
|
||||||
|
String itemRef = gtin.substring(0, 1) + gtin.substring(1 + prefixDigits, 13);
|
||||||
|
bits.append(toBinaryStringNumeric(itemRef, itemBits));
|
||||||
|
|
||||||
|
// 5. Serial number: 140 bits, 7-bit ASCII, zero-padded
|
||||||
|
StringBuilder serialBits = new StringBuilder();
|
||||||
|
for (char c : serial.toCharArray()) {
|
||||||
|
serialBits.append(toBinary((int) c, 7));
|
||||||
|
}
|
||||||
|
while (serialBits.length() < 140) {
|
||||||
|
serialBits.append('0');
|
||||||
|
}
|
||||||
|
bits.append(serialBits.substring(0, 140));
|
||||||
|
|
||||||
|
if (bits.length() != 198) {
|
||||||
|
throw new IllegalStateException("Bit length = " + bits.length() + ", expected 198");
|
||||||
|
}
|
||||||
|
|
||||||
|
return binaryToHexFixed(bits.toString(), 50); // 50 hex chars = 25 bytes = 200 bits (2-bit pad)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode SGTIN-198 — legacy version kept for reference (includes check digit in item ref, bug)
|
||||||
|
public static String encodeSgtin198_old(String gtin, String serial, int filter, int prefixDigits) {
|
||||||
|
int partition = getPartition(prefixDigits);
|
||||||
|
int prefixBits = getPrefixBits(partition);
|
||||||
|
int itemBits = getItemBits(partition);
|
||||||
|
|
||||||
|
StringBuilder bits = new StringBuilder();
|
||||||
|
|
||||||
|
bits.append("00110110");
|
||||||
|
bits.append(toBinary(filter, 3));
|
||||||
|
bits.append(toBinary(partition, 3));
|
||||||
|
|
||||||
|
String companyPrefix = gtin.substring(1, 1 + prefixDigits);
|
||||||
|
bits.append(toBinaryStringNumeric(companyPrefix, prefixBits));
|
||||||
|
|
||||||
|
// Bug: uses index 14, which includes the check digit at position 13
|
||||||
|
String itemRef = gtin.substring(0, 1) + gtin.substring(1 + prefixDigits, 14);
|
||||||
|
bits.append(toBinaryStringNumeric(itemRef, itemBits));
|
||||||
|
|
||||||
|
StringBuilder serialBits = new StringBuilder();
|
||||||
|
for (char c : serial.toCharArray()) {
|
||||||
|
serialBits.append(toBinary((int) c, 7));
|
||||||
|
}
|
||||||
|
while (serialBits.length() < 140) {
|
||||||
|
serialBits.append('0');
|
||||||
|
}
|
||||||
|
bits.append(serialBits.substring(0, 140));
|
||||||
|
|
||||||
|
if (bits.length() != 198) {
|
||||||
|
throw new IllegalStateException("Bit length = " + bits.length() + ", expected 198");
|
||||||
|
}
|
||||||
|
|
||||||
|
return binaryToHexFixed(bits.toString(), 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
// Tag memory builder //
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
|
||||||
|
// Build full EPC memory (CRC + PC word + EPC).
|
||||||
|
// epcBits: total bit length of the EPC (e.g. 96 or 198).
|
||||||
|
public static String buildFullMemory(String epcHex, int epcBits) {
|
||||||
|
int epcWords = (epcBits + 15) / 16; // ceiling division: 96→6, 198→13
|
||||||
|
int pc = epcWords << 11; // PC word: upper 5 bits = EPC length in words
|
||||||
|
String pcHex = String.format("%04X", pc);
|
||||||
|
String crcHex = "0000"; // dummy CRC — real value computed by RFID reader
|
||||||
|
return crcHex + pcHex + epcHex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
// Partition helpers //
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
|
||||||
|
private static int getPartition(int prefixDigits) {
|
||||||
|
switch (prefixDigits) {
|
||||||
|
case 12: return 0;
|
||||||
|
case 11: return 1;
|
||||||
|
case 10: return 2;
|
||||||
|
case 9: return 3;
|
||||||
|
case 8: return 4;
|
||||||
|
case 7: return 5;
|
||||||
|
case 6: return 6;
|
||||||
|
default: throw new IllegalArgumentException("Invalid prefix length: " + prefixDigits + " (must be 6–12)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getPrefixBits(int partition) {
|
||||||
|
return new int[]{40, 37, 34, 30, 27, 24, 20}[partition];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getItemBits(int partition) {
|
||||||
|
return new int[]{4, 7, 10, 14, 17, 20, 24}[partition];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
// Bit / hex utilities //
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
|
||||||
|
private static String toBinary(long value, int length) {
|
||||||
|
String b = Long.toBinaryString(value);
|
||||||
|
return "0".repeat(Math.max(0, length - b.length())) + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a numeric string to an exact-length binary string
|
||||||
|
private static String toBinaryStringNumeric(String number, int length) {
|
||||||
|
BigInteger value = new BigInteger(number);
|
||||||
|
String b = value.toString(2);
|
||||||
|
if (b.length() > length) {
|
||||||
|
return b.substring(b.length() - length); // truncate high bits (should not happen)
|
||||||
|
}
|
||||||
|
return "0".repeat(length - b.length()) + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a binary string to a fixed-length uppercase hex string
|
||||||
|
private static String binaryToHexFixed(String binary, int hexLength) {
|
||||||
|
int pad = (8 - (binary.length() % 8)) % 8;
|
||||||
|
binary = binary + "0".repeat(pad);
|
||||||
|
|
||||||
|
StringBuilder hex = new StringBuilder();
|
||||||
|
for (int i = 0; i < binary.length(); i += 8) {
|
||||||
|
int val = Integer.parseInt(binary.substring(i, i + 8), 2);
|
||||||
|
hex.append(String.format("%02X", val));
|
||||||
|
}
|
||||||
|
|
||||||
|
while (hex.length() < hexLength) {
|
||||||
|
hex.insert(0, '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
return hex.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import net.sf.saxon.lib.ExtensionFunctionCall;
|
||||||
|
import net.sf.saxon.lib.ExtensionFunctionDefinition;
|
||||||
|
import net.sf.saxon.om.Sequence;
|
||||||
|
import net.sf.saxon.om.StructuredQName;
|
||||||
|
import net.sf.saxon.expr.XPathContext;
|
||||||
|
import net.sf.saxon.trans.XPathException;
|
||||||
|
import net.sf.saxon.value.NumericValue;
|
||||||
|
import net.sf.saxon.value.SequenceType;
|
||||||
|
import net.sf.saxon.value.StringValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saxon-HE integrated extension function — no Saxon-PE/EE required.
|
||||||
|
* Registered programmatically via Processor.registerExtensionFunction().
|
||||||
|
*
|
||||||
|
* XSLT namespace: xmlns:gs1="urn:gs1:epc"
|
||||||
|
* XSLT call: gs1:encodeSgtin96FromIdoc($gtin14, $docnum, $posnr, 1, 7)
|
||||||
|
*/
|
||||||
|
public class Sgtin96Function extends ExtensionFunctionDefinition {
|
||||||
|
|
||||||
|
/** Must match the xmlns:gs1 URI declared in the stylesheet. */
|
||||||
|
public static final String NAMESPACE = "urn:gs1:epc";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StructuredQName getFunctionQName() {
|
||||||
|
return new StructuredQName("gs1", NAMESPACE, "encodeSgtin96FromIdoc");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SequenceType[] getArgumentTypes() {
|
||||||
|
return new SequenceType[]{
|
||||||
|
SequenceType.SINGLE_STRING, // gtin14 — GTIN-14 (14 digits)
|
||||||
|
SequenceType.SINGLE_STRING, // idocNumber — EDI_DC40/DOCNUM
|
||||||
|
SequenceType.SINGLE_STRING, // segmentPos — POSNR of the line item
|
||||||
|
SequenceType.SINGLE_INTEGER, // filter — EPC filter (1 = POS item)
|
||||||
|
SequenceType.SINGLE_INTEGER // prefixDigits — GS1 company prefix length (6–12)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SequenceType getResultType(SequenceType[] suppliedArgTypes) {
|
||||||
|
return SequenceType.SINGLE_STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExtensionFunctionCall makeCallExpression() {
|
||||||
|
return new ExtensionFunctionCall() {
|
||||||
|
@Override
|
||||||
|
public Sequence call(XPathContext ctx, Sequence[] args) throws XPathException {
|
||||||
|
try {
|
||||||
|
String gtin = args[0].head().getStringValue();
|
||||||
|
String idoc = args[1].head().getStringValue();
|
||||||
|
String posnr = args[2].head().getStringValue();
|
||||||
|
int filter = (int) ((NumericValue) args[3].head()).longValue();
|
||||||
|
int prefix = (int) ((NumericValue) args[4].head()).longValue();
|
||||||
|
|
||||||
|
String epc = Gs1EpcEncoder
|
||||||
|
.encodeSgtin96FromIdoc(gtin, idoc, posnr, filter, prefix)
|
||||||
|
.toUpperCase();
|
||||||
|
|
||||||
|
return new StringValue(epc);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new XPathException(
|
||||||
|
"Gs1EpcEncoder.encodeSgtin96FromIdoc failed: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import net.sf.saxon.s9api.Processor;
|
||||||
|
import net.sf.saxon.s9api.Serializer;
|
||||||
|
import net.sf.saxon.s9api.XsltCompiler;
|
||||||
|
import net.sf.saxon.s9api.XsltExecutable;
|
||||||
|
import net.sf.saxon.s9api.XsltTransformer;
|
||||||
|
import javax.xml.transform.stream.StreamSource;
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs DELIVERY.xsl via Saxon-HE with the Sgtin96Function extension registered.
|
||||||
|
* The XSLT calls gs1:encodeSgtin96FromIdoc() directly — no post-processing needed.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* java TransformAndEncode <idoc-xml> <xsl> <output-xml>
|
||||||
|
* java TransformAndEncode -h | --help
|
||||||
|
*/
|
||||||
|
public class TransformAndEncode {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
if (args.length == 0 || args[0].equals("-h") || args[0].equals("--help")) {
|
||||||
|
printHelp();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (args.length < 3) {
|
||||||
|
System.err.println("Error: expected 3 arguments. Run with -h for usage.");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
File idocFile = new File(args[0]);
|
||||||
|
File xslFile = new File(args[1]);
|
||||||
|
File outFile = new File(args[2]);
|
||||||
|
|
||||||
|
// Register the SGTIN-96 extension function with a Saxon-HE processor
|
||||||
|
Processor processor = new Processor(false);
|
||||||
|
processor.registerExtensionFunction(new Sgtin96Function());
|
||||||
|
|
||||||
|
// Compile and run the stylesheet
|
||||||
|
XsltCompiler compiler = processor.newXsltCompiler();
|
||||||
|
XsltExecutable executable = compiler.compile(new StreamSource(xslFile));
|
||||||
|
XsltTransformer transformer = executable.load();
|
||||||
|
transformer.setSource(new StreamSource(idocFile));
|
||||||
|
|
||||||
|
Serializer ser = processor.newSerializer(outFile);
|
||||||
|
ser.setOutputProperty(Serializer.Property.INDENT, "yes");
|
||||||
|
transformer.setDestination(ser);
|
||||||
|
transformer.transform();
|
||||||
|
|
||||||
|
System.out.println("Output: " + outFile.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void printHelp() {
|
||||||
|
System.out.println("TransformAndEncode — DELIVERY.xsl runner with SGTIN-96 EPC support");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("USAGE");
|
||||||
|
System.out.println(" java TransformAndEncode <idoc-xml> <xsl> <output-xml>");
|
||||||
|
System.out.println(" java TransformAndEncode -h | --help");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("ARGUMENTS");
|
||||||
|
System.out.println(" idoc-xml SAP ZFSHDLV IDOC input file");
|
||||||
|
System.out.println(" xsl DELIVERY.xsl stylesheet path");
|
||||||
|
System.out.println(" output-xml XMLDESADV output file");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("EPC GENERATION CONDITIONS (evaluated in the XSLT)");
|
||||||
|
System.out.println(" EDI_DC40/TEST = 'X' IDOC in test mode");
|
||||||
|
System.out.println(" E1EDL37 absent No handling unit segments");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("EXTENSION FUNCTION");
|
||||||
|
System.out.println(" Namespace: " + Sgtin96Function.NAMESPACE);
|
||||||
|
System.out.println(" Function: encodeSgtin96FromIdoc(gtin14, idoc, posnr, filter, prefixDigits)");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user