com.prowidesoftware.swift.model.MxId Maven / Gradle / Ivy
/*
* 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;
import java.util.Objects;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
/**
* Class for identification of MX messages.
*
* It is composed by the business process (business area), functionality (message type), variant and version.
* For a better understanding of ISO 20022 variants check https://www.iso20022.org/variants page
*
* @since 7.7
*/
public class MxId {
private static final Pattern pattern = Pattern.compile(".*([a-zA-Z]{4}).(\\d{3}).(\\d{3}).(\\d{2}).*");
private MxBusinessProcess businessProcess;
private String functionality;
private String variant;
private String version;
/**
* @since 9.5.0
*/
private transient String businessService;
public MxId() {}
/**
* Creates a new object getting data from an MX message namespace.
*
The implementation parses the namespace using a regex to detect the message type part.
*
* @param namespace a complete or partial namespace such as "urn:iso:std:iso:20022:tech:xsd:pain.001.001.03" or just "pain.001.001.03"
* @throws IllegalArgumentException if namespace parameter cannot be parsed as MX identification
*/
public MxId(final String namespace) {
Objects.requireNonNull(namespace);
final Matcher matcher = pattern.matcher(namespace);
if (matcher.matches()) {
final String bpStr = matcher.group(1);
try {
this.businessProcess = MxBusinessProcess.valueOf(bpStr);
} catch (final Exception e) {
throw new IllegalArgumentException(
"Unrecognized value for business process: '" + bpStr + "' see enum values in "
+ MxBusinessProcess.class.getName() + " for expected options",
e);
}
this.functionality = matcher.group(2);
this.variant = matcher.group(3);
this.version = matcher.group(4);
} else {
throw new IllegalArgumentException("Could not parse namespace '" + namespace + "'");
}
}
public MxId(
final MxBusinessProcess businessProcess,
final String funString,
final String varString,
final String verString) {
this.businessProcess = businessProcess;
this.functionality = funString;
this.variant = varString;
this.version = verString;
}
public MxId(final String bpString, final String funString, final String varString, final String verString) {
this(MxBusinessProcess.valueOf(bpString), funString, varString, verString);
}
/**
* Gets the business process (a.k.a. business area)
*
* @return the business process set
*/
public MxBusinessProcess getBusinessProcess() {
return businessProcess;
}
public MxId setBusinessProcess(final MxBusinessProcess businessProcess) {
this.businessProcess = businessProcess;
return this;
}
/**
* Gets the functionality (a.k.a. message type)
*
* @return the functionality set
*/
public String getFunctionality() {
return functionality;
}
public MxId setFunctionality(final String functionality) {
this.functionality = functionality;
return this;
}
public String getVariant() {
return variant;
}
public MxId setVariant(final String variant) {
this.variant = variant;
return this;
}
public String getVersion() {
return version;
}
public MxId setVersion(final String version) {
this.version = version;
return this;
}
public String camelized() {
final StringBuilder sb = new StringBuilder();
if (businessProcess != null) {
sb.append(Character.toUpperCase(businessProcess.name().charAt(0)));
sb.append(businessProcess.name().substring(1));
}
if (functionality != null) {
sb.append(functionality);
}
if (variant != null) {
sb.append(variant);
}
if (version != null) {
sb.append(version);
}
return sb.toString();
}
public int getVersionInt() {
return Integer.valueOf(this.version);
}
public int getVariantInt() {
return Integer.valueOf(this.variant);
}
public int getFunctionalityInt() {
return Integer.valueOf(this.functionality);
}
/**
* Creates the corresponding ISO 20022 namespace URI for this MX, for example: urn:swift:xsd:camt.003.001.04
* All id attributes should be properly filled.
*
* @return a string representing the namespace URI for the MX or null if any of the attributes is not set
*/
public String namespaceURI() {
return new StringBuilder("urn:swift:xsd:").append(id()).toString();
}
/**
* Get a string in the form of businessprocess.functionality.variant.version
*
* @return a string with the MX message type identification or null if any of the properties is null
* @since 7.7
*/
public String id() {
final StringBuilder sb = new StringBuilder();
if (businessProcess != null) {
sb.append(businessProcess.name());
} else {
return null;
}
if (functionality != null) {
sb.append("." + functionality);
} else {
return null;
}
if (variant != null) {
sb.append("." + variant);
} else {
return null;
}
if (version != null) {
sb.append("." + version);
} else {
return null;
}
return sb.toString();
}
@Override
public String toString() {
return id();
}
/**
* Indicates whether some other object is "equal to" this one.
*
*
Overrides the default implementation of {@link Object#equals(Object)}.
* This method considers two {@code MxId} objects equal if their
* {@code businessProcess}, {@code functionality}, {@code variant},
* and {@code version} fields are equal.
*
*
Notice the business service field is not included in the comparison.
*
* @param o the reference object with which to compare
* @return {@code true} if this object is the same as the obj argument; {@code false} otherwise
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MxId mxId = (MxId) o;
return businessProcess == mxId.businessProcess
&& Objects.equals(functionality, mxId.functionality)
&& Objects.equals(variant, mxId.variant)
&& Objects.equals(version, mxId.version);
}
/**
* Returns a hash code value for the object.
*
*
Overrides the default implementation of {@link Object#hashCode()}.
* This method generates a hash code based on the {@code businessProcess},
* {@code functionality}, {@code variant}, and {@code version} fields.
*
*
Notice the business service field is not included in the hash code calculation.
*
* @return a hash code value for this object
*/
@Override
public int hashCode() {
return Objects.hash(businessProcess, functionality, variant, version);
}
/**
* Check if this identification matches the given namespace.
*
*
This is particularly useful if this identifier is not completely filled, for example: if the business process
* is set to "pain" and the functionality is set to "002" but the variant and version are left null, then this
* identifier will match any namespace containing pain.002.*.* where the wildcard could be any number.
*
new MxId("pain", "002", null, null).matches("pain.002.001.03") will be true.
*
* @param namespace a complete or partial namespace such as "urn:iso:std:iso:20022:tech:xsd:pain.001.001.03" or just "pain.001.001.03"
* @return true if this id matches the parameter
* @throws IllegalArgumentException if namespace parameter cannot be parsed as MX identification
* @since 7.10.7
*/
public boolean matches(String namespace) {
return matches(new MxId(namespace));
}
/**
* Check if this identification matches another one.
*
*
This is particularly useful if this identifier is not completely filled, for example: if the business process
* is set to "pain" and the functionality is set to "002" but the variant and version are left null, then this
* identifier will match for example both pain.002.001.03 and pain.002.002.04.
*
*
The difference between this implementation and {@link #equals(Object)} is that here null and empty properties
* are treated as equals. Meaning it is not sensible to null versus blank properties, thus pain.001.001.null will
* match pain.001.001.empty.
*
* @param other an identification to compare
* @return true if this id matches the parameter
* @throws IllegalArgumentException if namespace parameter cannot be parsed as MX identification
* @since 7.10.7
*/
public boolean matches(MxId other) {
return this.businessProcess == other.getBusinessProcess()
&& (StringUtils.isBlank(this.functionality)
|| StringUtils.isBlank(other.getFunctionality())
|| StringUtils.equals(this.functionality, other.getFunctionality()))
&& (StringUtils.isBlank(this.variant)
|| StringUtils.isBlank(other.getVariant())
|| StringUtils.equals(this.variant, other.getVariant()))
&& (StringUtils.isBlank(this.version)
|| StringUtils.isBlank(other.getVersion())
|| StringUtils.equals(this.version, other.getVersion()));
}
/**
* Get the category of the message, which is the business process name.
* @return the business process name or null if not set
* @since 9.5.0
*/
public String category() {
return this.businessProcess != null ? this.businessProcess.name() : null;
}
/**
* Get the optional business service, which could be an additional value to differentiate messages within the same
* message type. For example "swift.cbprplus.cov.02". Notice when the MxId is created from the namespace this value
* is not set. This value must be set manually by the application when the MxId is extracted from a whole message
* and the AppHdr is available. Alternatively, the application can set this value with any other context information
* and use it as a discriminator.
* @return the business service value or null if not set
* @since 9.5.0
*/
public Optional getBusinessService() {
return Optional.ofNullable(StringUtils.trimToNull(this.businessService));
}
/**
* Set the business service, which could be an additional value to differentiate messages within the same message
* type. This is mainly intended to contain the business services from the message AppHdr, but could potentially
* be used to hold any other value that helps to differentiate messages within the same message type.
* @param businessService a string value to set as discriminator, for example "swift.cbprplus.cov.02"
* @since 9.5.0
*/
public MxId setBusinessService(String businessService) {
this.businessService = businessService;
return this;
}
}