Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc1fe845ed | ||
|
|
61fded16d2 | ||
|
|
6bdd26fadf | ||
|
|
0a85393b87 | ||
|
|
1ccf3596d8 | ||
|
|
470d492da2 | ||
|
|
a543212d1e | ||
|
|
096faeeda0 | ||
|
|
406eb91d4b | ||
|
|
8f91cb768c | ||
|
|
078eeb6ee5 | ||
|
|
1b38a648f5 | ||
|
|
29357b8dd2 | ||
|
|
1ce67c621c | ||
|
|
1787dfb0d5 | ||
|
|
2b1f9628bf | ||
|
|
2a38ac2491 | ||
|
|
63e89e708b | ||
|
|
a086cad36d | ||
|
|
11e10bc1eb | ||
|
|
e5aa694a45 | ||
|
|
38df434d0a | ||
|
|
89d8fce075 | ||
|
|
06700670ea | ||
|
|
2e521c2b6d | ||
|
|
160f780309 | ||
|
|
efa97a023f | ||
|
|
be54b9f38e | ||
|
|
b164af83c4 | ||
|
|
eb59dcc202 | ||
|
|
3758a48d90 | ||
|
|
a591b0269f | ||
|
|
376dcdec48 | ||
|
|
f29d8f7ff4 | ||
|
|
e81116d0c6 | ||
|
|
ba56dc321a | ||
|
|
449e321645 | ||
|
|
0b50ae3f1d | ||
|
|
4a1f165c0b | ||
|
|
b1884c29d1 | ||
|
|
04ba54e522 | ||
|
|
f1055fddce | ||
|
|
ab9bded8a6 | ||
|
|
cc5918dd50 | ||
|
|
0221f8881a | ||
|
|
d297625dfa | ||
|
|
37c6a9ce9e | ||
|
|
e91a272f73 | ||
|
|
8dd759f92f | ||
|
|
6be7738166 | ||
|
|
602974ad82 | ||
|
|
3f41a9179d |
+2
-1
@@ -1,4 +1,5 @@
|
||||
target
|
||||
.idea
|
||||
.gradle
|
||||
build
|
||||
out
|
||||
*.iws
|
||||
|
||||
+8
-3
@@ -1,7 +1,7 @@
|
||||
apply plugin:'java'
|
||||
apply plugin: 'maven'
|
||||
defaultTasks 'build'
|
||||
version='1.6.0'
|
||||
version='1.8.0'
|
||||
description='Java implementation of the ISO 8583 protocol, focused on making the creation, edition and reading of ISO8583 messages as simple and flexible as possible.'
|
||||
sourceCompatibility=6
|
||||
targetCompatibility=6
|
||||
@@ -17,9 +17,9 @@ configurations {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'org.slf4j:slf4j-api:1.7.2'
|
||||
compile 'org.slf4j:slf4j-api:1.7.5'
|
||||
testCompile 'junit:junit:4.11'
|
||||
testRuntime 'org.slf4j:slf4j-simple:1.7.2'
|
||||
testRuntime 'org.slf4j:slf4j-simple:1.7.5'
|
||||
}
|
||||
|
||||
tasks.javadoc.options.use=true
|
||||
@@ -34,6 +34,11 @@ task sourcesJar(type:Jar) {
|
||||
classifier='sources'
|
||||
}
|
||||
|
||||
tasks.withType(Compile) { Compile compile ->
|
||||
compile.options.debug = true
|
||||
compile.options.compilerArgs = ['-Xlint:all']
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives javadocJar
|
||||
archives sourcesJar
|
||||
|
||||
@@ -3,19 +3,18 @@
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="slf4j" level="application" />
|
||||
<orderEntry type="library" scope="TEST" name="jUnit" level="application" />
|
||||
<orderEntry type="library" scope="TEST" name="slf4j-runtime" level="application" />
|
||||
<orderEntry type="library" scope="RUNTIME" name="slf4j-runtime" level="application" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<option name="DEFAULT_COMPILER" value="Javac" />
|
||||
<resourceExtensions />
|
||||
<wildcardResourcePatterns>
|
||||
<entry name="?*.properties" />
|
||||
<entry name="?*.xml" />
|
||||
<entry name="?*.gif" />
|
||||
<entry name="?*.png" />
|
||||
<entry name="?*.jpeg" />
|
||||
<entry name="?*.jpg" />
|
||||
<entry name="?*.html" />
|
||||
<entry name="?*.dtd" />
|
||||
<entry name="?*.tld" />
|
||||
<entry name="?*.ftl" />
|
||||
</wildcardResourcePatterns>
|
||||
<annotationProcessing>
|
||||
<profile default="true" name="Default" enabled="false">
|
||||
<processorPath useClasspath="true" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
</component>
|
||||
<component name="CopyrightManager" default="">
|
||||
<module2copyright />
|
||||
</component>
|
||||
<component name="DependencyValidationManager">
|
||||
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
|
||||
</component>
|
||||
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
|
||||
<component name="EntryPointsManager">
|
||||
<entry_points version="2.0" />
|
||||
</component>
|
||||
<component name="GradleLocalSettings">
|
||||
<option name="availableTasks">
|
||||
<set>
|
||||
<GradleTaskDescriptor>
|
||||
<option name="description" value="Assembles the outputs of this project." />
|
||||
<option name="name" value="assemble" />
|
||||
</GradleTaskDescriptor>
|
||||
<GradleTaskDescriptor>
|
||||
<option name="description" value="Assembles and tests this project." />
|
||||
<option name="name" value="build" />
|
||||
</GradleTaskDescriptor>
|
||||
<GradleTaskDescriptor>
|
||||
<option name="description" value="Assembles and tests this project and all projects that depend on it." />
|
||||
<option name="name" value="buildDependents" />
|
||||
</GradleTaskDescriptor>
|
||||
<GradleTaskDescriptor>
|
||||
<option name="description" value="Assembles and tests this project and all projects it depends on." />
|
||||
<option name="name" value="buildNeeded" />
|
||||
</GradleTaskDescriptor>
|
||||
<GradleTaskDescriptor>
|
||||
<option name="description" value="Runs all checks." />
|
||||
<option name="name" value="check" />
|
||||
</GradleTaskDescriptor>
|
||||
<GradleTaskDescriptor>
|
||||
<option name="description" value="Assembles the main classes." />
|
||||
<option name="name" value="classes" />
|
||||
</GradleTaskDescriptor>
|
||||
<GradleTaskDescriptor>
|
||||
<option name="description" value="Deletes the build directory." />
|
||||
<option name="name" value="clean" />
|
||||
</GradleTaskDescriptor>
|
||||
<GradleTaskDescriptor>
|
||||
<option name="description" value="Compiles the main Java source." />
|
||||
<option name="name" value="compileJava" />
|
||||
</GradleTaskDescriptor>
|
||||
<GradleTaskDescriptor>
|
||||
<option name="description" value="Compiles the test Java source." />
|
||||
<option name="name" value="compileTestJava" />
|
||||
</GradleTaskDescriptor>
|
||||
<GradleTaskDescriptor>
|
||||
<option name="description" value="Assembles a jar archive containing the main classes." />
|
||||
<option name="name" value="jar" />
|
||||
</GradleTaskDescriptor>
|
||||
<GradleTaskDescriptor>
|
||||
<option name="description" value="Generates Javadoc API documentation for the main source code." />
|
||||
<option name="name" value="javadoc" />
|
||||
</GradleTaskDescriptor>
|
||||
<GradleTaskDescriptor>
|
||||
<option name="description" value="Processes the main resources." />
|
||||
<option name="name" value="processResources" />
|
||||
</GradleTaskDescriptor>
|
||||
<GradleTaskDescriptor>
|
||||
<option name="description" value="Processes the test resources." />
|
||||
<option name="name" value="processTestResources" />
|
||||
</GradleTaskDescriptor>
|
||||
<GradleTaskDescriptor>
|
||||
<option name="description" value="Runs the unit tests." />
|
||||
<option name="name" value="test" />
|
||||
</GradleTaskDescriptor>
|
||||
<GradleTaskDescriptor>
|
||||
<option name="description" value="Assembles the test classes." />
|
||||
<option name="name" value="testClasses" />
|
||||
</GradleTaskDescriptor>
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
<component name="GradleSettings">
|
||||
<option name="gradleHome" value="$PROJECT_DIR$/../../../gradle-1.4" />
|
||||
<option name="preferLocalInstallationToWrapper" value="true" />
|
||||
</component>
|
||||
<component name="GradleUISettings2">
|
||||
<setting name="root" />
|
||||
</component>
|
||||
<component name="IdProvider" IDEtalkID="E31FCDAA3C23FC998F967D8CCAB8763A" />
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profiles>
|
||||
<profile version="1.0" is_locked="false">
|
||||
<option name="myName" value="Project Default" />
|
||||
<option name="myLocal" value="false" />
|
||||
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||
<option name="processCode" value="true" />
|
||||
<option name="processLiterals" value="true" />
|
||||
<option name="processComments" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</profiles>
|
||||
<option name="PROJECT_PROFILE" value="Project Default" />
|
||||
<option name="USE_PROJECT_PROFILE" value="true" />
|
||||
<version value="1.0" />
|
||||
</component>
|
||||
<component name="JavadocGenerationManager">
|
||||
<option name="OUTPUT_DIRECTORY" />
|
||||
<option name="OPTION_SCOPE" value="protected" />
|
||||
<option name="OPTION_HIERARCHY" value="true" />
|
||||
<option name="OPTION_NAVIGATOR" value="true" />
|
||||
<option name="OPTION_INDEX" value="true" />
|
||||
<option name="OPTION_SEPARATE_INDEX" value="true" />
|
||||
<option name="OPTION_DOCUMENT_TAG_USE" value="false" />
|
||||
<option name="OPTION_DOCUMENT_TAG_AUTHOR" value="false" />
|
||||
<option name="OPTION_DOCUMENT_TAG_VERSION" value="false" />
|
||||
<option name="OPTION_DOCUMENT_TAG_DEPRECATED" value="true" />
|
||||
<option name="OPTION_DEPRECATED_LIST" value="true" />
|
||||
<option name="OTHER_OPTIONS" value="" />
|
||||
<option name="HEAP_SIZE" />
|
||||
<option name="LOCALE" />
|
||||
<option name="OPEN_IN_BROWSER" value="true" />
|
||||
</component>
|
||||
<component name="ModuleEditorState">
|
||||
<option name="LAST_EDITED_MODULE_NAME" />
|
||||
<option name="LAST_EDITED_TAB_NAME" />
|
||||
</component>
|
||||
<component name="Palette2">
|
||||
<group name="Swing">
|
||||
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
||||
</item>
|
||||
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
|
||||
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
||||
<initial-values>
|
||||
<property name="text" value="Button" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="RadioButton" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="CheckBox" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="Label" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
||||
<preferred-size width="-1" height="20" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
||||
</item>
|
||||
</group>
|
||||
</component>
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="ezamudio" />
|
||||
</component>
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/j8583.iml" filepath="$PROJECT_DIR$/j8583.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="Java 7" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
<component name="SvnConfiguration" maxAnnotateRevisions="500" myUseAcceleration="nothing" myAutoUpdateAfterCommit="false" cleanupOnStartRun="false">
|
||||
<option name="USER" value="" />
|
||||
<option name="PASSWORD" value="" />
|
||||
<option name="mySSHConnectionTimeout" value="30000" />
|
||||
<option name="mySSHReadTimeout" value="30000" />
|
||||
<option name="LAST_MERGED_REVISION" />
|
||||
<option name="MERGE_DRY_RUN" value="false" />
|
||||
<option name="MERGE_DIFF_USE_ANCESTRY" value="true" />
|
||||
<option name="UPDATE_LOCK_ON_DEMAND" value="false" />
|
||||
<option name="IGNORE_SPACES_IN_MERGE" value="false" />
|
||||
<option name="DETECT_NESTED_COPIES" value="true" />
|
||||
<option name="CHECK_NESTED_FOR_QUICK_MERGE" value="false" />
|
||||
<option name="IGNORE_SPACES_IN_ANNOTATE" value="true" />
|
||||
<option name="SHOW_MERGE_SOURCES_IN_ANNOTATE" value="true" />
|
||||
<option name="FORCE_UPDATE" value="false" />
|
||||
<option name="IGNORE_EXTERNALS" value="false" />
|
||||
<myIsUseDefaultProxy>false</myIsUseDefaultProxy>
|
||||
</component>
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.solab.iso8583;
|
||||
|
||||
/**
|
||||
* An extension of the CustomField interface, useful for binary fields.
|
||||
* CustomBinaryField encoders can return null for the two CustomField
|
||||
* methods IF they are only used with binary messages.
|
||||
*
|
||||
* @author Enrique Zamudio
|
||||
* Date: 07/05/13 13:04
|
||||
*/
|
||||
public interface CustomBinaryField<T> extends CustomField<T> {
|
||||
|
||||
public T decodeBinaryField(byte[] value, int offset, int length);
|
||||
|
||||
public byte[] encodeBinaryField(T value);
|
||||
|
||||
}
|
||||
@@ -51,6 +51,7 @@ public class IsoMessage {
|
||||
/** Flag to enforce secondary bitmap even if empty. */
|
||||
private boolean forceb2;
|
||||
private boolean binBitmap;
|
||||
private boolean forceStringEncoding;
|
||||
private String encoding = System.getProperty("file.encoding");
|
||||
|
||||
/** Creates a new empty message with no values set. */
|
||||
@@ -96,7 +97,15 @@ public class IsoMessage {
|
||||
return encoding;
|
||||
}
|
||||
|
||||
/** Sets the string to be sent as ISO header, that is, after the length header but before the message type.
|
||||
/** Specified whether the variable-length fields should encode their length
|
||||
* headers using string conversion with the proper character encoding. Default
|
||||
* is false, which is the old behavior (encoding as ASCII). This is only useful
|
||||
* for text format. */
|
||||
public void setForceStringEncoding(boolean flag) {
|
||||
forceStringEncoding = flag;
|
||||
}
|
||||
|
||||
/** Sets the string to be sent as ISO header, that is, after the length header but before the message type.
|
||||
* This is useful in case an application needs some custom data in the ISO header of each message (very rare). */
|
||||
public void setIsoHeader(String value) {
|
||||
isoHeader = value;
|
||||
@@ -205,6 +214,25 @@ public class IsoMessage {
|
||||
return this;
|
||||
}
|
||||
|
||||
/** A convenience method to set new values in fields that already contain values.
|
||||
* The field's type, length and custom encoder are taken from the current value.
|
||||
* This method can only be used with fields that have been previously set,
|
||||
* usually from a template in the MessageFactory.
|
||||
* @param index The field's index
|
||||
* @param value The new value to be set in that field.
|
||||
* @return The message itself.
|
||||
* @throws IllegalArgumentException if there is no current field at the specified index. */
|
||||
public <T> IsoMessage updateValue(int index, T value) {
|
||||
IsoValue<T> current = getField(index);
|
||||
if (current == null) {
|
||||
throw new IllegalArgumentException("Value-only field setter can only be used on existing fields");
|
||||
} else {
|
||||
setValue(index, value, current.getEncoder(), current.getType(), current.getLength());
|
||||
getField(index).setCharacterEncoding(current.getCharacterEncoding());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Returns true is the message has a value in the specified field.
|
||||
* @param idx The field number. */
|
||||
public boolean hasField(int idx) {
|
||||
@@ -290,6 +318,26 @@ public class IsoMessage {
|
||||
return buf;
|
||||
}
|
||||
|
||||
/** Creates a BitSet for the bitmap. */
|
||||
protected BitSet createBitmapBitSet() {
|
||||
BitSet bs = new BitSet(forceb2 ? 128 : 64);
|
||||
for (int i = 2 ; i < 129; i++) {
|
||||
if (fields[i] != null) {
|
||||
bs.set(i - 1);
|
||||
}
|
||||
}
|
||||
if (forceb2) {
|
||||
bs.set(0);
|
||||
} else if (bs.length() > 64) {
|
||||
//Extend to 128 if needed
|
||||
BitSet b2 = new BitSet(128);
|
||||
b2.or(bs);
|
||||
bs = b2;
|
||||
bs.set(0);
|
||||
}
|
||||
return bs;
|
||||
}
|
||||
|
||||
/** This calls writeInternal(), allowing applications to get the byte buffer containing the
|
||||
* message data, without the length header. */
|
||||
public byte[] writeData() {
|
||||
@@ -314,21 +362,7 @@ public class IsoMessage {
|
||||
}
|
||||
|
||||
//Bitmap
|
||||
BitSet bs = new BitSet(forceb2 ? 128 : 64);
|
||||
for (int i = 2 ; i < 129; i++) {
|
||||
if (fields[i] != null) {
|
||||
bs.set(i - 1);
|
||||
}
|
||||
}
|
||||
if (forceb2) {
|
||||
bs.set(0);
|
||||
} else if (bs.length() > 64) {
|
||||
//Extend to 128 if needed
|
||||
BitSet b2 = new BitSet(128);
|
||||
b2.or(bs);
|
||||
bs = b2;
|
||||
bs.set(0);
|
||||
}
|
||||
BitSet bs = createBitmapBitSet();
|
||||
//Write bitmap to stream
|
||||
if (binary || binBitmap) {
|
||||
int pos = 128;
|
||||
@@ -345,6 +379,11 @@ public class IsoMessage {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ByteArrayOutputStream bout2 = null;
|
||||
if (forceStringEncoding) {
|
||||
bout2 = bout;
|
||||
bout = new ByteArrayOutputStream();
|
||||
}
|
||||
int pos = 0;
|
||||
int lim = bs.size() / 4;
|
||||
for (int i = 0; i < lim; i++) {
|
||||
@@ -359,6 +398,15 @@ public class IsoMessage {
|
||||
nibble |= 1;
|
||||
bout.write(HEX[nibble]);
|
||||
}
|
||||
if (forceStringEncoding) {
|
||||
final String _hb = new String(bout.toByteArray());
|
||||
bout = bout2;
|
||||
try {
|
||||
bout.write(_hb.getBytes(encoding));
|
||||
} catch (IOException ignore) {
|
||||
//never happen
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Fields
|
||||
@@ -366,7 +414,7 @@ public class IsoMessage {
|
||||
IsoValue<?> v = fields[i];
|
||||
if (v != null) {
|
||||
try {
|
||||
v.write(bout, binary);
|
||||
v.write(bout, binary, forceStringEncoding);
|
||||
} catch (IOException ex) {
|
||||
//should never happen, writing to a ByteArrayOutputStream
|
||||
}
|
||||
@@ -375,9 +423,51 @@ public class IsoMessage {
|
||||
return bout.toByteArray();
|
||||
}
|
||||
|
||||
/** Returns a string representation of the message, as if it were encoded
|
||||
* in ASCII with no binary bitmap. */
|
||||
public String debugString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (isoHeader != null) {
|
||||
sb.append(isoHeader);
|
||||
}
|
||||
sb.append(String.format("%04x", type));
|
||||
|
||||
//Bitmap
|
||||
BitSet bs = createBitmapBitSet();
|
||||
int pos = 0;
|
||||
int lim = bs.size() / 4;
|
||||
for (int i = 0; i < lim; i++) {
|
||||
int nibble = 0;
|
||||
if (bs.get(pos++))
|
||||
nibble |= 8;
|
||||
if (bs.get(pos++))
|
||||
nibble |= 4;
|
||||
if (bs.get(pos++))
|
||||
nibble |= 2;
|
||||
if (bs.get(pos++))
|
||||
nibble |= 1;
|
||||
sb.append(new String(HEX, nibble, 1));
|
||||
}
|
||||
|
||||
//Fields
|
||||
for (int i = 2; i < 129; i++) {
|
||||
IsoValue<?> v = fields[i];
|
||||
if (v != null) {
|
||||
String desc = v.toString();
|
||||
if (v.getType() == IsoType.LLBIN || v.getType() == IsoType.LLVAR) {
|
||||
sb.append(String.format("%02d", desc.length()));
|
||||
} else if (v.getType() == IsoType.LLLBIN || v.getType() == IsoType.LLLVAR) {
|
||||
sb.append(String.format("%03d", desc.length()));
|
||||
}
|
||||
sb.append(desc);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
//These are for Groovy compat
|
||||
/** Sets the specified value in the specified field, just like {@link #setField(int, IsoValue)}. */
|
||||
public void putAt(int i, IsoValue<?> v) {
|
||||
public <T> void putAt(int i, IsoValue<T> v) {
|
||||
setField(i, v);
|
||||
}
|
||||
/** Returns the IsoValue in the specified field, just like {@link #getField(int)}. */
|
||||
@@ -387,7 +477,7 @@ public class IsoMessage {
|
||||
|
||||
//These are for Scala compat
|
||||
/** Sets the specified value in the specified field, just like {@link #setField(int, IsoValue)}. */
|
||||
public void update(int i, IsoValue<?> v) {
|
||||
public <T> void update(int i, IsoValue<T> v) {
|
||||
setField(i, v);
|
||||
}
|
||||
/** Returns the IsoValue in the specified field, just like {@link #getField(int)}. */
|
||||
|
||||
@@ -178,27 +178,27 @@ public enum IsoType {
|
||||
throw new IllegalArgumentException("Cannot format BigDecimal as " + this);
|
||||
}
|
||||
|
||||
public IsoValue<Object> value(Object val, int len) {
|
||||
return new IsoValue<Object>(this, val, len);
|
||||
public <T> IsoValue<T> value(T val, int len) {
|
||||
return new IsoValue<T>(this, val, len);
|
||||
}
|
||||
|
||||
public IsoValue<Object> value(Object val) {
|
||||
return new IsoValue<Object>(this, val);
|
||||
public <T> IsoValue<T> value(T val) {
|
||||
return new IsoValue<T>(this, val);
|
||||
}
|
||||
|
||||
public IsoValue<Object> call(Object val, int len) {
|
||||
return new IsoValue<Object>(this, val, len);
|
||||
public <T> IsoValue<T> call(T val, int len) {
|
||||
return new IsoValue<T>(this, val, len);
|
||||
}
|
||||
|
||||
public IsoValue<Object> call(Object val) {
|
||||
return new IsoValue<Object>(this, val);
|
||||
public <T> IsoValue<T> call(T val) {
|
||||
return new IsoValue<T>(this, val);
|
||||
}
|
||||
|
||||
public IsoValue<Object> apply(Object val, int len) {
|
||||
return new IsoValue<Object>(this, val, len);
|
||||
public <T> IsoValue<T> apply(T val, int len) {
|
||||
return new IsoValue<T>(this, val, len);
|
||||
}
|
||||
public IsoValue<Object> apply(Object val) {
|
||||
return new IsoValue<Object>(this, val);
|
||||
public <T> IsoValue<T> apply(T val) {
|
||||
return new IsoValue<T>(this, val);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.io.OutputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
import com.solab.iso8583.util.Bcd;
|
||||
import com.solab.iso8583.util.HexCodec;
|
||||
|
||||
/** Represents a value that is stored in a field inside an ISO8583 message.
|
||||
@@ -82,8 +83,9 @@ public class IsoValue<T> implements Cloneable {
|
||||
} else {
|
||||
length = value.toString().length() / 2 + (value.toString().length() % 2);
|
||||
}
|
||||
} else if (custom instanceof CustomBinaryField) {
|
||||
length = ((CustomBinaryField<T>)custom).encodeBinaryField(value).length;
|
||||
} else {
|
||||
//TODO special encoder, NO strings
|
||||
String enc = custom.encodeField(value);
|
||||
if (enc == null) {
|
||||
enc = value == null ? "" : value.toString();
|
||||
@@ -129,7 +131,13 @@ public class IsoValue<T> implements Cloneable {
|
||||
}
|
||||
} else if (t == IsoType.LLBIN || t == IsoType.LLLBIN) {
|
||||
if (len == 0) {
|
||||
//TODO customfield binary!
|
||||
if (custom == null) {
|
||||
length = ((byte[])val).length;
|
||||
} else if (custom instanceof CustomBinaryField) {
|
||||
length = ((CustomBinaryField<T>)custom).encodeBinaryField(value).length;
|
||||
} else {
|
||||
length = custom.encodeField(value).length();
|
||||
}
|
||||
length = custom == null ? ((byte[])val).length : custom.encodeField(value).length();
|
||||
}
|
||||
if (t == IsoType.LLBIN && length > 99) {
|
||||
@@ -237,48 +245,50 @@ public class IsoValue<T> implements Cloneable {
|
||||
return encoder;
|
||||
}
|
||||
|
||||
protected void writeLengthHeader(final int l, final OutputStream outs, final int digits,
|
||||
final boolean binary, final boolean forceStringEncoding)
|
||||
throws IOException {
|
||||
if (binary) {
|
||||
if (digits == 3) {
|
||||
outs.write(l / 100); //00 to 09 automatically in BCD
|
||||
}
|
||||
//BCD encode the rest of the length
|
||||
outs.write((((l % 100) / 10) << 4) | (l % 10));
|
||||
} else if (forceStringEncoding) {
|
||||
String lhead = Integer.toString(l);
|
||||
final int ldiff = digits - lhead.length();
|
||||
if (ldiff == 1) {
|
||||
lhead = '0' + lhead;
|
||||
} else if (ldiff == 2) {
|
||||
lhead = "00" + lhead;
|
||||
}
|
||||
outs.write(lhead.getBytes(encoding));
|
||||
} else {
|
||||
//write the length in ASCII
|
||||
if (digits == 3) {
|
||||
outs.write((l / 100) + 48);
|
||||
}
|
||||
if (l >= 10) {
|
||||
outs.write(((l % 100) / 10) + 48);
|
||||
} else {
|
||||
outs.write(48);
|
||||
}
|
||||
outs.write((l % 10) + 48);
|
||||
}
|
||||
}
|
||||
|
||||
/** Writes the formatted value to a stream, with the length header
|
||||
* if it's a variable length type. */
|
||||
public void write(OutputStream outs, boolean binary) throws IOException {
|
||||
* if it's a variable length type.
|
||||
* @param outs The stream to which the value will be written.
|
||||
* @param binary Specifies whether the value should be written in binary or text format.
|
||||
* @param forceStringEncoding When using text format, force the encoding of length headers
|
||||
* for variable-length fields to be done with the proper character encoding. When false,
|
||||
* the length headers are encoded as ASCII; this used to be the only behavior. */
|
||||
public void write(final OutputStream outs, final boolean binary, final boolean forceStringEncoding) throws IOException {
|
||||
if (type == IsoType.LLLVAR || type == IsoType.LLVAR) {
|
||||
if (binary) {
|
||||
if (type == IsoType.LLLVAR) {
|
||||
outs.write(length / 100); //00 to 09 automatically in BCD
|
||||
}
|
||||
//BCD encode the rest of the length
|
||||
outs.write((((length % 100) / 10) << 4) | (length % 10));
|
||||
} else {
|
||||
//write the length in ASCII
|
||||
if (type == IsoType.LLLVAR) {
|
||||
outs.write((length / 100) + 48);
|
||||
}
|
||||
if (length >= 10) {
|
||||
outs.write(((length % 100) / 10) + 48);
|
||||
} else {
|
||||
outs.write(48);
|
||||
}
|
||||
outs.write((length % 10) + 48);
|
||||
}
|
||||
writeLengthHeader(length, outs, type == IsoType.LLLVAR ? 3 : 2, binary, forceStringEncoding);
|
||||
} else if (type == IsoType.LLBIN || type == IsoType.LLLBIN) {
|
||||
if (binary) {
|
||||
if (type == IsoType.LLLBIN ) {
|
||||
outs.write(length / 100); //00 to 09 automatically in BCD
|
||||
}
|
||||
//BCD encode the rest of the length
|
||||
outs.write((((length % 100) / 10) << 4) | (length % 10));
|
||||
} else {
|
||||
int _l = length*2;
|
||||
//write the length in ASCII
|
||||
if (type == IsoType.LLLBIN) {
|
||||
outs.write((_l / 100) + 48);
|
||||
}
|
||||
if (_l >= 10) {
|
||||
outs.write(((_l % 100) / 10) + 48);
|
||||
} else {
|
||||
outs.write(48);
|
||||
}
|
||||
outs.write((_l % 10) + 48);
|
||||
}
|
||||
writeLengthHeader(binary ? length : length*2, outs, type == IsoType.LLLBIN ? 3 : 2, binary, forceStringEncoding);
|
||||
} else if (binary) {
|
||||
//numeric types in binary are coded like this
|
||||
byte[] buf = null;
|
||||
@@ -291,7 +301,7 @@ public class IsoValue<T> implements Cloneable {
|
||||
}
|
||||
//Encode in BCD if it's one of these types
|
||||
if (buf != null) {
|
||||
toBcd(toString(), buf);
|
||||
Bcd.encode(toString(), buf);
|
||||
outs.write(buf);
|
||||
return;
|
||||
}
|
||||
@@ -301,6 +311,10 @@ public class IsoValue<T> implements Cloneable {
|
||||
if (value instanceof byte[]) {
|
||||
outs.write((byte[])value);
|
||||
missing = length - ((byte[])value).length;
|
||||
} else if (encoder instanceof CustomBinaryField) {
|
||||
byte[] binval = ((CustomBinaryField<T>) encoder).encodeBinaryField(value);
|
||||
outs.write(binval);
|
||||
missing = length - binval.length;
|
||||
} else {
|
||||
byte[] binval = HexCodec.hexDecode(value.toString());
|
||||
outs.write(binval);
|
||||
@@ -316,24 +330,4 @@ public class IsoValue<T> implements Cloneable {
|
||||
}
|
||||
}
|
||||
|
||||
/** Encode the value as BCD and put it in the buffer. The buffer must be big enough
|
||||
* to store the digits in the original value (half the length of the string). */
|
||||
private void toBcd(String value, byte[] buf) {
|
||||
int charpos = 0; //char where we start
|
||||
int bufpos = 0;
|
||||
if (value.length() % 2 == 1) {
|
||||
//for odd lengths we encode just the first digit in the first byte
|
||||
buf[0] = (byte)(value.charAt(0) - 48);
|
||||
charpos = 1;
|
||||
bufpos = 1;
|
||||
}
|
||||
//encode the rest of the string
|
||||
while (charpos < value.length()) {
|
||||
buf[bufpos] = (byte)(((value.charAt(charpos) - 48) << 4)
|
||||
| (value.charAt(charpos + 1) - 48));
|
||||
charpos += 2;
|
||||
bufpos++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -56,9 +56,9 @@ public class MessageFactory<T extends IsoMessage> {
|
||||
/** This map stores the message template for each message type. */
|
||||
private Map<Integer, T> typeTemplates = new HashMap<Integer, T>();
|
||||
/** Stores the information needed to parse messages sorted by type. */
|
||||
private Map<Integer, Map<Integer, FieldParseInfo>> parseMap = new HashMap<Integer, Map<Integer, FieldParseInfo>>();
|
||||
protected Map<Integer, Map<Integer, FieldParseInfo>> parseMap = new HashMap<Integer, Map<Integer, FieldParseInfo>>();
|
||||
/** Stores the field numbers to be parsed, in order of appearance. */
|
||||
private Map<Integer, List<Integer>> parseOrder = new HashMap<Integer, List<Integer>>();
|
||||
protected Map<Integer, List<Integer>> parseOrder = new HashMap<Integer, List<Integer>>();
|
||||
|
||||
private TraceNumberGenerator traceGen;
|
||||
/** The ISO header to be included in each message type. */
|
||||
@@ -75,8 +75,23 @@ public class MessageFactory<T extends IsoMessage> {
|
||||
private boolean ignoreLast;
|
||||
private boolean forceb2;
|
||||
private boolean binBitmap;
|
||||
private boolean forceStringEncoding;
|
||||
private String encoding = System.getProperty("file.encoding");
|
||||
|
||||
/** This flag gets passed on to newly created messages and also sets this value for all
|
||||
* field parsers in parsing guides. */
|
||||
public void setForceStringEncoding(boolean flag) {
|
||||
forceStringEncoding = flag;
|
||||
for (Map<Integer,FieldParseInfo> pm : parseMap.values()) {
|
||||
for (FieldParseInfo parser : pm.values()) {
|
||||
parser.setForceStringDecoding(flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
public boolean isForceStringEncoding() {
|
||||
return forceStringEncoding;
|
||||
}
|
||||
|
||||
/** Tells the factory to create messages that encode their bitmaps in binary format
|
||||
* even when they're encoded as text. Has no effect on binary messages. */
|
||||
public void setUseBinaryBitmap(boolean flag) {
|
||||
@@ -94,13 +109,24 @@ public class MessageFactory<T extends IsoMessage> {
|
||||
throw new IllegalArgumentException("Cannot set null encoding.");
|
||||
}
|
||||
encoding = value;
|
||||
if (parseMap.size() > 0) {
|
||||
if (!parseMap.isEmpty()) {
|
||||
for (Map<Integer, FieldParseInfo> pt : parseMap.values()) {
|
||||
for (FieldParseInfo fpi : pt.values()) {
|
||||
fpi.setCharacterEncoding(encoding);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!typeTemplates.isEmpty()) {
|
||||
for (T tmpl : typeTemplates.values()) {
|
||||
tmpl.setCharacterEncoding(encoding);
|
||||
for (int i = 2 ; i<129; i++) {
|
||||
IsoValue<?> v = tmpl.getField(i);
|
||||
if (v != null) {
|
||||
v.setCharacterEncoding(encoding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the encoding used to parse ALPHA, LLVAR and LLLVAR fields. The default is the file.encoding
|
||||
@@ -113,7 +139,7 @@ public class MessageFactory<T extends IsoMessage> {
|
||||
public void setForceSecondaryBitmap(boolean flag) {
|
||||
forceb2 = flag;
|
||||
}
|
||||
public boolean getForceSecondaryBitmap() {
|
||||
public boolean isForceSecondaryBitmap() {
|
||||
return forceb2;
|
||||
}
|
||||
|
||||
@@ -143,12 +169,12 @@ public class MessageFactory<T extends IsoMessage> {
|
||||
}
|
||||
/** Returns a custom field encoder/decoder for the specified field number, if one is available. */
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> CustomField<T> getCustomField(int index) {
|
||||
public <F> CustomField<F> getCustomField(int index) {
|
||||
return customFields.get(index);
|
||||
}
|
||||
/** Returns a custom field encoder/decoder for the specified field number, if one is available. */
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> CustomField<T> getCustomField(Integer index) {
|
||||
public <F> CustomField<F> getCustomField(Integer index) {
|
||||
return customFields.get(index);
|
||||
}
|
||||
|
||||
@@ -157,6 +183,9 @@ public class MessageFactory<T extends IsoMessage> {
|
||||
* but is really convenient in case the MessageFactory is being configured from within, say, Spring. */
|
||||
public void setConfigPath(String path) throws IOException {
|
||||
ConfigParser.configureFromClasspathConfig(this, path);
|
||||
//Now re-set some properties that need to be propagated down to the recently assigned objects
|
||||
setCharacterEncoding(encoding);
|
||||
setForceStringEncoding(forceStringEncoding);
|
||||
}
|
||||
|
||||
/** Tells the receiver to create and parse binary messages if the flag is true.
|
||||
@@ -192,6 +221,7 @@ public class MessageFactory<T extends IsoMessage> {
|
||||
m.setForceSecondaryBitmap(forceb2);
|
||||
m.setBinaryBitmap(binBitmap);
|
||||
m.setCharacterEncoding(encoding);
|
||||
m.setForceStringEncoding(forceStringEncoding);
|
||||
|
||||
//Copy the values from the template
|
||||
IsoMessage templ = typeTemplates.get(type);
|
||||
@@ -257,11 +287,13 @@ public class MessageFactory<T extends IsoMessage> {
|
||||
if (buf.length < minlength) {
|
||||
throw new ParseException("Insufficient buffer length, needs to be at least " + minlength, 0);
|
||||
}
|
||||
final T m = createIsoMessage(isoHeaderLength > 0 ? new String(buf, 0, isoHeaderLength) : null);
|
||||
final T m = createIsoMessage(isoHeaderLength > 0 ? new String(buf, 0, isoHeaderLength, encoding) : null);
|
||||
m.setCharacterEncoding(encoding);
|
||||
final int type;
|
||||
if (useBinary) {
|
||||
type = ((buf[isoHeaderLength] & 0xff) << 8) | (buf[isoHeaderLength + 1] & 0xff);
|
||||
} else if (forceStringEncoding) {
|
||||
type = Integer.parseInt(new String(buf, isoHeaderLength, 4, encoding), 16);
|
||||
} else {
|
||||
type = ((buf[isoHeaderLength] - 48) << 12)
|
||||
| ((buf[isoHeaderLength + 1] - 48) << 8)
|
||||
@@ -300,45 +332,57 @@ public class MessageFactory<T extends IsoMessage> {
|
||||
} else {
|
||||
//ASCII parsing
|
||||
try {
|
||||
for (int i = isoHeaderLength + 4; i < isoHeaderLength + 20; i++) {
|
||||
if (buf[i] >= '0' && buf[i] <= '9') {
|
||||
bs.set(pos++, ((buf[i] - 48) & 8) > 0);
|
||||
bs.set(pos++, ((buf[i] - 48) & 4) > 0);
|
||||
bs.set(pos++, ((buf[i] - 48) & 2) > 0);
|
||||
bs.set(pos++, ((buf[i] - 48) & 1) > 0);
|
||||
} else if (buf[i] >= 'A' && buf[i] <= 'F') {
|
||||
bs.set(pos++, ((buf[i] - 55) & 8) > 0);
|
||||
bs.set(pos++, ((buf[i] - 55) & 4) > 0);
|
||||
bs.set(pos++, ((buf[i] - 55) & 2) > 0);
|
||||
bs.set(pos++, ((buf[i] - 5) & 1) > 0);
|
||||
} else if (buf[i] >= 'a' && buf[i] <= 'f') {
|
||||
bs.set(pos++, ((buf[i] - 87) & 8) > 0);
|
||||
bs.set(pos++, ((buf[i] - 87) & 4) > 0);
|
||||
bs.set(pos++, ((buf[i] - 87) & 2) > 0);
|
||||
bs.set(pos++, ((buf[i] - 87) & 1) > 0);
|
||||
}
|
||||
}
|
||||
final byte[] bitmapBuffer;
|
||||
if (forceStringEncoding) {
|
||||
byte[] _bb = new String(buf, isoHeaderLength+4, 16, encoding).getBytes();
|
||||
bitmapBuffer = new byte[36+isoHeaderLength];
|
||||
System.arraycopy(_bb, 0, bitmapBuffer, 4+isoHeaderLength, 16);
|
||||
} else {
|
||||
bitmapBuffer = buf;
|
||||
}
|
||||
for (int i = isoHeaderLength + 4; i < isoHeaderLength + 20; i++) {
|
||||
if (bitmapBuffer[i] >= '0' && bitmapBuffer[i] <= '9') {
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 48) & 8) > 0);
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 48) & 4) > 0);
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 48) & 2) > 0);
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 48) & 1) > 0);
|
||||
} else if (bitmapBuffer[i] >= 'A' && bitmapBuffer[i] <= 'F') {
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 55) & 8) > 0);
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 55) & 4) > 0);
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 55) & 2) > 0);
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 5) & 1) > 0);
|
||||
} else if (bitmapBuffer[i] >= 'a' && bitmapBuffer[i] <= 'f') {
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 87) & 8) > 0);
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 87) & 4) > 0);
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 87) & 2) > 0);
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 87) & 1) > 0);
|
||||
}
|
||||
}
|
||||
//Check for secondary bitmap and parse it if necessary
|
||||
if (bs.get(0)) {
|
||||
if (buf.length < minlength + 16) {
|
||||
throw new ParseException("Insufficient length for secondary bitmap", minlength);
|
||||
}
|
||||
if (forceStringEncoding) {
|
||||
byte[] _bb = new String(buf, isoHeaderLength+4, 16, encoding).getBytes();
|
||||
System.arraycopy(_bb, 0, bitmapBuffer, 20+isoHeaderLength, 16);
|
||||
}
|
||||
for (int i = isoHeaderLength + 20; i < isoHeaderLength + 36; i++) {
|
||||
if (buf[i] >= '0' && buf[i] <= '9') {
|
||||
bs.set(pos++, ((buf[i] - 48) & 8) > 0);
|
||||
bs.set(pos++, ((buf[i] - 48) & 4) > 0);
|
||||
bs.set(pos++, ((buf[i] - 48) & 2) > 0);
|
||||
bs.set(pos++, ((buf[i] - 48) & 1) > 0);
|
||||
} else if (buf[i] >= 'A' && buf[i] <= 'F') {
|
||||
bs.set(pos++, ((buf[i] - 55) & 8) > 0);
|
||||
bs.set(pos++, ((buf[i] - 55) & 4) > 0);
|
||||
bs.set(pos++, ((buf[i] - 55) & 2) > 0);
|
||||
bs.set(pos++, ((buf[i] - 5) & 1) > 0);
|
||||
} else if (buf[i] >= 'a' && buf[i] <= 'f') {
|
||||
bs.set(pos++, ((buf[i] - 87) & 8) > 0);
|
||||
bs.set(pos++, ((buf[i] - 87) & 4) > 0);
|
||||
bs.set(pos++, ((buf[i] - 87) & 2) > 0);
|
||||
bs.set(pos++, ((buf[i] - 87) & 1) > 0);
|
||||
if (bitmapBuffer[i] >= '0' && bitmapBuffer[i] <= '9') {
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 48) & 8) > 0);
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 48) & 4) > 0);
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 48) & 2) > 0);
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 48) & 1) > 0);
|
||||
} else if (bitmapBuffer[i] >= 'A' && bitmapBuffer[i] <= 'F') {
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 55) & 8) > 0);
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 55) & 4) > 0);
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 55) & 2) > 0);
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 5) & 1) > 0);
|
||||
} else if (bitmapBuffer[i] >= 'a' && bitmapBuffer[i] <= 'f') {
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 87) & 8) > 0);
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 87) & 4) > 0);
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 87) & 2) > 0);
|
||||
bs.set(pos++, ((bitmapBuffer[i] - 87) & 1) > 0);
|
||||
}
|
||||
}
|
||||
pos = 16 + minlength;
|
||||
@@ -426,11 +470,12 @@ public class MessageFactory<T extends IsoMessage> {
|
||||
|
||||
/** Creates a Iso message, override this method in the subclass to provide your
|
||||
* own implementations of IsoMessage.
|
||||
* @param header
|
||||
* @param header The optional ISO header that goes before the message type
|
||||
* @return IsoMessage
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected T createIsoMessage(String header) {
|
||||
return (T) new IsoMessage(header);
|
||||
return (T)new IsoMessage(header);
|
||||
}
|
||||
|
||||
/** Sets whether the factory should set the current date on newly created messages,
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.solab.iso8583.codecs;
|
||||
|
||||
import com.solab.iso8583.CustomBinaryField;
|
||||
import com.solab.iso8583.util.Bcd;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* A custom field encoder/decoder to be used with LLBIN/LLLBIN fields
|
||||
* that contain BigIntegers in BCD encoding.
|
||||
*
|
||||
* @author Enrique Zamudio
|
||||
* Date: 07/05/13 13:02
|
||||
*/
|
||||
public class BigIntBcdCodec implements CustomBinaryField<BigInteger> {
|
||||
|
||||
@Override
|
||||
public BigInteger decodeBinaryField(byte[] value, int pos, int len) {
|
||||
return Bcd.decodeToBigInteger(value, pos, len*2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeBinaryField(BigInteger value) {
|
||||
final String s = value.toString(10);
|
||||
final byte[] buf = new byte[s.length() / 2 + s.length() % 2];
|
||||
Bcd.encode(s, buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger decodeField(String value) {
|
||||
return new BigInteger(value, 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeField(BigInteger value) {
|
||||
return value.toString(10);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.solab.iso8583.codecs;
|
||||
|
||||
import com.solab.iso8583.CustomBinaryField;
|
||||
import com.solab.iso8583.util.Bcd;
|
||||
|
||||
/**
|
||||
* A custom field encoder/decoder to be used with LLBIN/LLLBIN fields
|
||||
* that contain Longs in BCD encoding.
|
||||
*
|
||||
* @author Enrique Zamudio
|
||||
* Date: 07/05/13 13:02
|
||||
*/
|
||||
public class LongBcdCodec implements CustomBinaryField<Long> {
|
||||
|
||||
@Override
|
||||
public Long decodeBinaryField(byte[] value, int pos, int length) {
|
||||
return Bcd.decodeToLong(value, pos, length*2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeBinaryField(Long value) {
|
||||
final String s = Long.toString(value, 10);
|
||||
final byte[] buf = new byte[s.length() / 2 + s.length() % 2];
|
||||
Bcd.encode(s, buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long decodeField(String value) {
|
||||
return Long.parseLong(value, 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeField(Long value) {
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
@@ -35,8 +35,9 @@ public abstract class AlphaNumericFieldParseInfo extends FieldParseInfo {
|
||||
super(t, len);
|
||||
}
|
||||
|
||||
public IsoValue<?> parse(final int field, final byte[] buf, final int pos,
|
||||
final CustomField<?> custom)
|
||||
@Override
|
||||
public <T> IsoValue<?> parse(final int field, final byte[] buf, final int pos,
|
||||
final CustomField<T> custom)
|
||||
throws ParseException, UnsupportedEncodingException {
|
||||
if (pos < 0) {
|
||||
throw new ParseException(String.format("Invalid ALPHA/NUM field %d position %d",
|
||||
@@ -53,12 +54,9 @@ public abstract class AlphaNumericFieldParseInfo extends FieldParseInfo {
|
||||
if (custom == null) {
|
||||
return new IsoValue<String>(type, _v, length, null);
|
||||
} else {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
IsoValue<?> v = new IsoValue(type, custom.decodeField(_v), length, custom);
|
||||
if (v.getValue() == null) {
|
||||
return new IsoValue<String>(type, _v, length, null);
|
||||
}
|
||||
return v;
|
||||
T decoded = custom.decodeField(_v);
|
||||
return decoded == null ? new IsoValue<String>(type, _v, length, null) :
|
||||
new IsoValue<T>(type, decoded, length, custom);
|
||||
}
|
||||
} catch (StringIndexOutOfBoundsException ex) {
|
||||
throw new ParseException(String.format(
|
||||
|
||||
@@ -35,8 +35,9 @@ public class AlphaParseInfo extends AlphaNumericFieldParseInfo {
|
||||
super(IsoType.ALPHA, len);
|
||||
}
|
||||
|
||||
public IsoValue<?> parseBinary(final int field, final byte[] buf, final int pos,
|
||||
final CustomField<?> custom)
|
||||
@Override
|
||||
public <T> IsoValue<?> parseBinary(final int field, final byte[] buf, final int pos,
|
||||
final CustomField<T> custom)
|
||||
throws ParseException, UnsupportedEncodingException {
|
||||
if (pos < 0) {
|
||||
throw new ParseException(String.format("Invalid bin ALPHA field %d position %d",
|
||||
@@ -50,12 +51,10 @@ public class AlphaParseInfo extends AlphaNumericFieldParseInfo {
|
||||
if (custom == null) {
|
||||
return new IsoValue<String>(type, new String(buf, pos, length, getCharacterEncoding()), length, null);
|
||||
} else {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
IsoValue<?> v = new IsoValue(type, custom.decodeField(new String(buf, pos, length, getCharacterEncoding())), length, custom);
|
||||
if (v.getValue() == null) {
|
||||
return new IsoValue<String>(type, new String(buf, pos, length, getCharacterEncoding()), length, null);
|
||||
}
|
||||
return v;
|
||||
T decoded = custom.decodeField(new String(buf, pos, length, getCharacterEncoding()));
|
||||
return decoded == null ?
|
||||
new IsoValue<String>(type, new String(buf, pos, length, getCharacterEncoding()), length, null) :
|
||||
new IsoValue<T>(type, decoded, length, custom);
|
||||
}
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new ParseException(String.format(
|
||||
|
||||
@@ -37,8 +37,9 @@ public class AmountParseInfo extends FieldParseInfo {
|
||||
super(IsoType.AMOUNT, 12);
|
||||
}
|
||||
|
||||
public IsoValue<BigDecimal> parse(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<?> custom)
|
||||
@Override
|
||||
public <T> IsoValue<BigDecimal> parse(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<T> custom)
|
||||
throws ParseException, UnsupportedEncodingException {
|
||||
if (pos < 0) {
|
||||
throw new ParseException(String.format("Invalid AMOUNT field %d position %d",
|
||||
@@ -50,7 +51,7 @@ public class AmountParseInfo extends FieldParseInfo {
|
||||
}
|
||||
String c = new String(buf, pos, 12, getCharacterEncoding());
|
||||
try {
|
||||
return new IsoValue<BigDecimal>(type, new BigDecimal(c).movePointLeft(2), null);
|
||||
return new IsoValue<BigDecimal>(type, new BigDecimal(c).movePointLeft(2));
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new ParseException(String.format("Cannot read amount '%s' field %d pos %d",
|
||||
c, field, pos), pos);
|
||||
@@ -60,8 +61,9 @@ public class AmountParseInfo extends FieldParseInfo {
|
||||
}
|
||||
}
|
||||
|
||||
public IsoValue<BigDecimal> parseBinary(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<?> custom)
|
||||
@Override
|
||||
public <T> IsoValue<BigDecimal> parseBinary(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<T> custom)
|
||||
throws ParseException {
|
||||
char[] digits = new char[13];
|
||||
digits[10] = '.';
|
||||
@@ -74,7 +76,7 @@ public class AmountParseInfo extends FieldParseInfo {
|
||||
}
|
||||
}
|
||||
try {
|
||||
return new IsoValue<BigDecimal>(IsoType.AMOUNT, new BigDecimal(new String(digits)), null);
|
||||
return new IsoValue<BigDecimal>(IsoType.AMOUNT, new BigDecimal(new String(digits)));
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new ParseException(String.format("Cannot read amount '%s' field %d pos %d",
|
||||
new String(digits), field, pos), pos);
|
||||
|
||||
@@ -38,8 +38,8 @@ public class BinaryParseInfo extends FieldParseInfo {
|
||||
}
|
||||
|
||||
@Override
|
||||
public IsoValue<?> parse(final int field, final byte[] buf, final int pos,
|
||||
final CustomField<?> custom)
|
||||
public <T> IsoValue<?> parse(final int field, final byte[] buf, final int pos,
|
||||
final CustomField<T> custom)
|
||||
throws ParseException, UnsupportedEncodingException {
|
||||
if (pos < 0) {
|
||||
throw new ParseException(String.format("Invalid BINARY field %d position %d",
|
||||
@@ -54,18 +54,15 @@ public class BinaryParseInfo extends FieldParseInfo {
|
||||
if (custom == null) {
|
||||
return new IsoValue<byte[]>(type, binval, binval.length, null);
|
||||
} else {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
IsoValue<?> v = new IsoValue(type, custom.decodeField(new String(buf, pos, length*2, getCharacterEncoding())), length, custom);
|
||||
if (v.getValue() == null) {
|
||||
return new IsoValue<byte[]>(type, binval, binval.length, null);
|
||||
}
|
||||
return v;
|
||||
T dec = custom.decodeField(new String(buf, pos, length*2, getCharacterEncoding()));
|
||||
return dec == null ? new IsoValue<byte[]>(type, binval, binval.length, null) :
|
||||
new IsoValue<T>(type, dec, length, custom);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IsoValue<?> parseBinary(final int field, final byte[] buf, final int pos,
|
||||
final CustomField<?> custom) throws ParseException {
|
||||
public <T> IsoValue<?> parseBinary(final int field, final byte[] buf, final int pos,
|
||||
final CustomField<T> custom) throws ParseException {
|
||||
if (pos < 0) {
|
||||
throw new ParseException(String.format("Invalid BINARY field %d position %d",
|
||||
field, pos), pos);
|
||||
@@ -80,12 +77,9 @@ public class BinaryParseInfo extends FieldParseInfo {
|
||||
if (custom == null) {
|
||||
return new IsoValue<byte[]>(type, _v, length, null);
|
||||
} else {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
IsoValue<?> v = new IsoValue(type, custom.decodeField(HexCodec.hexEncode(_v, 0, _v.length)), length, custom);
|
||||
if (v.getValue() == null) {
|
||||
return new IsoValue<byte[]>(type, _v, length, null);
|
||||
}
|
||||
return v;
|
||||
T dec = custom.decodeField(HexCodec.hexEncode(_v, 0, _v.length));
|
||||
return dec == null ? new IsoValue<byte[]>(type, _v, length, null) :
|
||||
new IsoValue<T>(type, dec, length, custom);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,21 +50,37 @@ public class ConfigParser {
|
||||
|
||||
private final static Logger log = LoggerFactory.getLogger(ConfigParser.class);
|
||||
|
||||
/** Creates a message factory configured from the default file, which is j8583.xml
|
||||
* located in the root of the classpath, using the specified ClassLoader. */
|
||||
public static MessageFactory<IsoMessage> createDefault(
|
||||
ClassLoader loader) throws IOException {
|
||||
if (loader.getResource("j8583.xml") == null) {
|
||||
log.warn("ISO8583 ConfigParser cannot find j8583.xml, returning empty message factory");
|
||||
return new MessageFactory<IsoMessage>();
|
||||
} else {
|
||||
return createFromClasspathConfig(loader, "j8583.xml");
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a message factory configured from the default file, which is j8583.xml
|
||||
* located in the root of the classpath. */
|
||||
public static <T extends IsoMessage> MessageFactory<T> createDefault() throws IOException {
|
||||
if (MessageFactory.class.getClassLoader().getResource("j8583.xml") == null) {
|
||||
log.warn("ISO8583 ConfigParser cannot find j8583.xml, returning empty message factory");
|
||||
return new MessageFactory<T>();
|
||||
} else {
|
||||
return createFromClasspathConfig("j8583.xml");
|
||||
}
|
||||
* located in the root of the classpath, using the MessageFactory's
|
||||
* ClassLoader. */
|
||||
public static MessageFactory<IsoMessage> createDefault() throws IOException {
|
||||
return createDefault(MessageFactory.class.getClassLoader());
|
||||
}
|
||||
|
||||
/** Creates a message factory from the specified path inside the classpath. */
|
||||
public static <T extends IsoMessage> MessageFactory<T> createFromClasspathConfig(String path) throws IOException {
|
||||
InputStream ins = MessageFactory.class.getClassLoader().getResourceAsStream(path);
|
||||
MessageFactory<T> mfact = new MessageFactory<T>();
|
||||
/** Creates a message factory from the specified path inside the classpath,
|
||||
* using the specified ClassLoader. */
|
||||
public static MessageFactory<IsoMessage> createFromClasspathConfig(
|
||||
String path) throws IOException {
|
||||
return createFromClasspathConfig(MessageFactory.class.getClassLoader(), path);
|
||||
}
|
||||
/** Creates a message factory from the specified path inside the classpath,
|
||||
* using MessageFactory's ClassLoader. */
|
||||
public static MessageFactory<IsoMessage> createFromClasspathConfig(
|
||||
ClassLoader loader, String path) throws IOException {
|
||||
InputStream ins = loader.getResourceAsStream(path);
|
||||
MessageFactory<IsoMessage> mfact = new MessageFactory<IsoMessage>();
|
||||
if (ins != null) {
|
||||
log.debug("ISO8583 Parsing config from classpath file {}", path);
|
||||
try {
|
||||
@@ -79,8 +95,8 @@ public class ConfigParser {
|
||||
}
|
||||
|
||||
/** Creates a message factory from the file located at the specified URL. */
|
||||
public static <T extends IsoMessage> MessageFactory<T> createFromUrl(URL url) throws IOException {
|
||||
MessageFactory<T> mfact = new MessageFactory<T>();
|
||||
public static MessageFactory<IsoMessage> createFromUrl(URL url) throws IOException {
|
||||
MessageFactory<IsoMessage> mfact = new MessageFactory<IsoMessage>();
|
||||
InputStream stream = url.openStream();
|
||||
try {
|
||||
parse(mfact, stream);
|
||||
@@ -104,7 +120,7 @@ public class ConfigParser {
|
||||
@Override
|
||||
public InputSource resolveEntity(String publicId, String systemId)
|
||||
throws SAXException, IOException {
|
||||
if (systemId.indexOf("j8583.dtd") >= 0) {
|
||||
if (systemId.contains("j8583.dtd")) {
|
||||
URL dtd = getClass().getResource("j8583.dtd");
|
||||
if (dtd == null) {
|
||||
log.warn("Cannot find j8583.dtd in classpath. j8583 config files will not be validated.");
|
||||
@@ -153,7 +169,8 @@ public class ConfigParser {
|
||||
throw new IOException("Invalid ISO8583 type for template: " + elem.getAttribute("type"));
|
||||
}
|
||||
NodeList fields = elem.getElementsByTagName("field");
|
||||
IsoMessage m = new IsoMessage();
|
||||
@SuppressWarnings("unchecked")
|
||||
T m = (T)new IsoMessage();
|
||||
m.setType(type);
|
||||
m.setCharacterEncoding(mfact.getCharacterEncoding());
|
||||
for (int j = 0; j < fields.getLength(); j++) {
|
||||
@@ -168,7 +185,7 @@ public class ConfigParser {
|
||||
CustomField<Object> _cf = mfact.getCustomField(num);
|
||||
m.setValue(num, _cf == null ? v : _cf.decodeField(v), _cf, itype, length);
|
||||
}
|
||||
mfact.addMessageTemplate((T)m);
|
||||
mfact.addMessageTemplate(m);
|
||||
}
|
||||
|
||||
//Read the parsing guides
|
||||
@@ -193,14 +210,13 @@ public class ConfigParser {
|
||||
}
|
||||
mfact.setParseMap(type, parseMap);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Configures a MessageFactory using the default configuration file j8583.xml. This is useful
|
||||
* if you have a MessageFactory created using Spring for example. */
|
||||
public static <T extends IsoMessage> void configureFromDefault(
|
||||
MessageFactory<T> mfact) throws IOException {
|
||||
if (MessageFactory.class.getClassLoader().getResource("j8583.xml") == null) {
|
||||
if (mfact.getClass().getClassLoader().getResource("j8583.xml") == null) {
|
||||
log.warn("ISO8583 config file j8583.xml not found!");
|
||||
} else {
|
||||
configureFromClasspathConfig(mfact, "j8583.xml");
|
||||
@@ -224,7 +240,7 @@ public class ConfigParser {
|
||||
* Spring-bound instances of MessageFactory for example. */
|
||||
public static <T extends IsoMessage> void configureFromClasspathConfig(
|
||||
MessageFactory<T> mfact, String path) throws IOException {
|
||||
InputStream ins = MessageFactory.class.getClassLoader().getResourceAsStream(path);
|
||||
InputStream ins = mfact.getClass().getClassLoader().getResourceAsStream(path);
|
||||
if (ins != null) {
|
||||
log.debug("ISO8583 Parsing config from classpath file {}", path);
|
||||
try {
|
||||
|
||||
@@ -18,6 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
package com.solab.iso8583.parse;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.text.ParseException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
@@ -42,9 +43,9 @@ public class Date10ParseInfo extends FieldParseInfo {
|
||||
}
|
||||
|
||||
@Override
|
||||
public IsoValue<Date> parse(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<?> custom)
|
||||
throws ParseException {
|
||||
public <T> IsoValue<Date> parse(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<T> custom)
|
||||
throws ParseException, UnsupportedEncodingException {
|
||||
if (pos < 0) {
|
||||
throw new ParseException(String.format("Invalid DATE10 field %d position %d",
|
||||
field, pos), pos);
|
||||
@@ -57,19 +58,27 @@ public class Date10ParseInfo extends FieldParseInfo {
|
||||
//we have to use the current date for reference and change what comes in the buffer
|
||||
Calendar cal = Calendar.getInstance();
|
||||
//Set the month in the date
|
||||
cal.set(Calendar.MONTH, ((buf[pos] - 48) * 10) + buf[pos + 1] - 49);
|
||||
cal.set(Calendar.DATE, ((buf[pos + 2] - 48) * 10) + buf[pos + 3] - 48);
|
||||
cal.set(Calendar.HOUR_OF_DAY, ((buf[pos + 4] - 48) * 10) + buf[pos + 5] - 48);
|
||||
cal.set(Calendar.MINUTE, ((buf[pos + 6] - 48) * 10) + buf[pos + 7] - 48);
|
||||
cal.set(Calendar.SECOND, ((buf[pos + 8] - 48) * 10) + buf[pos + 9] - 48);
|
||||
cal.set(Calendar.MILLISECOND,0);
|
||||
if (forceStringDecoding) {
|
||||
cal.set(Calendar.MONTH, Integer.parseInt(new String(buf, pos, 2, getCharacterEncoding()), 10)-1);
|
||||
cal.set(Calendar.DATE, Integer.parseInt(new String(buf, pos+2, 2, getCharacterEncoding()), 10));
|
||||
cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(new String(buf, pos+4, 2, getCharacterEncoding()), 10));
|
||||
cal.set(Calendar.MINUTE, Integer.parseInt(new String(buf, pos+6, 2, getCharacterEncoding()), 10));
|
||||
cal.set(Calendar.SECOND, Integer.parseInt(new String(buf, pos+8, 2, getCharacterEncoding()), 10));
|
||||
} else {
|
||||
cal.set(Calendar.MONTH, ((buf[pos] - 48) * 10) + buf[pos + 1] - 49);
|
||||
cal.set(Calendar.DATE, ((buf[pos + 2] - 48) * 10) + buf[pos + 3] - 48);
|
||||
cal.set(Calendar.HOUR_OF_DAY, ((buf[pos + 4] - 48) * 10) + buf[pos + 5] - 48);
|
||||
cal.set(Calendar.MINUTE, ((buf[pos + 6] - 48) * 10) + buf[pos + 7] - 48);
|
||||
cal.set(Calendar.SECOND, ((buf[pos + 8] - 48) * 10) + buf[pos + 9] - 48);
|
||||
}
|
||||
cal.set(Calendar.MILLISECOND,0);
|
||||
adjustWithFutureTolerance(cal);
|
||||
return new IsoValue<Date>(type, cal.getTime(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IsoValue<Date> parseBinary(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<?> custom)
|
||||
public <T> IsoValue<Date> parseBinary(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<T> custom)
|
||||
throws ParseException {
|
||||
if (pos < 0) {
|
||||
throw new ParseException(String.format("Invalid DATE10 field %d position %d",
|
||||
|
||||
@@ -18,6 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
package com.solab.iso8583.parse;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.text.ParseException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
@@ -37,8 +38,9 @@ public class Date4ParseInfo extends FieldParseInfo {
|
||||
}
|
||||
|
||||
@Override
|
||||
public IsoValue<Date> parse(final int field, final byte[] buf, final int pos,
|
||||
final CustomField<?> custom) throws ParseException {
|
||||
public <T> IsoValue<Date> parse(final int field, final byte[] buf, final int pos,
|
||||
final CustomField<T> custom)
|
||||
throws ParseException, UnsupportedEncodingException {
|
||||
if (pos < 0) {
|
||||
throw new ParseException(String.format("Invalid DATE4 field %d position %d",
|
||||
field, pos), pos);
|
||||
@@ -53,15 +55,20 @@ public class Date4ParseInfo extends FieldParseInfo {
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
//Set the month in the date
|
||||
cal.set(Calendar.MONTH, ((buf[pos] - 48) * 10) + buf[pos + 1] - 49);
|
||||
cal.set(Calendar.DATE, ((buf[pos + 2] - 48) * 10) + buf[pos + 3] - 48);
|
||||
if (forceStringDecoding) {
|
||||
cal.set(Calendar.MONTH, Integer.parseInt(new String(buf, pos, 2, getCharacterEncoding()), 10)-1);
|
||||
cal.set(Calendar.DATE, Integer.parseInt(new String(buf, pos+2, 2, getCharacterEncoding()), 10));
|
||||
} else {
|
||||
cal.set(Calendar.MONTH, ((buf[pos] - 48) * 10) + buf[pos + 1] - 49);
|
||||
cal.set(Calendar.DATE, ((buf[pos + 2] - 48) * 10) + buf[pos + 3] - 48);
|
||||
}
|
||||
Date10ParseInfo.adjustWithFutureTolerance(cal);
|
||||
return new IsoValue<Date>(type, cal.getTime(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IsoValue<Date> parseBinary(final int field, final byte[] buf, final int pos,
|
||||
final CustomField<?> custom) throws ParseException {
|
||||
public <T> IsoValue<Date> parseBinary(final int field, final byte[] buf, final int pos,
|
||||
final CustomField<T> custom) throws ParseException {
|
||||
int[] tens = new int[2];
|
||||
int start = 0;
|
||||
if (buf.length-pos < 2) {
|
||||
|
||||
@@ -18,6 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
package com.solab.iso8583.parse;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.text.ParseException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
@@ -37,9 +38,9 @@ public class DateExpParseInfo extends FieldParseInfo {
|
||||
}
|
||||
|
||||
@Override
|
||||
public IsoValue<Date> parse(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<?> custom)
|
||||
throws ParseException {
|
||||
public <T> IsoValue<Date> parse(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<T> custom)
|
||||
throws ParseException, UnsupportedEncodingException {
|
||||
if (pos < 0) {
|
||||
throw new ParseException(String.format("Invalid DATE_EXP field %d position %d",
|
||||
field, pos), pos);
|
||||
@@ -54,15 +55,21 @@ public class DateExpParseInfo extends FieldParseInfo {
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.DATE, 1);
|
||||
//Set the month in the date
|
||||
cal.set(Calendar.YEAR, cal.get(Calendar.YEAR) - (cal.get(Calendar.YEAR) % 100)
|
||||
+ ((buf[pos] - 48) * 10) + buf[pos + 1] - 48);
|
||||
cal.set(Calendar.MONTH, ((buf[pos + 2] - 48) * 10) + buf[pos + 3] - 49);
|
||||
if (forceStringDecoding) {
|
||||
cal.set(Calendar.YEAR, cal.get(Calendar.YEAR) - (cal.get(Calendar.YEAR) % 100)
|
||||
+ Integer.parseInt(new String(buf, pos, 2, getCharacterEncoding()), 10));
|
||||
cal.set(Calendar.MONTH, Integer.parseInt(new String(buf, pos+2, 2, getCharacterEncoding()), 10)-1);
|
||||
} else {
|
||||
cal.set(Calendar.YEAR, cal.get(Calendar.YEAR) - (cal.get(Calendar.YEAR) % 100)
|
||||
+ ((buf[pos] - 48) * 10) + buf[pos + 1] - 48);
|
||||
cal.set(Calendar.MONTH, ((buf[pos + 2] - 48) * 10) + buf[pos + 3] - 49);
|
||||
}
|
||||
return new IsoValue<Date>(type, cal.getTime(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IsoValue<Date> parseBinary(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<?> custom)
|
||||
public <T> IsoValue<Date> parseBinary(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<T> custom)
|
||||
throws ParseException {
|
||||
if (pos < 0) {
|
||||
throw new ParseException(String.format("Invalid DATE_EXP field %d position %d",
|
||||
|
||||
@@ -34,6 +34,7 @@ public abstract class FieldParseInfo {
|
||||
protected IsoType type;
|
||||
protected int length;
|
||||
private String encoding = System.getProperty("file.encoding");
|
||||
protected boolean forceStringDecoding;
|
||||
|
||||
/** Creates a new instance that parses a value of the specified type, with the specified length.
|
||||
* The length is only useful for ALPHA and NUMERIC types.
|
||||
@@ -47,6 +48,13 @@ public abstract class FieldParseInfo {
|
||||
length = len;
|
||||
}
|
||||
|
||||
/** Specified whether length headers for variable-length fields in text mode should
|
||||
* be decoded using proper string conversion with the character encoding. Default is false,
|
||||
* which means use the old behavior of decoding as ASCII. */
|
||||
public void setForceStringDecoding(boolean flag) {
|
||||
forceStringDecoding = flag;
|
||||
}
|
||||
|
||||
public void setCharacterEncoding(String value) {
|
||||
encoding = value;
|
||||
}
|
||||
@@ -70,9 +78,9 @@ public abstract class FieldParseInfo {
|
||||
* @param buf The full ISO message buffer.
|
||||
* @param pos The starting position for the field data.
|
||||
* @param custom A CustomField to decode the field. */
|
||||
public abstract IsoValue<?> parse(final int field, byte[] buf, int pos,
|
||||
CustomField<?> custom)
|
||||
throws ParseException, UnsupportedEncodingException;
|
||||
public abstract <T> IsoValue<?> parse(final int field, byte[] buf, int pos,
|
||||
CustomField<T> custom)
|
||||
throws ParseException, UnsupportedEncodingException;
|
||||
|
||||
/** Parses binary data from the buffer, creating and returning an IsoValue of the configured
|
||||
* type and length.
|
||||
@@ -80,9 +88,9 @@ public abstract class FieldParseInfo {
|
||||
* @param buf The full ISO message buffer.
|
||||
* @param pos The starting position for the field data.
|
||||
* @param custom A CustomField to decode the field. */
|
||||
public abstract IsoValue<?> parseBinary(final int field, byte[] buf, int pos,
|
||||
CustomField<?> custom)
|
||||
throws ParseException, UnsupportedEncodingException;
|
||||
public abstract <T> IsoValue<?> parseBinary(final int field, byte[] buf, int pos,
|
||||
CustomField<T> custom)
|
||||
throws ParseException, UnsupportedEncodingException;
|
||||
|
||||
/** Returns a new FieldParseInfo instance that can parse the specified type. */
|
||||
public static FieldParseInfo getInstance(IsoType t, int len, String encoding) {
|
||||
@@ -119,4 +127,14 @@ public abstract class FieldParseInfo {
|
||||
return fpi;
|
||||
}
|
||||
|
||||
protected int decodeLength(byte[] buf, int pos, int digits) throws UnsupportedEncodingException {
|
||||
if (forceStringDecoding) {
|
||||
return Integer.parseInt(new String(buf, pos, digits, encoding));
|
||||
} else if (digits == 3) {
|
||||
return ((buf[pos] - 48) * 100) + ((buf[pos + 1] - 48) * 10) + (buf[pos + 2] - 48);
|
||||
} else {
|
||||
return ((buf[pos] - 48) * 10) + (buf[pos + 1] - 48);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,8 +18,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
package com.solab.iso8583.parse;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.text.ParseException;
|
||||
|
||||
import com.solab.iso8583.CustomBinaryField;
|
||||
import com.solab.iso8583.CustomField;
|
||||
import com.solab.iso8583.IsoType;
|
||||
import com.solab.iso8583.IsoValue;
|
||||
@@ -37,9 +39,9 @@ public class LlbinParseInfo extends FieldParseInfo {
|
||||
}
|
||||
|
||||
@Override
|
||||
public IsoValue<?> parse(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<?> custom)
|
||||
throws ParseException {
|
||||
public <T> IsoValue<?> parse(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<T> custom)
|
||||
throws ParseException, UnsupportedEncodingException {
|
||||
if (pos < 0) {
|
||||
throw new ParseException(String.format("Invalid LLBIN field %d position %d",
|
||||
field, pos), pos);
|
||||
@@ -47,7 +49,7 @@ public class LlbinParseInfo extends FieldParseInfo {
|
||||
throw new ParseException(String.format("Insufficient LLBIN header field %d",
|
||||
field), pos);
|
||||
}
|
||||
length = ((buf[pos] - 48) * 10) + (buf[pos + 1] - 48);
|
||||
length = decodeLength(buf, pos, 2);
|
||||
if (length < 0) {
|
||||
throw new ParseException(String.format("Invalid LLBIN field %d length %d pos %d",
|
||||
field, length, pos), pos);
|
||||
@@ -60,15 +62,21 @@ public class LlbinParseInfo extends FieldParseInfo {
|
||||
byte[] binval = length == 0 ? new byte[0] : HexCodec.hexDecode(new String(buf, pos + 2, length));
|
||||
if (custom == null) {
|
||||
return new IsoValue<byte[]>(type, binval, binval.length, null);
|
||||
} else if (custom instanceof CustomBinaryField) {
|
||||
try {
|
||||
T dec = ((CustomBinaryField<T>)custom).decodeBinaryField(buf, pos + 2, length);
|
||||
return dec == null ? new IsoValue<byte[]>(type, binval, binval.length, null) :
|
||||
new IsoValue<T>(type, dec, 0, custom);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new ParseException(String.format(
|
||||
"Insufficient data for LLBIN field %d, pos %d (LEN states '%s')",
|
||||
field, pos, new String(buf, pos, 2)), pos);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
IsoValue<?> v = new IsoValue(type, custom.decodeField(
|
||||
new String(buf, pos + 2, length)), binval.length, custom);
|
||||
if (v.getValue() == null) {
|
||||
return new IsoValue<byte[]>(type, binval, binval.length, null);
|
||||
}
|
||||
return v;
|
||||
T dec = custom.decodeField(new String(buf, pos + 2, length));
|
||||
return dec == null ? new IsoValue<byte[]>(type, binval, binval.length, null) :
|
||||
new IsoValue<T>(type, dec, binval.length, custom);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new ParseException(String.format(
|
||||
"Insufficient data for LLBIN field %d, pos %d (LEN states '%s')",
|
||||
@@ -78,8 +86,8 @@ public class LlbinParseInfo extends FieldParseInfo {
|
||||
}
|
||||
|
||||
@Override
|
||||
public IsoValue<?> parseBinary(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<?> custom)
|
||||
public <T> IsoValue<?> parseBinary(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<T> custom)
|
||||
throws ParseException {
|
||||
if (pos < 0) {
|
||||
throw new ParseException(String.format("Invalid bin LLBIN field %d position %d",
|
||||
@@ -88,25 +96,33 @@ public class LlbinParseInfo extends FieldParseInfo {
|
||||
throw new ParseException(String.format("Insufficient bin LLBIN header field %d",
|
||||
field), pos);
|
||||
}
|
||||
length = (((buf[pos] & 0xf0) >> 4) * 10) + (buf[pos] & 0x0f);
|
||||
if (length < 0) {
|
||||
final int l = (((buf[pos] & 0xf0) >> 4) * 10) + (buf[pos] & 0x0f);
|
||||
if (l < 0) {
|
||||
throw new ParseException(String.format("Invalid bin LLBIN length %d pos %d", length, pos), pos);
|
||||
}
|
||||
if (length+pos+1 > buf.length) {
|
||||
if (l+pos+1 > buf.length) {
|
||||
throw new ParseException(String.format(
|
||||
"Insufficient data for bin LLBIN field %d, pos %d", field, pos), pos);
|
||||
"Insufficient data for bin LLBIN field %d, pos %d: need %d, only %d available",
|
||||
field, pos, l, buf.length), pos);
|
||||
}
|
||||
byte[] _v = new byte[length];
|
||||
System.arraycopy(buf, pos+1, _v, 0, length);
|
||||
byte[] _v = new byte[l];
|
||||
System.arraycopy(buf, pos+1, _v, 0, l);
|
||||
if (custom == null) {
|
||||
return new IsoValue<byte[]>(type, _v, null);
|
||||
} else if (custom instanceof CustomBinaryField) {
|
||||
try {
|
||||
T dec = ((CustomBinaryField<T>)custom).decodeBinaryField(buf, pos + 1, l);
|
||||
return dec == null ? new IsoValue<byte[]>(type, _v, _v.length, null) :
|
||||
new IsoValue<T>(type, dec, l, custom);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new ParseException(String.format(
|
||||
"Insufficient data for LLBIN field %d, pos %d length %d",
|
||||
field, pos, l), pos);
|
||||
}
|
||||
} else {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
IsoValue<?> v = new IsoValue(type, custom.decodeField(HexCodec.hexEncode(_v, 0, _v.length)), custom);
|
||||
if (v.getValue() == null) {
|
||||
return new IsoValue<byte[]>(type, _v, null);
|
||||
}
|
||||
return v;
|
||||
T dec = custom.decodeField(HexCodec.hexEncode(_v, 0, _v.length));
|
||||
return dec == null ? new IsoValue<byte[]>(type, _v, null) :
|
||||
new IsoValue<T>(type, dec, custom);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
package com.solab.iso8583.parse;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.text.ParseException;
|
||||
|
||||
import com.solab.iso8583.CustomBinaryField;
|
||||
import com.solab.iso8583.CustomField;
|
||||
import com.solab.iso8583.IsoType;
|
||||
import com.solab.iso8583.IsoValue;
|
||||
@@ -37,9 +39,9 @@ public class LllbinParseInfo extends FieldParseInfo {
|
||||
}
|
||||
|
||||
@Override
|
||||
public IsoValue<?> parse(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<?> custom)
|
||||
throws ParseException {
|
||||
public <T> IsoValue<?> parse(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<T> custom)
|
||||
throws ParseException, UnsupportedEncodingException {
|
||||
if (pos < 0) {
|
||||
throw new ParseException(String.format("Invalid LLLBIN field %d pos %d",
|
||||
field, pos), pos);
|
||||
@@ -47,31 +49,33 @@ public class LllbinParseInfo extends FieldParseInfo {
|
||||
throw new ParseException(String.format("Insufficient LLLBIN header field %d",
|
||||
field), pos);
|
||||
}
|
||||
if (!(Character.isDigit(buf[pos]) && Character.isDigit(buf[pos+1]) && Character.isDigit(buf[pos+2]))) {
|
||||
throw new ParseException(String.format("Invalid LLLBIN length '%s' field %d pos %d",
|
||||
new String(buf, pos, 3), field, pos), pos);
|
||||
}
|
||||
length = ((buf[pos] - 48) * 100) + ((buf[pos + 1] - 48) * 10) + (buf[pos + 2] - 48);
|
||||
if (length < 0) {
|
||||
final int l = decodeLength(buf, pos, 3);
|
||||
if (l < 0) {
|
||||
throw new ParseException(String.format("Invalid LLLBIN length %d field %d pos %d",
|
||||
length, field, pos), pos);
|
||||
} else if (length+pos+3 > buf.length) {
|
||||
l, field, pos), pos);
|
||||
} else if (l+pos+3 > buf.length) {
|
||||
throw new ParseException(String.format(
|
||||
"Insufficient data for LLLBIN field %d, pos %d", field, pos), pos);
|
||||
}
|
||||
byte[] binval = length == 0 ? new byte[0] : HexCodec.hexDecode(new String(buf, pos + 3, length));
|
||||
byte[] binval = l == 0 ? new byte[0] : HexCodec.hexDecode(new String(buf, pos + 3, l));
|
||||
if (custom == null) {
|
||||
return new IsoValue<byte[]>(type, binval, binval.length, null);
|
||||
} else if (custom instanceof CustomBinaryField) {
|
||||
try {
|
||||
T dec = ((CustomBinaryField<T>)custom).decodeBinaryField(
|
||||
buf, pos + 3, l);
|
||||
return dec == null ? new IsoValue<byte[]>(type, binval, binval.length, null) :
|
||||
new IsoValue<T>(type, dec, 0, custom);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new ParseException(String.format(
|
||||
"Insufficient data for LLLBIN field %d, pos %d", field, pos), pos);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
IsoValue<?> v = new IsoValue(type, custom.decodeField(
|
||||
length == 0 ? "" : new String(buf, pos + 3, length)), length, custom);
|
||||
if (v.getValue() == null) {
|
||||
//problems decoding? return the string
|
||||
return new IsoValue<byte[]>(type, binval, binval.length, null);
|
||||
}
|
||||
return v;
|
||||
T dec = custom.decodeField(
|
||||
l == 0 ? "" : new String(buf, pos + 3, l));
|
||||
return dec == null ? new IsoValue<byte[]>(type, binval, binval.length, null) :
|
||||
new IsoValue<T>(type, dec, l, custom);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new ParseException(String.format(
|
||||
"Insufficient data for LLLBIN field %d, pos %d", field, pos), pos);
|
||||
@@ -80,36 +84,44 @@ public class LllbinParseInfo extends FieldParseInfo {
|
||||
}
|
||||
|
||||
@Override
|
||||
public IsoValue<?> parseBinary(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<?> custom)
|
||||
public <T> IsoValue<?> parseBinary(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<T> custom)
|
||||
throws ParseException {
|
||||
if (pos < 0) {
|
||||
throw new ParseException(String.format("Invalid bin LLLBIN field %d pos %d",
|
||||
field, pos), pos);
|
||||
} else if (pos+3 > buf.length) {
|
||||
} else if (pos+2 > buf.length) {
|
||||
throw new ParseException(String.format("Insufficient LLLBIN header field %d",
|
||||
field), pos);
|
||||
}
|
||||
length = ((buf[pos] & 0x0f) * 100) + (((buf[pos + 1] & 0xf0) >> 4) * 10) + (buf[pos + 1] & 0x0f);
|
||||
if (length < 0) {
|
||||
final int l = ((buf[pos] & 0x0f) * 100) + (((buf[pos + 1] & 0xf0) >> 4) * 10) + (buf[pos + 1] & 0x0f);
|
||||
if (l < 0) {
|
||||
throw new ParseException(String.format("Invalid LLLBIN length %d field %d pos %d",
|
||||
length, field, pos), pos);
|
||||
l, field, pos), pos);
|
||||
}
|
||||
if (length+pos+2 > buf.length) {
|
||||
if (l+pos+2 > buf.length) {
|
||||
throw new ParseException(String.format(
|
||||
"Insufficient data for bin LLLBIN field %d, pos %d", field, pos), pos);
|
||||
"Insufficient data for bin LLLBIN field %d, pos %d requires %d, only %d available",
|
||||
field, pos, l, buf.length-pos+1), pos);
|
||||
}
|
||||
byte[] _v = new byte[length];
|
||||
System.arraycopy(buf, pos+2, _v, 0, length);
|
||||
byte[] _v = new byte[l];
|
||||
System.arraycopy(buf, pos+2, _v, 0, l);
|
||||
if (custom == null) {
|
||||
return new IsoValue<byte[]>(type, _v, null);
|
||||
} else if (custom instanceof CustomBinaryField) {
|
||||
try {
|
||||
T dec = ((CustomBinaryField<T>)custom).decodeBinaryField(
|
||||
buf, pos + 2, l);
|
||||
return dec == null ? new IsoValue<byte[]>(type, _v, _v.length, null) :
|
||||
new IsoValue<T>(type, dec, l, custom);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new ParseException(String.format(
|
||||
"Insufficient data for LLLBIN field %d, pos %d", field, pos), pos);
|
||||
}
|
||||
} else {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
IsoValue<?> v = new IsoValue(type, custom.decodeField(HexCodec.hexEncode(_v, 0, _v.length)), custom);
|
||||
if (v.getValue() == null) {
|
||||
return new IsoValue<byte[]>(type, _v, null);
|
||||
}
|
||||
return v;
|
||||
T dec = custom.decodeField(HexCodec.hexEncode(_v, 0, _v.length));
|
||||
return dec == null ? new IsoValue<byte[]>(type, _v, null) :
|
||||
new IsoValue<T>(type, dec, custom);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,8 +36,8 @@ public class LllvarParseInfo extends FieldParseInfo {
|
||||
super(IsoType.LLLVAR, 0);
|
||||
}
|
||||
|
||||
public IsoValue<?> parse(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<?> custom)
|
||||
public <T> IsoValue<?> parse(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<T> custom)
|
||||
throws ParseException, UnsupportedEncodingException {
|
||||
if (pos < 0) {
|
||||
throw new ParseException(String.format("Invalid LLLVAR field %d pos %d",
|
||||
@@ -46,11 +46,7 @@ public class LllvarParseInfo extends FieldParseInfo {
|
||||
throw new ParseException(String.format(
|
||||
"Insufficient data for LLLVAR header field %d pos %d", field, pos), pos);
|
||||
}
|
||||
if (!(Character.isDigit(buf[pos]) && Character.isDigit(buf[pos+1]) && Character.isDigit(buf[pos+2]))) {
|
||||
throw new ParseException(String.format("Invalid LLLVAR length '%s' field %d pos %d",
|
||||
new String(buf, pos, 3), field, pos), pos);
|
||||
}
|
||||
length = ((buf[pos] - 48) * 100) + ((buf[pos + 1] - 48) * 10) + (buf[pos + 2] - 48);
|
||||
length = decodeLength(buf, pos, 3);
|
||||
if (length < 0) {
|
||||
throw new ParseException(String.format("Invalid LLLVAR length %d field %d pos %d",
|
||||
length, field, pos), pos);
|
||||
@@ -74,14 +70,15 @@ public class LllvarParseInfo extends FieldParseInfo {
|
||||
if (custom == null) {
|
||||
return new IsoValue<String>(type, _v, length, null);
|
||||
} else {
|
||||
Object decoded = custom.decodeField(_v);
|
||||
T decoded = custom.decodeField(_v);
|
||||
//If decode fails, return string; otherwise use the decoded object and its codec
|
||||
return new IsoValue(type, decoded == null ? _v : decoded, length, decoded == null ? null : custom);
|
||||
return decoded == null ? new IsoValue<String>(type, _v, length, null) :
|
||||
new IsoValue<T>(type, decoded, length, custom);
|
||||
}
|
||||
}
|
||||
|
||||
public IsoValue<?> parseBinary(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<?> custom)
|
||||
public <T> IsoValue<?> parseBinary(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<T> custom)
|
||||
throws ParseException, UnsupportedEncodingException {
|
||||
if (pos < 0) {
|
||||
throw new ParseException(String.format("Invalid bin LLLVAR field %d pos %d", field, pos), pos);
|
||||
@@ -100,8 +97,7 @@ public class LllvarParseInfo extends FieldParseInfo {
|
||||
if (custom == null) {
|
||||
return new IsoValue<String>(type, new String(buf, pos + 2, length, getCharacterEncoding()), null);
|
||||
} else {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
IsoValue<?> v = new IsoValue(type, custom.decodeField(new String(buf, pos + 2, length, getCharacterEncoding())), custom);
|
||||
IsoValue<T> v = new IsoValue<T>(type, custom.decodeField(new String(buf, pos + 2, length, getCharacterEncoding())), custom);
|
||||
if (v.getValue() == null) {
|
||||
return new IsoValue<String>(type, new String(buf, pos + 2, length, getCharacterEncoding()), null);
|
||||
}
|
||||
|
||||
@@ -35,15 +35,16 @@ public class LlvarParseInfo extends FieldParseInfo {
|
||||
super(IsoType.LLVAR, 0);
|
||||
}
|
||||
|
||||
public IsoValue<?> parse(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<?> custom)
|
||||
@Override
|
||||
public <T> IsoValue<?> parse(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<T> custom)
|
||||
throws ParseException, UnsupportedEncodingException {
|
||||
if (pos < 0) {
|
||||
throw new ParseException(String.format("Invalid LLVAR field %d %d", field, pos), pos);
|
||||
} else if (pos+2 > buf.length) {
|
||||
throw new ParseException(String.format("Insufficient data for LLVAR header, pos %d", pos), pos);
|
||||
}
|
||||
length = ((buf[pos] - 48) * 10) + (buf[pos + 1] - 48);
|
||||
length = decodeLength(buf, pos, 2);
|
||||
if (length < 0) {
|
||||
throw new ParseException(String.format(
|
||||
"Invalid LLVAR length %d, field %d pos %d", length, field, pos), pos);
|
||||
@@ -67,17 +68,15 @@ public class LlvarParseInfo extends FieldParseInfo {
|
||||
if (custom == null) {
|
||||
return new IsoValue<String>(type, _v, length, null);
|
||||
} else {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
IsoValue<?> v = new IsoValue(type, custom.decodeField(_v), length, custom);
|
||||
if (v.getValue() == null) {
|
||||
return new IsoValue<String>(type, _v, length, null);
|
||||
}
|
||||
return v;
|
||||
T dec = custom.decodeField(_v);
|
||||
return dec == null ? new IsoValue<String>(type, _v, length, null) :
|
||||
new IsoValue<T>(type, dec, length, custom);
|
||||
}
|
||||
}
|
||||
|
||||
public IsoValue<?> parseBinary(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<?> custom)
|
||||
@Override
|
||||
public <T> IsoValue<?> parseBinary(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<T> custom)
|
||||
throws ParseException, UnsupportedEncodingException {
|
||||
if (pos < 0) {
|
||||
throw new ParseException(String.format("Invalid bin LLVAR field %d pos %d",
|
||||
@@ -98,12 +97,9 @@ public class LlvarParseInfo extends FieldParseInfo {
|
||||
if (custom == null) {
|
||||
return new IsoValue<String>(type, new String(buf, pos + 1, length, getCharacterEncoding()), null);
|
||||
} else {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
IsoValue<?> v = new IsoValue(type, custom.decodeField(new String(buf, pos + 1, length, getCharacterEncoding())), custom);
|
||||
if (v.getValue() == null) {
|
||||
return new IsoValue<String>(type, new String(buf, pos + 1, length, getCharacterEncoding()), null);
|
||||
}
|
||||
return v;
|
||||
T dec = custom.decodeField(new String(buf, pos + 1, length, getCharacterEncoding()));
|
||||
return dec == null ? new IsoValue<String>(type, new String(buf, pos + 1, length, getCharacterEncoding()), null) :
|
||||
new IsoValue<T>(type, dec, custom);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.text.ParseException;
|
||||
import com.solab.iso8583.CustomField;
|
||||
import com.solab.iso8583.IsoType;
|
||||
import com.solab.iso8583.IsoValue;
|
||||
import com.solab.iso8583.util.Bcd;
|
||||
|
||||
/** This class is used to parse NUMERIC fields.
|
||||
*
|
||||
@@ -35,8 +36,9 @@ public class NumericParseInfo extends AlphaNumericFieldParseInfo {
|
||||
super(IsoType.NUMERIC, len);
|
||||
}
|
||||
|
||||
public IsoValue<Number> parseBinary(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<?> custom)
|
||||
@Override
|
||||
public <T> IsoValue<Number> parseBinary(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<T> custom)
|
||||
throws ParseException {
|
||||
if (pos < 0) {
|
||||
throw new ParseException(String.format("Invalid bin NUMERIC field %d pos %d",
|
||||
@@ -48,30 +50,18 @@ public class NumericParseInfo extends AlphaNumericFieldParseInfo {
|
||||
}
|
||||
//A long covers up to 18 digits
|
||||
if (length < 19) {
|
||||
long l = 0;
|
||||
long power = 1L;
|
||||
for (int i = pos + (length / 2) + (length % 2) - 1; i >= pos; i--) {
|
||||
l += (buf[i] & 0x0f) * power;
|
||||
power *= 10L;
|
||||
l += ((buf[i] & 0xf0) >> 4) * power;
|
||||
power *= 10L;
|
||||
}
|
||||
return new IsoValue<Number>(IsoType.NUMERIC, l, length, null);
|
||||
return new IsoValue<Number>(IsoType.NUMERIC, Bcd.decodeToLong(buf, pos, length),
|
||||
length, null);
|
||||
} else {
|
||||
//Use a BigInteger
|
||||
char[] digits = new char[length];
|
||||
int start = 0;
|
||||
try {
|
||||
for (int i = pos; i < pos + (length / 2) + (length % 2); i++) {
|
||||
digits[start++] = (char)(((buf[i] & 0xf0) >> 4) + 48);
|
||||
digits[start++] = (char)((buf[i] & 0x0f) + 48);
|
||||
}
|
||||
return new IsoValue<Number>(IsoType.NUMERIC,
|
||||
Bcd.decodeToBigInteger(buf, pos, length), length, null);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new ParseException(String.format(
|
||||
"Insufficient data for bin %s field %d of length %d, pos %d",
|
||||
type, field, length, pos), pos);
|
||||
"Insufficient data for bin %s field %d of length %d, pos %d",
|
||||
type, field, length, pos), pos);
|
||||
}
|
||||
return new IsoValue<Number>(IsoType.NUMERIC, new BigInteger(new String(digits)), length, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
package com.solab.iso8583.parse;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.text.ParseException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
@@ -38,9 +39,9 @@ public class TimeParseInfo extends FieldParseInfo {
|
||||
}
|
||||
|
||||
@Override
|
||||
public IsoValue<Date> parse(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<?> custom)
|
||||
throws ParseException {
|
||||
public <T> IsoValue<Date> parse(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<T> custom)
|
||||
throws ParseException, UnsupportedEncodingException {
|
||||
if (pos < 0) {
|
||||
throw new ParseException(String.format("Invalid TIME field %d pos %d",
|
||||
field, pos), pos);
|
||||
@@ -49,15 +50,21 @@ public class TimeParseInfo extends FieldParseInfo {
|
||||
"Insufficient data for TIME field %d, pos %d", field, pos), pos);
|
||||
}
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.set(Calendar.HOUR_OF_DAY, ((buf[pos] - 48) * 10) + buf[pos + 1] - 48);
|
||||
cal.set(Calendar.MINUTE, ((buf[pos + 2] - 48) * 10) + buf[pos + 3] - 48);
|
||||
cal.set(Calendar.SECOND, ((buf[pos + 4] - 48) * 10) + buf[pos + 5] - 48);
|
||||
if (forceStringDecoding) {
|
||||
cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(new String(buf, pos, 2, getCharacterEncoding()), 10));
|
||||
cal.set(Calendar.MINUTE, Integer.parseInt(new String(buf, pos+2, 2, getCharacterEncoding()), 10));
|
||||
cal.set(Calendar.SECOND, Integer.parseInt(new String(buf, pos+4, 2, getCharacterEncoding()), 10));
|
||||
} else {
|
||||
cal.set(Calendar.HOUR_OF_DAY, ((buf[pos] - 48) * 10) + buf[pos + 1] - 48);
|
||||
cal.set(Calendar.MINUTE, ((buf[pos + 2] - 48) * 10) + buf[pos + 3] - 48);
|
||||
cal.set(Calendar.SECOND, ((buf[pos + 4] - 48) * 10) + buf[pos + 5] - 48);
|
||||
}
|
||||
return new IsoValue<Date>(type, cal.getTime(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IsoValue<Date> parseBinary(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<?> custom)
|
||||
public <T> IsoValue<Date> parseBinary(final int field, final byte[] buf,
|
||||
final int pos, final CustomField<T> custom)
|
||||
throws ParseException {
|
||||
if (pos < 0) {
|
||||
throw new ParseException(String.format("Invalid bin TIME field %d pos %d",
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.solab.iso8583.util;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.text.ParseException;
|
||||
|
||||
/**
|
||||
* Routines for Binary Coded Digits.
|
||||
*
|
||||
* @author Enrique Zamudio
|
||||
* Date: 23/04/13 11:24
|
||||
*/
|
||||
public final class Bcd {
|
||||
|
||||
private Bcd(){}
|
||||
|
||||
/** Decodes a BCD-encoded number as a long.
|
||||
* @param buf The byte buffer containing the BCD data.
|
||||
* @param pos The starting position in the buffer.
|
||||
* @param length The number of DIGITS (not bytes) to read. */
|
||||
public static long decodeToLong(byte[] buf, int pos, int length)
|
||||
throws IndexOutOfBoundsException {
|
||||
if (length > 18) {
|
||||
throw new IndexOutOfBoundsException("Buffer too big to decode as long");
|
||||
}
|
||||
long l = 0;
|
||||
long power = 1L;
|
||||
for (int i = pos + (length / 2) + (length % 2) - 1; i >= pos; i--) {
|
||||
l += (buf[i] & 0x0f) * power;
|
||||
power *= 10L;
|
||||
l += ((buf[i] & 0xf0) >> 4) * power;
|
||||
power *= 10L;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
/** Encode the value as BCD and put it in the buffer. The buffer must be big enough
|
||||
* to store the digits in the original value (half the length of the string). */
|
||||
public static void encode(String value, byte[] buf) {
|
||||
int charpos = 0; //char where we start
|
||||
int bufpos = 0;
|
||||
if (value.length() % 2 == 1) {
|
||||
//for odd lengths we encode just the first digit in the first byte
|
||||
buf[0] = (byte)(value.charAt(0) - 48);
|
||||
charpos = 1;
|
||||
bufpos = 1;
|
||||
}
|
||||
//encode the rest of the string
|
||||
while (charpos < value.length()) {
|
||||
buf[bufpos] = (byte)(((value.charAt(charpos) - 48) << 4)
|
||||
| (value.charAt(charpos + 1) - 48));
|
||||
charpos += 2;
|
||||
bufpos++;
|
||||
}
|
||||
}
|
||||
|
||||
/** Decodes a BCD-encoded number as a BigInteger.
|
||||
* @param buf The byte buffer containing the BCD data.
|
||||
* @param pos The starting position in the buffer.
|
||||
* @param length The number of DIGITS (not bytes) to read. */
|
||||
public static BigInteger decodeToBigInteger(byte[] buf, int pos, int length)
|
||||
throws IndexOutOfBoundsException {
|
||||
char[] digits = new char[length];
|
||||
int start = 0;
|
||||
for (int i = pos; i < pos + (length / 2) + (length % 2); i++) {
|
||||
digits[start++] = (char)(((buf[i] & 0xf0) >> 4) + 48);
|
||||
digits[start++] = (char)((buf[i] & 0x0f) + 48);
|
||||
}
|
||||
return new BigInteger(new String(digits));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
package com.solab.iso8583.util;
|
||||
|
||||
/** Utility class to perform HEX encoding/decoding of values. */
|
||||
public class HexCodec {
|
||||
public final class HexCodec {
|
||||
|
||||
static final char[] HEX = new char[]{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
static final char[] HEX = "0123456789ABCDEF".toCharArray();
|
||||
|
||||
private HexCodec(){}
|
||||
|
||||
public static String hexEncode(byte[] buffer, int start, int length) {
|
||||
if (buffer.length == 0) {
|
||||
|
||||
@@ -31,7 +31,7 @@ public class SimpleParser {
|
||||
}
|
||||
|
||||
public static void main(String [] args) throws IOException, ParseException {
|
||||
final MessageFactory mf = new MessageFactory();
|
||||
final MessageFactory<IsoMessage> mf = new MessageFactory<IsoMessage>();
|
||||
if (args.length == 0) {
|
||||
ConfigParser.configureFromDefault(mf);
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
<item name="Download" href="http://sourceforge.net/projects/j8583/files/j8583/"/>
|
||||
</links>
|
||||
|
||||
<menu name="j8583 1.5.7">
|
||||
<menu name="j8583 1.8.0">
|
||||
<item name="Introduction" href="index.html"/>
|
||||
<item name="API docs" href="javadoc/index.html" />
|
||||
<item name="Beginner's Guide" href="guide.html"/>
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
package j8583;
|
||||
|
||||
import com.solab.iso8583.IsoType;
|
||||
import com.solab.iso8583.MessageFactory;
|
||||
import com.solab.iso8583.IsoMessage;
|
||||
import com.solab.iso8583.codecs.BigIntBcdCodec;
|
||||
import com.solab.iso8583.codecs.LongBcdCodec;
|
||||
import com.solab.iso8583.parse.FieldParseInfo;
|
||||
import com.solab.iso8583.parse.LlbinParseInfo;
|
||||
import com.solab.iso8583.parse.LllbinParseInfo;
|
||||
import com.solab.iso8583.util.HexCodec;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.math.BigInteger;
|
||||
import java.text.ParseException;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Unit tests for the numeric CustomBinaryField codecs.
|
||||
*
|
||||
* @author Enrique Zamudio
|
||||
* Date: 07/05/13 13:20
|
||||
*/
|
||||
public class CustomBinCodecs {
|
||||
|
||||
private final BigInteger b29 = new BigInteger("12345678901234567890123456789");
|
||||
final byte[] longData2 = new byte[]{ 0x12, 0x34, 0x56, 0x78, (byte)0x90, 00, 00, 00, 00, 00 };
|
||||
final byte[] bigintData1 = new byte[]{ 1, 0x23, 0x45, 0x67, (byte)0x89, 1, 0x23, 0x45, 0x67, (byte)0x89, 1, 0x23, 0x45, 0x67, (byte)0x89, 00, 00, 00, 00, 00 };
|
||||
|
||||
@Test
|
||||
public void testLongCodec() {
|
||||
final LongBcdCodec longCodec = new LongBcdCodec();
|
||||
final byte[] data1 = new byte[]{ 1, 0x23, 0x45, (byte)0x67, (byte)0x89, 00, 00, 00, 00, 00 };
|
||||
Assert.assertEquals(123456789l, (long) longCodec.decodeBinaryField(data1, 0, 5));
|
||||
Assert.assertEquals(1234567890l, (long)longCodec.decodeBinaryField(longData2, 0, 5));
|
||||
final byte[] cod1 = longCodec.encodeBinaryField(123456789l);
|
||||
final byte[] cod2 = longCodec.encodeBinaryField(1234567890l);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
Assert.assertEquals("LONG Data1 differs at pos " + i, data1[i], cod1[i]);
|
||||
Assert.assertEquals("LONG Data2 differs at pos " + i, longData2[i], cod2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBigIntCodec() {
|
||||
final BigInteger b30 = new BigInteger("123456789012345678901234567890");
|
||||
final BigIntBcdCodec bigintCodec = new BigIntBcdCodec();
|
||||
final byte[] data2 = new byte[]{ 0x12, 0x34, 0x56, 0x78, (byte)0x90, 0x12, 0x34, 0x56, 0x78, (byte)0x90, 0x12, 0x34, 0x56, (byte)0x78, (byte)0x90, 00, 00, 00, 00, 00 };
|
||||
Assert.assertEquals(b29, bigintCodec.decodeBinaryField(bigintData1, 0, 15));
|
||||
Assert.assertEquals(b30, bigintCodec.decodeBinaryField(data2, 0, 15));
|
||||
final byte[] cod1 = bigintCodec.encodeBinaryField(b29);
|
||||
final byte[] cod2 = bigintCodec.encodeBinaryField(b30);
|
||||
for (int i = 0; i < 15; i++) {
|
||||
Assert.assertEquals("BIGINT Data1 differs at pos " + i, bigintData1[i], cod1[i]);
|
||||
Assert.assertEquals("BIGINT Data2 differs at pos " + i, data2[i], cod2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void testFieldType(final IsoType type, final FieldParseInfo fieldParser, int offset1, int offset2) throws UnsupportedEncodingException, ParseException {
|
||||
final BigIntBcdCodec bigintCodec = new BigIntBcdCodec();
|
||||
final LongBcdCodec longCodec = new LongBcdCodec();
|
||||
final MessageFactory<IsoMessage> mfact = new MessageFactory<IsoMessage>();
|
||||
IsoMessage tmpl = new IsoMessage();
|
||||
tmpl.setBinary(true);
|
||||
tmpl.setType(0x200);
|
||||
tmpl.setValue(2, 1234567890l, longCodec, type, 0);
|
||||
tmpl.setValue(3, b29, bigintCodec, type, 0);
|
||||
mfact.addMessageTemplate(tmpl);
|
||||
mfact.setCustomField(2, longCodec);
|
||||
mfact.setCustomField(3, bigintCodec);
|
||||
HashMap<Integer, FieldParseInfo> parser = new HashMap<Integer,FieldParseInfo>();
|
||||
parser.put(2, fieldParser);
|
||||
parser.put(3, fieldParser);
|
||||
mfact.setParseMap(0x200, parser);
|
||||
mfact.setUseBinaryMessages(true);
|
||||
//Test encoding
|
||||
tmpl = mfact.newMessage(0x200);
|
||||
byte[] buf = tmpl.writeData();
|
||||
System.out.println("MENSAJE: " + HexCodec.hexEncode(buf, 0, buf.length));
|
||||
for (int i = 0; i < 5; i++) {
|
||||
Assert.assertEquals("LONG Data differs at pos " + i, longData2[i], buf[i+offset1]);
|
||||
}
|
||||
for (int i = 0; i < 15; i++) {
|
||||
Assert.assertEquals("BIGINT Data differs at pos " + i, bigintData1[i], buf[i+offset2]);
|
||||
}
|
||||
//Test parsing
|
||||
tmpl = mfact.parseMessage(buf, 0);
|
||||
Assert.assertEquals(1234567890l, tmpl.getObjectValue(2));
|
||||
Assert.assertEquals(b29, tmpl.getObjectValue(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLLBIN() throws ParseException, UnsupportedEncodingException {
|
||||
testFieldType(IsoType.LLBIN, new LlbinParseInfo(), 11, 17);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLLLBIN() throws ParseException, UnsupportedEncodingException {
|
||||
testFieldType(IsoType.LLLBIN, new LllbinParseInfo(), 12, 19);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import java.io.UnsupportedEncodingException;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.ParseException;
|
||||
|
||||
import com.solab.iso8583.IsoType;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -16,8 +17,8 @@ import com.solab.iso8583.util.HexCodec;
|
||||
/** Test binary message encoding and binary fields. */
|
||||
public class TestBinaries {
|
||||
|
||||
private MessageFactory mfactAscii = new MessageFactory();
|
||||
private MessageFactory mfactBin = new MessageFactory();
|
||||
private MessageFactory<IsoMessage> mfactAscii = new MessageFactory<IsoMessage>();
|
||||
private MessageFactory<IsoMessage> mfactBin = new MessageFactory<IsoMessage>();
|
||||
|
||||
@Before
|
||||
public void setup() throws IOException {
|
||||
@@ -35,26 +36,26 @@ public class TestBinaries {
|
||||
Assert.assertEquals(new BigDecimal("1234.00"), m.getObjectValue(4));
|
||||
Assert.assertTrue("No field 7!", m.hasField(7));
|
||||
Assert.assertEquals("Wrong trace", "000123", m.getField(11).toString());
|
||||
byte[] buf = (byte[])m.getObjectValue(41);
|
||||
byte[] buf = m.getObjectValue(41);
|
||||
byte[] exp = new byte[]{ (byte)0xab, (byte)0xcd, (byte)0xef, 0, 0, 0, 0, 0};
|
||||
Assert.assertEquals("Field 41 wrong length", 8, buf.length);
|
||||
Assert.assertArrayEquals("Field 41 wrong value", exp, buf);
|
||||
buf = (byte[])m.getObjectValue(42);
|
||||
buf = m.getObjectValue(42);
|
||||
exp = new byte[]{ (byte)0x0a, (byte)0xbc, (byte)0xde, 0 };
|
||||
Assert.assertEquals("field 42 wrong length", 4, buf.length);
|
||||
Assert.assertArrayEquals("Field 42 wrong value", exp, buf);
|
||||
Assert.assertTrue(((String)m.getObjectValue(43)).startsWith("Field of length 40"));
|
||||
buf = (byte[])m.getObjectValue(62);
|
||||
buf = m.getObjectValue(62);
|
||||
exp = new byte[]{ 1, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xab, (byte)0xcd, (byte)0xef,
|
||||
0x62, 1, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xab, (byte)0xcd };
|
||||
Assert.assertArrayEquals(exp, buf);
|
||||
buf = (byte[])m.getObjectValue(64);
|
||||
buf = m.getObjectValue(64);
|
||||
exp[8] = 0x64;
|
||||
Assert.assertArrayEquals(exp, buf);
|
||||
buf = (byte[])m.getObjectValue(63);
|
||||
buf = m.getObjectValue(63);
|
||||
exp = new byte[]{ 0, (byte)0x12, (byte)0x34, (byte)0x56, (byte)0x78, (byte)0x63 };
|
||||
Assert.assertArrayEquals(exp, buf);
|
||||
buf = (byte[])m.getObjectValue(65);
|
||||
buf = m.getObjectValue(65);
|
||||
exp[5] = 0x65;
|
||||
Assert.assertArrayEquals(exp, buf);
|
||||
}
|
||||
@@ -83,6 +84,11 @@ public class TestBinaries {
|
||||
//Compare values, should be the same
|
||||
testParsed(bin2);
|
||||
Assert.assertEquals(bin.getObjectValue(7).toString(), bin2.getObjectValue(7).toString());
|
||||
//Test the debug string
|
||||
ascii.setValue(60, "XXX", IsoType.LLVAR, 0);
|
||||
bin.setValue(60, "XXX", IsoType.LLVAR, 0);
|
||||
Assert.assertEquals("Debug strings differ", ascii.debugString(), bin.debugString());
|
||||
Assert.assertTrue("LLVAR fields wrong", ascii.debugString().contains("03XXX"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -6,12 +6,10 @@ import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import com.solab.iso8583.IsoMessage;
|
||||
import org.junit.*;
|
||||
|
||||
import com.solab.iso8583.IsoType;
|
||||
import com.solab.iso8583.IsoValue;
|
||||
import com.solab.iso8583.MessageFactory;
|
||||
import com.solab.iso8583.parse.Date10ParseInfo;
|
||||
import com.solab.iso8583.parse.Date4ParseInfo;
|
||||
|
||||
@@ -21,16 +19,6 @@ import com.solab.iso8583.parse.Date4ParseInfo;
|
||||
*/
|
||||
public class TestDates {
|
||||
|
||||
private MessageFactory<IsoMessage> mf;
|
||||
|
||||
@Before
|
||||
public void init() throws IOException {
|
||||
mf = new MessageFactory<IsoMessage>();
|
||||
mf.setCharacterEncoding("UTF-8");
|
||||
mf.setCustomField(48, new CustomField48());
|
||||
mf.setConfigPath("config.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDate4FutureTolerance() throws ParseException, IOException {
|
||||
GregorianCalendar today = new GregorianCalendar();
|
||||
@@ -44,7 +32,7 @@ public class TestDates {
|
||||
Assert.assertEquals(comp.getValue(), today.getTime());
|
||||
//Now with the binary
|
||||
ByteArrayOutputStream bout = new ByteArrayOutputStream();
|
||||
comp.write(bout, true);
|
||||
comp.write(bout, true, false);
|
||||
IsoValue<Date> bin = new Date4ParseInfo().parseBinary(0, bout.toByteArray(), 0, null);
|
||||
Assert.assertEquals(comp.getValue().getTime(), bin.getValue().getTime());
|
||||
}
|
||||
@@ -57,7 +45,7 @@ public class TestDates {
|
||||
assert comp.getValue().after(new Date());
|
||||
//Now with the binary
|
||||
ByteArrayOutputStream bout = new ByteArrayOutputStream();
|
||||
comp.write(bout, true);
|
||||
comp.write(bout, true, false);
|
||||
IsoValue<Date> bin = new Date10ParseInfo().parseBinary(0, bout.toByteArray(), 0, null);
|
||||
Assert.assertEquals(comp.getValue().getTime(), bin.getValue().getTime());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
package j8583;
|
||||
|
||||
import com.solab.iso8583.IsoMessage;
|
||||
import com.solab.iso8583.IsoType;
|
||||
import com.solab.iso8583.IsoValue;
|
||||
import com.solab.iso8583.MessageFactory;
|
||||
import com.solab.iso8583.parse.*;
|
||||
import com.solab.iso8583.util.HexCodec;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Test for EBCDIC support.
|
||||
*
|
||||
* @author Enrique Zamudio
|
||||
* Date: 13/05/13 18:12
|
||||
*/
|
||||
public class TestEbcdic {
|
||||
|
||||
private IsoValue<String> llvar = new IsoValue<String>(IsoType.LLVAR, "Testing, testing, 123");
|
||||
|
||||
@Test
|
||||
public void testAscii() throws IOException, ParseException {
|
||||
llvar.setCharacterEncoding("UTF-8");
|
||||
final ByteArrayOutputStream bout = new ByteArrayOutputStream();
|
||||
llvar.write(bout, false, false);
|
||||
byte[] buf = bout.toByteArray();
|
||||
Assert.assertEquals(50, buf[0]);
|
||||
Assert.assertEquals(49, buf[1]);
|
||||
final LlvarParseInfo parser = new LlvarParseInfo();
|
||||
parser.setCharacterEncoding("UTF-8");
|
||||
IsoValue<?> field = parser.parse(1, buf, 0, null);
|
||||
Assert.assertEquals(llvar.getValue(), field.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEbcdic() throws IOException, ParseException {
|
||||
llvar.setCharacterEncoding("Cp1047");
|
||||
final ByteArrayOutputStream bout = new ByteArrayOutputStream();
|
||||
llvar.write(bout, false, true);
|
||||
byte[] buf = bout.toByteArray();
|
||||
Assert.assertEquals((byte)242, buf[0]);
|
||||
Assert.assertEquals((byte) 241, buf[1]);
|
||||
final LlvarParseInfo parser = new LlvarParseInfo();
|
||||
parser.setCharacterEncoding("Cp1047");
|
||||
parser.setForceStringDecoding(true);
|
||||
IsoValue<?> field = parser.parse(1, buf, 0, null);
|
||||
Assert.assertEquals(llvar.getValue(), field.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParsers() throws UnsupportedEncodingException, ParseException {
|
||||
final byte[] stringA = "A".getBytes("Cp1047");
|
||||
final LllvarParseInfo lllvar = new LllvarParseInfo();
|
||||
lllvar.setCharacterEncoding("Cp1047");
|
||||
lllvar.setForceStringDecoding(true);
|
||||
IsoValue<?> field = lllvar.parse(1, new byte[]{(byte)240, (byte)240, (byte)241, (byte)193}, 0, null);
|
||||
Assert.assertEquals(new String(stringA, "Cp1047"), field.getValue());
|
||||
|
||||
final LllbinParseInfo lllbin = new LllbinParseInfo();
|
||||
lllbin.setCharacterEncoding("Cp1047");
|
||||
lllbin.setForceStringDecoding(true);
|
||||
field = lllbin.parse(1, new byte[]{(byte)240, (byte)240, (byte)242, 67, 49}, 0, null);
|
||||
Assert.assertArrayEquals(stringA, (byte[]) field.getValue());
|
||||
|
||||
final LlbinParseInfo llbin = new LlbinParseInfo();
|
||||
llbin.setCharacterEncoding("Cp1047");
|
||||
llbin.setForceStringDecoding(true);
|
||||
field = llbin.parse(1, new byte[]{(byte)240, (byte)242, 67, 49}, 0, null);
|
||||
Assert.assertArrayEquals(stringA, (byte[]) field.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessageType() throws UnsupportedEncodingException, ParseException {
|
||||
final IsoMessage msg = new IsoMessage();
|
||||
msg.setType(0x1100);
|
||||
msg.setBinaryBitmap(true);
|
||||
msg.setCharacterEncoding("Cp1047");
|
||||
final byte[] enc = msg.writeData();
|
||||
Assert.assertEquals(12, enc.length);
|
||||
Assert.assertEquals((byte) 241, enc[0]);
|
||||
Assert.assertEquals((byte) 241, enc[1]);
|
||||
Assert.assertEquals((byte)240, enc[2]);
|
||||
Assert.assertEquals((byte) 240, enc[3]);
|
||||
MessageFactory<IsoMessage> mf = new MessageFactory<IsoMessage>();
|
||||
HashMap<Integer, FieldParseInfo> pmap = new HashMap<Integer, FieldParseInfo>();
|
||||
mf.setForceStringEncoding(true);
|
||||
mf.setUseBinaryBitmap(true);
|
||||
mf.setCharacterEncoding("Cp1047");
|
||||
mf.setParseMap(0x1100, pmap);
|
||||
IsoMessage m2 = mf.parseMessage(enc, 0);
|
||||
Assert.assertEquals(msg.getType(), m2.getType());
|
||||
//Now with text bitmap
|
||||
msg.setBinaryBitmap(false);
|
||||
msg.setForceStringEncoding(true);
|
||||
final byte[] enc2 = msg.writeData();
|
||||
Assert.assertEquals(20, enc2.length);
|
||||
mf.setUseBinaryBitmap(false);
|
||||
m2 = mf.parseMessage(enc2, 0);
|
||||
Assert.assertEquals(msg.getType(), m2.getType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDate4() throws UnsupportedEncodingException, ParseException {
|
||||
final Date4ParseInfo parser = new Date4ParseInfo();
|
||||
parser.setCharacterEncoding("Cp1047");
|
||||
parser.setForceStringDecoding(true);
|
||||
final IsoValue<Date> v = parser.parse(1, new byte[]{(byte)240, (byte)241, (byte)242, (byte)245}, 0, null);
|
||||
Assert.assertEquals("01", String.format("%Tm", v.getValue()));
|
||||
Assert.assertEquals("25", String.format("%Td", v.getValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDate10() throws UnsupportedEncodingException, ParseException {
|
||||
final Date10ParseInfo parser = new Date10ParseInfo();
|
||||
parser.setCharacterEncoding("Cp1047");
|
||||
parser.setForceStringDecoding(true);
|
||||
final IsoValue<Date> v = parser.parse(1, new byte[]{(byte)240, (byte)241, (byte)242, (byte)245, (byte)242, (byte)243, (byte)245, (byte)249, (byte)245, (byte)249}, 0, null);
|
||||
Assert.assertEquals("01", String.format("%Tm", v.getValue()));
|
||||
Assert.assertEquals("25", String.format("%Td", v.getValue()));
|
||||
Assert.assertEquals("23:59:59", String.format("%TT", v.getValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDateExp() throws UnsupportedEncodingException, ParseException {
|
||||
final DateExpParseInfo parser = new DateExpParseInfo();
|
||||
parser.setCharacterEncoding("Cp1047");
|
||||
parser.setForceStringDecoding(true);
|
||||
final IsoValue<Date> v = parser.parse(1, new byte[]{(byte)241, (byte)247, (byte)241, (byte)242}, 0, null);
|
||||
Assert.assertEquals("12", String.format("%Tm", v.getValue()));
|
||||
Assert.assertEquals("2017", String.format("%TY", v.getValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTime() throws UnsupportedEncodingException, ParseException {
|
||||
final TimeParseInfo parser = new TimeParseInfo();
|
||||
parser.setCharacterEncoding("Cp1047");
|
||||
parser.setForceStringDecoding(true);
|
||||
final IsoValue<Date> v = parser.parse(1, new byte[]{(byte)242, (byte)241, (byte)243, (byte)244, (byte)245, (byte)246}, 0, null);
|
||||
Assert.assertEquals("21:34:56", String.format("%TT", v.getValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAmount() throws UnsupportedEncodingException, ParseException {
|
||||
final AmountParseInfo parser = new AmountParseInfo();
|
||||
parser.setCharacterEncoding("Cp1047");
|
||||
parser.setForceStringDecoding(true);
|
||||
IsoValue<BigDecimal> v = parser.parse(4, new byte[]{(byte)240, (byte)240, (byte)240, (byte)241, (byte)242, (byte)243, (byte)244, (byte)245, (byte)246, (byte)247, (byte)248, (byte)249}, 0, null);
|
||||
Assert.assertEquals(new BigDecimal("1234567.89"), v.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessage() throws ParseException, UnsupportedEncodingException {
|
||||
final byte[] trama = HexCodec.hexDecode("f1f8f1f42030010002000000f0f0f0f0f0f0f0f1f5f9f5f5f1f3f0f6f1f2f1f1f2f9f0f8f8f3f1f8f0f0");
|
||||
MessageFactory<IsoMessage> mfact = new MessageFactory<IsoMessage>();
|
||||
mfact.setUseBinaryBitmap(true);
|
||||
HashMap<Integer, FieldParseInfo> pinfo = new HashMap<Integer, FieldParseInfo>();
|
||||
pinfo.put(3, new AlphaParseInfo(6));
|
||||
pinfo.put(11, new AlphaParseInfo(6));
|
||||
pinfo.put(12, new AlphaParseInfo(12));
|
||||
pinfo.put(24, new AlphaParseInfo(3));
|
||||
pinfo.put(39, new AlphaParseInfo(3));
|
||||
mfact.setParseMap(0x1814, pinfo);
|
||||
mfact.setCharacterEncoding("Cp1047");
|
||||
mfact.setForceStringEncoding(true);
|
||||
IsoMessage iso = mfact.parseMessage(trama, 0);
|
||||
Assert.assertNotNull(iso);
|
||||
Assert.assertEquals("000000", iso.getObjectValue(3));
|
||||
Assert.assertEquals("015955", iso.getObjectValue(11));
|
||||
Assert.assertEquals("130612112908", iso.getObjectValue(12));
|
||||
Assert.assertEquals("831", iso.getObjectValue(24));
|
||||
Assert.assertEquals("800", iso.getObjectValue(39));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package j8583;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.solab.iso8583.util.HexCodec;
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.text.ParseException;
|
||||
|
||||
import com.solab.iso8583.IsoValue;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -102,15 +103,30 @@ public class TestIsoMessage {
|
||||
IsoMessage iso1 = mf.newMessage(0x200);
|
||||
IsoMessage iso2 = mf.newMessage(0x200);
|
||||
assert iso1 != iso2;
|
||||
assert iso1.getObjectValue(3) == iso1.getObjectValue(3);
|
||||
assert iso1.getObjectValue(3).equals(iso2.getObjectValue(3));
|
||||
assert iso1.getField(3) != iso2.getField(3);
|
||||
assert iso1.getField(48) != iso2.getField(48);
|
||||
CustomField48 cf48_1 = (CustomField48)iso1.getObjectValue(48);
|
||||
CustomField48 cf48_1 = iso1.getObjectValue(48);
|
||||
int origv = cf48_1.getValue2();
|
||||
cf48_1.setValue2(origv + 1000);
|
||||
CustomField48 cf48_2 = (CustomField48)iso2.getObjectValue(48);
|
||||
CustomField48 cf48_2 = iso2.getObjectValue(48);
|
||||
assert cf48_1 == cf48_2;
|
||||
assert cf48_2.getValue2() == origv + 1000;
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testSimpleFieldSetter() {
|
||||
IsoMessage iso = mf.newMessage(0x200);
|
||||
IsoValue<String> f3 = iso.getField(3);
|
||||
iso.updateValue(3, "999999");
|
||||
assert iso.getObjectValue(3).equals("999999");
|
||||
IsoValue<String> nf3 = iso.getField(3);
|
||||
assert f3 != nf3;
|
||||
assert f3.getType() == nf3.getType();
|
||||
assert f3.getLength() == nf3.getLength();
|
||||
assert f3.getEncoder() == nf3.getEncoder();
|
||||
iso.updateValue(4, "INVALID!");
|
||||
throw new RuntimeException("Update failed!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import java.text.ParseException;
|
||||
import com.solab.iso8583.IsoMessage;
|
||||
import com.solab.iso8583.IsoValue;
|
||||
import com.solab.iso8583.parse.NumericParseInfo;
|
||||
import junit.framework.Assert;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user