diff --git a/src/main/java/net/sf/cb2java/Settings.java b/src/main/java/net/sf/cb2java/Settings.java old mode 100644 new mode 100755 index b73793e..4bfb793 --- a/src/main/java/net/sf/cb2java/Settings.java +++ b/src/main/java/net/sf/cb2java/Settings.java @@ -23,33 +23,23 @@ import java.util.Properties; import net.sf.cb2java.types.SignPosition; -public interface Settings { - static Settings DEFAULT = new Default(); - - String getEncoding(); - - Values getValues(); - - boolean getLittleEndian(); - - String getFloatConversion(); - - SignPosition getSignPosition(); - - int getColumnStart(); - - int getColumnEnd(); - - static class Default implements Settings { - private static final String DEFAULT_ENCODING; - private static final boolean DEFAULT_LITTLE_ENDIAN; - private static final String DEFAULT_FLOAT_CONVERSION; - private static final SignPosition DEFAULT_SIGN_POSITION; - private static final Values DEFAULT_VALUES = new Values(); - private static final int DEFAULT_COLUMN_START; - private static final int DEFAULT_COLUMN_END; - - static { +public class Settings { + + String encoding = System.getProperty("file.encoding"); + boolean littleEndian = false; + String floatConversion = "net.sf.cb2java.copybook.floating.IEEE754"; + SignPosition signPosition = SignPosition.TRAILING; + int columnStart = 6; + int columnEnd = 72; + Values values = new Values(); + boolean resiliant = false; + boolean trimStrings = false; + String EBCDICVariant = ""; + + static Settings DEFAULT; + static public Settings DEFAULT() { + if(DEFAULT == null) { + DEFAULT = new Settings(); Properties props = new Properties(); try (InputStream is = Settings.class.getResourceAsStream("/copybook.props")) { @@ -63,52 +53,108 @@ static class Default implements Settings { System.out.println("Could not load 'copybook.props' file, reverting to defaults."); } - DEFAULT_ENCODING = getSetting("encoding", System.getProperty("file.encoding"), props); - DEFAULT_LITTLE_ENDIAN = "false".equals(getSetting("little-endian", "false", props)); - DEFAULT_FLOAT_CONVERSION = getSetting("float-conversion", "net.sf.cb2java.copybook.floating.IEEE754", - props); - DEFAULT_SIGN_POSITION = "leading".equalsIgnoreCase(getSetting("default-sign-position", "trailing", props)) - ? SignPosition.LEADING : SignPosition.TRAILING; - DEFAULT_COLUMN_START = Integer.parseInt(getSetting("column.start", "6", props)); - DEFAULT_COLUMN_END = Integer.parseInt(getSetting("column.end", "72", props)); + DEFAULT.setEncoding(getSetting("encoding", DEFAULT.getEncoding(), props)); + DEFAULT.setLittleEndian("false".equals(getSetting("little-endian", DEFAULT.isLittleEndian() + "", props))); + DEFAULT.setFloatConversion(getSetting("float-conversion", DEFAULT.getFloatConversion(), props)); + DEFAULT.setSignPosition("leading".equalsIgnoreCase(getSetting("default-sign-position", "trailing", props)) + ? SignPosition.LEADING : SignPosition.TRAILING); + DEFAULT.setColumnStart(Integer.parseInt(getSetting("column.start", DEFAULT.getColumnStart() + "", props))); + DEFAULT.setColumnEnd(Integer.parseInt(getSetting("column.end", DEFAULT.getColumnEnd() + "", props))); + DEFAULT.setResiliant("false".equals(getSetting("resilient", DEFAULT.isResiliant() + "", props))); + DEFAULT.setTrimStrings("true".equals(getSetting("trim", DEFAULT.getTrimStrings() + "", props))); + DEFAULT.setEBCDICVariant(getSetting("EBCDIC-variant", DEFAULT.getEBCDICVariant(), props)); } + return DEFAULT; + } - private static String getSetting(String name, String defaultValue, Properties props) { - String result = defaultValue; - try { - result = System.getProperty("cb2java." + name, result); - result = props.getProperty(name, result); - } catch (RuntimeException e) { - } - return result; - } + public String getEncoding() { + return encoding; + } - public String getEncoding() { - return DEFAULT_ENCODING; - } + public void setEncoding(String encoding) { + this.encoding = encoding; + } - public String getFloatConversion() { - return DEFAULT_FLOAT_CONVERSION; - } + public boolean isLittleEndian() { + return littleEndian; + } - public boolean getLittleEndian() { - return DEFAULT_LITTLE_ENDIAN; - } + public void setLittleEndian(boolean littleEndian) { + this.littleEndian = littleEndian; + } - public Values getValues() { - return DEFAULT_VALUES; - } + public String getFloatConversion() { + return floatConversion; + } - public SignPosition getSignPosition() { - return DEFAULT_SIGN_POSITION; - } + public void setFloatConversion(String floatConversion) { + this.floatConversion = floatConversion; + } + + public SignPosition getSignPosition() { + return signPosition; + } + + public void setSignPosition(SignPosition signPosition) { + this.signPosition = signPosition; + } + + public int getColumnStart() { + return columnStart; + } + + public void setColumnStart(int columnStart) { + this.columnStart = columnStart; + } + + public int getColumnEnd() { + return columnEnd; + } + + public void setColumnEnd(int columnEnd) { + this.columnEnd = columnEnd; + } + + public Values getValues() { + return values; + } + + public void setValues(Values values) { + this.values = values; + } + + public boolean isResiliant() { + return resiliant; + } + + public void setResiliant(boolean resiliant) { + this.resiliant = resiliant; + } + + public boolean getTrimStrings() { + return trimStrings; + } + + public void setTrimStrings(boolean trimStrings) { + this.trimStrings = trimStrings; + } + + public String getEBCDICVariant() { + return EBCDICVariant; + } + + public void setEBCDICVariant(String EBCDICVariant) { + this.EBCDICVariant = EBCDICVariant; + } - public int getColumnStart() { - return DEFAULT_COLUMN_START; - } - public int getColumnEnd() { - return DEFAULT_COLUMN_END; + static private String getSetting(String name, String defaultValue, Properties props) { + String result = defaultValue; + try { + result = System.getProperty("cb2java." + name, result); + result = props.getProperty(name, result); + } catch (RuntimeException e) { } + return result; } } diff --git a/src/main/java/net/sf/cb2java/copybook/CobolPreprocessor.java b/src/main/java/net/sf/cb2java/copybook/CobolPreprocessor.java old mode 100644 new mode 100755 index e1b94a9..33b7f97 --- a/src/main/java/net/sf/cb2java/copybook/CobolPreprocessor.java +++ b/src/main/java/net/sf/cb2java/copybook/CobolPreprocessor.java @@ -38,10 +38,10 @@ public class CobolPreprocessor { private CobolPreprocessor() { } - public static String preProcess(Reader reader) { + public static String preProcess(Reader reader, Settings settings) { // TODO: figure out a way to pass copybook specific settings for non-default margins treated as comment. - int columnStart = Settings.DEFAULT.getColumnStart(); - int columnEnd = Settings.DEFAULT.getColumnEnd(); + int columnStart = settings.getColumnStart(); + int columnEnd = settings.getColumnEnd(); StringBuffer sb = new StringBuffer(); diff --git a/src/main/java/net/sf/cb2java/copybook/Copybook.java b/src/main/java/net/sf/cb2java/copybook/Copybook.java old mode 100644 new mode 100755 index 231e1cf..11f964f --- a/src/main/java/net/sf/cb2java/copybook/Copybook.java +++ b/src/main/java/net/sf/cb2java/copybook/Copybook.java @@ -40,13 +40,8 @@ * * @author James Watson */ -public class Copybook extends Group implements Settings +public class Copybook extends Group //implements Settings { - private String encoding = Settings.DEFAULT.getEncoding(); - private boolean littleEndian = Settings.DEFAULT.getLittleEndian(); - private String floatConversion = Settings.DEFAULT.getFloatConversion(); - private SignPosition signPosition = Settings.DEFAULT.getSignPosition(); - private Map redefines = new HashMap(); private final Values values; @@ -56,12 +51,13 @@ public class Copybook extends Group implements Settings * * @param name the name of the copybook */ - Copybook(String name, Values values) + Copybook(String name, Values values, Settings settings) { super(name, 0, 0); this.values = values; - this.values.setEncoding(encoding); + this.setSettings(settings); + this.values.setEncoding(settings.getEncoding()); } public Values getValues() @@ -126,67 +122,6 @@ public List parseData(InputStream stream) throws IOException return list; } - - /** - * Sets the encoding for the copybook instance, used for parsing - * and writing of data - * - * @param encoding the encoding for the system - */ - public void setEncoding(String encoding) - { - this.encoding = encoding; - } - - /** - * retrieves the current encoding for text - * - * @return the encoding for text - */ - public String getEncoding() - { - return encoding; - } - - public void setLittleEndian(boolean littleEndian) - { - this.littleEndian = littleEndian; - } - - public boolean getLittleEndian() - { - return littleEndian; - } - - public void setFloatConversion(String className) - { - this.floatConversion = className; - } - - public String getFloatConversion() - { - return floatConversion; - } - - public void setSignPosition(SignPosition position) - { - this.signPosition = position; - } - - public SignPosition getSignPosition() - { - return signPosition; - } - - @Override - public int getColumnStart() { - return Settings.DEFAULT.getColumnStart(); - } - - @Override - public int getColumnEnd() { - return Settings.DEFAULT.getColumnEnd(); - } /** * a helper class for buffering the data as it is processed diff --git a/src/main/java/net/sf/cb2java/copybook/CopybookAnalyzer.java b/src/main/java/net/sf/cb2java/copybook/CopybookAnalyzer.java old mode 100644 new mode 100755 index 63b508b..1497a8d --- a/src/main/java/net/sf/cb2java/copybook/CopybookAnalyzer.java +++ b/src/main/java/net/sf/cb2java/copybook/CopybookAnalyzer.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.StringTokenizer; +import net.sf.cb2java.Settings; import net.sf.cb2java.Values; import net.sf.cb2java.types.Element; import net.sf.cb2java.types.Group; @@ -84,6 +85,7 @@ class CopybookAnalyzer extends DepthFirstAdapter private Parser parser; private Item document; private Item current; + private Settings settings; /** * Creates a new instance with the given parser and @@ -92,12 +94,13 @@ class CopybookAnalyzer extends DepthFirstAdapter * @param copyBookName the name to give this copybook * @param parser sablecc parser instance */ - CopybookAnalyzer(String copyBookName, Parser parser) + CopybookAnalyzer(String copyBookName, Parser parser, Settings settings) { - document = new Item(values, true); + document = new Item(values, true, settings); document.name = copyBookName; current = document; this.parser = parser; + this.settings = settings; } /** @@ -127,7 +130,7 @@ public void outARecordDescription(ARecordDescription node) private void walkTree(Item item) { - item.getElement().setSettings((Copybook) document.getElement()); + item.getElement().setSettings(document.getElement().getSettings()); for (Iterator i = item.children.iterator(); i.hasNext();) { Item child = (Item) i.next(); @@ -176,7 +179,7 @@ public void checkForComments(Token node) public void inAItem(AItem node) { Item prevItem = current; - current = new Item(values, false); + current = new Item(values, false, settings); current.level = Integer.parseInt(node.getNumberNot88().toString().trim()); current.name = node.getDataNameOrFiller().toString().trim(); @@ -265,12 +268,12 @@ public void inASignClause(ASignClause node) public void inALeadingLeadingOrTrailing(ALeadingLeadingOrTrailing node) { - current.signPosition = SignPosition.LEADING; + current.getSettings().setSignPosition(SignPosition.LEADING); } public void inATrailimngLeadingOrTrailing(ALeadingLeadingOrTrailing node) { - current.signPosition = SignPosition.TRAILING; + current.getSettings().setSignPosition(SignPosition.TRAILING); } //======================= USAGE CLAUSE ========================== diff --git a/src/main/java/net/sf/cb2java/copybook/CopybookParser.java b/src/main/java/net/sf/cb2java/copybook/CopybookParser.java old mode 100644 new mode 100755 index 2e84dcb..f44cfc8 --- a/src/main/java/net/sf/cb2java/copybook/CopybookParser.java +++ b/src/main/java/net/sf/cb2java/copybook/CopybookParser.java @@ -25,6 +25,7 @@ import java.io.Reader; import java.io.StringReader; +import net.sf.cb2java.Settings; import net.sf.cb2xml.sablecc.lexer.Lexer; import net.sf.cb2xml.sablecc.lexer.LexerException; import net.sf.cb2xml.sablecc.node.Start; @@ -58,7 +59,12 @@ public static Copybook parse(String name, InputStream stream) { return parse(name, new InputStreamReader(stream)); } - + + public static Copybook parse(Settings settings, String name, InputStream stream) + { + return parse(settings, name, new InputStreamReader(stream)); + } + /** * Parses a copybook definition and returns a Copybook instance * @@ -67,16 +73,16 @@ public static Copybook parse(String name, InputStream stream) * * @return a copybook instance containing the parse tree for the definition */ - public static Copybook parse(String name, Reader reader) + public static Copybook parse(Settings settings, String name, Reader reader) { - String preProcessed = CobolPreprocessor.preProcess(reader); + String preProcessed = CobolPreprocessor.preProcess(reader, settings); StringReader sr = new StringReader(preProcessed); PushbackReader pbr = new PushbackReader(sr, 1000); Lexer lexer = debug ? new DebugLexer(pbr) : new Lexer(pbr); Parser parser = new Parser(lexer); - CopybookAnalyzer copyBookAnalyzer = new CopybookAnalyzer(name, parser); + CopybookAnalyzer copyBookAnalyzer = new CopybookAnalyzer(name, parser, settings); Start ast; try { ast = parser.parse(); @@ -91,4 +97,10 @@ public static Copybook parse(String name, Reader reader) return copyBookAnalyzer.getDocument(); } + + public static Copybook parse(String name, Reader reader) { + + return parse(Settings.DEFAULT(), name, reader); + } + } \ No newline at end of file diff --git a/src/main/java/net/sf/cb2java/copybook/Item.java b/src/main/java/net/sf/cb2java/copybook/Item.java old mode 100644 new mode 100755 index d08ad87..b6d81c6 --- a/src/main/java/net/sf/cb2java/copybook/Item.java +++ b/src/main/java/net/sf/cb2java/copybook/Item.java @@ -20,6 +20,8 @@ import java.util.ArrayList; import java.util.List; +import java.util.Set; + import net.sf.cb2java.Settings; import net.sf.cb2java.Value; import net.sf.cb2java.Values; @@ -42,14 +44,22 @@ class Item final boolean document; final Values values; + + public Settings getSettings() { + return settings; + } + + final Settings settings; /** * @param analyzer */ - Item(final Values values, final boolean document) + Item(final Values values, final boolean document, Settings settings) { this.values = values; this.document = document; + this.settings = settings; + signPosition = settings.getSignPosition(); } String name; @@ -66,7 +76,7 @@ class Item boolean isAlpha; boolean signSeparate; - SignPosition signPosition = Settings.DEFAULT.getSignPosition(); + SignPosition signPosition; String picture; Value value; @@ -128,7 +138,7 @@ void createElement() private void createDocument() { - element = new Copybook(name, values); + element = new Copybook(name, values, settings); } private void createGroup() diff --git a/src/main/java/net/sf/cb2java/types/Binary.java b/src/main/java/net/sf/cb2java/types/Binary.java old mode 100644 new mode 100755 index b302a9b..2b4af87 --- a/src/main/java/net/sf/cb2java/types/Binary.java +++ b/src/main/java/net/sf/cb2java/types/Binary.java @@ -122,12 +122,12 @@ public Native(String name, int level, int occurs, String picture) { @Override public byte[] toBytes(Object data) { byte[] bytes = super.toBytes(data); - return getSettings().getLittleEndian() ? reverse(bytes) : bytes; + return getSettings().isLittleEndian() ? reverse(bytes) : bytes; } @Override public Data parse(byte[] input) { - return super.parse(getSettings().getLittleEndian() ? reverse(input) : input); + return super.parse(getSettings().isLittleEndian() ? reverse(input) : input); } } } \ No newline at end of file diff --git a/src/main/java/net/sf/cb2java/types/Decimal.java b/src/main/java/net/sf/cb2java/types/Decimal.java old mode 100644 new mode 100755 index 00e823f..db4df5d --- a/src/main/java/net/sf/cb2java/types/Decimal.java +++ b/src/main/java/net/sf/cb2java/types/Decimal.java @@ -49,7 +49,7 @@ private char getChar(boolean positive, char overpunch) { return overpunch; } else if (positive) { switch(overpunch) { - case '0': return '{'; + case '0': return getSettings().getEBCDICVariant().contains("297") ? 'é' : '{'; case '1': return 'A'; case '2': return 'B'; case '3': return 'C'; @@ -71,7 +71,7 @@ private char getChar(boolean positive, char overpunch) { case '3': return 'L'; case '2': return 'K'; case '1': return 'J'; - case '0': return '}'; + case '0': return getSettings().getEBCDICVariant().contains("297") ? 'è' : '}'; } } @@ -98,6 +98,7 @@ private boolean isPositive(char overpunched) { case '7': case '8': case '9': + case 'é': case '{': case 'A': case 'B': @@ -110,6 +111,7 @@ private boolean isPositive(char overpunched) { case 'I': return true; case '0': + case 'è': case '}': case 'J': case 'K': @@ -174,9 +176,11 @@ private char getNumber(char overpunched) { case 'J': case 'A': return '1'; - case '0': - case '}': - case '{': + case '0': + case '}': + case '{': + case 'é': + case 'è': return '0'; } } @@ -186,41 +190,49 @@ private char getNumber(char overpunched) { @Override public Data parse(byte[] bytes) { - String input = getString(bytes).trim(); - String s = input; - - if (input.length() < 1) { - s = null; - } else if (signed()) { - if (getSignPosition() == SignPosition.LEADING) { - char c = input.charAt(0); - s = (isPositive(c) ? "" : "-") + getNumber(c) - + (input.length() > 1 ? input.substring(1) : ""); - } else if (getSignPosition() == SignPosition.TRAILING) { - int last = input.length() - 1; - char c = input.charAt(last); - s = (isPositive(c) ? "" : "-") - + (input.length() > 1 ? input.substring(0, last) : "") + getNumber(c); - } else { - throw new IllegalStateException("undefined sign position"); - } - } - BigInteger big = s == null ? null : new BigInteger(s); - Data data = create(); - - if (data instanceof DecimalData) { - DecimalData dData = (DecimalData) data; - BigDecimal bigD = big == null ? null : new BigDecimal(big, decimalPlaces()); - - dData.setValue(bigD); - - return data; - } else { - IntegerData iData = (IntegerData) data; - - iData.setValue(big); - - return data; + try { + String input = getString(bytes).trim(); + String s = input; + + if (input.length() < 1) { + s = null; + } else if (signed()) { + if (getSignPosition() == SignPosition.LEADING) { + char c = input.charAt(0); + s = (isPositive(c) ? "" : "-") + getNumber(c) + + (input.length() > 1 ? input.substring(1) : ""); + } else if (getSignPosition() == SignPosition.TRAILING) { + int last = input.length() - 1; + char c = input.charAt(last); + s = (isPositive(c) ? "" : "-") + + (input.length() > 1 ? input.substring(0, last) : "") + getNumber(c); + } else { + throw new IllegalStateException("undefined sign position"); + } + } + BigInteger big = s == null ? null : new BigInteger(s); + Data data = create(); + + if (data instanceof DecimalData) { + DecimalData dData = (DecimalData) data; + BigDecimal bigD = big == null ? null : new BigDecimal(big, decimalPlaces()); + + dData.setValue(bigD); + + return data; + } else { + IntegerData iData = (IntegerData) data; + + iData.setValue(big); + + return data; + } + } catch (RuntimeException e) { + if(getSettings().isResiliant()) { + return create(); + } else { + throw(e); + } } } diff --git a/src/main/java/net/sf/cb2java/types/Element.java b/src/main/java/net/sf/cb2java/types/Element.java old mode 100644 new mode 100755 index be36e5e..661ad0e --- a/src/main/java/net/sf/cb2java/types/Element.java +++ b/src/main/java/net/sf/cb2java/types/Element.java @@ -42,7 +42,7 @@ public abstract class Element { /** the absolute position of the where this item starts in data */ private int position; /** the instance that represents the data that defines this element */ - private Settings settings = Settings.DEFAULT; + private Settings settings; /** the default value of this element */ private Value value; /** the parent of this element */ @@ -185,7 +185,12 @@ public final void write(OutputStream stream, Object data) throws IOException { */ public final String getString(byte[] data) { try { - return new String(data, getSettings().getEncoding()); + String value = new String(data, getSettings().getEncoding()); + if(getSettings().getTrimStrings()) { + return value.trim(); + } else { + return value; + } } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } @@ -219,7 +224,7 @@ public void setSettings(Settings settings) { * * @return the parent of this element */ - private Group getParent(){ + public Group getParent(){ return parent; } @@ -237,13 +242,13 @@ public void setParent(Group parent){ * * @return the settings for this element */ - protected Settings getSettings() { + public Settings getSettings() { if (settings != null) { return settings; } else if (getParent() != null) { return getParent().getSettings(); } else { - return null; + return Settings.DEFAULT(); } } diff --git a/src/main/java/net/sf/cb2java/types/Group.java b/src/main/java/net/sf/cb2java/types/Group.java old mode 100644 new mode 100755 index 3685def..d33bcca --- a/src/main/java/net/sf/cb2java/types/Group.java +++ b/src/main/java/net/sf/cb2java/types/Group.java @@ -109,7 +109,12 @@ public Data parse(final byte[] bytes) { private byte[] sub(byte[] in, int start, int end) { byte[] out = new byte[end - start]; - System.arraycopy(in, start, out, 0, out.length); + if(!getSettings().isResiliant() || start < in.length) { + // if resilient, allow for the input to be shorter compared to the copybook definition + // and then consider that the values are 0's + System.arraycopy(in, start, out, 0, + getSettings().isResiliant() ? Math.min(in.length - start, out.length) : out.length); + } return out; }