diff --git a/CHANGELOG.md b/CHANGELOG.md index b210504d3..c1e156112 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,11 @@ # Prowide ISO 20022 - CHANGELOG -#### 9.4.8 - SNAPSHOT - * (PW-2113) Added API in the `MxParseUtils` to extract comments from XML string - * (PW-2113) Added API in the `MxParseUtils` to extract the enclosed MT from a multi-format MX message +#### 9.5.5 - SNAPSHOT + * (PW-2113) `MxParseUtils` added a method to extract the enclosed MT from a multi-format MX message + * (PW-2113) `MxParseUtils` added methods to extract comments from XML string + * Added new SettlementInfo class, and added MxParseUtils#getSettlementInfo to extract it from a raw MX message. + * Moved and enhanced the MxSwiftMessage#findElement to support multiple element's path -#### 9.4.7 - August 2024 - * (PW-1958) Fixed the `DefaultMxMetadataStrategy` NPE issue when the amount values are null - #### 9.5.4 - August 2024 * (PW-1958) Fixed the `DefaultMxMetadataStrategy` NPE issue when the amount values are null diff --git a/iso20022-core/src/main/java/com/prowidesoftware/swift/model/SettlementInfo.java b/iso20022-core/src/main/java/com/prowidesoftware/swift/model/SettlementInfo.java new file mode 100644 index 000000000..ab50ea454 --- /dev/null +++ b/iso20022-core/src/main/java/com/prowidesoftware/swift/model/SettlementInfo.java @@ -0,0 +1,42 @@ +package com.prowidesoftware.swift.model; + +/** + * Class for identification of MX messages. + * + *

