/** * find a level by its type in a scheme. This involves iterating through the list of levels. The * main reason for doing it this way is to avoid being dependent on the order in which the levels * are coded in the xml, which is not explicitly constrained. */ private Level findLevel(Scheme scheme, LevelTypeList levelType) { Level level = null; for (Enumeration e = scheme.enumerateLevel(); e.hasMoreElements(); ) { Level lev = (Level) e.nextElement(); if (lev.getType() == levelType) { level = lev; break; } } if (level == null) throw new Error("Couldn't find type " + levelType + " in scheme " + scheme); return level; }
/** * find a option by its type in a scheme. This involves iterating through the list of options. The * main reason for doing it this way is to avoid being dependent on the order in which the options * are coded in the xml, which is not explicitly constrained. */ private Option findOption(Level level, String optionKey) { Option option = null; for (Enumeration e = level.enumerateOption(); e.hasMoreElements(); ) { Option opt = (Option) e.nextElement(); if (opt.getOptionKey().equals(optionKey)) { option = opt; break; } } if (option == null) throw new Error("Couldn't find option for " + optionKey + " in level " + level); return option; }
/** * TDTEngine - constructor for a new Tag Data Translation engine * * @param confdir the string value of the path to a configuration directory consisting of two * subdirectories, <code>schemes</code> and <code>auxiliary</code>. * <p>When the class TDTEngine is constructed, the path to a local directory must be * specified, by passing it as a single string parameter to the constructor method (without * any trailing slash or file separator). e.g. <code> * <pre>TDTEngine engine = new TDTEngine("/opt/TDT");</pre></code> * <p>The specified directory must contain two subdirectories named auxiliary and schemes. The * Tag Data Translation definition files for the various coding schemes should be located * inside the subdirectory called <code>schemes</code>. Any auxiliary lookup files (such as * <code>ManagerTranslation.xml</code>) should be located inside the subdirectory called * <code>auxiliary</code>. * <p>Files within the schemes directory ending in <code>.xml</code> are read in and * unmarshalled using <a href = "http://www.castor.org">Castor</a>. */ public TDTEngine(String confdir) throws FileNotFoundException, MarshalException, ValidationException, TDTException { xmldir = confdir; long t = System.currentTimeMillis(); File[] dir = new java.io.File(confdir + File.separator + "schemes").listFiles(new XMLFilenameFilter()); // java.util.Arrays.sort(dir); // Sort it if (dir == null) throw new TDTException("Cannot find schemes in " + confdir); Unmarshaller unmar = new Unmarshaller(); for (File f : dir) { EpcTagDataTranslation tdt = (EpcTagDataTranslation) unmar.unmarshal(EpcTagDataTranslation.class, new FileReader(f)); initFromTDT(tdt); } // need to populate the hashmap gs1cpi from the ManagerTranslation.xml table in auxiliary. // Unmarshaller unmar = new Unmarshaller(); GEPC64Table cpilookup = (GEPC64Table) unmar.unmarshal( GEPC64Table.class, new FileReader( confdir + File.separator + "auxiliary" + File.separator + "ManagerTranslation.xml")); for (Enumeration e = cpilookup.enumerateEntry(); e.hasMoreElements(); ) { Entry entry = (Entry) e.nextElement(); String comp = entry.getCompanyPrefix(); String indx = Integer.toString(entry.getIndex()); gs1cpi.put(indx, comp); gs1cpi.put(comp, indx); } // System.out.println("Loaded schemas in " + // (System.currentTimeMillis() - t) // + " millisecs"); }
/** convert from a particular scheme / level */ private String convertLevel( Scheme tdtscheme, Level tdtlevel, String input, Map<String, String> inputParameters, LevelTypeList outboundlevel) { String outboundstring; Map<String, String> extraparams = // new NoisyMap (new HashMap<String, String>(inputParameters)); // get the scheme's option key, which is the name of a // parameter whose value is matched to the option key of the // level. String optionkey = tdtscheme.getOptionKey(); String optionValue = extraparams.get(optionkey); // the name of a parameter which allows the appropriate option // to be selected // now consider the various options within the scheme and // level for each option element inside the level, check // whether the pattern attribute matches as a regular // expression String matchingOptionKey = null; Option matchingOption = null; Matcher prefixMatcher = null; for (Enumeration e = tdtlevel.enumerateOption(); e.hasMoreElements(); ) { Option opt = (Option) e.nextElement(); if (optionValue == null || optionValue.equals(opt.getOptionKey())) { // possible match Matcher matcher = Pattern.compile(opt.getPattern()).matcher(input); if (matcher.matches()) { if (prefixMatcher != null) throw new TDTException("Multiple patterns matched"); prefixMatcher = matcher; matchingOptionKey = opt.getOptionKey(); matchingOption = opt; } } } if (prefixMatcher == null) throw new TDTException("No patterns matched"); optionValue = matchingOptionKey; for (Enumeration e = matchingOption.enumerateField(); e.hasMoreElements(); ) { Field field = (Field) e.nextElement(); int seq = field.getSeq(); String strfieldname = field.getName(); int fieldlength = field.getLength(); String strfieldvalue = prefixMatcher.group(seq); // System.out.println(" processing field " + strfieldname + " = '" + strfieldvalue + "'"); if (field.getCompaction() == null) { // if compaction is null, treat field as an integer if (field.getCharacterSet() != null) { // if the character set is specified Matcher charsetmatcher = Pattern.compile("^" + field.getCharacterSet() + "$").matcher(strfieldvalue); if (!charsetmatcher.matches()) { throw new TDTException( "field " + strfieldname + " (" + strfieldvalue + ") does not conform to the allowed character set (" + field.getCharacterSet() + ") "); } } BigInteger bigvalue = null; if (tdtlevel.getType() == LevelTypeList.BINARY) { // if the input was BINARY bigvalue = new BigInteger(strfieldvalue, 2); extraparams.put(strfieldname, bigvalue.toString()); } else { if (field.getDecimalMinimum() != null || field.getDecimalMaximum() != null) bigvalue = new BigInteger(strfieldvalue); extraparams.put(strfieldname, strfieldvalue); } if (field.getDecimalMinimum() != null) { // if the decimal minimum is specified BigInteger bigmin = new BigInteger(field.getDecimalMinimum()); if (bigvalue.compareTo(bigmin) == -1) { // throw an exception if the field value is less than the decimal minimum throw new TDTException( "field " + strfieldname + " (" + bigvalue + ") is less than DecimalMinimum (" + field.getDecimalMinimum() + ") allowed"); } } if (field.getDecimalMaximum() != null) { // if the decimal maximum is specified BigInteger bigmax = new BigInteger(field.getDecimalMaximum()); if (bigvalue.compareTo(bigmax) == 1) { // throw an excpetion if the field value is greater than the decimal maximum throw new TDTException( "field " + strfieldname + " (" + bigvalue + ") is greater than DecimalMaximum (" + field.getDecimalMaximum() + ") allowed"); } } // after extracting the field, it may be necessary to pad it. padField(extraparams, field); } else { // compaction is specified - interpret binary as a string value using a truncated byte per // character CompactionMethodList compaction = field.getCompaction(); PadDirectionList padDir = field.getPadDir(); String padchar = field.getPadChar(); String s; if (compaction == CompactionMethodList.VALUE_5) // "5-bit" s = bin2uppercasefive(strfieldvalue); else if (compaction == CompactionMethodList.VALUE_4) // 6-bit s = bin2alphanumsix(strfieldvalue); else if (compaction == CompactionMethodList.VALUE_3) // 7-bit s = bin2asciiseven(strfieldvalue); else if (compaction == CompactionMethodList.VALUE_2) // 8-bit s = bin2bytestring(strfieldvalue); else throw new Error("unsupported compaction method " + compaction); extraparams.put(strfieldname, stripPadChar(s, padDir, padchar)); } } // for each field; /** * the EXTRACT rules are performed after parsing the input, in order to determine additional * fields that are to be derived from the fields obtained by the pattern match process */ int seq = 0; for (Enumeration e = tdtlevel.enumerateRule(); e.hasMoreElements(); ) { Rule tdtrule = (Rule) e.nextElement(); if (tdtrule.getType() == ModeList.EXTRACT) { assert seq < tdtrule.getSeq() : "Rule out of sequence order"; seq = tdtrule.getSeq(); processRules(extraparams, tdtrule); } } /** * Now we need to consider the corresponding output level and output option. The scheme must * remain the same, as must the value of optionkey (to select the corresponding option element * nested within the required outbound level) */ Level tdtoutlevel = findLevel(tdtscheme, outboundlevel); Option tdtoutoption = findOption(tdtoutlevel, optionValue); /** * the FORMAT rules are performed before formatting the output, in order to determine additional * fields that are required for preparation of the outbound format */ seq = 0; for (Enumeration e = tdtoutlevel.enumerateRule(); e.hasMoreElements(); ) { Rule tdtrule = (Rule) e.nextElement(); if (tdtrule.getType() == ModeList.FORMAT) { assert seq < tdtrule.getSeq() : "Rule out of sequence order"; seq = tdtrule.getSeq(); processRules(extraparams, tdtrule); } } /** * Now we need to ensure that all fields required for the outbound grammar are suitably padded * etc. processPadding takes care of firstly padding the non-binary fields if padChar and * padDir, length are specified then (if necessary) converting to binary and padding the binary * representation to the left with zeros if the bit string is has fewer bits than the bitLength * attribute specifies. N.B. TDTv1.1 will be more specific about bit-level padding rather than * assuming that it is always to the left with the zero bit. */ // System.out.println(" prior to processPadding, " + extraparams); for (Enumeration e = tdtoutoption.enumerateField(); e.hasMoreElements(); ) { Field field = (Field) e.nextElement(); // processPadding(extraparams, field, outboundlevel, tdtoutoption); padField(extraparams, field); if (outboundlevel == LevelTypeList.BINARY) binaryPadding(extraparams, field); } /** * Construct the output from the specified grammar (in ABNF format) together with the field * values stored in inputparams */ outboundstring = buildGrammar(tdtoutoption.getGrammar(), extraparams); // System.out.println("final extraparams = " + extraparams); // System.out.println("returned " + outboundstring); return outboundstring; }