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 *

* @@ -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 *

    - *
  1. Component 1: DateorStartDate: Calendar
  2. + *
  3. Component 1: StartDate: Calendar
  4. *
  5. Component 2: EndDate: Calendar
  6. *
* @@ -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()); + } +}