It is composed of the Settlement Method and Clearing System (Code or Property). + * Used in combination with MxId to identify a specific MX message. + * + * @since 9.5.5 + */ +public class SettlementInfo { + + private SettlementMethod settlementMethod; + private String clrSysCd; // Clearing System Code + private String clrSysPrtry; // Clearing System Proprietary Code + + public SettlementInfo() {} + + public SettlementMethod getSettlementMethod() { + return settlementMethod; + } + + public void setSettlementMethod(SettlementMethod sttlmMtd) { + this.settlementMethod = sttlmMtd; + } + + public String getClrSysCd() { + return clrSysCd; + } + + public void setClrSysCd(String clrSysCd) { + this.clrSysCd = clrSysCd; + } + + public String getClrSysPrtry() { + return clrSysPrtry; + } + + public void setClrSysPrtry(String clrSysPrtry) { + this.clrSysPrtry = clrSysPrtry; + } +} diff --git a/iso20022-core/src/main/java/com/prowidesoftware/swift/model/SettlementMethod.java b/iso20022-core/src/main/java/com/prowidesoftware/swift/model/SettlementMethod.java new file mode 100644 index 000000000..13ac55d1c --- /dev/null +++ b/iso20022-core/src/main/java/com/prowidesoftware/swift/model/SettlementMethod.java @@ -0,0 +1,55 @@ +package com.prowidesoftware.swift.model; + +import java.util.Arrays; +import java.util.Optional; + +/** + * Enum representing different settlement methods for financial transactions. + * + *

The settlement methods are:

+ * + * + * @since 9.5.5 + */ +public enum SettlementMethod { + INDA("INDA", "Instructed Agent"), + INGA("INGA", "Instructing Agent"), + COVE("COVE", "Cover Method"), + CLRG("CLRG", "Clearing System"); + + private final String label; + private final String name; + + // Constructor + SettlementMethod(String label, String name) { + this.label = label; + this.name = name; + } + + public static Optional findByLabel(String label) { + return Arrays.stream(values()) + .filter(method -> method.label.equalsIgnoreCase(label)) + .findFirst(); + } + + public static Optional findByName(String name) { + return Arrays.stream(values()) + .filter(method -> method.name.equalsIgnoreCase(name)) + .findFirst(); + } + + // Getter for label + public String getLabel() { + return label; + } + + // Getter for name + public String getName() { + return name; + } +} diff --git a/iso20022-core/src/main/java/com/prowidesoftware/swift/model/mx/MxParseUtils.java b/iso20022-core/src/main/java/com/prowidesoftware/swift/model/mx/MxParseUtils.java index 09047718f..3c94302bf 100644 --- a/iso20022-core/src/main/java/com/prowidesoftware/swift/model/mx/MxParseUtils.java +++ b/iso20022-core/src/main/java/com/prowidesoftware/swift/model/mx/MxParseUtils.java @@ -16,8 +16,12 @@ package com.prowidesoftware.swift.model.mx; import com.prowidesoftware.ProwideException; +import com.prowidesoftware.deprecation.ProwideDeprecated; +import com.prowidesoftware.deprecation.TargetYear; import com.prowidesoftware.swift.model.DistinguishedName; import com.prowidesoftware.swift.model.MxId; +import com.prowidesoftware.swift.model.SettlementInfo; +import com.prowidesoftware.swift.model.SettlementMethod; import com.prowidesoftware.swift.model.mt.AbstractMT; import com.prowidesoftware.swift.utils.SafeXmlUtils; import java.io.IOException; @@ -26,6 +30,7 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.Stack; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import java.util.logging.Logger; @@ -46,7 +51,7 @@ * @since 9.1.2 */ public class MxParseUtils { - private static final transient Logger log = Logger.getLogger(MxParseUtils.class.getName()); + private static final Logger log = Logger.getLogger(MxParseUtils.class.getName()); /** * Creates a {@link SAXSource} for the given XML, filtering a specific element with the @@ -131,19 +136,18 @@ static void handleParseException(Exception e) { if (e instanceof XMLStreamException) { throw new ProwideException("Error parsing message: " + e.getMessage()); } - log.severe("An error occurred while reading XML: " + e.getMessage()); - e.printStackTrace(); + log.log(Level.SEVERE, "An error occurred while reading XML: " + e.getMessage(), e); } /** * Parse an object from XML with optional wrapper and sibling elements that will be ignored. * - * @param targetClass calss of the object being parsed + * @param targetClass class of the object being parsed * @param xml the XML content, can contain wrapper elements that will be ignored * @param classes the object classes to build a jaxb context * @param localName the specific element to parse within the parameter XML * @param params not null unmarshalling parameters - * @return parsed element or null if cannot be parsed + * @return parsed element or null if content cannot be parsed * @throws ProwideException if severe errors occur during parse * @since 9.2.6 */ @@ -206,10 +210,10 @@ public static Optional identifyMessage(final String xml) { } // if the Document does not have a namespace, try to identify the message from the header - Optional element = NamespaceReader.findElement(xml, "MsgDefIdr"); + Optional element = findElementByTags(xml, "MsgDefIdr"); if (!element.isPresent()) { // Legacy ahv10 header - element = NamespaceReader.findElement(xml, "MsgName"); + element = findElementByTags(xml, "MsgName"); } if (element.isPresent()) { try { @@ -226,7 +230,7 @@ private static Optional enrichBusinessService(MxId mxId, final String xml) if (mxId == null) { return Optional.empty(); } - Optional element = NamespaceReader.findElement(xml, "BizSvc"); + Optional element = findElementByTags(xml, "BizSvc"); if (element.isPresent()) { try { mxId.setBusinessService(element.get().getElementText()); @@ -265,7 +269,7 @@ public static String makeXmlLenient(String xml) { * @throws NullPointerException if the {@code xml} is null * @throws IllegalArgumentException if the {@code xml} is blank or empty * - * @since 9.4.8 + * @since 9.5.5 */ public static List parseComments(final String xml) { Objects.requireNonNull(xml, "XML to parse must not be null"); @@ -306,7 +310,7 @@ public static List parseComments(final String xml) { * @throws NullPointerException if the {@code xml} is null * @throws IllegalArgumentException if the {@code xml} is blank or empty * - * @since 9.4.8 + * @since 9.5.5 */ public static List parseCommentsStartsWith(final String xml, final String startWith) { return parseComments(xml).stream() @@ -326,7 +330,7 @@ public static List parseCommentsStartsWith(final String xml, final Strin * @throws NullPointerException if the {@code xml} is null * @throws IllegalArgumentException if the {@code xml} is blank or empty * - * @since 9.4.8 + * @since 9.5.5 */ public static List parseCommentsContains(final String xml, final String contains) { return parseComments(xml).stream() @@ -350,7 +354,7 @@ public static List parseCommentsContains(final String xml, final String * otherwise, an empty {@link Optional} * @throws NullPointerException if the {@code xml} is null * - * @since 9.4.8 + * @since 9.5.5 */ public static Optional parseMtFromMultiformatMessage(final String xml) { List MTs = MxParseUtils.parseCommentsStartsWith(xml, "{1:F0"); @@ -365,4 +369,186 @@ public static Optional parseMtFromMultiformatMessage(final String xm } return Optional.empty(); } + + /** + * Extracts settlement information from the given XML document. + * + *

This method attempts to parse the provided XML and extract information + * related to the settlement method and clearing system codes. Specifically: + *

    + *
  • {@code SttlmMtd} - The settlement method.
  • + *
  • {@code ClrSys > Cd} - The clearing system code.
  • + *
  • {@code ClrSys > Prtry} - The clearing system proprietary.
  • + *
+ * + *

If any of these elements are found, a {@link SettlementInfo} object is + * created and populated with the extracted values. + * + * @param xml the XML document as a {@link String} to parse for settlement information. + * @return an {@link Optional} containing the {@link SettlementInfo} if at least one + * of the required elements is found; otherwise, an empty {@link Optional}. + * @throws NullPointerException if the {@code xml} is null. + * @since 9.5.5 + */ + public static Optional getSettlementInfo(final String xml) { + + Optional sttlmMtdMaybe = findElementByTags(xml, "SttlmMtd"); + Optional clrSysCdMaybe = findElementByTags(xml, "ClrSys", "Cd"); + Optional clrSysPrtryMaybe = findElementByTags(xml, "ClrSys", "Prtry"); + + if (sttlmMtdMaybe.isPresent() || clrSysCdMaybe.isPresent() || clrSysPrtryMaybe.isPresent()) { + SettlementInfo settlementInfo = new SettlementInfo(); + try { + if (sttlmMtdMaybe.isPresent()) { + + Optional sttlmMtdLabel = + SettlementMethod.findByLabel(sttlmMtdMaybe.get().getElementText()); + sttlmMtdLabel.ifPresent(settlementInfo::setSettlementMethod); + } + if (clrSysCdMaybe.isPresent()) { + settlementInfo.setClrSysCd(clrSysCdMaybe.get().getElementText()); + } + if (clrSysPrtryMaybe.isPresent()) { + settlementInfo.setClrSysPrtry(clrSysPrtryMaybe.get().getElementText()); + } + return Optional.of(settlementInfo); + } catch (XMLStreamException e) { + log.finer("Error identifying business service: " + e.getMessage()); + } + } + return Optional.empty(); + } + + /** + * Finds an XML element within a document by traversing a specified tag hierarchy. + * + *

This method uses an {@link XMLStreamReader} to parse the provided XML document. + * It searches for an element that matches the specified sequence of tag names (hierarchy). + * For example, to find the {@code } tag within {@code }, you would call: + *

+     *     findElement(xml, "ClrSys", "Cd");
+     * 
+ * + * @param xml the XML document as a {@link String} to search. + * @param tags the sequence of tag names that define the hierarchy of the target element. + * @return an {@link Optional} containing the {@link XMLStreamReader} positioned at the + * matching element if found; otherwise, an empty {@link Optional}. + * @throws NullPointerException if the {@code xml} or {@code tags} are null. + * @throws IllegalArgumentException if the {@code xml} is a blank string. + * @since 9.5.5 + */ + public static Optional findElementByTags(final String xml, String... tags) { + Objects.requireNonNull(xml, "XML to parse must not be null"); + Validate.notBlank(xml, "XML to parse must not be a blank string"); + Objects.requireNonNull(xml, "tags to find must not be null"); + + final XMLInputFactory xif = SafeXmlUtils.inputFactory(); + int tagsIndex = 0; + try { + final XMLStreamReader reader = + xif.createXMLStreamReader(new StringReader(MxParseUtils.makeXmlLenient(xml))); + while (reader.hasNext()) { + int event = reader.next(); + if (XMLStreamConstants.START_ELEMENT == event) { + if (reader.getLocalName().equals(tags[tagsIndex])) { + if (tagsIndex == tags.length - 1) { + return Optional.of(reader); + } + tagsIndex++; + } + } + } + } catch (XMLStreamException e) { + log.log(Level.WARNING, "Error reading XML", e); + } + return Optional.empty(); + } + + /** + * Finds an XML element within a document by traversing a specified tag hierarchy. + * + *

This method uses an {@link XMLStreamReader} to parse the provided XML document. + * It searches for an element that matches the specified sequence of tag names (hierarchy). + * For example, to find the {@code } tag within {@code }, you would call: + *

+     *     findElement(xml, "ClrSys", "Cd");
+     * 
+ * + * @param xml the XML document as a {@link String} to search. + * @param targetPath the path of the field to find into the xml. + * @return an {@link Optional} containing the {@link XMLStreamReader} positioned at the + * matching element if found; otherwise, an empty {@link Optional}. + * @throws NullPointerException if the {@code xml} or {@code tags} are null. + * @throws IllegalArgumentException if the {@code xml} is a blank string. + * @since 9.5.5 + */ + public static Optional findElementByAbsolutePath(String xml, String targetPath) { + Objects.requireNonNull(xml, "XML to parse must not be null"); + Validate.notBlank(xml, "XML to parse must not be a blank string"); + Objects.requireNonNull(xml, "targetPath to find must not be null"); + XMLInputFactory factory = XMLInputFactory.newInstance(); + + try { + + final XMLStreamReader reader = + factory.createXMLStreamReader(new StringReader(MxParseUtils.makeXmlLenient(xml))); + + Stack pathStack = new Stack<>(); + + while (reader.hasNext()) { + int event = reader.next(); + + switch (event) { + case XMLStreamConstants.START_ELEMENT: + // Push the current element onto the path stack + if (!reader.getLocalName().equals("RequestPayload")) { + pathStack.push(reader.getLocalName()); + // Build the current path + String currentPath = buildCurrentPath(pathStack); + + // Check if the current path matches the target path + if (currentPath.equals(targetPath)) { + return Optional.of(reader); + } + break; + } + case XMLStreamConstants.END_ELEMENT: + // Pop the element from the path stack + if (!pathStack.isEmpty()) { + pathStack.pop(); + } + break; + + default: + break; + } + } + } catch (XMLStreamException e) { + throw new RuntimeException(e); + } + + return Optional.empty(); // Return empty if the path is not found + } + + /** + * + * @param pathStack + * @return the current path + * Join the stack elements with "/" to form the current path + */ + private static String buildCurrentPath(Stack pathStack) { + return "/" + String.join("/", pathStack); + } + + /** + * Checks if an element exists in the XML + * + * @param xml the XML content + * @param localName the element name + * @return true if at least one element with the given name is found + * @since 9.5.5 + */ + public static boolean elementExists(final String xml, final String localName) { + return findElementByTags(xml, localName).isPresent(); + } } diff --git a/iso20022-core/src/main/java/com/prowidesoftware/swift/model/mx/NamespaceReader.java b/iso20022-core/src/main/java/com/prowidesoftware/swift/model/mx/NamespaceReader.java index c1744ed22..f2266c721 100644 --- a/iso20022-core/src/main/java/com/prowidesoftware/swift/model/mx/NamespaceReader.java +++ b/iso20022-core/src/main/java/com/prowidesoftware/swift/model/mx/NamespaceReader.java @@ -15,18 +15,12 @@ */ package com.prowidesoftware.swift.model.mx; -import com.prowidesoftware.swift.utils.SafeXmlUtils; -import java.io.StringReader; -import java.util.Objects; -import java.util.Optional; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLStreamConstants; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamReader; +import com.prowidesoftware.deprecation.ProwideDeprecated; +import com.prowidesoftware.deprecation.TargetYear; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.Validate; + +import javax.xml.stream.XMLStreamReader; +import java.util.Optional; /** * Helper API to extract information from an XML using lightweight XML streams API @@ -34,7 +28,6 @@ * @since 9.2.1 */ public class NamespaceReader { - private static final Logger log = Logger.getLogger(NamespaceReader.class.getName()); /** * Extracts the document namespace from the XML, if the Document element is present @@ -64,7 +57,7 @@ public static Optional findAppHdrNamespace(final String xml) { * @return found namespace or empty if the element is not found or does not contain a namespace */ public static Optional findNamespaceForLocalName(final String xml, final String localName) { - Optional reader = findElement(xml, localName); + Optional reader = MxParseUtils.findElementByTags(xml, localName); return reader.map(NamespaceReader::readNamespace); } @@ -88,36 +81,11 @@ private static String readNamespace(final XMLStreamReader reader) { } /** - * Checks if an element exists in the XML - * - * @param xml the XML content - * @param localName the element name - * @return true if at least one element with the given name is found + * @deprecated use {@link MxParseUtils#elementExists(String, String)} instead */ + @Deprecated + @ProwideDeprecated(phase2 = TargetYear.SRU2025) public static boolean elementExists(final String xml, final String localName) { - return findElement(xml, localName).isPresent(); - } - - static Optional findElement(final String xml, final String localName) { - Objects.requireNonNull(xml, "XML to parse must not be null"); - Validate.notBlank(xml, "XML to parse must not be a blank string"); - Objects.requireNonNull(xml, "localName to find must not be null"); - - final XMLInputFactory xif = SafeXmlUtils.inputFactory(); - try { - final XMLStreamReader reader = - xif.createXMLStreamReader(new StringReader(MxParseUtils.makeXmlLenient(xml))); - while (reader.hasNext()) { - int event = reader.next(); - if (XMLStreamConstants.START_ELEMENT == event) { - if (reader.getLocalName().equals(localName)) { - return Optional.of(reader); - } - } - } - } catch (XMLStreamException e) { - log.log(Level.WARNING, "Error reading XML", e); - } - return Optional.empty(); + return MxParseUtils.elementExists(xml, localName); } } diff --git a/iso20022-core/src/test/java/com/prowidesoftware/swift/model/mx/MxParseUtilsTest.java b/iso20022-core/src/test/java/com/prowidesoftware/swift/model/mx/MxParseUtilsTest.java index 47f37d7a4..04aac75e2 100644 --- a/iso20022-core/src/test/java/com/prowidesoftware/swift/model/mx/MxParseUtilsTest.java +++ b/iso20022-core/src/test/java/com/prowidesoftware/swift/model/mx/MxParseUtilsTest.java @@ -19,9 +19,13 @@ import com.prowidesoftware.swift.model.MxBusinessProcess; import com.prowidesoftware.swift.model.MxId; +import com.prowidesoftware.swift.model.SettlementInfo; +import com.prowidesoftware.swift.model.SettlementMethod; import com.prowidesoftware.swift.model.mt.AbstractMT; import java.util.List; import java.util.Optional; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; import org.junit.jupiter.api.Test; public class MxParseUtilsTest { @@ -267,6 +271,75 @@ public class MxParseUtilsTest { + " " + ""; + String xml_pacs_008_001_01 = "" + + "" + + " " + + " " + + " MSGID-pacs028-20190529-1" + + " 2019-05-29T09:30:47Z" + + " " + + " " + + " FOOICHBBXXX" + + " " + + " " + + " " + + " " + + " FOOTDEFFXXX" + + " " + + " " + + " " + + " " + + " MSGID-camt056-20190522-2-SEPA" + + " camt.056.001.01" + + " " + + " " + + " STSREQID-pacs028StatusReqId4713" + + " XCORW-xzng943XzmxvoRwIvu5287" + + " 1234567891" + + " 79809477-7-9998" + + " " + + " " + + " INDA" + + " " + + " ABE" + + " " + + " " + + " " + + " " + + " SEPA" + + " " + + " " + + " " + + " Horlogerie du Joux, Mueller et Cie." + + " " + + " " + + " " + + " CH5598064001234567890" + + " " + + " " + + " " + + " " + + " FOOICHBBXXX" + + " " + + " " + + " " + + " " + + " FOOYDEFFXXX" + + " " + + " " + + " " + + " Uhrengrosshandel Buxtehude, Peter Maier und Co." + + " " + + " " + + " " + + " DE47100100001234567890" + + " " + + " " + + " " + + " " + + " " + + ""; + @Test public void testIdentifyMessage_01() { final String xml = "" @@ -445,4 +518,323 @@ public void testParseMtFromMultiFormatMessage() { .getFieldByName("72") .getValue()); } + + @Test + void identifySettlementMethodCLRG() { + String xml = "" + + " " + + " " + + " ou=xxx,o=irvtdefx,o=swift" + + " cn=rtgs,o=xxxtxepm,o=swift" + + " esmig.t2.iast!pu" + + " pacs.009.001.08" + + " " + + " " + + " " + + " " + + " " + + " " + + " FOOGDEFXXXX" + + " " + + " DEFOOGDEFXXXX" + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " FOOVFRPPXXX" + + " " + + " " + + " " + + " VKS7695859976572" + + " pacs.009.001.08CORE" + + " cbn.rtgs.01" + + " 2023-04-14T12:31:30.236Z" + + " " + + " " + + " " + + " " + + " NONREF" + + " 2023-04-14T12:31:30+00:00" + + " 1" + + " " + + " CLRG" + + " " + + " TGT" + + " " + + " " + + " " + + " " + + " " + + " VKS7695859976572" + + " YMBNK991G7GX51X" + + " 33087b8e-2dfb-40b5-a432-769585867545" + + " " + + " " + + " " + + " G004" + + " " + + " " + + " SUPP" + + " " + + " " + + " 1000.00" + + " 2023-04-14" + + " " + + " " + + " FOOUFR21XXX" + + " " + + " " + + " " + + " " + + " FOOGDEFXXXX" + + " " + + " " + + " " + + " " + + " FOOVFRPPXXX" + + " " + + " " + + " " + + " " + + " FOOWDK22XXX" + + " ARBEJDSMARKEDETS TILLAEGSPENSION" + + " " + + " 3400" + + " REGION HOVEDSTADEN" + + " DK" + + " " + + " " + + " " + + " " + + " " + + " FOOGBEBBXXX" + + " THE BANK OF NEW YORK MELLON SA/NV" + + " " + + " 1000" + + " BRUXELLES-CAPITALE" + + " BE" + + " " + + " " + + " " + + " " + + " " + + " FOOVGB2LLON" + + " SOCIETE GENERALE" + + " " + + " E14 4SG" + + " GREATER LONDON" + + " GB" + + " " + + " " + + " " + + " " + + " " + + " FOOVNL2AXXX" + + " SOCIETE GENERALE" + + " " + + " 1096 HA" + + " NOORD-HOLLAND" + + " NL" + + " " + + " " + + " " + + " " + + " " + + " " + + ""; + MxId id = MxParseUtils.identifyMessage(xml).orElse(null); + assertNotNull(id); + assertEquals("pacs.009.001.08", id.id()); + assertEquals("cbn.rtgs.01", id.getBusinessService().orElse(null)); + Optional settlementInfo = MxParseUtils.getSettlementInfo(xml); + assertTrue(settlementInfo.isPresent()); + assertEquals(SettlementMethod.CLRG, settlementInfo.get().getSettlementMethod()); + assertEquals("TGT", settlementInfo.get().getClrSysCd()); + } + + @Test + void identifySettlementMethodINDA() { + MxId id = MxParseUtils.identifyMessage(xml_pacs_008_001_01).orElse(null); + assertNotNull(id); + assertEquals("pacs.028.001.01", id.id()); + assertFalse(id.getBusinessService().isPresent()); + Optional settlementInfo = MxParseUtils.getSettlementInfo(xml_pacs_008_001_01); + assertTrue(settlementInfo.isPresent()); + assertEquals(SettlementMethod.INDA, settlementInfo.get().getSettlementMethod()); + assertEquals("ABE", settlementInfo.get().getClrSysPrtry()); + } + + @Test + void testFindFieldValueByTags() throws XMLStreamException { + + MxId id = MxParseUtils.identifyMessage(xml_pacs_008_001_01).orElse(null); + assertNotNull(id); + assertEquals("pacs.028.001.01", id.id()); + + Optional msgId = + MxParseUtils.findElementByTags(xml_pacs_008_001_01, "FIToFIPmtStsReq", "GrpHdr", "MsgId"); + Optional BICFI = MxParseUtils.findElementByTags( + xml_pacs_008_001_01, "FIToFIPmtStsReq", "GrpHdr", "InstgAgt", "FinInstnId", "BICFI"); + Optional sttlmMtd = MxParseUtils.findElementByTags( + xml_pacs_008_001_01, "FIToFIPmtStsReq", "TxInf", "OrgnlTxRef", "SttlmInf", "SttlmMtd"); + Optional prtry = MxParseUtils.findElementByTags( + xml_pacs_008_001_01, "FIToFIPmtStsReq", "TxInf", "OrgnlTxRef", "SttlmInf", "ClrSys", "Prtry"); + Optional IBAN = MxParseUtils.findElementByTags( + xml_pacs_008_001_01, "FIToFIPmtStsReq", "TxInf", "OrgnlTxRef", "CdtrAcct", "Id", "IBAN"); + Optional IBAN_invalid = + MxParseUtils.findElementByTags(xml_pacs_008_001_01, "FIToFIPmtStsReq", "TxInf", "Id", "Foo", "IBAN"); + + assertTrue(msgId.isPresent()); + assertEquals("MSGID-pacs028-20190529-1", msgId.get().getElementText()); + + assertTrue(BICFI.isPresent()); + assertEquals("FOOICHBBXXX", BICFI.get().getElementText()); + + assertTrue(sttlmMtd.isPresent()); + assertEquals("INDA", sttlmMtd.get().getElementText()); + + assertTrue(prtry.isPresent()); + assertEquals("ABE", prtry.get().getElementText()); + + assertTrue(IBAN.isPresent()); + assertEquals("DE47100100001234567890", IBAN.get().getElementText()); + + assertFalse(IBAN_invalid.isPresent()); + } + + @Test + void testFindFieldValueByAbsolutePathPacs() throws XMLStreamException { + + MxId id = MxParseUtils.identifyMessage(xml_pacs_008_001_01).orElse(null); + assertNotNull(id); + assertEquals("pacs.028.001.01", id.id()); + + Optional msgId = + MxParseUtils.findElementByAbsolutePath(xml_pacs_008_001_01, "/Document/FIToFIPmtStsReq/GrpHdr/MsgId"); + Optional BICFI = MxParseUtils.findElementByAbsolutePath( + xml_pacs_008_001_01, "/Document/FIToFIPmtStsReq/GrpHdr/InstgAgt/FinInstnId/BICFI"); + Optional sttlmMtd = MxParseUtils.findElementByAbsolutePath( + xml_pacs_008_001_01, "/Document/FIToFIPmtStsReq/TxInf/OrgnlTxRef/SttlmInf/SttlmMtd"); + Optional prtry = MxParseUtils.findElementByAbsolutePath( + xml_pacs_008_001_01, "/Document/FIToFIPmtStsReq/TxInf/OrgnlTxRef/SttlmInf/ClrSys/Prtry"); + Optional IBAN = MxParseUtils.findElementByAbsolutePath( + xml_pacs_008_001_01, "/Document/FIToFIPmtStsReq/TxInf/OrgnlTxRef/CdtrAcct/Id/IBAN"); + Optional IBAN_invalid = MxParseUtils.findElementByAbsolutePath( + xml_pacs_008_001_01, "/Document/FIToFIPmtStsReq/TxInf/Id/Foo/IBAN"); + + assertTrue(msgId.isPresent()); + assertEquals("MSGID-pacs028-20190529-1", msgId.get().getElementText()); + + assertTrue(BICFI.isPresent()); + assertEquals("FOOICHBBXXX", BICFI.get().getElementText()); + + assertTrue(sttlmMtd.isPresent()); + assertEquals("INDA", sttlmMtd.get().getElementText()); + + assertTrue(prtry.isPresent()); + assertEquals("ABE", prtry.get().getElementText()); + + assertTrue(IBAN.isPresent()); + assertEquals("DE47100100001234567890", IBAN.get().getElementText()); + + assertFalse(IBAN_invalid.isPresent()); + } + + @Test + void testFindFieldValueByAbsolutePathCamt() throws XMLStreamException { + + String xml_camt_053_001_12 = "\n" + "\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " FOOBAR22XXX\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " ABNABRSPBHE\n" + + " \n" + + " \n" + + " \n" + + " asdfasdsdd\n" + + " camt.053.001.12\n" + + " 2024-12-09T09:45:41-03:00\n" + + " 2024-12-09T09:45:41-03:00\n" + + "\n" + + "\n" + + " \n" + + " \n" + + " asdfasd\n" + + " 2024-12-09T09:36:46-03:00\n" + + " \n" + + " 1234\n" + + " false\n" + + " \n" + + " \n" + + " \n" + + " asdfasdf\n" + + " 2233\n" + + " 2445\n" + + " \n" + + " USD\n" + + " asdff\n" + + " \n" + + " \n" + + " TELE\n" + + " \n" + + " 23423\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " FWAV\n" + + " \n" + + " \n" + + " 2323\n" + + " CRDT\n" + + " \n" + + " 2024-12-10\n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n" + + ""; + + MxId id = MxParseUtils.identifyMessage(xml_camt_053_001_12).orElse(null); + assertNotNull(id); + assertEquals("camt.053.001.12", id.id()); + + Optional BICFI = + MxParseUtils.findElementByAbsolutePath(xml_camt_053_001_12, "/AppHdr/Fr/FIId/FinInstnId/BICFI"); + Optional bizPrcgDt = + MxParseUtils.findElementByAbsolutePath(xml_camt_053_001_12, "/AppHdr/BizPrcgDt"); + Optional pgNb = MxParseUtils.findElementByAbsolutePath( + xml_camt_053_001_12, "/Document/BkToCstmrStmt/GrpHdr/MsgPgntn/PgNb"); + Optional lastPgInd = MxParseUtils.findElementByAbsolutePath( + xml_camt_053_001_12, "/Document/BkToCstmrStmt/GrpHdr/MsgPgntn/LastPgInd"); + Optional IBAN_invalid = MxParseUtils.findElementByAbsolutePath( + xml_camt_053_001_12, "/Document/FIToFIPmtStsReq/TxInf/Id/Foo/IBAN"); + + assertTrue(BICFI.isPresent()); + assertEquals("FOOBAR22XXX", BICFI.get().getElementText()); + + assertTrue(bizPrcgDt.isPresent()); + assertEquals("2024-12-09T09:45:41-03:00", bizPrcgDt.get().getElementText()); + + assertTrue(pgNb.isPresent()); + assertEquals("1234", pgNb.get().getElementText()); + + assertTrue(lastPgInd.isPresent()); + assertEquals("false", lastPgInd.get().getElementText()); + + assertFalse(IBAN_invalid.isPresent()); + } } diff --git a/iso20022-core/src/test/java/com/prowidesoftware/swift/model/mx/MxWriteTest.java b/iso20022-core/src/test/java/com/prowidesoftware/swift/model/mx/MxWriteTest.java index f8ac6d94b..e6ab25b57 100644 --- a/iso20022-core/src/test/java/com/prowidesoftware/swift/model/mx/MxWriteTest.java +++ b/iso20022-core/src/test/java/com/prowidesoftware/swift/model/mx/MxWriteTest.java @@ -21,10 +21,13 @@ import java.math.BigDecimal; import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.OS; /** * General MX into XML serialization test cases */ +@DisabledOnOs(OS.WINDOWS) public class MxWriteTest { @Test