From 5a1ab03f0007884366d8b501a9ff9b3b88d34460 Mon Sep 17 00:00:00 2001 From: Alejandro Revilla Date: Sat, 29 Aug 2015 22:41:40 -0300 Subject: [PATCH] contributed FSDMsgX module - THANK YOU @chhil! --- doc/src/asciidoc/book.adoc | 11 +- doc/src/asciidoc/module_fsdmsgX.adoc | 149 +++++++++ modules/fsdmsgx/FSDMsgX.pdf | Bin 0 -> 192957 bytes modules/fsdmsgx/build.gradle | 6 + .../main/java/com/sample/hsm/HSMMessage.java | 226 +++++++++++++ .../jpos/fsdpackager/AFSDFieldPackager.java | 78 +++++ .../jpos/fsdpackager/BranchFieldPackager.java | 195 +++++++++++ .../java/org/jpos/fsdpackager/FSDField.java | 38 +++ .../java/org/jpos/fsdpackager/FSDMsgX.java | 259 +++++++++++++++ .../jpos/fsdpackager/FixedFieldPackager.java | 165 ++++++++++ .../jpos/fsdpackager/IFSDFieldPackager.java | 81 +++++ .../jpos/fsdpackager/LookAheadPackager.java | 182 ++++++++++ .../jpos/fsdpackager/OptionalPackager.java | 82 +++++ .../fsdpackager/VariableFieldPackager.java | 180 ++++++++++ .../compliance/APCICompliance.java | 28 ++ .../compliance/IPCICompliance.java | 27 ++ .../fsdpackager/compliance/NoCompliance.java | 32 ++ .../compliance/TrackDataCompliance.java | 32 ++ .../compliance/WipeCompliance.java | 38 +++ .../main/java/org/jpos/iso/FSDISOMsgX.java | 105 ++++++ .../fsdpackager/BranchFieldPackagerTest.java | 311 ++++++++++++++++++ .../fsdpackager/FixedFieldPackagerTest.java | 235 +++++++++++++ .../fsdpackager/LookAheadPackagerTest.java | 195 +++++++++++ .../VariableFieldPackagerTest.java | 240 ++++++++++++++ settings.gradle | 3 +- 25 files changed, 2894 insertions(+), 4 deletions(-) create mode 100644 doc/src/asciidoc/module_fsdmsgX.adoc create mode 100644 modules/fsdmsgx/FSDMsgX.pdf create mode 100644 modules/fsdmsgx/build.gradle create mode 100644 modules/fsdmsgx/src/main/java/com/sample/hsm/HSMMessage.java create mode 100644 modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/AFSDFieldPackager.java create mode 100644 modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/BranchFieldPackager.java create mode 100644 modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/FSDField.java create mode 100644 modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/FSDMsgX.java create mode 100644 modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/FixedFieldPackager.java create mode 100644 modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/IFSDFieldPackager.java create mode 100644 modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/LookAheadPackager.java create mode 100644 modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/OptionalPackager.java create mode 100644 modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/VariableFieldPackager.java create mode 100644 modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/compliance/APCICompliance.java create mode 100644 modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/compliance/IPCICompliance.java create mode 100644 modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/compliance/NoCompliance.java create mode 100644 modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/compliance/TrackDataCompliance.java create mode 100644 modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/compliance/WipeCompliance.java create mode 100644 modules/fsdmsgx/src/main/java/org/jpos/iso/FSDISOMsgX.java create mode 100644 modules/fsdmsgx/src/test/java/org/jpos/fsdpackager/BranchFieldPackagerTest.java create mode 100644 modules/fsdmsgx/src/test/java/org/jpos/fsdpackager/FixedFieldPackagerTest.java create mode 100644 modules/fsdmsgx/src/test/java/org/jpos/fsdpackager/LookAheadPackagerTest.java create mode 100644 modules/fsdmsgx/src/test/java/org/jpos/fsdpackager/VariableFieldPackagerTest.java diff --git a/doc/src/asciidoc/book.adoc b/doc/src/asciidoc/book.adoc index 6bb4558e7b..3949c00e65 100644 --- a/doc/src/asciidoc/book.adoc +++ b/doc/src/asciidoc/book.adoc @@ -2,8 +2,9 @@ jPOS Extended Edition ===================== :author: Alejandro Revilla :email: apr@jpos.org -:revnumber: 2.0.1 -:jposee_version: 2.0.1 +:revnumber: 2.0.9 +:jposee_version: 2.0.9 +:toc: Introduction ============ @@ -56,6 +57,10 @@ include::module_client_simulator.adoc[] include::module_sshd.adoc[] +== Contributed modules + +include::module_fsdmsgX.adoc[] + //------------------------------------------------------------ // ** UNIMPLEMENTED ** // @@ -74,5 +79,5 @@ Appendices include::appendix_copyright.adoc[] include::appendix_license.adoc[] include::appendix_CLA.adoc[] -include::appendix_CCLA.adoc[] +// include::appendix_CCLA.adoc[] diff --git a/doc/src/asciidoc/module_fsdmsgX.adoc b/doc/src/asciidoc/module_fsdmsgX.adoc new file mode 100644 index 0000000000..aab487c3d1 --- /dev/null +++ b/doc/src/asciidoc/module_fsdmsgX.adoc @@ -0,0 +1,149 @@ +=== FSDMsgX + +[frame="none",cols="20%,80%"] +|================================================================= +|*What:*| This field/mesage packager can be used to wire a message parser with java code. Its utility library to enable you to parse grammar usually used by text (can be binary as well) messages +that are field separator delimited, fixed length, branching based on data +parsed, looking ahead in the stream for a specific byte and base future +parsing decisions. Provides out of the box PCI compliance and ability to add java objects to meet you compliance needs. +|*When:*| Available as of jPOS-EE 2.0.9 +|*Who:*| The jPOS.org team (contributed by @chhil) +|*How:*| Posted by the jPOS-EE team. +|*Where:*| Directory modules/fsdpackager available in the jPOS-EE main git repository +|*Why:*| When schema based FSD does not meet your parsing needs to write more complex parsing rules. +|*Status:*| Production grade +|*Dependencies:*| module jpos +|*License:*| <> +|================================================================= + +==== Using the packagers + +===== FixedFieldPackager + +Consider a specification that states field 1 is a fixed field of 6 and field 2 is a fixed stram of 3 bytes. +Stream of bytes=123456AB +If the specification is followed: +Field1 = 123456 +Field2 = AB + +.FixedFieldPackager Usage [unpacking raw bytes] +[source,java] +------------- + + FSDMsgX msg = new FSDMsgX("Example1"); + FixedFieldPackager field1 = new FixedFieldPackager( + "Field1", 6, AsciiInterpreter.INSTANCE + ); + FixedFieldPackager field2 = new FixedFieldPackager( + "Field2", 2, AsciiInterpreter.INSTANCE + ); + msg.add(field1); + msg.add(field2); + + String s = "123456ABEXTRA";// there are EXTRA bytes in the stream + + int offset = msg.unpack(s.getBytes()); + System.out.println("Offset="+offset); + System.out.println("Field1="+msg.get("Field1")); + System.out.println("Field2="+msg.get("Field2")); + System.out.println(msg.dump("dump")); + System.out.println(msg.getParserTree("tree>")); + System.out.println(msg.hexDump("")); + +------------- + +*Output* + +[source,xml] +------------ +Offset=8 +Field1=123456 +Field2=AB +dump +dump +dump +dump +tree>[Example1] +tree>Field [Field1] : Fixed [6] : 123456 +tree>Field [Field2] : Fixed [2] : AB +0000 31 32 33 34 35 36 41 42 123456AB +------------ + +* Create the main container object FSDMsgX. +* Create the individual field packagers for field1 and field2. +* Add the individual field packagers to the container. +* Call the unpack method on the input bytes to parse the stream. +* The unpac method returns the offset in the stream where the parser has reached, + we parsed a total of 8 bytes, the offset is 8 (its 0 based so its at the 9^th^ + position. + +* Notice the fields are accessible via the containers get method. +* The containers dump method, provides a pretty xml ( the prefix of "dump" to identify it in the output. +* The container has a getParseTree method that display your composite packager. + This will help once you get into complex composite packager. The use of of the + prefix "tree" is used to identify its output. +* The container has a hexdump method that dumps the hex equivalent of the + unpacked stream. Notice EXTRA is not there as there was no rule to unpack it. +* If the input string was s = "123456" then an ISOException would be thrown + telling you precisely what was wrong. org.jpos.iso.ISOException: Field + [Field2] at offset [6]:Expecting 2 bytes found 0 + +.FixedFieldPackager Usage [packing object into bytes] +[source,java] +------------- + FSDMsgX msg = new FSDMsgX("Example1"); + FixedFieldPackager field1 = new FixedFieldPackager( + "Field1", 6, AsciiInterpreter.INSTANCE + ); + FixedFieldPackager field2 = new FixedFieldPackager( + "Field2", 2, AsciiInterpreter.INSTANCE + ); + + msg.add(field1); + msg.add(field2); + + msg.set("Field1", "ABCDEF"); + msg.set("Field2", "12"); + + byte[] outStream = msg.pack(); + + System.out.println(msg.dump("dump")); + System.out.println(msg.getParserTree("tree>")); + System.out.println(msg.hexDump("")); + System.out.println(ISOUtil.hexdump(outStream)); +------------- + +*Output* +[source,xml] +------------ +dump +dump +dump +dump +tree>[Example1] +tree>Field [Field1] : Fixed [6] : ABCDEF +tree>Field [Field2] : Fixed [2] : 12 +0000 41 42 43 44 45 46 31 32 ABCDEF12 +0000 41 42 43 44 45 46 31 32 ABCDEF12 +------------ + +* Set the fields in the container. +* Call the unpack method on the container to serialize the object into a byte + array. +* You can verify that data looks accurate in dump method. +* You can verify that the parser parsed it correctly. +* You can verify the hexdump of the actual packed vyte array outstream is the + same as the hexdump of the container. + +===== VariableFieldPackager +Used when the size of the field is variable and needs a delimiter to indicate +the end of the field. + +Consider a specification that indicates a field FirstName can have a maximulm +of 20 characters and will be terminate/delimited by a semi colon followed by a +Lastname with a maximum of 10 characters terminated by a period. The delimiter +is important because I could have a name Tom, Tommy, Thomas to indicate the end +of a name a delimiter is needed. If I did bot have a FirstName, a semi colon +would be needed to indicate there is no first name. + + diff --git a/modules/fsdmsgx/FSDMsgX.pdf b/modules/fsdmsgx/FSDMsgX.pdf new file mode 100644 index 0000000000000000000000000000000000000000..716b96caa972cf6c989c19847feb321fb4ed93e9 GIT binary patch literal 192957 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 z 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 Q7ci42Uw!rR^{XfU1>x*Ega7~l literal 0 HcmV?d00001 diff --git a/modules/fsdmsgx/build.gradle b/modules/fsdmsgx/build.gradle new file mode 100644 index 0000000000..8d45f3fd17 --- /dev/null +++ b/modules/fsdmsgx/build.gradle @@ -0,0 +1,6 @@ +description = 'jPOS-EE :: FSDMsgX' + +dependencies { + compile libraries.jpos +} + diff --git a/modules/fsdmsgx/src/main/java/com/sample/hsm/HSMMessage.java b/modules/fsdmsgx/src/main/java/com/sample/hsm/HSMMessage.java new file mode 100644 index 0000000000..c710d7cbe2 --- /dev/null +++ b/modules/fsdmsgx/src/main/java/com/sample/hsm/HSMMessage.java @@ -0,0 +1,226 @@ +package com.sample.hsm; + +import java.util.HashMap; +import java.util.Map; + +import org.jpos.fsdpackager.AFSDFieldPackager; +import org.jpos.fsdpackager.BranchFieldPackager; +import org.jpos.fsdpackager.FSDMsgX; +import org.jpos.fsdpackager.FixedFieldPackager; +import org.jpos.fsdpackager.LookAheadPackager; +import org.jpos.fsdpackager.OptionalPackager; +import org.jpos.fsdpackager.compliance.TrackDataCompliance; +import org.jpos.iso.AsciiInterpreter; +import org.jpos.iso.ISOException; +import org.jpos.iso.ISOUtil; + +public class HSMMessage { + + public FSDMsgX getFSDMessage() { + + FSDMsgX message = new FSDMsgX("HSM"); + FixedFieldPackager stan = new FixedFieldPackager("stan", 4, AsciiInterpreter.INSTANCE); + message.add(stan); + + FixedFieldPackager command = new FixedFieldPackager("command", 2, AsciiInterpreter.INSTANCE); + message.add(command); + + Map commandCases = new HashMap(); + commandCases.put("A0", getA0Packager()); + commandCases.put("A1", getA1Packager()); + commandCases.put("A6", getA6Packager()); + commandCases.put("FA", getFAPackager()); + + BranchFieldPackager branchCommand = new BranchFieldPackager("commandBrancher", "command", commandCases, null); + + message.add(branchCommand); + return message; + + } + + private static AFSDFieldPackager getFAPackager() { + FixedFieldPackager f1 = new FixedFieldPackager("zmk-scheme", 1, AsciiInterpreter.INSTANCE); + FixedFieldPackager f2 = new FixedFieldPackager("zmk", 32, AsciiInterpreter.INSTANCE); + FixedFieldPackager f3 = new FixedFieldPackager("import-key-scheme", 1, AsciiInterpreter.INSTANCE); + FixedFieldPackager f4 = new FixedFieldPackager("key-to-import-under-zmk", 32, AsciiInterpreter.INSTANCE); + FixedFieldPackager f5 = new FixedFieldPackager("mode", 1, AsciiInterpreter.INSTANCE); + OptionalPackager f6 = new OptionalPackager("optiona-variant", + new FixedFieldPackager("attalla-variant", 1, AsciiInterpreter.INSTANCE)); + + FSDMsgX optionalGrp1 = new FSDMsgX("OptionalGroup1"); + AFSDFieldPackager semicolon = new FixedFieldPackager("delimiter",";",AsciiInterpreter.INSTANCE); + AFSDFieldPackager reserved1 = new FixedFieldPackager("Reserved-1","O",AsciiInterpreter.INSTANCE); + AFSDFieldPackager keySchemeLMK = new FixedFieldPackager("key-scheme-lmk",1,AsciiInterpreter.INSTANCE); + AFSDFieldPackager keyCheckValue = new FixedFieldPackager("key-check-value-len",1,AsciiInterpreter.INSTANCE); + optionalGrp1.add(semicolon); + optionalGrp1.add(reserved1); + optionalGrp1.add(keySchemeLMK); + optionalGrp1.add(keyCheckValue); + LookAheadPackager determineGrp1 = new LookAheadPackager("LAGRP1", 0, new Byte((byte)';') ,optionalGrp1, null, new String[]{"key-scheme-lmk","key-check-value-len"}, null); + + FSDMsgX optionalGrp2 = new FSDMsgX("OptionalGroup2"); + + AFSDFieldPackager lmkid = new FixedFieldPackager("LMK-ID",2,AsciiInterpreter.INSTANCE); + AFSDFieldPackager percent = new FixedFieldPackager("delimiter-2","%",AsciiInterpreter.INSTANCE); + optionalGrp2.add(percent); + optionalGrp2.add(lmkid); + LookAheadPackager determineGrp2 = new LookAheadPackager("LAGRP2", 0, new Byte((byte)'%') ,optionalGrp2, null, new String[]{"LMK-ID"}, null); + + + FSDMsgX container = new FSDMsgX("TranslateZPKReq-FA"); + container.add(f1); + container.add(f2); + container.add(f3); + container.add(f4); + container.add(f5); + container.add(f6); + container.add(determineGrp1); + container.add(determineGrp2); + + return container; + + } + + private static FSDMsgX getA0Packager() { + + FixedFieldPackager f1 = new FixedFieldPackager("mode", 1, AsciiInterpreter.INSTANCE); + FixedFieldPackager f2 = new FixedFieldPackager("key-type", 3, AsciiInterpreter.INSTANCE); + FixedFieldPackager f3 = new FixedFieldPackager("key-scheme-key-under-lmk", 1, AsciiInterpreter.INSTANCE); + FixedFieldPackager f4 = new FixedFieldPackager("scheme-zmk-or-tmk", 1, AsciiInterpreter.INSTANCE); + FixedFieldPackager f5 = new FixedFieldPackager("key-zmk-or-tmk", 32, AsciiInterpreter.INSTANCE); + FixedFieldPackager f6 = new FixedFieldPackager("key-scheme-key-under-zmk-or-tmk", 1, AsciiInterpreter.INSTANCE); + + FSDMsgX container = new FSDMsgX("GenerateKeyReq-A0"); + container.add(f1); + container.add(f2); + container.add(f3); + container.add(f4); + container.add(f5); + container.add(f6); + return container; + + } + + private static AFSDFieldPackager getA6Packager() { + FixedFieldPackager f1 = new FixedFieldPackager("key-type", 3, AsciiInterpreter.INSTANCE); + FixedFieldPackager f2 = new FixedFieldPackager("zmk-scheme", 1, AsciiInterpreter.INSTANCE); + FixedFieldPackager f3 = new FixedFieldPackager("zmk", 32, AsciiInterpreter.INSTANCE, new TrackDataCompliance()); + FixedFieldPackager f4 = new FixedFieldPackager("import-key-scheme", 1, AsciiInterpreter.INSTANCE); + FixedFieldPackager f5 = new FixedFieldPackager("key-to-import-under-zmk", 32, AsciiInterpreter.INSTANCE, + new TrackDataCompliance()); + FixedFieldPackager f6 = new FixedFieldPackager("scheme-key-encrpt-under-lmk", 1, AsciiInterpreter.INSTANCE); + OptionalPackager f7 = new OptionalPackager("optiona-variant", + new FixedFieldPackager("attalla-variant", 1, AsciiInterpreter.INSTANCE)); + + FSDMsgX container = new FSDMsgX("ImportKeyReq-A6"); + container.add(f1); + container.add(f2); + container.add(f3); + container.add(f4); + container.add(f5); + container.add(f6); + container.add(f7); + return container; + + } + + private static AFSDFieldPackager getA1Packager() { + + FixedFieldPackager f0 = new FixedFieldPackager("error", 2, AsciiInterpreter.INSTANCE); + + FixedFieldPackager f1 = new FixedFieldPackager("scheme-key-under-lmk", 1, AsciiInterpreter.INSTANCE); + FixedFieldPackager f2 = new FixedFieldPackager("key-under-lmk", 32, AsciiInterpreter.INSTANCE); + FixedFieldPackager f3 = new FixedFieldPackager("scheme-key-under-zmk", 1, AsciiInterpreter.INSTANCE); + FixedFieldPackager f4 = new FixedFieldPackager("key-under-zmk", 32, AsciiInterpreter.INSTANCE); + FixedFieldPackager f5 = new FixedFieldPackager("check-value", 6, AsciiInterpreter.INSTANCE); + + FSDMsgX container = new FSDMsgX("GenerateKeyRsp-A1"); + container.add(f0); + container.add(f1); + container.add(f2); + container.add(f3); + container.add(f4); + container.add(f5); + return container; + } + + public static void main(String[] args) throws ISOException { + HSMMessage m = new HSMMessage(); +// FSDMsgX fsd1 = m.getFSDMessage(); +// fsd1.set("stan", "1234"); +// fsd1.set("command", "A0"); +// fsd1.set("mode", "1"); +// fsd1.set("key-type", "001"); +// fsd1.set("key-scheme-key-under-lmk", "A"); +// fsd1.set("scheme-zmk-or-tmk", "B"); +// fsd1.set("key-zmk-or-tmk", ISOUtil.padleft("", 32, 'F')); +// fsd1.set("key-scheme-key-under-zmk-or-tmk", "C"); +// byte[] outStream = fsd1.pack(); +// System.out.println(ISOUtil.hexdump(outStream)); +// System.out.println(fsd1.dump("")); +// System.out.println(fsd1.getParserTree("")); +// +// FSDMsgX fsd3 = m.getFSDMessage(); +// fsd3.set("stan", "1234"); +// fsd3.set("command", "A6"); +// fsd3.set("key-type", "001"); +// fsd3.set("zmk-scheme", "A"); +// fsd3.set("zmk", ISOUtil.padleft("", 32, 'A')); +// fsd3.set("import-key-scheme", "X"); +// fsd3.set("key-to-import-under-zmk", ISOUtil.padleft("", 32, 'B')); +// fsd3.set("scheme-key-encrpt-under-lmk", "C"); +// fsd3.set("attalla-variant", "V"); +// byte[] outStream3 = fsd3.pack(); +// System.out.println(ISOUtil.hexdump(outStream3)); +// System.out.println(fsd3.dump("")); +// System.out.println(fsd3.getParserTree("")); +// +// FSDMsgX fsd4 = m.getFSDMessage(); +// fsd4.set("stan", "1234"); +// fsd4.set("command", "A6"); +// fsd4.set("key-type", "001"); +// fsd4.set("zmk-scheme", "A"); +// fsd4.set("zmk", ISOUtil.padleft("", 32, 'A')); +// fsd4.set("import-key-scheme", "X"); +// fsd4.set("key-to-import-under-zmk", ISOUtil.padleft("", 32, 'B')); +// fsd4.set("scheme-key-encrpt-under-lmk", "C"); +// byte[] outStream4 = fsd4.pack(); +// System.out.println(ISOUtil.hexdump(outStream4)); +// System.out.println(fsd4.dump("")); +// System.out.println(fsd4.getParserTree("")); + + + FSDMsgX fsd2 = m.getFSDMessage(); + fsd2.set("stan", "1234"); + fsd2.set("command", "FA"); + fsd2.set("zmk-scheme", "A"); + fsd2.set("zmk", ISOUtil.padleft("", 32, 'A')); + fsd2.set("import-key-scheme", "B"); + fsd2.set("key-to-import-under-zmk",ISOUtil.padleft("", 32, 'A')); + fsd2.set("mode", "1"); + fsd2.set("attalla-variant", "V"); + + //optional group fields + fsd2.set("key-scheme-lmk", "U"); + fsd2.set("key-check-value-len", "0"); + + //optional grp2 field + fsd2.set("LMK-ID", "00"); + + + + byte[] outStream2 = fsd2.pack(); + System.out.println(ISOUtil.hexdump(outStream2)); + System.out.println(fsd2.dump("")); + System.out.println(fsd2.getParserTree("")); + + +// FSDMsgX mess = m.getFSDMessage(); +// mess.unpack(outStream2); +// System.out.println(mess.dump("")); +// System.out.println(mess.getParserTree("")); + + + } + +} diff --git a/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/AFSDFieldPackager.java b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/AFSDFieldPackager.java new file mode 100644 index 0000000000..4d05b248df --- /dev/null +++ b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/AFSDFieldPackager.java @@ -0,0 +1,78 @@ +/* + * jPOS Project [http://jpos.org] + * Copyright (C) 2000-2015 Alejandro P. Revilla + * + * 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.fsdpackager; + +import java.util.Map; + +import org.jpos.iso.ISOException; + +public class AFSDFieldPackager implements IFSDFieldPackager { + + private String name; + + + /* (non-Javadoc) + * @see org.jpos.fsdpackager.IFSDFieldPackager#pack(java.util.Map) + */ + public byte[] pack(Map setfields) throws ISOException { + // TODO Auto-generated method stub + return null; + } + + public String getValue() { + // TODO Auto-generated method stub + return null; + } + + public void setValue(String value) { + // TODO Auto-generated method stub + + } + + public int unpack(byte[] inStream, int offset, Map fields) throws ISOException { + // TODO Auto-generated method stub + return 0; + } + + public String getName() { + return name; + } + + public void setName(String fieldName) { + this.name = fieldName; + } + + public String dump(String prefix, Map setfields) { + + return ""; + + } + + public byte[] hexDump(String prefix,Map setfields) { + // TODO Auto-generated method stub + return null; + } + + public String getParserTree(String prefix) { + // TODO Auto-generated method stub + return null; + } + + +} diff --git a/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/BranchFieldPackager.java b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/BranchFieldPackager.java new file mode 100644 index 0000000000..f7463336e6 --- /dev/null +++ b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/BranchFieldPackager.java @@ -0,0 +1,195 @@ +/* + * jPOS Project [http://jpos.org] + * Copyright (C) 2000-2015 Alejandro P. Revilla + * + * 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.fsdpackager; + +import java.util.Map; + +import org.jpos.iso.ISOException; + +/** + * @author Murtuza + * + */ +public class BranchFieldPackager extends AFSDFieldPackager { + + private String switchField; + private Map switchCases; + private AFSDFieldPackager defaultCase; + + /** + * @param name + * Name of this field. + * @param switchField + * Name of the field whose parsed value value will be used to + * determine packager to follow. + * @param switchCases + * A map containing values to packager mapping. + * @param defaultCase + * If the map cannot find the value in the map, uses this default + * packager to handle it. + */ + public BranchFieldPackager(String name, String switchField, Map switchCases, + AFSDFieldPackager defaultCase) { + + this.setName(name); + this.switchField = switchField; + this.switchCases = switchCases; + this.defaultCase = defaultCase; + + } + + /* + * (non-Javadoc) + * + * @see org.jpos.fsdpackager.AFSDFieldPackager#unpack(byte[], int, + * java.util.Map) + */ + @Override + public int unpack(byte[] inStream, int offset, Map fields) throws ISOException { + + String fieldValue = fields.get(switchField); + if (fieldValue == null) { + if (defaultCase != null) { + + offset = defaultCase.unpack(inStream, offset, fields); + fields.put(defaultCase.getName(), defaultCase.getValue()); + } + } else { + AFSDFieldPackager temp = switchCases.get(fieldValue); + if (temp == null) { + if (defaultCase != null) { + offset = defaultCase.unpack(inStream, offset, fields); + fields.put(defaultCase.getName(), defaultCase.getValue()); + } + } else { + offset = temp.unpack(inStream, offset, fields); + fields.put(temp.getName(), temp.getValue()); + } + } + + return offset; + } + + /* + * (non-Javadoc) + * + * @see org.jpos.fsdpackager.AFSDFieldPackager#pack(java.util.Map) + */ + @Override + public byte[] pack(Map fields) throws ISOException { + + String value = fields.get(switchField); + + if (value == null) { + if (defaultCase != null) { + defaultCase.setValue(fields.get(defaultCase.getName())); + return defaultCase.pack(fields); + } + return null; + } + AFSDFieldPackager selectedPackager = switchCases.get(value); + if (selectedPackager == null) { + + if (defaultCase != null) { + defaultCase.setValue(fields.get(defaultCase.getName())); + return defaultCase.pack(fields); + } + return null; + + } + selectedPackager.setValue(fields.get(selectedPackager.getName())); + return selectedPackager.pack(fields); + + } + + @Override + public String getValue() { + // TODO Auto-generated method stub + return ""; + } + + @Override + public void setValue(String value) { + // TODO Auto-generated method stub + + } + + @Override + public String dump(String prefix, Map setfields) { + + String fieldValue = setfields.get(switchField); + if (fieldValue == null) { + if (defaultCase != null) { + + return defaultCase.dump(prefix, setfields); + } + } else { + AFSDFieldPackager temp = switchCases.get(fieldValue); + if (temp == null) { + if (defaultCase != null) { + return defaultCase.dump(prefix, setfields); + } + } else { + + return temp.dump(prefix, setfields); + } + } + return ""; + + } + + /** + * Provides a description of this parser rule. + * e.g. output + *
+	 * Field [F1] : Fixed [5] : ABCDE
+	 * Field [F2] : Fixed [2] : 02
+	 * Field [F3] : [Branch]
+	 * switch (F2)
+	 * 	01:
+	 * 		Field [F4] : Fixed [3] 
+	 * 	02:
+	 * 		Field [F5] : Fixed [4] : 4444
+	 * 	default:
+	 * 
+ */ + @Override + public String getParserTree(String prefix) { + + String cases = ""; + if (switchCases == null) { + + cases += "[Not Set]" + System.lineSeparator(); + } else { + for (Map.Entry entry : switchCases.entrySet()) { + + AFSDFieldPackager fPkgr = entry.getValue(); + + cases += "\t\t" + entry.getKey() + ":" + System.lineSeparator() + fPkgr.getParserTree("\t\t\t"); + + } + } + cases += "\t\tdefault:" + System.lineSeparator() + ((defaultCase != null) + ? System.lineSeparator() + defaultCase.getParserTree("\t\t\t") : "\t\t\t[Not Set]"); + + return String.format("%sField [%s] : [Branch]%n" + "\tswitch (%s)%n" + "%s", prefix, getName(), switchField, + cases); + } + +} diff --git a/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/FSDField.java b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/FSDField.java new file mode 100644 index 0000000000..d48eb89064 --- /dev/null +++ b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/FSDField.java @@ -0,0 +1,38 @@ +/* + * jPOS Project [http://jpos.org] + * Copyright (C) 2000-2015 Alejandro P. Revilla + * + * 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.fsdpackager; + +public class FSDField { + + private String name; + private String value; + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public String getValue() { + return value; + } + public void setValue(String value) { + this.value = value; + } + +} diff --git a/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/FSDMsgX.java b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/FSDMsgX.java new file mode 100644 index 0000000000..5a0a63ad8c --- /dev/null +++ b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/FSDMsgX.java @@ -0,0 +1,259 @@ +/* + * jPOS Project [http://jpos.org] + * Copyright (C) 2000-2015 Alejandro P. Revilla + * + * 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.fsdpackager; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.jpos.iso.ISOException; +import org.jpos.iso.ISOUtil; + +public class FSDMsgX extends AFSDFieldPackager { + + public String dump(String prefix, Map setfields) { + // TODO Auto-generated method stub + return dump(prefix); + } + + private Map fieldPackagers = new LinkedHashMap(); + private Map setfields = new LinkedHashMap(); + + public FSDMsgX(String name) { + setName(name); + } + + private FSDMsgX() { + }; + + public void add(String name, AFSDFieldPackager fsdFieldPackager) { + + getFields().put(name,fsdFieldPackager); + + } + + public void add(AFSDFieldPackager fsdFieldPackager) { + + getFields().put(fsdFieldPackager.getName(), fsdFieldPackager); + + } + @Override + public byte[] pack(Map setfields) throws ISOException { + this.setSetfields(setfields); + return pack(); + + } + + @Override + public int unpack(byte[] inStream, int offset, Map fields) throws ISOException { + // TODO Auto-generated method stub + Map f = getFields(); + + int innerOffset = unpack(inStream, offset); + + for (Map.Entry entry : getSetfields().entrySet()) { + + fields.put(entry.getKey(), entry.getValue()); + } + + return innerOffset; + + } + + public void set(String fieldName, String value) { + + IFSDFieldPackager p = getFields().get(fieldName); + + getSetfields().put(fieldName, value); + if (p != null) + p.setValue(value); + + } + + public String get(String fieldName) { + + return setfields.get(fieldName); + } + + public int unpack(byte[] inStream, int offset) throws ISOException { + + for (Map.Entry entry : getFields().entrySet()) { + + FSDField f = new FSDField(); + f.setName(entry.getKey()); + + offset = entry.getValue().unpack(inStream, offset, getSetfields()); + getSetfields().put(entry.getValue().getName(), entry.getValue().getValue()); + + } + return offset; + + } + + public int unpack(byte[] inStream) throws ISOException { + + int offset = 0; + for (Map.Entry entry : getFields().entrySet()) { + + FSDField f = new FSDField(); + f.setName(entry.getKey()); + + offset = entry.getValue().unpack(inStream, offset, getSetfields()); + getSetfields().put(entry.getValue().getName(), entry.getValue().getValue()); + + } + return offset; + + } + + public byte[] pack() throws ISOException { + + byte[] outStream = null; + for (Map.Entry entry : getFields().entrySet()) { + + byte[] temp = entry.getValue().pack(getSetfields()); + if (temp != null) { + if (outStream == null) { + outStream = temp; + } else { + byte[] outStream2 = new byte[outStream.length + temp.length]; + System.arraycopy(outStream, 0, outStream2, 0, outStream.length); + System.arraycopy(temp, 0, outStream2, outStream.length, temp.length); + outStream = outStream2; + } + } + } + return outStream; + + } + + public String dump(String prefix) { + + StringBuilder sb = new StringBuilder(); + sb.append(String.format("%s%n", prefix, getName())); + String inner_prefix = prefix + "\t"; + for (Map.Entry entry : getFields().entrySet()) { + + sb.append(entry.getValue().dump(inner_prefix, getSetfields())); + + } + sb.append(String.format("%s%n", prefix)); + return sb.toString(); + } + + public String hexDump(String prefix) { + + StringBuilder sb = new StringBuilder(); + byte[] outStream = null; + for (Map.Entry entry : getFields().entrySet()) { + + Map setFieldmap = getSetfields(); + Map fieldPackager = getFields(); + AFSDFieldPackager fPkgr = entry.getValue(); + if (fPkgr instanceof FSDMsgX) { + byte[] innerOutStream = hexDump(prefix, ((FSDMsgX) fPkgr).getSetfields()); + if (innerOutStream != null) { + + if (outStream == null) { + outStream = innerOutStream; + } else { + byte[] outStream2 = new byte[outStream.length + innerOutStream.length]; + System.arraycopy(outStream, 0, outStream2, 0, outStream.length); + System.arraycopy(innerOutStream, 0, outStream2, outStream.length, innerOutStream.length); + outStream = outStream2; + } + + } + + } else { + byte[] temp = entry.getValue().hexDump("", getSetfields()); + if (temp != null) { + if (outStream == null) { + outStream = temp; + } else { + byte[] outStream2 = new byte[outStream.length + temp.length]; + System.arraycopy(outStream, 0, outStream2, 0, outStream.length); + System.arraycopy(temp, 0, outStream2, outStream.length, temp.length); + outStream = outStream2; + } + } + } + + } + sb.append(ISOUtil.hexdump(outStream)); + sb.append(System.lineSeparator()); + return sb.toString(); + + } + + public Map getFields() { + return fieldPackagers; + } + + public void setFields(Map fields) { + this.fieldPackagers = fields; + } + + public Map getSetfields() { + return setfields; + } + + public void setSetfields(Map setfields) { + this.setfields = setfields; + } + + @Override + public byte[] hexDump(String prefix, Map setfields) { + + byte[] outStream = null; + for (Map.Entry entry : fieldPackagers.entrySet()) { + + byte[] temp = entry.getValue().hexDump(prefix, getSetfields()); + if (temp != null) { + if (outStream == null) { + outStream = temp; + } else { + byte[] outStream2 = new byte[outStream.length + temp.length]; + System.arraycopy(outStream, 0, outStream2, 0, outStream.length); + System.arraycopy(temp, 0, outStream2, outStream.length, temp.length); + outStream = outStream2; + } + } + + } + return outStream; + // sb.append(ISOUtil.hexdump(outStream)); + + } + + @Override + public String getParserTree(String prefix) { + StringBuilder sb = new StringBuilder(String.format("%s[%s]%n", prefix,getName())); + + for (Map.Entry entry : getFields().entrySet()) { + + AFSDFieldPackager fPkgr = entry.getValue(); + sb.append(fPkgr.getParserTree(prefix)); + } + + return sb.toString(); + } + + + +} diff --git a/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/FixedFieldPackager.java b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/FixedFieldPackager.java new file mode 100644 index 0000000000..7039e7d73f --- /dev/null +++ b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/FixedFieldPackager.java @@ -0,0 +1,165 @@ +/* + * jPOS Project [http://jpos.org] + * Copyright (C) 2000-2015 Alejandro P. Revilla + * + * 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.fsdpackager; + +import java.util.Map; + +import org.jpos.fsdpackager.compliance.APCICompliance; +import org.jpos.fsdpackager.compliance.NoCompliance; +import org.jpos.iso.ISOException; +import org.jpos.iso.ISOUtil; +import org.jpos.iso.Interpreter; + +public class FixedFieldPackager extends AFSDFieldPackager { + + private String value; + Interpreter interpretter; + private int size; + private boolean padLeft = true; + private boolean padright = false; + private Byte padCharacter = new Byte((byte) 0x20); //space + private APCICompliance compliance = new NoCompliance(); + + public FixedFieldPackager(String name,int size,Interpreter interpretter) { + this.interpretter = interpretter; + this.size = size; + this.setName(name); + } + public FixedFieldPackager(String name,int size,Interpreter interpretter, APCICompliance compliance) { + + this(name,size,interpretter); + this.compliance = compliance; + } + public FixedFieldPackager(String name,String value, Interpreter interpretter) { + this.interpretter = interpretter; + this.setName(name); + this.setValue(value); + this.size = value.length(); + } + + @Override + public int unpack(byte[] inStream, int offset, Map fields) throws ISOException { + + + int packedSize = interpretter.getPackedLength(size); + if (inStream.length - offset < packedSize) { + throw new ISOException(String.format("Field [%s] at offset [%d]:Expecting %d bytes found %d", getName(),offset,packedSize, inStream.length - offset)); + } + String interprettedvalue = interpretter.uninterpret(inStream, offset, size); + if (getValue() != null) { + if (!getValue().equals(interprettedvalue)) { + throw new ISOException(String.format("Field [%s] at offset [%d]:Expected %s but found %s", getName(),offset,getValue(), interprettedvalue)); + } + } + + fields.put(getName(),interprettedvalue); + value = interprettedvalue; + return offset + packedSize; + + } + + @Override + public byte[] pack(Map fields) throws ISOException { + + if (value== null) {// if the hardcoded value is in the constructor , use it. + value = fields.get(getName()); + + } + else { + fields.put(getName(), value); + } + if (value==null){ + throw new ISOException(String.format("Field [%s]: Unable to pack as field is not set",getName())); + } + if (value.length() <= size) { + if (padLeft){ + ISOUtil.padleft(getValue(), size,(char) padCharacter.byteValue() ); + } + else { + ISOUtil.padright(getValue(), size,(char) padCharacter.byteValue() ); + } + } + else { + throw new ISOException(String.format("Field [%s]:Cannot pack as data has size %d and size needs to be %d",getName(), value.length(),size)); + } + + byte[] packedbyte = new byte[interpretter.getPackedLength(size)]; + interpretter.interpret(getValue(), packedbyte, 0); + return packedbyte; + + } + + @Override + public String getValue() { + return value; + } + + @Override + public void setValue(String value) { + this.value = value; + } + + + + public void setPad(boolean padLeft) { + this.padLeft = padLeft; + this.padright = !padLeft; + } + + public Byte getPadCharacter() { + return padCharacter; + } + + public void setPadCharacter(Byte padCharacter) { + this.padCharacter = padCharacter; + } + + @Override + public String dump(String prefix,Map setfields) { + if (getValue()!=null) + return String.format("%s%n", prefix,getName(),compliance.makeCompliant(getValue())); + return ""; + } + @Override + public byte[] hexDump(String prefix,Map setfields) { + + int numberOfPackedBytes = interpretter.getPackedLength(getValue().length()); + String compliant = compliance.makeCompliant(getValue()); + byte[] temp = new byte[numberOfPackedBytes]; + try { + interpretter.interpret(compliant, temp, 0); + } catch (ISOException e) { + // TODO Auto-generated catch block + return null; + } + return temp; + + + + + } + @Override + public String getParserTree(String prefix) { + + return String.format("%sField [%s] : Fixed [%d] %s%n", prefix,getName(),size, (value==null)?"":": "+value); + } + + + +} diff --git a/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/IFSDFieldPackager.java b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/IFSDFieldPackager.java new file mode 100644 index 0000000000..e2d0ebe41e --- /dev/null +++ b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/IFSDFieldPackager.java @@ -0,0 +1,81 @@ +/* + * jPOS Project [http://jpos.org] + * Copyright (C) 2000-2015 Alejandro P. Revilla + * + * 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.fsdpackager; + +import java.util.Map; + +import org.jpos.iso.ISOException; + +public interface IFSDFieldPackager { + + /** + * @param inStream + * the byte array containing data to be parsed and unpacked into + * the {@link FSDMsgX} object. + * @param offset + * The current position where the parser is at. + * @param fields + * A map containing the field name and value. + * + * @return The offset in the instream after unpacking using current field + * packager. + * @throws ISOException + */ + public int unpack(byte[] inStream, int offset, Map fields) throws ISOException; + + /** + * @param setfields + * A map containing the field name and values tha are set up. + * @return The byte array containing data the current fieldpackager packed. + * @throws ISOException + */ + public byte[] pack(Map setfields) throws ISOException; + + public String getValue(); + + public void setValue(String value); + + /** + * @param prefix + * Prepending Text. + * @param setfields + * A map containing the field name as key and text representation + * of the value. + * @return A pretty printed xml string. + */ + public String dump(String prefix, Map setfields); + + /** + * @param prefix + * Prepending decorative text. + * @param setfields + * A map containing the field name as key and text representation + * of the value. + * @return A hexdump of the data stream. + */ + public byte[] hexDump(String prefix, Map setfields); + + /** + * @param prefix + * Prepending decorative text. + * @return A descriptive string of the parsing rule. + */ + public String getParserTree(String prefix); + +} diff --git a/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/LookAheadPackager.java b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/LookAheadPackager.java new file mode 100644 index 0000000000..023ff3e83f --- /dev/null +++ b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/LookAheadPackager.java @@ -0,0 +1,182 @@ +/* + * jPOS Project [http://jpos.org] + * Copyright (C) 2000-2015 Alejandro P. Revilla + * + * 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.fsdpackager; + +import java.util.Map; + +import org.jpos.iso.ISOException; + +public class LookAheadPackager extends AFSDFieldPackager { + + private int lookAheadOffset; + private Byte lookUpData; + private AFSDFieldPackager useIfSet; + private AFSDFieldPackager useIfNotSet; + private boolean addLooklookUpValuePresent; + private String[] fieldNamesSet; + + /** + * @param name + * the name of the field + * @param lookAheadOffset + * A positive or negative number to look up a byte. + * @param lookUpData + * Value to lookup at the lookAheadOffset + * @param useIfSet + * If lookUpData was found, the stream to follow + * @param useIfNotSet + * If lookUpData was not found, the stream to follow + * @param fieldNamesSet + * Used to determine if the lookUpData should be set at packing + * time based on fields that are set. + * @param fieldNamesnotSet + * Used to determine if the lookUpData should be set at packing + * time based on fields that are not set. + */ + public LookAheadPackager(String name, int lookAheadOffset, Byte lookUpData, AFSDFieldPackager useIfSet, + AFSDFieldPackager useIfNotSet, String[] fieldNamesSet, String[] fieldNamesnotSet) { + + this.setName(name); + this.lookAheadOffset = lookAheadOffset; + this.lookUpData = lookUpData; + this.useIfSet = useIfSet; + this.useIfNotSet = useIfNotSet; + this.fieldNamesSet = fieldNamesSet; + } + + @Override + public byte[] pack(Map setfields) throws ISOException { + + addLooklookUpValuePresent = true; + for (String fieldName : fieldNamesSet) { + if (setfields.get(fieldName) != null) { + continue; + } + addLooklookUpValuePresent = false; + break; + } + + if (addLooklookUpValuePresent) { + if (useIfSet != null) + return useIfSet.pack(setfields); + } else { + if (useIfNotSet != null) { + return useIfNotSet.pack(setfields); + } + } + + return null; + // TODO Auto-generated method stub + //return super.pack(setfields); + } + + @Override + public int unpack(byte[] inStream, int offset, Map fields) throws ISOException { + + if ((offset + lookAheadOffset) < inStream.length) { + + if (inStream[offset + lookAheadOffset] == lookUpData.byteValue()) { + if (useIfSet != null) { + offset = useIfSet.unpack(inStream, offset, fields); + fields.put(useIfSet.getName(), useIfSet.getValue()); + } + } else { + if (useIfNotSet != null) { + offset = useIfNotSet.unpack(inStream, offset, fields); + fields.put(useIfNotSet.getName(), useIfNotSet.getValue()); + + } + } + } + return offset; + + } + + @Override + public String dump(String prefix, Map setfields) { + addLooklookUpValuePresent = true; + for (String fieldName : fieldNamesSet) { + if (setfields.get(fieldName) != null) { + continue; + } + addLooklookUpValuePresent = false; + break; + } + + if (addLooklookUpValuePresent) { + if (useIfSet != null) + return useIfSet.dump(prefix, setfields); + } else { + if (useIfNotSet != null) { + return useIfNotSet.dump(prefix, setfields); + } + } + return ""; + + } + + @Override + public byte[] hexDump(String prefix, Map setfields) { + + addLooklookUpValuePresent = true; + for (String fieldName : fieldNamesSet) { + if (setfields.get(fieldName) != null) { + continue; + } + addLooklookUpValuePresent = false; + break; + } + + if (addLooklookUpValuePresent) { + if (useIfSet != null) + return useIfSet.hexDump(prefix, setfields); + } else { + if (useIfNotSet != null) { + return useIfNotSet.hexDump(prefix, setfields); + } + } + return null; + + } + + @Override + public String getParserTree(String prefix) { + + String setFieldsNames=""; + for (String name:fieldNamesSet){ + + setFieldsNames+=(name+","); + + } + String ifset = useIfSet!=null?useIfSet.getParserTree("\t\t\t\t\t"):"\t\t\t\t\t[Not Set]"+System.lineSeparator(); + String ifnotset=useIfNotSet!=null?useIfNotSet.getParserTree("\t\t\t\t\t"):"\t\t\t\t\t[Not Set]"+System.lineSeparator(); + String s = String.format("%sField [%s] : [LookAhead]%n" + + "\t\t\toffset[%d] find[0x%X]%n" + + "\t\t\t\t[if found]%n" + + "%s" + + "\t\t\t\t[if not found]%n" + + "%s" + + "\t\t\tCheck Field[%s]%n", prefix,getName(),lookAheadOffset,lookUpData.byteValue(),ifset,ifnotset,setFieldsNames); + // TODO Auto-generated method stub + return s; + } + + + +} diff --git a/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/OptionalPackager.java b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/OptionalPackager.java new file mode 100644 index 0000000000..a271f8e30d --- /dev/null +++ b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/OptionalPackager.java @@ -0,0 +1,82 @@ +/* + * jPOS Project [http://jpos.org] + * Copyright (C) 2000-2015 Alejandro P. Revilla + * + * 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.fsdpackager; + +import java.util.Map; + +import org.jpos.iso.ISOException; + +public class OptionalPackager extends AFSDFieldPackager { + + private AFSDFieldPackager fieldPackager; + + public OptionalPackager(String name,AFSDFieldPackager fieldPackager){ + + this.fieldPackager = fieldPackager; + setName(name); + + } + + @Override + public byte[] pack(Map setfields) throws ISOException { + + try { + return fieldPackager.pack(setfields); + + } catch (Exception e) { + ;// If optional was absent, pack would have thrown an exception as field was not set + } + return null; + } + + @Override + public int unpack(byte[] inStream, int offset, Map fields) throws ISOException { + try{ + return fieldPackager.unpack(inStream, offset, fields); + } + catch (Exception ex){ + ;//Do nothing, means optional field not present + } + return offset; // return original offset passed in + } + + @Override + public String getParserTree(String prefix) { + // TODO Auto-generated method stub + return String.format("%sField [%s] : [OPTIONAL]%n",prefix,getName()) +fieldPackager.getParserTree(prefix+"\t"); + + } + + @Override + public String dump(String prefix, Map setfields) { + + try { + return fieldPackager.dump(prefix, setfields); + } catch (Exception e) { + // TODO: handle exception + } + return ""; + } + + + + + + +} diff --git a/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/VariableFieldPackager.java b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/VariableFieldPackager.java new file mode 100644 index 0000000000..2fe0131edf --- /dev/null +++ b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/VariableFieldPackager.java @@ -0,0 +1,180 @@ +/* + * jPOS Project [http://jpos.org] + * Copyright (C) 2000-2015 Alejandro P. Revilla + * + * 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.fsdpackager; + +import java.util.Map; + +import org.jpos.fsdpackager.compliance.APCICompliance; +import org.jpos.fsdpackager.compliance.NoCompliance; +import org.jpos.iso.AsciiInterpreter; +import org.jpos.iso.ISOException; +import org.jpos.iso.Interpreter; + +public class VariableFieldPackager extends AFSDFieldPackager { + + String value; + Byte delimiter; + Interpreter interpreter = AsciiInterpreter.INSTANCE; + private int maxSize = Integer.MAX_VALUE; + int size; + boolean mandatory = false; + private APCICompliance compliance = new NoCompliance(); + + @Override + public int unpack(byte[] inStream, int offset, Map fields) throws ISOException { + + boolean delimiterFound = false; + int i = offset; + int lengthTotraverse; + if (maxSize <= inStream.length - offset) // length-offset = remaining + // bytes + lengthTotraverse = maxSize + offset + 1;// to incluse th delimiter + else + lengthTotraverse = inStream.length; + while (i < lengthTotraverse) { // there is no point traversing to the + // end, if we can we should traverse to + // the maxsize + if (inStream[i] == delimiter.byteValue()) { + delimiterFound = true; + break; + } + + i++; + } + if (delimiterFound || i == inStream.length) { + + byte[] dest = new byte[i - offset]; + if ((i - offset) == 0) { + // Means there is no data and its terminated by delimiter + value = ""; + } else if ((i - offset) <= maxSize) { + System.arraycopy(inStream, offset, dest, 0, i - offset); + value = interpreter.uninterpret(dest, 0, i - offset); + + } else + throw new ISOException( + String.format("Field size [%d] is greater than max size [%d] of field ", i - offset, maxSize)); + + } else { + throw new ISOException(String.format("Field [%s]: Delimiter %x not present after max size %d", getName(), + delimiter.byteValue(), maxSize)); + } + setValue(value); + return i - offset + 1; + } + + @Override + public byte[] pack(Map fields) throws ISOException { + + if (value == null || value.equals("")) { + // if field is not set, make sure to send the delimiter to indicate + // its presence. + return new byte[] { delimiter.byteValue() }; + } + if (value.length() <= maxSize) { + byte[] b = new byte[interpreter.getPackedLength(value.length() + 1)]; + interpreter.interpret(value, b, 0); + b[b.length - 1] = delimiter.byteValue(); + + return b; + } + throw new ISOException(String.format("Size [%d] is greater than maxSize[%d] ", value.length(), maxSize)); + } + + public VariableFieldPackager(String name, int maxSize, Byte delimiter, Interpreter interpretter) { + + this.maxSize = maxSize; + this.interpreter = interpretter; + this.delimiter = delimiter; + this.setName(name); + } + + public VariableFieldPackager(String name, int maxSize, Byte delimiter, Interpreter interpretter, + APCICompliance compliance) { + this(name, maxSize, delimiter, interpretter); + this.compliance = compliance; + } + + public VariableFieldPackager(String name, Byte delimiter, Interpreter interpretter) { + + this.interpreter = interpretter; + this.delimiter = delimiter; + this.setName(name); + } + + public VariableFieldPackager(String name, Byte delimiter, Interpreter interpretter, APCICompliance compliance) { + + this.interpreter = interpretter; + this.delimiter = delimiter; + this.setName(name); + this.compliance = compliance; + } + + public VariableFieldPackager(String name, int maxSize, Byte delimiter, Interpreter interpretter, + boolean mandatory) { + + this(name, maxSize, delimiter, interpretter); + this.mandatory = mandatory; + } + + @Override + public String getValue() { + // TODO Auto-generated method stub + return value; + } + + @Override + public void setValue(String value) { + this.value = value; + // TODO Auto-generated method stub + + } + + @Override + public String dump(String prefix, Map setfields) { + if (getValue()!=null) + return String.format("%s%n", prefix, getName(), + compliance.makeCompliant(getValue())); + return ""; + } + + @Override + public byte[] hexDump(String prefix, Map setfields) { + + int numberOfPackedBytes = interpreter.getPackedLength(getValue().length()); + String compliant = compliance.makeCompliant(getValue()); + byte[] temp = new byte[numberOfPackedBytes]; + try { + interpreter.interpret(compliant, temp, 0); + } catch (ISOException e) { + // TODO Auto-generated catch block + return null; + } + return temp; + + } + + @Override + public String getParserTree(String prefix) { + + return String.format("%sField [%s] : VAR[0..%d] delimiter[0x%X] or EOM%n %s", prefix, getName(),maxSize, delimiter.byteValue(), getValue()==null?"":": "+getValue()); + + } + +} diff --git a/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/compliance/APCICompliance.java b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/compliance/APCICompliance.java new file mode 100644 index 0000000000..98ac78e00b --- /dev/null +++ b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/compliance/APCICompliance.java @@ -0,0 +1,28 @@ +/* + * jPOS Project [http://jpos.org] + * Copyright (C) 2000-2015 Alejandro P. Revilla + * + * 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.fsdpackager.compliance; + +public class APCICompliance implements IPCICompliance { + + public String makeCompliant(String noncompliant) { + // TODO Auto-generated method stub + return noncompliant; + } + +} diff --git a/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/compliance/IPCICompliance.java b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/compliance/IPCICompliance.java new file mode 100644 index 0000000000..252207737c --- /dev/null +++ b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/compliance/IPCICompliance.java @@ -0,0 +1,27 @@ +/* + * jPOS Project [http://jpos.org] + * Copyright (C) 2000-2015 Alejandro P. Revilla + * + * 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.fsdpackager.compliance; + +public interface IPCICompliance { + + public String makeCompliant(String noncompliant); + + + +} diff --git a/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/compliance/NoCompliance.java b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/compliance/NoCompliance.java new file mode 100644 index 0000000000..fe20c4a434 --- /dev/null +++ b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/compliance/NoCompliance.java @@ -0,0 +1,32 @@ +/* + * jPOS Project [http://jpos.org] + * Copyright (C) 2000-2015 Alejandro P. Revilla + * + * 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.fsdpackager.compliance; + +import org.jpos.iso.ISOException; +import org.jpos.iso.ISOUtil; + +public class NoCompliance extends APCICompliance { + + @Override + public String makeCompliant(String noncompliant) { + + return super.makeCompliant(noncompliant); + } + +} diff --git a/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/compliance/TrackDataCompliance.java b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/compliance/TrackDataCompliance.java new file mode 100644 index 0000000000..e4bdaec442 --- /dev/null +++ b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/compliance/TrackDataCompliance.java @@ -0,0 +1,32 @@ +/* + * jPOS Project [http://jpos.org] + * Copyright (C) 2000-2015 Alejandro P. Revilla + * + * 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.fsdpackager.compliance; + +import org.jpos.iso.ISOUtil; + +public class TrackDataCompliance extends APCICompliance { + + @Override + public String makeCompliant(String noncompliant) { + + return ISOUtil.protect(noncompliant); + } + + +} diff --git a/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/compliance/WipeCompliance.java b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/compliance/WipeCompliance.java new file mode 100644 index 0000000000..65a4b8d345 --- /dev/null +++ b/modules/fsdmsgx/src/main/java/org/jpos/fsdpackager/compliance/WipeCompliance.java @@ -0,0 +1,38 @@ +/* + * jPOS Project [http://jpos.org] + * Copyright (C) 2000-2015 Alejandro P. Revilla + * + * 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.fsdpackager.compliance; + +import org.jpos.iso.ISOException; +import org.jpos.iso.ISOUtil; + +public class WipeCompliance extends APCICompliance { + + @Override + public String makeCompliant(String noncompliant) { + + try { + return (ISOUtil.padleft("", noncompliant.length(), '*')); + } catch (ISOException e) { + + return "BAD DATA"; + } + + } + +} diff --git a/modules/fsdmsgx/src/main/java/org/jpos/iso/FSDISOMsgX.java b/modules/fsdmsgx/src/main/java/org/jpos/iso/FSDISOMsgX.java new file mode 100644 index 0000000000..f474287cb6 --- /dev/null +++ b/modules/fsdmsgx/src/main/java/org/jpos/iso/FSDISOMsgX.java @@ -0,0 +1,105 @@ +/* + * jPOS Project [http://jpos.org] + * Copyright (C) 2000-2015 Alejandro P. Revilla + * + * 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 java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.PrintStream; +import java.util.Iterator; +import java.util.Map; + +import org.jdom.JDOMException; +import org.jpos.fsdpackager.FSDMsgX; +import org.jpos.iso.ISOException; +import org.jpos.iso.ISOMsg; +import org.jpos.util.FSDMsg; + +public class FSDISOMsgX extends ISOMsg { + FSDMsgX fsd; + public FSDISOMsgX () { + super(); + } + public FSDISOMsgX (FSDMsgX fsd) { + super(); + this.fsd = fsd; + } + public String getMTI() { + return getString(0); + } + public byte[] pack() throws ISOException { + try { + return fsd.pack(); + } catch (Exception e) { + throw new ISOException (e); + } + } + public int unpack(byte[] b) throws ISOException { + try { + return fsd.unpack (b); + + } catch (Exception e) { + throw new ISOException (e); + } + } + public void unpack (InputStream in) throws IOException, ISOException { + throw new UnsupportedOperationException(); + } + + public FSDMsgX getFSDMsg() { + return fsd; + } + public String getString (int fldno) { + return fsd.get (Integer.toString(fldno)); + } + public String getString (String fld) { + return fsd.get (fld); + } + public boolean hasField (int fldno) { + return getString(fldno) != null; + } + public boolean hasField (String fld) { + return getString(fld) != null; + } + public void dump (PrintStream p, String indent) { + if (fsd != null) + p.println( fsd.dump (indent)); + } + public void writeExternal (ObjectOutput out) throws IOException { + throw new UnsupportedOperationException(); + } + public void readExternal (ObjectInput in) + throws IOException, ClassNotFoundException + { + throw new UnsupportedOperationException(); + + } + public void setResponseMTI() { + try { + super.setResponseMTI(); + } catch (ISOException ignored) { } + } + public void set (String name, String value) { + if (value != null) + this.fsd.set (name, value); + } + private static final long serialVersionUID = 1L; +} + diff --git a/modules/fsdmsgx/src/test/java/org/jpos/fsdpackager/BranchFieldPackagerTest.java b/modules/fsdmsgx/src/test/java/org/jpos/fsdpackager/BranchFieldPackagerTest.java new file mode 100644 index 0000000000..a9f85704f3 --- /dev/null +++ b/modules/fsdmsgx/src/test/java/org/jpos/fsdpackager/BranchFieldPackagerTest.java @@ -0,0 +1,311 @@ +/* + * jPOS Project [http://jpos.org] + * Copyright (C) 2000-2015 Alejandro P. Revilla + * + * 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.fsdpackager; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import java.util.HashMap; +import java.util.Map; + +import org.jpos.iso.AsciiInterpreter; +import org.jpos.iso.ISOException; +import org.jpos.iso.LiteralInterpreter; +import org.junit.Test; + +public class BranchFieldPackagerTest { + + + @Test + public void unpackTest06() throws ISOException{ + + AFSDFieldPackager f1 = new FixedFieldPackager("F1", 5, AsciiInterpreter.INSTANCE); + AFSDFieldPackager f2 = new FixedFieldPackager("F2", 2, AsciiInterpreter.INSTANCE); + + + FSDMsgX innerFSDCase01 = new FSDMsgX("inner-1"); + AFSDFieldPackager f7 = new FixedFieldPackager("F7", 3, AsciiInterpreter.INSTANCE); + AFSDFieldPackager f6 = new FixedFieldPackager("F6", 3, AsciiInterpreter.INSTANCE); + + innerFSDCase01.add("F7",f7); + innerFSDCase01.add("F6",f6); + FSDMsgX innerFSDCase02 = new FSDMsgX("inner-1"); + + AFSDFieldPackager f8 = new FixedFieldPackager("F8", 4, AsciiInterpreter.INSTANCE); + AFSDFieldPackager f9 = new FixedFieldPackager("F9", 4, AsciiInterpreter.INSTANCE); + + innerFSDCase02.add("F8",f8); + innerFSDCase02.add("F9",f9); + Map caseMap = new HashMap(); + caseMap.put("01", innerFSDCase01); + caseMap.put("02", innerFSDCase02); + AFSDFieldPackager f3 = new BranchFieldPackager("F3", "F2", caseMap, null); + + FSDMsgX msg = new FSDMsgX("Test"); + msg.add("F1", f1); + msg.add("F2", f2); + msg.add("F3", f3); + + msg.unpack("ABCDE0244445555".getBytes()); + System.out.println(msg.dump("")); + assertEquals("ABCDE", msg.get("F1")); + assertEquals("02", msg.get("F2")); + + assertEquals("4444", msg.get("F8")); + assertEquals("5555", msg.get("F9")); + + + + } + @Test + public void packTest03() throws ISOException{ + + AFSDFieldPackager f1 = new FixedFieldPackager("F1", 5, AsciiInterpreter.INSTANCE); + AFSDFieldPackager f2 = new FixedFieldPackager("F2", 2, AsciiInterpreter.INSTANCE); + + AFSDFieldPackager case02 = new FixedFieldPackager("F5", 4, AsciiInterpreter.INSTANCE); + + FSDMsgX innerFSDCase01 = new FSDMsgX("Test"); + AFSDFieldPackager f4 = new FixedFieldPackager("F4", 3, AsciiInterpreter.INSTANCE); + + innerFSDCase01.add("Inner",f4); + Map caseMap = new HashMap(); + caseMap.put("01", innerFSDCase01); + caseMap.put("02", case02); + AFSDFieldPackager f3 = new BranchFieldPackager("F3", "F2", caseMap, null); + + FSDMsgX msg = new FSDMsgX("Test"); + msg.add("F1", f1); + msg.add("F2", f2); + msg.add("F3", f3); + + msg.set("F1", "ABCDE"); + msg.set("F2", "01"); + msg.set("F4", "333"); + msg.set("F5", "4444"); + + + byte[] outStream = msg.pack(); + + assertArrayEquals("ABCDE01333".getBytes(), outStream); + + + + } + + + @Test + public void packTest04() throws ISOException{ + + AFSDFieldPackager f1 = new FixedFieldPackager("F1", 5, AsciiInterpreter.INSTANCE); + AFSDFieldPackager f2 = new FixedFieldPackager("F2", 2, AsciiInterpreter.INSTANCE); + + AFSDFieldPackager case01 = new FixedFieldPackager("F4", 3, AsciiInterpreter.INSTANCE); + AFSDFieldPackager case02 = new FixedFieldPackager("F5", 4, AsciiInterpreter.INSTANCE); + + Map caseMap = new HashMap(); + caseMap.put("01", case01); + caseMap.put("02", case02); + AFSDFieldPackager f3 = new BranchFieldPackager("F3", "F2", caseMap, null); + + FSDMsgX msg = new FSDMsgX("Test"); + msg.add("F1", f1); + msg.add("F2", f2); + msg.add("F3", f3); + + msg.set("F1", "ABCDE"); + msg.set("F2", "03"); + msg.set("F4", "333"); + msg.set("F5", "4444"); + + + byte[] outStream = msg.pack(); + + assertArrayEquals("ABCDE03".getBytes(), outStream); + //System.out.println(ISOUtil.hexdump(outStream)); + + + + } + + @Test + public void packTest05() throws ISOException{ + + AFSDFieldPackager f1 = new FixedFieldPackager("F1", 5, AsciiInterpreter.INSTANCE); + AFSDFieldPackager f2 = new FixedFieldPackager("F2", 2, AsciiInterpreter.INSTANCE); + + AFSDFieldPackager case01 = new FixedFieldPackager("F4", 3, AsciiInterpreter.INSTANCE); + AFSDFieldPackager case02 = new FixedFieldPackager("F5", 4, AsciiInterpreter.INSTANCE); + + Map caseMap = new HashMap(); + caseMap.put("01", case01); + caseMap.put("02", case02); + AFSDFieldPackager f3 = new BranchFieldPackager("F3", "F2", caseMap, null); + + FSDMsgX msg = new FSDMsgX("Test"); + msg.add("F1", f1); + msg.add("F2", f2); + msg.add("F3", f3); + + msg.set("F1", "ABCDE"); + msg.set("F2", "02"); + msg.set("F4", "333"); + msg.set("F5", "4444"); + + + byte[] outStream = msg.pack(); + System.out.println(msg.getParserTree("")); + + assertArrayEquals("ABCDE024444".getBytes(), outStream); + //System.out.println(ISOUtil.hexdump(outStream)); + + + + } + + @Test + public void packTest08() throws ISOException{ + + AFSDFieldPackager f1 = new FixedFieldPackager("F1", 5, AsciiInterpreter.INSTANCE); + AFSDFieldPackager f2 = new FixedFieldPackager("F2", 2, AsciiInterpreter.INSTANCE); + + AFSDFieldPackager case01 = new FixedFieldPackager("F4", 3, AsciiInterpreter.INSTANCE); + AFSDFieldPackager case02 = new FixedFieldPackager("F5", 4, AsciiInterpreter.INSTANCE); + + Map caseMap = new HashMap(); + caseMap.put("01", case01); + caseMap.put("02", case02); + AFSDFieldPackager f3 = new BranchFieldPackager("F3", "F2", caseMap, null); + AFSDFieldPackager f6 = new FixedFieldPackager("F6", 1, LiteralInterpreter.INSTANCE); + + FSDMsgX msg = new FSDMsgX("Test"); + msg.add("F1", f1); + msg.add("F2", f2); + msg.add("F3", f3); + msg.add("F6", f6); + + msg.set("F1", "ABCDE"); + msg.set("F2", "01"); + msg.set("F4", "333"); + msg.set("F5", "4444"); + msg.set("F6", String.valueOf((char)0x02)); + + + byte[] outStream = msg.pack(); + + assertArrayEquals(("ABCDE01333"+(char)0x02).getBytes(), outStream); + //System.out.println(ISOUtil.hexdump(outStream)); + + } + + @Test + public void packTest07() throws ISOException{ + + AFSDFieldPackager f1 = new FixedFieldPackager("F1", 5, AsciiInterpreter.INSTANCE); + AFSDFieldPackager f2 = new FixedFieldPackager("F2", 2, AsciiInterpreter.INSTANCE); + + AFSDFieldPackager case01 = new FixedFieldPackager("F4", 3, AsciiInterpreter.INSTANCE); + AFSDFieldPackager case02 = new FixedFieldPackager("F5", 4, AsciiInterpreter.INSTANCE); + + Map caseMap = new HashMap(); + caseMap.put("01", case01); + caseMap.put("02", case02); + AFSDFieldPackager f3 = new BranchFieldPackager("F3", "F2", caseMap, null); + AFSDFieldPackager f6 = new FixedFieldPackager("F6", 1, LiteralInterpreter.INSTANCE); + + FSDMsgX msg = new FSDMsgX("Test"); + msg.add("F1", f1); + msg.add("F2", f2); + msg.add("F3", f3); + // F6 is not added to the message. + + msg.set("F1", "ABCDE"); + msg.set("F2", "01"); + msg.set("F4", "333"); + msg.set("F5", "4444"); + msg.set("F6", String.valueOf((char)0x02)); + // But F6 is set, should have no impact on the output. + + byte[] outStream = msg.pack(); + + assertArrayEquals("ABCDE01333".getBytes(), outStream); + //System.out.println(ISOUtil.hexdump(outStream)); + + } + + @Test + public void packTest06() throws ISOException{ + + AFSDFieldPackager f1 = new FixedFieldPackager("F1", 5, AsciiInterpreter.INSTANCE); + AFSDFieldPackager f2 = new FixedFieldPackager("F2", 2, AsciiInterpreter.INSTANCE); + + AFSDFieldPackager case01 = new FixedFieldPackager("F4", 3, AsciiInterpreter.INSTANCE); + AFSDFieldPackager case02 = new FixedFieldPackager("F5", 4, AsciiInterpreter.INSTANCE); + + Map caseMap = new HashMap(); + caseMap.put("01", case01); + caseMap.put("02", case02); + AFSDFieldPackager f3 = new BranchFieldPackager("F3", "F2", caseMap, null); + + FSDMsgX msg = new FSDMsgX("Test"); + msg.add("F1", f1); + msg.add("F2", f2); + msg.add("F3", f3); + + msg.set("F1", "ABCDE"); + msg.set("F2", "01"); + msg.set("F4", "333"); + msg.set("F5", "4444"); + + + byte[] outStream = msg.pack(); + + assertArrayEquals("ABCDE01333".getBytes(), outStream); + //System.out.println(ISOUtil.hexdump(outStream)); + + } + + @Test + public void unpacktest07() throws ISOException{ + AFSDFieldPackager f1 = new FixedFieldPackager("F1", 5, AsciiInterpreter.INSTANCE); + AFSDFieldPackager f2 = new FixedFieldPackager("F2", 2, AsciiInterpreter.INSTANCE); + + AFSDFieldPackager case01 = new FixedFieldPackager("F4", 3, AsciiInterpreter.INSTANCE); + AFSDFieldPackager case02 = new FixedFieldPackager("F5", 4, AsciiInterpreter.INSTANCE); + + Map caseMap = new HashMap(); + caseMap.put("01", case01); + caseMap.put("02", case02); + AFSDFieldPackager f3 = new BranchFieldPackager("F3", "F2", caseMap, null); + + FSDMsgX msg = new FSDMsgX("Test"); + msg.add("F1", f1); + msg.add("F2", f2); + msg.add("F3", f3); + + String raw = "12345" + "02" + "0000"; + msg.unpack(raw.getBytes()); + + //System.out.println(msg.dump("")); + + } + + + +} diff --git a/modules/fsdmsgx/src/test/java/org/jpos/fsdpackager/FixedFieldPackagerTest.java b/modules/fsdmsgx/src/test/java/org/jpos/fsdpackager/FixedFieldPackagerTest.java new file mode 100644 index 0000000000..d7f6597c3a --- /dev/null +++ b/modules/fsdmsgx/src/test/java/org/jpos/fsdpackager/FixedFieldPackagerTest.java @@ -0,0 +1,235 @@ +/* + * jPOS Project [http://jpos.org] + * Copyright (C) 2000-2015 Alejandro P. Revilla + * + * 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.fsdpackager; + +import static org.junit.Assert.*; + +import java.util.HashMap; +import java.util.Map; + +import org.jpos.fsdpackager.compliance.TrackDataCompliance; +import org.jpos.iso.AsciiInterpreter; +import org.jpos.iso.BCDInterpreter; +import org.jpos.iso.EbcdicInterpreter; +import org.jpos.iso.ISOException; +import org.jpos.iso.ISOUtil; +import org.jpos.iso.LiteralInterpreter; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class FixedFieldPackagerTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void unpackTest01() throws ISOException { + int size = 5; + FixedFieldPackager p = new FixedFieldPackager("F1", size, AsciiInterpreter.INSTANCE); + FSDMsgX msg = new FSDMsgX("Test"); + ; + msg.add("F1", p); + + String s = "12ABCDEFH"; + int offset = msg.unpack(s.getBytes()); + assertEquals(size, offset); + assertEquals("12ABC", msg.get("F1")); + System.out.println(msg.get("F1")); + System.out.println(msg.dump("")); + + } + + @Test + public void unpackTest02() throws ISOException { + + // byte[] f = ISOUtil.str2bcd("12345", false); + // String s = ISOUtil.bcd2str(f, 0, 5, false); + int size = 5; + FixedFieldPackager p = new FixedFieldPackager("F1", size, BCDInterpreter.RIGHT_PADDED); + FSDMsgX msg = new FSDMsgX("Test"); + ; + msg.add("F1", p); + + byte[] b = { 0x12, 0x34, 0x56, 0x78 }; + int offset = msg.unpack(b); + assertEquals(BCDInterpreter.RIGHT_PADDED.getPackedLength(5), offset); + assertEquals("12345", msg.get("F1")); + + } + + @Test + public void unpackTest03() throws ISOException { + thrown.expect(ISOException.class); + thrown.expectMessage("Field [F1] at offset [0]:Expecting 5 bytes found 4"); + + int size = 5; + FixedFieldPackager p = new FixedFieldPackager("F1", size, AsciiInterpreter.INSTANCE); + String s = "12AB"; + FSDMsgX msg = new FSDMsgX("Test"); + ; + msg.add("F1", p); + + int offset = msg.unpack(s.getBytes()); + assertEquals(size + 1, offset); + assertEquals("12ABC", msg.get("F1")); + + } + + @Test + public void unpackTest04() throws ISOException { + + FixedFieldPackager p = new FixedFieldPackager("F1", "12345", AsciiInterpreter.INSTANCE); + String s = "12345"; + FSDMsgX msg = new FSDMsgX("Test"); + ; + msg.add("F1", p); + + int offset = msg.unpack(s.getBytes()); + assertEquals(5, offset); + assertEquals("12345", msg.get("F1")); + + } + + @Test + public void unpackTest05() throws ISOException { + + thrown.expect(ISOException.class); + thrown.expectMessage("Expected 12345 but found 12346"); + FixedFieldPackager p = new FixedFieldPackager("F1", "12345", AsciiInterpreter.INSTANCE); + String s = "12346"; + FSDMsgX msg = new FSDMsgX("Test"); + ; + msg.add("F1", p); + + int offset = msg.unpack(s.getBytes()); + assertEquals(5 + 1, offset); + assertEquals("12345", msg.get("F1")); + + } + + @Test + public void unpackTest06() throws ISOException { + + thrown.expect(ISOException.class); + thrown.expectMessage("Expecting 5 bytes found 3"); + + FixedFieldPackager p = new FixedFieldPackager("F1", "12345", AsciiInterpreter.INSTANCE); + String s = "ABC"; + FSDMsgX msg = new FSDMsgX("Test"); + ; + msg.add("F1", p); + + int offset = msg.unpack(s.getBytes()); + assertEquals(5 + 1, offset); + assertEquals("12345", msg.get("F1")); + + } + + @Test + public void packTest01() throws ISOException { + thrown.expect(ISOException.class); + thrown.expectMessage("Cannot pack as data has size 7 and size needs to be 5"); + + FixedFieldPackager p = new FixedFieldPackager("F1", 5, AsciiInterpreter.INSTANCE); + String s = "ABC1234"; + FSDMsgX msg = new FSDMsgX("Test"); + ; + msg.add("F1", p); + msg.set("F1", s); + byte[] b = msg.pack(); + assertEquals(null, b); + + } + + @Test + public void packTest02() throws ISOException { + + FixedFieldPackager p = new FixedFieldPackager("F1", 5, AsciiInterpreter.INSTANCE); + String s = "ABC12"; + FSDMsgX msg = new FSDMsgX("Test"); + ; + msg.add("F1", p); + msg.set("F1", s); + byte[] stream = msg.pack(); + assertArrayEquals("ABC12".getBytes(), stream); + + } + + @Test + public void packTest03() throws ISOException { + + int size = 5; + FixedFieldPackager p = new FixedFieldPackager("F1", size, BCDInterpreter.RIGHT_PADDED); + + FSDMsgX msg = new FSDMsgX("Test"); + ; + msg.add("F1", p); + msg.set("F1", "12345"); + + byte[] b = { 0x12, 0x34, 0x50 }; + byte[] stream = msg.pack(); + assertArrayEquals(b, stream); + + } + + @Test + public void packTest09() throws ISOException { + + int size = 16; + FixedFieldPackager p = new FixedFieldPackager("F1", size, AsciiInterpreter.INSTANCE, new TrackDataCompliance()); + FSDMsgX msg = new FSDMsgX("Test"); + ; + msg.add("F1", p); + + String s = "1234567890123456"; + + msg.set("F1", s); + + byte[] b = msg.pack(); + System.out.println(ISOUtil.hexdump(b)); + System.out.println(msg.dump("")); + System.out.println(msg.hexDump("")); + System.out.println(msg.dump("")); + + } + + @Test + public void packTest10() throws ISOException { + + int size = 16; + FixedFieldPackager p = new FixedFieldPackager("F1", size, EbcdicInterpreter.INSTANCE, + new TrackDataCompliance()); + FSDMsgX msg = new FSDMsgX("Test"); + ; + msg.add("F1", p); + + String s = "1234567890123456"; + + msg.set("F1", s); + + byte[] b = msg.pack(); + System.out.println(ISOUtil.hexdump(b)); + System.out.println(msg.dump("")); + System.out.println(msg.hexDump("")); + System.out.println(msg.dump("")); + + } + +} diff --git a/modules/fsdmsgx/src/test/java/org/jpos/fsdpackager/LookAheadPackagerTest.java b/modules/fsdmsgx/src/test/java/org/jpos/fsdpackager/LookAheadPackagerTest.java new file mode 100644 index 0000000000..82e64a32f0 --- /dev/null +++ b/modules/fsdmsgx/src/test/java/org/jpos/fsdpackager/LookAheadPackagerTest.java @@ -0,0 +1,195 @@ +/* + * jPOS Project [http://jpos.org] + * Copyright (C) 2000-2015 Alejandro P. Revilla + * + * 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.fsdpackager; + +import org.jpos.iso.AsciiInterpreter; +import org.jpos.iso.ISOException; +import org.junit.Test; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +public class LookAheadPackagerTest { + + /** + * Lookahead in the stream to see if a certain byte is present, if present + * start parsing the stream based on the set lookup value. + * + * @throws ISOException + */ + @Test + + public void unpacktest01() throws ISOException { + + String s = "ABCD1234XYZ"; + FSDMsgX ifSet = new FSDMsgX("ifSet"); + AFSDFieldPackager f0_a = new FixedFieldPackager("F0", new String(new byte[] { (byte) ';' }), + AsciiInterpreter.INSTANCE); + AFSDFieldPackager f1_a = new FixedFieldPackager("F1", 4, AsciiInterpreter.INSTANCE); + AFSDFieldPackager f2_a = new FixedFieldPackager("F2", 4, AsciiInterpreter.INSTANCE); + + ifSet.add("F1", f1_a); + ifSet.add("F2", f2_a); + ifSet.add("F0", f0_a); + + FSDMsgX ifNotSet = new FSDMsgX("ifnotSet"); + AFSDFieldPackager f1_b = new FixedFieldPackager("F1", 8, AsciiInterpreter.INSTANCE); + ifNotSet.add("F1", f1_b); + + AFSDFieldPackager f3 = new FixedFieldPackager("F3", 3, AsciiInterpreter.INSTANCE); + + FSDMsgX main = new FSDMsgX("main"); + String[] setFields = { "F0" }; + AFSDFieldPackager look = new LookAheadPackager("LA", 8, Byte.valueOf((byte) ';'), ifSet, ifNotSet, setFields, + null); + main.add("LA", look); + main.add("F3", f3); + System.out.println(main.getParserTree("")); + + main.unpack(s.getBytes()); +// assertEquals("ABCD", main.get("F1")); +// assertEquals("1234", main.get("F2")); +// assertEquals(";", main.get("F0")); +// assertEquals("XYZ", main.get("F3")); + System.out.println(main.dump("")); + System.out.println(main.hexDump("")); + + System.out.println(main.getParserTree("")); + + } + + /** + * Lookahead in the stream to see if a certain byte is present, since its not present, choose the ifnotset path + * @throws ISOException + */ + + @Test + + public void unpacktest02() throws ISOException { + + String s = "ABCD1234XYZ"; + + FSDMsgX ifSet = new FSDMsgX("ifSet"); + AFSDFieldPackager f0_a = new FixedFieldPackager("F0", new String(new byte[] { (byte) ';' }), + AsciiInterpreter.INSTANCE); + AFSDFieldPackager f1_a = new FixedFieldPackager("F1", 4, AsciiInterpreter.INSTANCE); + AFSDFieldPackager f2_a = new FixedFieldPackager("F2", 4, AsciiInterpreter.INSTANCE); + + ifSet.add("F1", f1_a); + ifSet.add("F2", f2_a); + ifSet.add("F0", f0_a); + + FSDMsgX ifNotSet = new FSDMsgX("ifnotSet"); + AFSDFieldPackager f1_b = new FixedFieldPackager("F1", 8, AsciiInterpreter.INSTANCE); + ifNotSet.add("F1", f1_b); + + AFSDFieldPackager f3 = new FixedFieldPackager("F3", 3, AsciiInterpreter.INSTANCE); + + FSDMsgX main = new FSDMsgX("main"); + String[] setFields = { "F0" }; + AFSDFieldPackager look = new LookAheadPackager("LA", 8, Byte.valueOf((byte) ';'), ifSet, ifNotSet, setFields, + null); + main.add("LA", look); + main.add("F3", f3); + main.unpack(s.getBytes()); + assertEquals("ABCD1234", main.get("F1")); + assertEquals("XYZ", main.get("F3")); + + System.out.println(main.dump("")); + System.out.println(main.hexDump("")); + System.out.println(main.getParserTree("")); + + } + + + @Test + public void packtest02() throws ISOException { + + + FSDMsgX ifSet = new FSDMsgX("ifSet"); + AFSDFieldPackager f0_a = new FixedFieldPackager("F0", new String(new byte[] { (byte) ';' }), + AsciiInterpreter.INSTANCE); + AFSDFieldPackager f1_a = new FixedFieldPackager("F1", 4, AsciiInterpreter.INSTANCE); + AFSDFieldPackager f2_a = new FixedFieldPackager("F2", 4, AsciiInterpreter.INSTANCE); + + ifSet.add("F1", f1_a); + ifSet.add("F2", f2_a); + ifSet.add("F0", f0_a); + + FSDMsgX ifNotSet = new FSDMsgX("ifnotSet"); + AFSDFieldPackager f1_b = new FixedFieldPackager("F1", 8, AsciiInterpreter.INSTANCE); + ifNotSet.add("F1", f1_b); + + AFSDFieldPackager f3 = new FixedFieldPackager("F3", 3, AsciiInterpreter.INSTANCE); + + FSDMsgX main = new FSDMsgX("main"); + String[] setFields = { "F0" }; + AFSDFieldPackager look = new LookAheadPackager("LA", 8, Byte.valueOf((byte) ';'), ifSet, ifNotSet, setFields, + null); + main.add("LA", look); + main.add("F3", f3); + + main.set("F1", "ABCD1234"); + main.set("F3", "XYZ"); + byte[] outStream = main.pack(); + assertArrayEquals("ABCD1234XYZ".getBytes(), outStream); + + System.out.println(main.dump("")); + System.out.println(main.hexDump("")); + + } + + @Test + public void packtest01() throws ISOException { + + FSDMsgX ifSet = new FSDMsgX("ifSet"); + AFSDFieldPackager f0_a = new FixedFieldPackager("F0", new String(new byte[] { (byte) ';' }), + AsciiInterpreter.INSTANCE); + AFSDFieldPackager f1_a = new FixedFieldPackager("F1", 4, AsciiInterpreter.INSTANCE); + AFSDFieldPackager f2_a = new FixedFieldPackager("F2", 4, AsciiInterpreter.INSTANCE); + + ifSet.add("F1", f1_a); + ifSet.add("F2", f2_a); + ifSet.add("F0", f0_a); + + FSDMsgX ifNotSet = new FSDMsgX("ifnotSet"); + AFSDFieldPackager f1_b = new FixedFieldPackager("F1", 8, AsciiInterpreter.INSTANCE); + ifNotSet.add("F1", f1_b); + + AFSDFieldPackager f3 = new FixedFieldPackager("F3", 3, AsciiInterpreter.INSTANCE); + + FSDMsgX main = new FSDMsgX("main"); + String[] setFields = { "F0" }; + AFSDFieldPackager look = new LookAheadPackager("LA", 8, Byte.valueOf((byte) ';'), ifSet, ifNotSet, setFields, + null); + main.add("LA", look); + main.add("F3", f3); + + main.set("F1", "ABCD"); + main.set("F2", "1234"); + main.set("F3", "XYZ"); + main.set("F0", ";"); + byte[] outStream = main.pack(); + assertArrayEquals("ABCD1234;XYZ".getBytes(), outStream); + + System.out.println(main.dump("")); + System.out.println(main.hexDump("")); + + } + +} diff --git a/modules/fsdmsgx/src/test/java/org/jpos/fsdpackager/VariableFieldPackagerTest.java b/modules/fsdmsgx/src/test/java/org/jpos/fsdpackager/VariableFieldPackagerTest.java new file mode 100644 index 0000000000..193c8507e8 --- /dev/null +++ b/modules/fsdmsgx/src/test/java/org/jpos/fsdpackager/VariableFieldPackagerTest.java @@ -0,0 +1,240 @@ +/* + * jPOS Project [http://jpos.org] + * Copyright (C) 2000-2015 Alejandro P. Revilla + * + * 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.fsdpackager; + +import static org.junit.Assert.*; + +import org.jpos.iso.AsciiInterpreter; +import org.jpos.iso.ISOException; +import org.jpos.iso.ISOUtil; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class VariableFieldPackagerTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + /** + * Happy day scenario where a variable field within the max size with a + * field separator is received. The field should be available for use + * without the delimiter. + * + * @throws ISOException + */ + @Test + public void unpackTest01() throws ISOException { + + VariableFieldPackager p = new VariableFieldPackager("F1", 20, new Byte((byte) 0x1c), AsciiInterpreter.INSTANCE); + FSDMsgX msg = new FSDMsgX("Test");; + msg.add("F1", p); + String inStream = "123456" + (char) 0x1c; + msg.unpack(inStream.getBytes()); + + assertEquals("123456", msg.get("F1")); + + } + + /** + * Happy day scenario where a variable field is set without using a + * delimiter. The resulting byte array will have the data and the delimiter + * in it. + * + * @throws ISOException + */ + @Test + public void packTest01() throws ISOException { + + VariableFieldPackager f1 = new VariableFieldPackager("F1", 20, new Byte((byte) 0x1c), + AsciiInterpreter.INSTANCE); + FSDMsgX msg = new FSDMsgX("Test");; + msg.add("F1", f1); + msg.set("F1", "123456"); + + byte[] outStream = msg.pack(); + + assertArrayEquals(("123456" + (char) 0x1c).getBytes(), outStream); + + } + + /** + * Happy day scenario where a 2 variable field within the max size with + * their respective delimiters is received. The field should be available + * for use without the delimiter. + * + * @throws ISOException + */ + @Test + public void unpackTest02() throws ISOException { + + VariableFieldPackager f1 = new VariableFieldPackager("F1", 20, new Byte((byte) 0x1c), + AsciiInterpreter.INSTANCE); + VariableFieldPackager f2 = new VariableFieldPackager("F2", 5, new Byte((byte) 0x1d), AsciiInterpreter.INSTANCE); + + FSDMsgX msg = new FSDMsgX("Test");; + msg.add("F1", f1); + msg.add("F2", f2); + + String inStream = "123456" + (char) 0x1c + "ABC" + (char) 0x1d; + msg.unpack(inStream.getBytes()); + + assertEquals("123456", msg.get("F1")); + assertEquals("ABC", msg.get("F2")); + + } + + /** + * Happy day scenario where a multiple variable fields are set without using + * a delimiter. The resulting byte array will have the data of the fields + * with their respective delimiters in it. + * + * @throws ISOException + */ + + @Test + public void packTest02() throws ISOException { + + VariableFieldPackager f1 = new VariableFieldPackager("F1", 20, new Byte((byte) 0x1c), + AsciiInterpreter.INSTANCE); + VariableFieldPackager f2 = new VariableFieldPackager("F2", 5, new Byte((byte) 0x1d), AsciiInterpreter.INSTANCE); + + FSDMsgX msg = new FSDMsgX("Test");; + msg.add("F1", f1); + msg.add("F2", f2); + + msg.set("F1", "123456"); + msg.set("F2", "ABC"); + + byte[] outStream = msg.pack(); + + assertArrayEquals(("123456" + (char) 0x1c + "ABC" + (char) 0x1d).getBytes(), outStream); + + } + + /** + * The order of the packed fields is dependent on the FSDMsg2's add. Setting + * of fields can be in any order. + * + * @throws ISOException + */ + @Test + public void packTest03() throws ISOException { + + VariableFieldPackager f1 = new VariableFieldPackager("F1", 20, new Byte((byte) 0x1c), + AsciiInterpreter.INSTANCE); + VariableFieldPackager f2 = new VariableFieldPackager("F2", 5, new Byte((byte) 0x1d), AsciiInterpreter.INSTANCE); + + FSDMsgX msg = new FSDMsgX("Test");; + msg.add("F1", f1); + msg.add("F2", f2); + + msg.set("F2", "ABC"); + msg.set("F1", "123456"); + + byte[] outStream = msg.pack(); + + assertArrayEquals(("123456" + (char) 0x1c + "ABC" + (char) 0x1d).getBytes(), outStream); + + } + + /** + * Field size can be upto 20 followed by delimiter. The data available is 25 + * wide and no delimiter. An exception should be thrown indicating a + * delimiter wasnt found after the max of 20 allowed. + * + * @throws ISOException + */ + @Test + public void unpackTest03() throws ISOException { + + thrown.expect(ISOException.class); + thrown.expectMessage("Field [F1]: Delimiter 1c not present after max size 20"); + + VariableFieldPackager p = new VariableFieldPackager("F1", 20, new Byte((byte) 0x1c), AsciiInterpreter.INSTANCE); + FSDMsgX msg = new FSDMsgX("Test");; + msg.add("F1", p); + + String inStream = ISOUtil.padleft("", 25, '1') + (char) 0x1c; + msg.unpack(inStream.getBytes()); + + } + + /** + * If a variable stream comes in with data that reaches end of stream and + * does not contain a delimitter its valid. There is no point to have a + * delimiter at the end of a field if its the last field. + * + * @throws ISOException + */ + @Test + public void unpackTest04() throws ISOException { + + VariableFieldPackager p = new VariableFieldPackager("F1", 20, new Byte((byte) 0x1c), AsciiInterpreter.INSTANCE); + FSDMsgX msg = new FSDMsgX("Test");; + msg.add("F1", p); + + String inStream = ISOUtil.padleft("", 10, '1'); + msg.unpack(inStream.getBytes()); + + assertEquals(inStream, msg.get("F1")); + + } + + /** + * variable fields indicate their presence with the delimiter. If there is + * no data a delimiter needs to be sent indicating the presence of the field + * with no data. The field will be set with an empty string. + * + * @throws ISOException + */ + @Test + public void unpackTest05() throws ISOException { + + VariableFieldPackager p = new VariableFieldPackager("F1", 20, new Byte((byte) 0x1c), AsciiInterpreter.INSTANCE); + FSDMsgX msg = new FSDMsgX("Test");; + msg.add("F1", p); + + String inStream = new String(new byte[] { 0x1c }); + msg.unpack(inStream.getBytes()); + + assertEquals("", msg.get("F1")); + + } + + /** + * At time of packing if the field is not set, make sure that the delimiter + * is still available. + * + * @throws ISOException + */ + @Test + public void packTest04() throws ISOException { + + VariableFieldPackager p = new VariableFieldPackager("F1", 20, new Byte((byte) 0x1c), AsciiInterpreter.INSTANCE); + FSDMsgX msg = new FSDMsgX("Test");; + msg.add("F1", p); + + byte[] outStream = msg.pack(); + + assertArrayEquals(new byte[] { 0x1c }, outStream); + + } + +} diff --git a/settings.gradle b/settings.gradle index 127e228e84..26d3e1db08 100644 --- a/settings.gradle +++ b/settings.gradle @@ -23,7 +23,8 @@ include ':modules:core',\ ':modules:visitor', \ ':modules:rspace', \ ':modules:testbed', \ - ':modules:eerest' + ':modules:eerest', \ + ':modules:fsdmsgx' rootProject.name = 'jposee'