Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into SRU2024
Browse files Browse the repository at this point in the history
  • Loading branch information
zubri committed May 18, 2024
2 parents 909b517 + cb01bcf commit 51a91c0
Show file tree
Hide file tree
Showing 11 changed files with 192 additions and 19 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
* SWIFT Standard release update 2024 (live 16 November 2025)
* Yearly revision of deprecation phase (see https://dev.prowidesoftware.com/SRU2024/getting-started/deprecation/)

#### 9.4.16 - May 2024
* (PW-1862) Added NarrativeFragment class for detailed line information in StructuredNarrative fragments
* Fixed SwiftMessage getPDE(): return empty value instead of null when codeword exists and has no value
* Added isPercentage() helper method to field 37K

#### 9.4.15 - March 2024
* (PW-1812) Updated the narrative resolver, format 2 (used in field 72 for example), to allow empty values as part of the narrative fragment
* Updated validators for BIC, country, and currency constraints to utilize keywords for i18n-compatible messages
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public SwiftBlock2 deserialize(
&& jsonObject.get(DIRECTION).getAsString().equals("O")) {
return getSwiftBlock2OutputObject(jsonObject);
} else {
// defult to INPUT
// default to INPUT
return getSwiftBlock2InputObject(jsonObject);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1219,7 +1219,7 @@ public String getPDE() {
if (this.block5 != null) {
Optional<Tag> t = this.block5.getTag(SwiftBlock5Field.PDE);
if (t.isPresent()) {
return t.get().getValue();
return t.get().getValue() != null ? t.get().getValue() : "";
}
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ public Field37P setComponent1(java.math.BigDecimal component1) {
return this;
}
/**
* Alternative method setter for field's Rate (component 1) as as Number
* Alternative method setter for field's Rate (component 1) as Number
*
* This method supports java constant value boxing for simpler coding styles (ex: 10.0 becomes an Float)
*
Expand Down Expand Up @@ -393,7 +393,7 @@ public Field37P setRate(java.math.BigDecimal component1) {
}

/**
* Alternative method setter for field's Rate (component 1) as as Number
* Alternative method setter for field's Rate (component 1) as Number
*
* This method supports java constant value boxing for simpler coding styles (ex: 10 becomes an Integer)
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ public Field37U setComponent1(java.math.BigDecimal component1) {
return this;
}
/**
* Alternative method setter for field's Rate (component 1) as as Number
* Alternative method setter for field's Rate (component 1) as Number
*
* This method supports java constant value boxing for simpler coding styles (ex: 10.0 becomes an Float)
*
Expand Down Expand Up @@ -393,7 +393,7 @@ public Field37U setRate(java.math.BigDecimal component1) {
}

/**
* Alternative method setter for field's Rate (component 1) as as Number
* Alternative method setter for field's Rate (component 1) as Number
*
* This method supports java constant value boxing for simpler coding styles (ex: 10 becomes an Integer)
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright 2006-2023 Prowide
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.prowidesoftware.swift.model.field;

/**
* Simple POJO for a fragment in a StructuredNarrative.
*
* <p>This model contains the narrative text, the line index (1 based) and the line length.
*
* <p>It is used by the {@link StructuredNarrative} class to get additional information of each line in the narrative.
*
* @since 9.4.16
*/
public class NarrativeFragment {
private final String text;
private final int lineIndex;
private final int lineLength;

/**
* Creates a new fragment without line index or length.
*
* @param text narrative line text
*/
public NarrativeFragment(final String text) {
this(text, 0, 0);
}

/**
* Creates a new fragment.
*
* @param text narrative line text
* @param lineIndex complete narrative line index
* @param lineLength complete line length
*/
public NarrativeFragment(final String text, final int lineIndex, final int lineLength) {
this.text = text;
this.lineIndex = lineIndex;
this.lineLength = lineLength;
}

public String getText() {
return text;
}

/**
* This is the 1-based index of the line in the complete narrative. Thus, regardless of the codeword position and
* on the number of line continuations, this index reflects this particular fragment position in the original
* field value before parsing. And it can be used for example to know if the fragment was located in the first
* line of the field value.
*
* @return 1-based index of the line this fragment belongs to, in the complete field value
*/
public int getLineIndex() {
return lineIndex;
}

/**
* This is the length of the complete line in the original field value before parsing. Thus, this number could
* contain for example the length of the codeword and slash separators before the actual narrative fragment, or
* when it is not the first fragment for a given codeword this could contain the length of the double slash used
* as continuation indicator. All in all, this value will be at least the size of the fragment, and in most cases
* it will be more. The purpose of this value is to provide a hint of the original line length, which could be used
* when reassembling the complete narrative for a codeword.
*
* @return the length of the complete line in the original field value before parsing
*/
public int getLineLength() {
return lineLength;
}

public String toString() {
return text;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,11 @@ private static Narrative parseFormat(
StructuredNarrative structured = new StructuredNarrative();
boolean firstSupplementAdded = false;
List<String> valueLines = notEmptyLines(value);
int lineIndex = 0;
for (String valueLine : valueLines) {
lineIndex++;
final int lineLength = valueLine.length();

if (unstructuredSection) {
narrative.addUnstructuredFragment(valueLine);
continue;
Expand All @@ -123,9 +127,9 @@ private static Narrative parseFormat(
if (supportsSupplement) {
firstSupplementAdded = addNarrativeSupplement(firstSupplementAdded, valueLine, structured);
} else if (StringUtils.isNotEmpty(valueLine)) {
structured.addNarrativeFragment(valueLine);
structured.addNarrativeFragment(valueLine, lineIndex, lineLength);
}
} else structured.addNarrativeFragment(valueLine);
} else structured.addNarrativeFragment(valueLine, lineIndex, lineLength);
} else {
// new codeword
String codeword = StringUtils.substringBetween(valueLine, "/", "/");
Expand Down Expand Up @@ -172,21 +176,23 @@ private static Narrative parseFormat(
if (supportsCountry) {
if (!textWithoutBankCode.isEmpty()) {
structured.addNarrativeFragment(
textWithoutBankCode); // structured.addNarrativeFragment(null);
textWithoutBankCode,
lineIndex,
lineLength); // structured.addNarrativeFragment(null);
}
} else {
structured.addNarrativeFragment(textWithoutBankCode);
structured.addNarrativeFragment(textWithoutBankCode, lineIndex, lineLength);
}
}

narrative.add(structured);
} else if (!additionalNarrativesStartWithDoubleSlash && !structured.isEmpty()) {
structured.addNarrativeFragment(valueLine);
structured.addNarrativeFragment(valueLine, lineIndex, lineLength);
unstructuredSection = false;
}
}
} else if (!additionalNarrativesStartWithDoubleSlash && !structured.isEmpty()) {
structured.addNarrativeFragment(valueLine);
structured.addNarrativeFragment(valueLine, lineIndex, lineLength);
unstructuredSection = false;
}
if (unstructuredSection) narrative.addUnstructuredFragment(valueLine);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@
*/
public class StructuredNarrative {
private final List<String> narrativeFragments = new ArrayList<>();
private final transient List<NarrativeFragment> narrativeFragmentsDetail = new ArrayList<>();
private final List<String> narrativeSupplementFragments = new ArrayList<>();
private String codeword;
private String currency;
private BigDecimal amount;
private String country;

private String bankCode;

/**
Expand Down Expand Up @@ -136,10 +136,34 @@ public List<String> getNarrativeFragments() {
}

/**
* @see #getNarrativeFragments()
* Returns the list of text fragments in the narrative including the line index and line length,
* fragments are segments of the text that is wrapped in lines.
* If the narrative is a single string in a single line, the list will contain a single element.
* To get the narrative as a simple joined string use {@link #getNarrative(String)}
*
* @return the narrative fragments or an empty list if the narrative does not have any text
*/
public List<NarrativeFragment> getNarrativeFragmentsDetail() {
return narrativeFragmentsDetail;
}

/**
* @see #addNarrativeFragment(String, int, int)
*/
StructuredNarrative addNarrativeFragment(String narrativeFragment) {
return addNarrativeFragment(narrativeFragment, 0, 0);
}

/**
* Adds a fragment indicating the line index (1 based) and the line length.
*
* @param narrativeFragment text of the fragment
* @param lineIndex complete narrative line index
* @param lineLength complete line length
*/
StructuredNarrative addNarrativeFragment(String narrativeFragment, int lineIndex, int lineLength) {
this.narrativeFragments.add(narrativeFragment);
this.narrativeFragmentsDetail.add(new NarrativeFragment(narrativeFragment, lineIndex, lineLength));
return this;
}

Expand Down Expand Up @@ -183,7 +207,7 @@ public String getNarrative() {
public String getNarrative(String delimiter) {
if (!this.narrativeFragments.isEmpty()) {
String s = delimiter != null ? delimiter : "";
return String.join(s, this.narrativeFragments);
return String.join(s, this.getNarrativeFragments());
}
return null;
}
Expand Down Expand Up @@ -216,7 +240,7 @@ public String getNarrativeSupplement(String delimiter) {
}

/**
* @return true if non of the narrative fields are set
* @return true if none of the narrative fields are set
*/
public boolean isEmpty() {
return this.codeword == null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,7 @@ protected Tag[] tags(final String tagName) {
public String toJson() {
final Gson gson = new GsonBuilder()
.registerTypeAdapter(AbstractMT.class, new AbstractMTAdapter())
.registerTypeAdapter(SwiftBlock2.class, new SwiftBlock2Adapter())
.setPrettyPrinting()
.create();
return gson.toJson(this, AbstractMT.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,56 @@ public void testFormat2_12() {
n.getStructured("ACC").getNarrativeFragments().get(1));
}

/**
* valid input
*/
@Test
public void testFormat2_13() {
String v = "/BNF/RETN\n"
+ "//THE TRANSACTION IS REJECTED DUE T\n"
+ "//O INTERNAL POLICY\n"
+ "/MREF/XXX55444/220424\n"
+ "/TEXT/WE CONSIDER YR MT103 AS NULL\n"
+ "//AND VOID\n";
Narrative n = NarrativeResolver.parse(new Field72(v));

assertNull(n.getUnstructured());

assertEquals(3, n.getStructured().size()); // This count only the CodeWords

StructuredNarrative BNF = n.getStructured().get(0);
StructuredNarrative MREF = n.getStructured().get(1);
StructuredNarrative TEXT = n.getStructured().get(2);

assertEquals(3, BNF.getNarrativeFragmentsDetail().size());
assertEquals(1, MREF.getNarrativeFragmentsDetail().size());
assertEquals(2, TEXT.getNarrativeFragmentsDetail().size());

// "/BNF/RETN"
assertEquals(9, BNF.getNarrativeFragmentsDetail().get(0).getLineLength());
assertEquals(1, BNF.getNarrativeFragmentsDetail().get(0).getLineIndex());

// "//THE TRANSACTION IS REJECTED DUE T"
assertEquals(35, BNF.getNarrativeFragmentsDetail().get(1).getLineLength());
assertEquals(2, BNF.getNarrativeFragmentsDetail().get(1).getLineIndex());

// "//O INTERNAL POLICY"
assertEquals(19, BNF.getNarrativeFragmentsDetail().get(2).getLineLength());
assertEquals(3, BNF.getNarrativeFragmentsDetail().get(2).getLineIndex());

// "/MREF/XXX55444/220424"
assertEquals(21, MREF.getNarrativeFragmentsDetail().get(0).getLineLength());
assertEquals(4, MREF.getNarrativeFragmentsDetail().get(0).getLineIndex());

// "/TEXT/WE CONSIDER YR MT103 AS NULL"
assertEquals(34, TEXT.getNarrativeFragmentsDetail().get(0).getLineLength());
assertEquals(5, TEXT.getNarrativeFragmentsDetail().get(0).getLineIndex());

// "//AND VOID"
assertEquals(10, TEXT.getNarrativeFragmentsDetail().get(1).getLineLength());
assertEquals(6, TEXT.getNarrativeFragmentsDetail().get(1).getLineIndex());
}

/*
* FORMAT 3
* Line 1: /8c/[3!a13d][additional information] (Code)(Currency)(Amount)(Narrative)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class MT020Test {

private final String sample3 = "{1:F01VNDZBET2AXXX0000000000}{2:I020DYDYXXXXXXXXN}{4:"
+ "{102:VNDZBET2AXXX}"
+ "{253:050719MVNDZBET2AXXX0181000391}}";
+ "{253:050719VNDZBET2AXXX0181000391}}";

private final String sample4 = "{1:F01VNDZBET2AXXX0000000000}{2:I020DYDYXXXXXXXXN}{4:"
+ "{102:VNDZBET2AXXX}"
Expand Down Expand Up @@ -91,7 +91,7 @@ public void test_parse3() {
assertEquals("VNDZBET2AXXX", m.getField102().getValue());

assertNotNull(m.getField253());
assertEquals("050719MVNDZBET2AXXX0181000391", m.getField253().getValue());
assertEquals("050719VNDZBET2AXXX0181000391", m.getField253().getValue());
}

@Test
Expand Down Expand Up @@ -184,7 +184,7 @@ public void test_create3() {
m.setSender("VNDZBET2AXXX");
m.setReceiver("DYDYXXXXFXXX");
m.append(new Field102("VNDZBET2AXXX"));
m.append(new Field253("050719MVNDZBET2AXXX0181000391"));
m.append(new Field253("050719VNDZBET2AXXX0181000391"));

assertEquals(sample3, m.message());
}
Expand Down

0 comments on commit 51a91c0

Please sign in to comment.