Welcome to my 100th blog post! It is hard to believe that when I started this effort a year or so ago that I would have had enough words to say to fill up 100 articles.
Ok, enough about that. Let’s turn to the matter at hand which is an ongoing saga concerning parsing wire transfer messages in Java. I began this series with an article which presented the code to parse FedWire(TM) messages. After some retrospection, I realized that simply providing code without discussing some of the history and concepts underlying wire transfers was an oversight. So, my second installment in the series discusses where wire transfers originated and some key concepts inherent in wires . With this foundation, I will now boldly continue my exposition in code on parsing wire-transfer messages by introducing the Clearing House Interbank Payment System (CHIPS) format as promised.
As a reminder of what I stated in part 1, all of this code is distributed under the Mozilla Public License and is free for your use. The MPL’s header text states:
The contents of this file are subject to the Mozilla Public License Version 1.1 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an “AS IS” basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.
The CHIPS message format
Let me begin with a caveat. I recently cobbled this code together but I confess that CHIPS is a very new concept to me so I cannot be certain it is perfect for every situation. This tentiveness is partly driven also by the fact that I do not have an official standards document in hand. What you have here is derived from a CHIPS payment message structure reference card (which is not online so I cannot share it) and a confirming website by Paras Verma. There are still several large blocks of incomplete knowledge that I will have to update eventually. However, this code should parse out messages for CHIPS and allow for knowledgeable users to derive components that they need.
The CHIPS format appears to be relatively similar to the FedWire type except that its tags are three-digits long and enclosed in square brackets. Also, it appears that following the closing bracket for a CHIPS tag, a hard-coded space is inserted before the value’s contents unlike the Fed message which is completely left-justified.
CHIPS Message Test and Common Code
The common code for this CHIPS code is the same as that for the Fed code posted in Part 1 (makes sense since it is “common code!”). This was built using the same TDD approach as the other so we will start off with the JUnit test module’s code:
/* TestChipsParser.java
*
* Copyright (c) 2010, Chris Laforet Software/Christopher Laforet
* All Rights Reserved
*
* Started: Nov 25, 2010
* Revision Information: $Date$
* $Revision$
*
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* The Initial Developer of the Original Code is Chris Laforet from Chris Laforet Software.
* Portions created by Chris Laforet Software are Copyright (C) 2010. All Rights Reserved.
*
* Contributor(s): Chris Laforet Software.
*/
package com.chrislaforetsoftware.chips.sanitytests;
import com.chrislaforetsoftware.chips.message.ChipsMessage;
import com.chrislaforetsoftware.chips.message.ChipsMessageParser;
import com.chrislaforetsoftware.util.Field;
import com.chrislaforetsoftware.util.MessageParseResults;
import junit.framework.TestCase;
/**
* @author Christopher Laforet
*
*/
public class TestChipsParser extends TestCase
{
static public String GoodChipsMessage =
"[031] 01 19920508 0509 1 03 125956\r\n" +
"SSN: 0045348, ISN: 001673, OSN: 003406\r\n" +
"[221] 0008 B\r\n" +
"[260] 000220769618\r\n" +
"[270] 001663\r\n" +
"[320] 74300T743013042\r\n" +
"[321] FX DEAL\r\n" +
"[412] D 10990765 CITIBANK, LONDON, ENGLAND\r\n" +
"[422] B UBSWCHZH80A\r\n" +
"[500] C 005419\r\n" +
"[507] C 005419 D 000019350043 B BOFSGB2L BANK OF SCOTLAND\r\n" +
"INTL. DIV., OPERATIONS DEPT.\r\n" +
"POB 778, BISHOPSGATE EXCHANGE LONDON EC2M 3UB, ENGLAND";
public void testFieldParser() throws Exception
{
MessageParseResults results = ChipsMessageParser.parseMessage(GoodChipsMessage);
assertTrue(results != null);
assertTrue(results.fieldCount() != 0);
//System.err.println(results.getErrors());
assertTrue(results.errorCount() == 0);
assertTrue(results.fieldCount() == 10);
Field field = results.getFields().get(0);
assertEquals(field.getTag(),"031");
assertEquals(field.getValue(),"01 19920508 0509 1 03 125956\r\nSSN: 0045348, ISN: 001673, OSN: 003406");
assertEquals(field.toString(),"[031] 01 19920508 0509 1 03 125956\r\nSSN: 0045348, ISN: 001673, OSN: 003406");
field = results.getFields().get(9);
assertEquals(field.getTag(),"507");
assertEquals(field.getValue(),"C 005419 D 000019350043 B BOFSGB2L BANK OF SCOTLAND\r\nINTL. DIV., OPERATIONS DEPT.\r\nPOB 778, BISHOPSGATE EXCHANGE LONDON EC2M 3UB, ENGLAND");
field = results.getFields().get(1);
assertEquals(field.getTag(),"221");
assertEquals(field.getValue(),"0008 B");
}
public void testMessage() throws Exception
{
MessageParseResults results = ChipsMessageParser.parseMessage(GoodChipsMessage);
ChipsMessage message = (ChipsMessage)results.getMessage();
assertTrue(message != null);
assertTrue(message.doesFieldExist("031"));
assertTrue(message.doesFieldExist("321"));
assertTrue(message.doesFieldExist("412"));
assertTrue(message.doesFieldExist("507"));
assertFalse(message.doesFieldExist("199"));
assertFalse(message.doesFieldExist("324"));
assertEquals(message.getFieldValue("999"),"");
assertEquals(message.getFieldValue("221"),"0008 B");
assertEquals(message.getFieldValue("500"),"C 005419");
assertEquals(message.getFieldValue("320"),"74300T743013042");
assertEquals(message.getFieldValue("321"),"FX DEAL");
assertEquals(message.getFieldValue("507"),"C 005419 D 000019350043 B BOFSGB2L BANK OF SCOTLAND\r\nINTL. DIV., OPERATIONS DEPT.\r\nPOB 778, BISHOPSGATE EXCHANGE LONDON EC2M 3UB, ENGLAND");
assertEquals(message.toString(),GoodChipsMessage);
assertEquals(message.getAmount(),"000220769618");
assertEquals(message.getPSN(),"001663");
assertEquals(message.getSendParticipantReference(),"74300T743013042");
assertEquals(message.getBeneficiaryBank(),"D 10990765 CITIBANK, LONDON, ENGLAND");
assertEquals(message.getBeneficiary(),"B UBSWCHZH80A");
assertEquals(message.getOriginator(),"C 005419");
assertEquals(message.getBeneficiaryBankID(),"D 10990765");
assertEquals(message.getBeneficiaryID(),"B UBSWCHZH80A");
assertEquals(message.getOriginatorID(),"C 005419");
assertEquals(ChipsMessage.lookupIDCode(message.getBeneficiaryBankID().substring(0,1)),"Demand Deposit Account (DDA)");
assertEquals(ChipsMessage.lookupIDCode(message.getBeneficiaryID().substring(0,1)),"BIC/SWIFT");
assertEquals(ChipsMessage.lookupIDCode(message.getOriginatorID().substring(0,1)),"CHIPS Universal ID");
}
}
CHIPS Message Parser and Container Code
Having outlined the test code, now it is time to share the code for the actual CHIPS parser. The parser produces a ChipsMessage which contains several ChipsField objects. Like the FedWireField extended the common Field class, this ChipsField extends the same class to permit it to appropriately format CHIPS message tag/value lines.
So, first let’s look at the ChipsMessage code.
/* ChipsMessage.java
*
* Copyright (c) 2010, Chris Laforet Software/Christopher Laforet
* All Rights Reserved
*
* Started: Nov 25, 2010
* Revision Information: $Date$
* $Revision$
*
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* The Initial Developer of the Original Code is Chris Laforet from Chris Laforet Software.
* Portions created by Chris Laforet Software are Copyright (C) 2010. All Rights Reserved.
*
* Contributor(s): Chris Laforet Software.
*/
package com.chrislaforetsoftware.chips.message;
import java.util.HashMap;
import java.util.List;
import com.chrislaforetsoftware.util.CodeLookup;
import com.chrislaforetsoftware.util.Field;
/** Contains a CHIPS message. CHIPS is the CLEARING HOUSE PAYMENTS COMPANY L.L.C.
*
* @author Christopher Laforet
*/
public class ChipsMessage
{
// CHIPS Message Types
public static String ChipsPaymentRequestMessage = "10";
public static String ChipsPaymentStoredResponseMessage = "25";
public static String ChipsPaymentPreferenceMessage = "02";
public static String ChipsPaymentResolverNotificationMessage = "38";
public static String ChipsPaymentDeleteMessage = "01";
public static String ChipsGeneralServiceMessage = "22";
public static String ChipsServiceResponseMessage = "27";
public static String ChipsServiceNotificationMessage = "36";
// mandatory fields
private String _amount;
private String _psn;
private String _sendParticipantReference;
// lookup for identifier type
private static CodeLookup [] _idCode =
{
new CodeLookup("C","CHIPS Universal ID"),
new CodeLookup("D","Demand Deposit Account (DDA)"),
new CodeLookup("B","BIC/SWIFT"),
new CodeLookup("F","Financial Telecommunications"),
new CodeLookup("1","Non-Bank Identifier"),
new CodeLookup("2","Non-Bank Identifier"),
new CodeLookup("3","Non-Bank Identifier"),
new CodeLookup("4","Non-Bank Identifier"),
new CodeLookup("5","Non-Bank Identifier"),
new CodeLookup("9","Non-Bank Identifier")
};
// lookup for additional payment data (APD) from field 820
private static CodeLookup [] _apdCode =
{
new CodeLookup("01","UN-EDIFACT"),
new CodeLookup("02","ANSI X12"),
new CodeLookup("03","SWIFT"),
new CodeLookup("04","IXML (ISO 20022)"),
new CodeLookup("05","GXML (General XML)"),
new CodeLookup("06","S820 (STP 820)"),
new CodeLookup("07","Related Remittance Information (field 825) required"),
new CodeLookup("08","RMTS (Structured remittance)"),
new CodeLookup("09","PROP (Proprietary code)")
};
// additional fields
private List<Field> _fields;
private HashMap<String,Field> _fieldMap = new HashMap<String,Field>();
/** Attempts to create a FedWireMessage object from a collection
* of fields.
*
* @param Fields the fields contained in the message.
*/
public ChipsMessage(List<Field> Fields,List<String> Errors)
{
_fields = Fields;
for (Field field : Fields)
{
if (_fieldMap.containsKey(field.getTag()))
Errors.add("Duplicate field: " + field.getTag() + " exists more than once.");
else
_fieldMap.put(field.getTag(),field);
}
// mandatory fields
Field match = _fieldMap.get("260");
if (match != null)
_amount = match.getValue();
else
Errors.add("Missing mandatory field: Field 260.");
match = _fieldMap.get("270");
if (match != null)
_psn = match.getValue();
else
Errors.add("Missing mandatory field: Field 270.");
match = _fieldMap.get("320");
if (match != null)
_sendParticipantReference = match.getValue();
else
Errors.add("Missing mandatory field: Field 320.");
}
/** Retrieves field 260, the amount field..
*
* @return the value or empty string if it does not exist.
*/
public String getAmount()
{
return _amount == null ? "" : _amount;
}
/** Retrieves field 270, the payment sequence number (PSN) field..
*
* @return the value or empty string if it does not exist.
*/
public String getPSN()
{
return _psn == null ? "" : _psn;
}
/** Retrieves field 320, the send participant reference field..
*
* @return the value or empty string if it does not exist.
*/
public String getSendParticipantReference()
{
return _sendParticipantReference == null ? "" : _sendParticipantReference;
}
/** Attempts to return field 031, if it exists. This is header information.
*
* @return the value if found or an empty string if field does not exist.
*/
public String getHeader()
{
return getFieldValue("031");
}
/** Attempts to return field 201, if it exists. This is the
* Identification Tag formed of the Format version, value date,
* and send participant number.
*
* @return the value if found or an empty string if field does not exist.
*/
public String getIdentificationTag()
{
return getFieldValue("201");
}
/** Attempts to return field 211, if it exists. This is the
* Disposition Tag formed of the receive participant, the
* beneficiary type, compression flag, and the preference flag.
*
* @return the value if found or an empty string if field does not exist.
*/
public String getDispositionTag()
{
return getFieldValue("211");
}
/** Attempts to return field 221, if it exists. This is the
* Delivery Tag formed of the receive participant and the beneficiary type.
*
* @return the value if found or an empty string if field does not exist.
*/
public String getDeliveryTag()
{
return getFieldValue("221");
}
/** Attempts to return field 301, if it exists. This is the
* Charges Information which contains details of charges if the
* beneficiary is N.
*
* @return the value if found or an empty string if field does not exist.
*/
public String getChargesInformation()
{
return getFieldValue("301");
}
/** Attempts to return field 321, if it exists. This is the
* Related Bank Reference Number.
*
* @return the value if found or an empty string if field does not exist.
*/
public String getRelatedBankReference()
{
return getFieldValue("321");
}
/** Attempts to return field 400/401/402, if one exists. This is the
* Intermediary Bank Information (IBK).
*
* @return the value if found or an empty string if field does not exist.
*/
public String getIntermediaryBank()
{
String value = getFieldValue("400");
if (value.length() > 0)
return value;
value = getFieldValue("401");
if (value.length() > 0)
return value;
return getFieldValue("402");
}
/** Attempts to return the ID-code and identifier from field 400/401/402,
* the Intermediary Financial Institution, if it exists.
*
* @return the value if found or an empty string if field does not exist.
*/
public String getIntermediaryBankID()
{
String fi = getIntermediaryBank();
if (fi.length() == 0)
return "";
return decodeID(fi);
}
/** Attempts to retrieve the ID code and number from
* the field (code-letter+space+ID).
*
* @param Value the value string to parse.
* @return a string containing the ID or an empty string if not found.
*/
private String decodeID(String Value)
{
if (Value.length() <= 2)
return "";
if (Value.charAt(1) != ' ')
return "";
int index = Value.indexOf(' ',2);
if (index > 0)
return Value.substring(0,index);
return Value;
}
/** Attempts to return field 410/411/412, if one exists. This is the
* Beneficiary Bank Information (BBK).
*
* @return the value if found or an empty string if field does not exist.
*/
public String getBeneficiaryBank()
{
String value = getFieldValue("410");
if (value.length() > 0)
return value;
value = getFieldValue("411");
if (value.length() > 0)
return value;
return getFieldValue("412");
}
/** Attempts to return the ID-code and identifier from field 410/411/412,
* the Intermediary Financial Institution, if it exists.
*
* @return the value if found or an empty string if field does not exist.
*/
public String getBeneficiaryBankID()
{
String fi = getBeneficiaryBank();
if (fi.length() == 0)
return "";
return decodeID(fi);
}
/** Attempts to return field 420/421/422, if one exists. This is the
* Beneficiary (BNF).
*
* @return the value if found or an empty string if field does not exist.
*/
public String getBeneficiary()
{
String value = getFieldValue("420");
if (value.length() > 0)
return value;
value = getFieldValue("421");
if (value.length() > 0)
return value;
return getFieldValue("422");
}
/** Attempts to return the ID-code and identifier from field 420/421/422,
* the Beneficiary, if it exists.
*
* @return the value if found or an empty string if field does not exist.
*/
public String getBeneficiaryID()
{
String fi = getBeneficiary();
if (fi.length() == 0)
return "";
return decodeID(fi);
}
/** Attempts to return field 500/501/502, if one exists. This is the
* Originator Information (ORG).
*
* @return the value if found or an empty string if field does not exist.
*/
public String getOriginator()
{
String value = getFieldValue("500");
if (value.length() > 0)
return value;
value = getFieldValue("501");
if (value.length() > 0)
return value;
return getFieldValue("502");
}
/** Attempts to return the ID-code and identifier from field 500/501/502,
* the Originator, if it exists.
*
* @return the value if found or an empty string if field does not exist.
*/
public String getOriginatorID()
{
String fi = getOriginator();
if (fi.length() == 0)
return "";
return decodeID(fi);
}
/** Attempts to return field 510/511/512, if one exists. This is the
* Originator's Bank (OGB).
*
* @return the value if found or an empty string if field does not exist.
*/
public String getOriginatorBank()
{
String value = getFieldValue("510");
if (value.length() > 0)
return value;
value = getFieldValue("511");
if (value.length() > 0)
return value;
return getFieldValue("512");
}
/** Attempts to return the ID-code and identifier from field 510/511/512,
* the Originator's Bank, if it exists.
*
* @return the value if found or an empty string if field does not exist.
*/
public String getOriginatorBankID()
{
String fi = getOriginatorBank();
if (fi.length() == 0)
return "";
return decodeID(fi);
}
/** Attempts to return field 520/521/522, if one exists. This is the
* Instructing Bank (INS).
*
* @return the value if found or an empty string if field does not exist.
*/
public String getInstructingBank()
{
String value = getFieldValue("520");
if (value.length() > 0)
return value;
value = getFieldValue("521");
if (value.length() > 0)
return value;
return getFieldValue("522");
}
/** Attempts to return the ID-code and identifier from field 520/521/522,
* the Instructing Bank, if it exists.
*
* @return the value if found or an empty string if field does not exist.
*/
public String getInstructingBankID()
{
String fi = getInstructingBank();
if (fi.length() == 0)
return "";
return decodeID(fi);
}
/** Determines if the field exists in the message.
*
* @param FieldTag the tag to look for.
* @return true if there is a field tagged appropriately.
*/
public boolean doesFieldExist(String FieldTag)
{
return _fieldMap.containsKey(FieldTag);
}
/** Attempts to return the value attached to a field tag
* if it is found in the list of fields.
*
* @param FieldTag the tag (e.g. 1500) to find.
* @return the value if found or an empty string if field does not exist.
*/
public String getFieldValue(String FieldTag)
{
if (_fieldMap.containsKey(FieldTag))
return _fieldMap.get(FieldTag).getValue();
return "";
}
/** Returns the full message formatted with CRLF between each field.
*
* @return the message.
*/
@Override
public String toString()
{
StringBuilder sb = new StringBuilder(2048);
for (Field field : _fields)
{
if (sb.length() > 0)
sb.append("\r\n");
sb.append(field.toString());
}
return sb.toString();
}
// ---------------------------------------
// Helpful utility methods
// ---------------------------------------
/** Decodes an ID code and returns its descriptive text.
*
* @param Code the code to look up.
* @return the description if found or an empty string if not found.
*/
public static String lookupIDCode(String Code)
{
for (CodeLookup lookup : _idCode)
{
if (lookup.getCode().compareTo(Code) == 0)
return lookup.getDescription();
}
return "";
}
}
This code, as stated before, depends upon the ChipsField class which is very simple. Here is the code for ChipsField itself:
/* ChipsField.java
*
* Copyright (c) 2010, Chris Laforet Software/Christopher Laforet
* All Rights Reserved
*
* Started: Nov 26, 2010
* Revision Information: $Date$
* $Revision$
*
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* The Initial Developer of the Original Code is Chris Laforet from Chris Laforet Software.
* Portions created by Chris Laforet Software are Copyright (C) 2010. All Rights Reserved.
*
* Contributor(s): Chris Laforet Software.
*/
package com.chrislaforetsoftware.chips.util;
import com.chrislaforetsoftware.util.Field;
/** Container for a Chips message field.
*
* @author Christopher Laforet
*/
public class ChipsField extends Field
{
public ChipsField(String Tag,String Value)
{
super(Tag,Value);
}
/* (non-Javadoc)
* @see com.wachovia.util.Field#getField()
*/
@Override
public String getFormattedField()
{
return "[" + getTag() + "] " + getValue();
}
}
Finally, the only code remaining is the actual parsing code for the CHIPS format itself. This is very similar to the FedWireParser code we saw earlier but it takes the differences in format into account. Here is the ChipsParser Java code:
/* ChipsMessageParser.java
*
* Copyright (c) 2010, Chris Laforet Software/Christopher Laforet
* All Rights Reserved
*
* Started: Nov 26, 2010
* Revision Information: $Date$
* $Revision$
*
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* The Initial Developer of the Original Code is Chris Laforet from Chris Laforet Software.
* Portions created by Chris Laforet Software are Copyright (C) 2010. All Rights Reserved.
*
* Contributor(s): Chris Laforet Software.
*/
package com.chrislaforetsoftware.chips.message;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import com.chrislaforetsoftware.chips.util.ChipsField;
import com.chrislaforetsoftware.util.Field;
import com.chrislaforetsoftware.util.MessageParseResults;
import com.chrislaforetsoftware.util.ParseSupport;
/** Contains the parser for Chips messages.
*
* @author Christopher Laforet
*/
public class ChipsMessageParser
{
/** Attempts to parse a CHIPS message from the provided
* content and return it. The message must be properly formed.
*
* @param Contents a string containing a FedWire message.
* @return a parsed message if successful contained in a MessageParseResults object.
* @throws IOException if an error occurs while parsing.
*/
static public MessageParseResults parseMessage(String Contents) throws IOException
{
ParseSupport support = new ParseSupport(new BufferedReader(new StringReader(Contents)));
List<Field> fieldList = new ArrayList<Field>(30);
while (true)
{
String nextLine = support.reader.readLine();
if (nextLine == null)
break;
++support.lineNumber;
if (nextLine.trim().length() == 0)
break;
Field field = extractField(nextLine,support);
if (field != null)
fieldList.add(field);
else
support.errorList.add("Line " + support.lineNumber + ": Invalid field or malformed field tag");
}
// checkFields(fieldList,support);
ChipsMessage message = new ChipsMessage(fieldList,support.errorList);
return new MessageParseResults(message,fieldList,support.errorList);
}
/** Handles parsing the tag out of the current line and
* checks to see if the tag wraps onto other lines. If so,
* it reads the wrapped lines. It does not check for field
* legality.
*
* @param Line the first (or only) line of the tagged field.
* @param Support the parser support class containing the message to parse.
* @return a Field object containing tag and field data or null if malformed line.
* @throws IOException if an error occurs reading from the parsing object.
*/
static private Field extractField(String Line,ParseSupport Support) throws IOException
{
int offset = Line.indexOf("[");
if (offset < 0)
{
Support.errorList.add("Line " + Support.lineNumber + ": Missing open curly ([) on tag number.");
return null;
}
else if (offset > 0)
Support.errorList.add("Line " + Support.lineNumber + ": Incorrectly placed open curly (]) on tag number...not first character on line.");
int tagStart = offset + 1;
offset = Line.indexOf("]",offset);
if (offset < 0)
{
Support.errorList.add("Line " + Support.lineNumber + ": Missing close curly (]) on tag number.");
return null;
}
String tag = Line.substring(tagStart,offset);
if (tag.length() != 3)
Support.errorList.add("Line " + Support.lineNumber + ": Tag is invalid length of " + tag.length() + " characters instead of 4.");
for (char ch : tag.toCharArray())
{
if (!Character.isDigit(ch))
{
Support.errorList.add("Line " + Support.lineNumber + ": Tag contains one or more invalid non-numeric characters.");
break;
}
}
StringBuilder contents = new StringBuilder(256);
if (offset == Line.length() - 2)
Support.errorList.add("Line " + Support.lineNumber + ": No data follows the tag...empty value field.");
else
contents.append(Line.substring(offset + 2));
while (true)
{
Support.reader.mark(1);
int nextChar = Support.reader.read();
Support.reader.reset();
if (nextChar == -1)
break;
if ((char)nextChar == '[')
break;
String line = Support.reader.readLine();
Support.lineNumber++;
contents.append("\r\n");
contents.append(line);
}
return new ChipsField(tag,contents.toString());
}
}
And that’s all there is to this parser. The ChipsMessage class provides several utility methods for retrieving information from the parsed message. Just as with the FedWireMessage class, this one is meant to be queried for the specific pieces of data required to achieve the desired effect.
So, what now?
I intend to publish library code for decoding simple MTS messages. As I wrote the article describing funds transfer messages, it occurred to me that I would also need to produce a SWIFT message parser for completeness. I have already written a comprehensive SWIFT library for work in both Java and .NET incarnations so I really did not need to explore this format in greater depth (see my introductory comments in part 1). Yet, I am sure that providing code to parse the SWIFT format would be helpful to many. I think that I will attempt to produce a lightweight parser for SWIFT in keeping with the code presented here but will have to tread carefully to not expose code that I produced beforehand (unless I can get explicit permission to share parts of that code from my former boss). I promise that I will work on this.
Therefore, I am not certain which code the next installment of this series will contain. It will either be the MTS code or SWIFT code but that depends upon what I work on next. Of course, if I can be granted permission to base my SWIFT code on my other work, then there is a good chance that that would be next in line. We’ll just have to play it by ear until then…





Pingback: Java Library for Wire Transfer Messages (Part 4) | Chris' Creative Musings