diff --git a/.gitignore b/.gitignore
index eaab63fe8..6c06de9f5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,7 +28,5 @@ release.sh
.idea/runConfigurations.xml
.idea/.name
*.pb
-.idea/sonarlint/issuestore/
-.idea/sonarlint/issuestore/
-.idea/sonarlint/issuestore/
.java-version
+.idea/sonarlint/
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index eabbc57ad..ac8e4afa0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
# Prowide Core - CHANGELOG
+#### 10.1.12 - November 2023
+ * (PW-1697) Fixed validation/parse pattern in field 29O
+ * (PW-1697) MT306 changes in field 30I
+ * Added DistinguishedName with Builder in order to encapsulate the BIC branch name logic
+
#### 10.1.11 - November 2023
* (PW-1697) Fixed validation pattern in fields 14[H,K,L,M,N,O] and 29J
diff --git a/src/generated/java/com/prowidesoftware/swift/model/field/Field29O.java b/src/generated/java/com/prowidesoftware/swift/model/field/Field29O.java
index bb3d39e5e..2282e6221 100644
--- a/src/generated/java/com/prowidesoftware/swift/model/field/Field29O.java
+++ b/src/generated/java/com/prowidesoftware/swift/model/field/Field29O.java
@@ -55,7 +55,7 @@
*
Structure definition
*
* - validation pattern:
4!c/<HHMM><HHMM>
- * - parser pattern:
S/S
+ * - parser pattern:
S/<HHMM><HHMM>
* - components pattern:
SHH
*
*
@@ -85,7 +85,7 @@ public class Field29O extends Field implements Serializable {
*/
@Deprecated
@ProwideDeprecated(phase4 = TargetYear.SRU2024)
- public static final String PARSER_PATTERN = "S/S";
+ public static final String PARSER_PATTERN = "S/";
/**
* @deprecated Use {@link #typesPattern()} method instead.
@@ -193,8 +193,19 @@ public static Tag emptyTag() {
@Override
public void parse(final String value) {
init(3);
- setComponent1(SwiftParseUtils.getTokenFirst(value, "/"));
- setComponent2(SwiftParseUtils.getTokenSecondLast(value, "/"));
+ if (value != null) {
+ setComponent1(SwiftParseUtils.getTokenFirst(value, "/"));
+ String toparse = SwiftParseUtils.getTokenSecondLast(value, "/");
+ if (toparse != null) {
+ if (toparse.length() >= 1) {
+ int endIndex = Math.min(4, toparse.length());
+ setComponent2(StringUtils.substring(toparse, 0, endIndex));
+ }
+ if (toparse.length() > 4) {
+ setComponent3(StringUtils.substring(toparse, 4));
+ }
+ }
+ }
}
/**
@@ -206,6 +217,7 @@ public String getValue() {
append(result, 1);
result.append("/");
append(result, 2);
+ append(result, 3);
return result.toString();
}
@@ -273,7 +285,7 @@ public String typesPattern() {
*/
@Override
public String parserPattern() {
- return "S/S";
+ return "S/";
}
/**
diff --git a/src/generated/java/com/prowidesoftware/swift/model/field/Field30I.java b/src/generated/java/com/prowidesoftware/swift/model/field/Field30I.java
index 48987a2ad..d018f2c3b 100644
--- a/src/generated/java/com/prowidesoftware/swift/model/field/Field30I.java
+++ b/src/generated/java/com/prowidesoftware/swift/model/field/Field30I.java
@@ -15,32 +15,19 @@
*/
package com.prowidesoftware.swift.model.field;
-import com.prowidesoftware.swift.model.Tag;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
import com.prowidesoftware.Generated;
import com.prowidesoftware.deprecation.ProwideDeprecated;
import com.prowidesoftware.deprecation.TargetYear;
-
-import java.io.Serializable;
-import java.util.Locale;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.HashMap;
-
-import java.util.Calendar;
-
-import com.prowidesoftware.swift.model.field.DateContainer;
-import com.prowidesoftware.swift.model.field.DateResolver;
-
-import org.apache.commons.lang3.StringUtils;
-
-import com.prowidesoftware.swift.model.field.SwiftParseUtils;
-import com.prowidesoftware.swift.model.field.Field;
-import com.prowidesoftware.swift.model.*;
+import com.prowidesoftware.swift.model.SwiftMessage;
+import com.prowidesoftware.swift.model.SwiftTagListBlock;
+import com.prowidesoftware.swift.model.Tag;
import com.prowidesoftware.swift.utils.SwiftFormatUtils;
+import org.apache.commons.lang3.StringUtils;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
+import java.io.Serializable;
+import java.util.*;
/**
* SWIFT MT Field 30I.
@@ -49,7 +36,7 @@
*
* Subfields (components) Data types
*
- * - Component 1: DateorStartDate:
Calendar
+ * - Component 1: StartDate:
Calendar
* - Component 2: EndDate:
Calendar
*
*
@@ -103,9 +90,16 @@ public class Field30I extends Field implements Serializable, DateContainer {
public static final String TYPES_PATTERN = "DD";
/**
- * Component number for the Date or Start Date subfield.
+ * Component number for the Start Date subfield.
*/
- public static final Integer DATE_OR_START_DATE = 1;
+ public static final Integer START_DATE = 1;
+
+ /**
+ * @deprecated use #START_DATE instead
+ */
+ @Deprecated
+ @ProwideDeprecated(phase4 = TargetYear.SRU2024)
+ public static final Integer DATE_OR_START_DATE = 1;
/**
* Component number for the End Date subfield.
@@ -322,7 +316,7 @@ public int componentsSize() {
@Override
public List getComponentLabels() {
List result = new ArrayList<>();
- result.add("Date or Start Date");
+ result.add("Start Date");
result.add("End Date");
return result;
}
@@ -334,7 +328,7 @@ public List getComponentLabels() {
@Override
protected Map getComponentMap() {
Map result = new HashMap<>();
- result.put(1, "dateorStartDate");
+ result.put(1, "startDate");
result.put(2, "endDate");
return result;
}
@@ -350,13 +344,15 @@ protected Map getLabelMap() {
return super.labelMap;
}
super.labelMap = new HashMap<>();
+ super.labelMap.put("startdate", 1);
+ // alias name
super.labelMap.put("dateorstartdate", 1);
super.labelMap.put("enddate", 2);
return super.labelMap;
}
/**
- * Gets the component 1 (Date or Start Date).
+ * Gets the component 1 (Start Date).
* @return the component 1
*/
public String getComponent1() {
@@ -373,21 +369,41 @@ public java.util.Calendar getComponent1AsCalendar() {
}
/**
- * Gets the Date or Start Date (component 1).
- * @return the Date or Start Date from component 1
+ * Gets the Start Date (component 1).
+ * @return the Start Date from component 1
*/
- public String getDateorStartDate() {
+ public String getStartDate() {
return getComponent1();
}
/**
- * Get the Date or Start Date (component 1) as Calendar
- * @return the Date or Start Date from component 1 converted to Calendar or null if cannot be converted
+ * Alternative DEPRECATED method getter for field's Start Date
+ * @deprecated use #getStartDate() instead
+ * @since 9.2.7
*/
- public java.util.Calendar getDateorStartDateAsCalendar() {
+ @Deprecated
+ @ProwideDeprecated(phase4 = TargetYear.SRU2024)
+ public String getDateorStartDate() {
+ return getStartDate();
+ }
+
+ /**
+ * Get the Start Date (component 1) as Calendar
+ * @return the Start Date from component 1 converted to Calendar or null if cannot be converted
+ */
+ public java.util.Calendar getStartDateAsCalendar() {
return getComponent1AsCalendar();
}
+ /**
+ * @deprecated use #getStartDateAsCalendar() instead
+ */
+ @Deprecated
+ @ProwideDeprecated(phase4 = TargetYear.SRU2024)
+ public java.util.Calendar getDateorStartDateAsCalendar() {
+ return getStartDateAsCalendar();
+ }
+
/**
* Gets the component 2 (End Date).
* @return the component 2
@@ -422,9 +438,9 @@ public java.util.Calendar getEndDateAsCalendar() {
}
/**
- * Set the component 1 (Date or Start Date).
+ * Set the component 1 (Start Date).
*
- * @param component1 the Date or Start Date to set
+ * @param component1 the Start Date to set
* @return the field object to enable build pattern
*/
public Field30I setComponent1(String component1) {
@@ -435,7 +451,7 @@ public Field30I setComponent1(String component1) {
/**
* Set the component1 from a Calendar object.
*
- * @param component1 the Calendar with the Date or Start Date content to set
+ * @param component1 the Calendar with the Start Date content to set
* @return the field object to enable build pattern
*/
public Field30I setComponent1(java.util.Calendar component1) {
@@ -444,27 +460,45 @@ public Field30I setComponent1(java.util.Calendar component1) {
}
/**
- * Set the Date or Start Date (component 1).
+ * Set the Start Date (component 1).
*
- * @param component1 the Date or Start Date to set
+ * @param component1 the Start Date to set
* @return the field object to enable build pattern
*/
- public Field30I setDateorStartDate(String component1) {
+ public Field30I setStartDate(String component1) {
return setComponent1(component1);
}
/**
- * Set the Date or Start Date (component 1) from a Calendar object.
+ * Set the Start Date (component 1) from a Calendar object.
*
* @see #setComponent1(java.util.Calendar)
*
- * @param component1 Calendar with the Date or Start Date content to set
+ * @param component1 Calendar with the Start Date content to set
* @return the field object to enable build pattern
*/
- public Field30I setDateorStartDate(java.util.Calendar component1) {
+ public Field30I setStartDate(java.util.Calendar component1) {
return setComponent1(component1);
}
+ /**
+ * @deprecated use #setStartDate(String) instead
+ */
+ @Deprecated
+ @ProwideDeprecated(phase4 = TargetYear.SRU2024)
+ public Field30I setDateorStartDate(String component1) {
+ return setStartDate(component1);
+ }
+
+ /**
+ * @deprecated use #setComponent1(java.util.Calendar) instead
+ */
+ @Deprecated
+ @ProwideDeprecated(phase4 = TargetYear.SRU2024)
+ public Field30I setDateorStartDate(java.util.Calendar component1) {
+ return setStartDate(component1);
+ }
+
/**
* Set the component 2 (End Date).
*
@@ -613,12 +647,18 @@ public static Field30I fromJson(final String json) {
final JsonObject jsonObject = JsonParser.parseString(json).getAsJsonObject();
- // **** COMPONENT 1 - Date or Start Date
+ // **** COMPONENT 1 - Start Date
+ // first try using alias's names (including deprecated ones, if any)
if (jsonObject.get("dateorStartDate") != null) {
field.setComponent1(jsonObject.get("dateorStartDate").getAsString());
}
+ // last try using the official component's name (overwrites alternatives and DEPRECATED)
+ if (jsonObject.get("startDate") != null) {
+ field.setComponent1(jsonObject.get("startDate").getAsString());
+ }
+
// **** COMPONENT 2 - End Date
if (jsonObject.get("endDate") != null) {
diff --git a/src/main/java/com/prowidesoftware/swift/model/BIC.java b/src/main/java/com/prowidesoftware/swift/model/BIC.java
index 993765fda..a9bff6b6e 100644
--- a/src/main/java/com/prowidesoftware/swift/model/BIC.java
+++ b/src/main/java/com/prowidesoftware/swift/model/BIC.java
@@ -356,14 +356,11 @@ public String distinguishedName() {
* @since 9.3.15
*/
public String distinguishedName(boolean includeDefaultBranch) {
- StringBuilder result = new StringBuilder();
+ DistinguishedName.Builder dnBuilder = new DistinguishedName.Builder(getBic8());
if (includeDefaultBranch || !Objects.equals(getBranchOrDefault(), "XXX")) {
- result.append("ou=")
- .append(StringUtils.lowerCase(getBranchOrDefault()))
- .append(",");
+ dnBuilder.withBranch(getBranchOrDefault());
}
- result.append("o=").append(StringUtils.lowerCase(getBic8())).append(",o=swift");
- return result.toString();
+ return dnBuilder.build().toString();
}
@Override
diff --git a/src/main/java/com/prowidesoftware/swift/model/DistinguishedName.java b/src/main/java/com/prowidesoftware/swift/model/DistinguishedName.java
new file mode 100644
index 000000000..e2974bc6e
--- /dev/null
+++ b/src/main/java/com/prowidesoftware/swift/model/DistinguishedName.java
@@ -0,0 +1,94 @@
+package com.prowidesoftware.swift.model;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * Represents a Distinguished Name (DN) in the context of a directory service.
+ * The DN is constructed using the organization unit (ou), Bank Identifier Code (BIC8), and SWIFT code.
+ * This class provides a builder pattern for creating DistinguishedName instances.
+ */
+public class DistinguishedName {
+
+ /**
+ * The organization unit (ou) representing the branch in the Distinguished Name.
+ */
+ private final String branch;
+
+ /**
+ * The Bank Identifier Code (BIC8) in lowercase format.
+ */
+ private final String bic8;
+
+ /**
+ * The SWIFT code representing the organization.
+ */
+ private final String swift;
+
+ /**
+ * Private constructor to create a DistinguishedName instance using the Builder.
+ *
+ * @param builder The builder containing the required parameters for the DistinguishedName.
+ */
+ private DistinguishedName(Builder builder) {
+ this.branch = builder.branch;
+ this.bic8 = builder.bic8;
+ this.swift = builder.swift;
+ }
+
+ public String getBranch() {
+ return branch;
+ }
+
+ public String getBic8() {
+ return bic8;
+ }
+
+ public String getSwift() {
+ return swift;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder dn = new StringBuilder();
+ if (branch != null && !branch.isEmpty()) {
+ dn.append("ou=").append(branch).append(",");
+ }
+ dn.append("o=").append(bic8).append(",o=").append(swift);
+ return dn.toString();
+ }
+
+ public static String parseBIC(final String dn) {
+ if (StringUtils.isBlank(dn)) {
+ return null;
+ }
+ for (String s : StringUtils.split(dn, ",")) {
+ if (StringUtils.startsWith(s, "o=") && !StringUtils.equals(s, "o=swift")) {
+ return StringUtils.upperCase(StringUtils.substringAfter(s, "o="));
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Builder class for constructing DistinguishedName instances.
+ */
+ public static class Builder {
+ private String branch;
+ private final String bic8;
+ private final String swift;
+
+ public Builder(String bic8) {
+ this.bic8 = StringUtils.lowerCase(bic8);
+ this.swift = "swift";
+ }
+
+ public Builder withBranch(String ou) {
+ this.branch = StringUtils.lowerCase(ou);
+ return this;
+ }
+
+ public DistinguishedName build() {
+ return new DistinguishedName(this);
+ }
+ }
+}
diff --git a/src/test/java/com/prowidesoftware/swift/model/DistinguishedNameTest.java b/src/test/java/com/prowidesoftware/swift/model/DistinguishedNameTest.java
new file mode 100644
index 000000000..821791651
--- /dev/null
+++ b/src/test/java/com/prowidesoftware/swift/model/DistinguishedNameTest.java
@@ -0,0 +1,60 @@
+package com.prowidesoftware.swift.model;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import org.junit.jupiter.api.Test;
+
+class DistinguishedNameTest {
+
+ @Test
+ void testDistinguishedNameWithCommonNameAndOU() {
+ String bic8 = "BIC8CODE";
+ String ou = "FOO";
+ DistinguishedName dn =
+ new DistinguishedName.Builder(bic8).withBranch(ou).build();
+ assertEquals("ou=foo,o=bic8code,o=swift", dn.toString());
+ }
+
+ @Test
+ void testDistinguishedNameWithOUOnly() {
+ String bic8 = "bic8code";
+ String ou = "bar";
+ DistinguishedName dn =
+ new DistinguishedName.Builder(bic8).withBranch(ou).build();
+ assertEquals("ou=bar,o=bic8code,o=swift", dn.toString());
+ }
+
+ @Test
+ void testDistinguishedNameWithNoOUOrCN() {
+ String bic8 = "BIC8CODE";
+ DistinguishedName dn = new DistinguishedName.Builder(bic8).build();
+ assertEquals("o=bic8code,o=swift", dn.toString());
+ }
+
+ @Test
+ void testDistinguishedNameEmptyValues() {
+ String bic8 = "BIC8CODE";
+ DistinguishedName dn =
+ new DistinguishedName.Builder(bic8).withBranch("").build();
+ assertEquals("o=bic8code,o=swift", dn.toString());
+ }
+
+ @Test
+ void testParseBIC() {
+ String dn1 = "cn=John Doe,o=swift,ou=users,dc=example,dc=com";
+ assertNull(DistinguishedName.parseBIC(dn1));
+
+ dn1 = "o=bic8code,o=swift";
+ assertEquals("BIC8CODE", DistinguishedName.parseBIC(dn1));
+
+ dn1 = "ou=bar,o=bic8code,o=swift";
+ assertEquals("BIC8CODE", DistinguishedName.parseBIC(dn1));
+
+ assertNull(DistinguishedName.parseBIC(null));
+
+ assertNull(DistinguishedName.parseBIC(""));
+
+ assertNull(DistinguishedName.parseBIC("XXX"));
+ }
+}
diff --git a/src/test/java/com/prowidesoftware/swift/model/field/Field29OTest.java b/src/test/java/com/prowidesoftware/swift/model/field/Field29OTest.java
new file mode 100644
index 000000000..0832e8f8b
--- /dev/null
+++ b/src/test/java/com/prowidesoftware/swift/model/field/Field29OTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test for Field29O and similar fields.
+ *
+ * @since 9.4.13
+ */
+public class Field29OTest extends AbstractFieldTest {
+
+ @Override
+ @Test
+ public void testSerialization() {
+ testSerializationImpl("29O", "ABCD/10231045");
+ }
+
+ /**
+ * 4!c/
+ */
+ @Test
+ public void testField29O() {
+ Field29O f = new Field29O((String) null);
+ assertNull(f.getComponent1());
+ assertNull(f.getComponent2());
+ assertNull(f.getComponent3());
+
+ f = new Field29O("");
+ assertNull(f.getComponent1());
+ assertNull(f.getComponent2());
+ assertNull(f.getComponent3());
+
+ f = new Field29O("A");
+ assertEquals("A", f.getComponent1());
+ assertNull(f.getComponent2());
+ assertNull(f.getComponent3());
+
+ f = new Field29O("XCVB/");
+ assertEquals("XCVB", f.getComponent1());
+ assertNull(f.getComponent2());
+ assertNull(f.getComponent3());
+
+ f = new Field29O("XCVB/12");
+ assertEquals("XCVB", f.getComponent1());
+ assertEquals("12", f.getComponent2());
+ assertNull(f.getComponent3());
+
+ f = new Field29O("XCVB/1212");
+ assertEquals("XCVB", f.getComponent1());
+ assertEquals("1212", f.getComponent2());
+ assertNull(f.getComponent3());
+
+ f = new Field29O("XCVB/121210");
+ assertEquals("XCVB", f.getComponent1());
+ assertEquals("1212", f.getComponent2());
+ assertEquals("10", f.getComponent3());
+
+ f = new Field29O("XCVB/12121011");
+ assertEquals("XCVB", f.getComponent1());
+ assertEquals("1212", f.getComponent2());
+ assertEquals("1011", f.getComponent3());
+
+ f = new Field29O("XCVB/1212101122");
+ assertEquals("XCVB", f.getComponent1());
+ assertEquals("1212", f.getComponent2());
+ assertEquals("101122", f.getComponent3());
+ }
+}
diff --git a/src/test/java/com/prowidesoftware/swift/model/field/Field30ITest.java b/src/test/java/com/prowidesoftware/swift/model/field/Field30ITest.java
new file mode 100644
index 000000000..00034e3f7
--- /dev/null
+++ b/src/test/java/com/prowidesoftware/swift/model/field/Field30ITest.java
@@ -0,0 +1,81 @@
+/*
+ * 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;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test for Field30I and similar fields.
+ *
+ * @since 9.4.13
+ */
+class Field30ITest extends AbstractFieldTest {
+
+ @Override
+ @Test
+ public void testSerialization() {
+ testSerializationImpl("30I", "20231027", "20231027/20240426");
+ }
+
+ /**
+ * S[/S]
+ */
+ @Test
+ void testField30I() {
+ Field30I f = new Field30I((String) null);
+ assertNull(f.getComponent1());
+ assertNull(f.getComponent2());
+
+ f = new Field30I("");
+ assertNull(f.getComponent1());
+ assertNull(f.getComponent2());
+
+ f = new Field30I("2023");
+ assertEquals("2023", f.getComponent1());
+ assertNull(f.getComponent2());
+
+ f = new Field30I("20231027");
+ assertEquals("20231027", f.getComponent1());
+ assertNull(f.getComponent2());
+
+ f = new Field30I("2023102711");
+ assertEquals("2023102711", f.getComponent1());
+ assertNull(f.getComponent2());
+
+ f = new Field30I("20231027/");
+ assertEquals("20231027", f.getComponent1());
+ assertNull(f.getComponent2());
+
+ f = new Field30I("20231027/12");
+ assertEquals("20231027", f.getComponent1());
+ assertEquals("12", f.getComponent2());
+
+ f = new Field30I("20231027/20240426");
+ assertEquals("20231027", f.getComponent1());
+ assertEquals("20240426", f.getComponent2());
+
+ f = new Field30I("20231027/2023102712");
+ assertEquals("20231027", f.getComponent1());
+ assertEquals("2023102712", f.getComponent2());
+
+ f = new Field30I("2023102711/2023102712");
+ assertEquals("2023102711", f.getComponent1());
+ assertEquals("2023102712", f.getComponent2());
+ }
+}