com.prowidesoftware.swift.model.SwiftBlock1 Maven / Gradle / Ivy
Show all versions of pw-swift-core Show documentation
/*
* 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 com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.prowidesoftware.swift.model.mt.ServiceIdType;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Objects;
import java.util.logging.Level;
import org.apache.commons.lang3.Validate;
/**
* Base class for SWIFT Basic Header Block (block 1).
* It contains information about the source of the message.
*
* The basic header block is fixed-length and continuous with
* no field delimiters. This class contains its
* elements as individual attributes for easier management
* of the block value.
* This block is mandatory for all SWIFT messages.
*
* @author sebastian
* @since 4.0
*/
// TODO: add parameter checks (Validate.*) and complete javadocs
public class SwiftBlock1 extends SwiftValueBlock implements Serializable {
/**
* Constant for FIN messages in application id
*
* @since 4.1
*/
public static final transient String APPLICATION_ID_FIN = "F";
/**
* Constant for GPA (General Purpose Application) messages in application id
*
* @since 4.1
*/
public static final transient String APPLICATION_ID_GPA = "A";
/**
* Constant for Logins and so messages in application id
*
* @since 4.1
*/
public static final transient String APPLICATION_ID_LOGINS = "L";
private static final transient java.util.logging.Logger log =
java.util.logging.Logger.getLogger(SwiftBlock1.class.getName());
private static final long serialVersionUID = 4229511645041690763L;
/**
* String of 1 character containing the Application ID as follows:
* F = FIN (financial application)
* A = GPA (general purpose application)
* L = GPA (for logins, and so on)
* This designates the application that has established the
* association used to convey the message. You always use F for FIN messages.
* It is set by default to F (FIN messages).
*/
private String applicationId = "F";
/**
* String of 2 characters containing Service ID as follows:
* 01 = GPA/FIN Message (system and user-to-user)
* 02 = GPA Login
* 03 = GPA Select
* 05 = FIN Quit
* 06 = GPA Logout
* 12 = GPA System Remove AP Request
* 13 = GPA System Abort AP Confirmation
* 14 = GPA System Remove LT Request
* 15 = GPA System Abort LT Confirmation
* 21 = GPA/FIN Message (ACK/NAK/UAK/UNK)
* 22 = GPA Login ACK (LAK)
* 23 = GPA Select ACK (SAK)
* 25 = FIN Quit ACK
* 26 = GPA Logout ACK
* 33 = GPA User Abort AP Request
* 35 = GPA User Abort LT Request
* 42 = GPA Login NAK (LNK)
* 43 = GPA Select NAK (SNK)
* It is set by default to 01 (FIN messages).
*/
private String serviceId = "01";
/**
* The Logical Terminal address of the sender for messages
* sent or the receiver for messages received from the
* SWIFT network.
* Identifies a logical channel connection to SWIFT, and the network uses it
* for addressing. It is composed by the BIC code, an optional terminal
* identifier (A, B or C) if the institution has more than one terminal or an X,
* and the branch code (padded with "X" if no branch is used).
* For example BFOOARBSAXXX or BFOOARBSXXXX.
* .
*/
private String logicalTerminal;
/**
* Session number. 4 characters. It is generated by the user's computer.
* As appropriate, the current application session number based
* on the Login. It is padded with zeros.
*/
private String sessionNumber = "0000";
/**
* Sequence number is a 6 characters string that is generated by the
* user's computer.
* For all FIN messages with a Service Identifier of 01 or 05,
* this number is the next expected sequence number appropriate to
* the direction of the transmission.
* For FIN messages with a Service Identifier of 21 or 25,
* the sequence number is that of the acknowledged service message.
* It is padded with zeros.
*/
private String sequenceNumber = "000000";
/**
* Constructor for specific values
*
* @param applicationId the application id
* @param serviceId the service id
* @param logicalTerminal the logical terminal name
* @param sessionNumber the session number
* @param sequenceNumber the message sequence number
*/
public SwiftBlock1(
final String applicationId,
final String serviceId,
final String logicalTerminal,
final String sessionNumber,
final String sequenceNumber) {
this.applicationId = applicationId;
this.serviceId = serviceId;
this.logicalTerminal = logicalTerminal;
this.sessionNumber = sessionNumber;
this.sequenceNumber = sequenceNumber;
}
/**
* Default constructor
*/
public SwiftBlock1() {}
/**
* Creates the block with lenient false, meaning it expects a fixed length value.
* Example of supported values:
* "F01BANKBEBBXXXX2222123456" or "1:F01BANKBEBBAXXX2222123456"
*
* @param value a fixed length string of 25 or 27 (which must start with '1:') characters containing the blocks value
* @throws IllegalArgumentException if parameter is not 25 or 27 characters
* @see #SwiftBlock1(String, boolean)
*/
public SwiftBlock1(final String value) {
this(value, false);
}
/**
* Creates a block 1 object setting attributes by parsing the fixed string argument;
*
* @param value string containing the entire blocks value
* @param lenient if true the value will be parsed with a best effort heuristic, if false it will throw a IllegalArgumentException if the value has an invalid total size
* @see #setValue(String, boolean)
* @since 7.7
*/
public SwiftBlock1(final String value, boolean lenient) {
this.setValue(value, lenient);
}
/**
* Copy constructor
*
* @param block an existing block1 to copy
* @since 7.10.4
*/
public SwiftBlock1(SwiftBlock1 block) {
this(
block.getApplicationId(),
block.getServiceId(),
block.getLogicalTerminal(),
block.getSessionNumber(),
block.getSequenceNumber());
}
/**
* This method deserializes the JSON data into an block 1 object.
* @param json json representation
* @return block 1 object
* @see #toJson()
* @since 7.9.8
*/
public static SwiftBlock1 fromJson(String json) {
final Gson gson = new GsonBuilder().create();
return gson.fromJson(json, SwiftBlock1.class);
}
/**
* Sets the block number.
*
* @param blockNumber the block number to set
* @throws IllegalArgumentException if parameter blockName is not the integer 1
* @since 5.0
*/
@Override
protected void setBlockNumber(final Integer blockNumber) {
// sanity check
Objects.requireNonNull(blockNumber, "parameter 'blockNumber' cannot be null");
Validate.isTrue(blockNumber == 1, "blockNumber must be 1");
}
/**
* Sets the block name. Will cause an exception unless setting block number to 1.
*
* @param blockName the block name to set
* @throws IllegalArgumentException if parameter blockName is not the string "1"
* @since 5.0
*/
@Override
protected void setBlockName(final String blockName) {
// sanity check
Objects.requireNonNull(blockName, "parameter 'blockName' cannot be null");
Validate.isTrue(blockName.compareTo("1") == 0, "blockName must be string '1'");
}
/**
* Returns the block number (the value 1 as an integer)
*
* @return Integer containing the block's number
*/
@Override
public Integer getNumber() {
return 1;
}
/**
* Returns the block name (the value 1 as a string)
*
* @return block name
* @since 5.0
*/
@Override
public String getName() {
return "1";
}
/**
* Gets the application ID field in block 1
*
* @return application ID field in block 1
*/
public String getApplicationId() {
return applicationId;
}
/**
* Sets the applicationId
*
* @param applicationId String of 1 character containing the Application ID (F, A or L)
*/
public void setApplicationId(final String applicationId) {
this.applicationId = applicationId;
}
/**
* Gets the service ID field in block 1
*
* @return service ID field in block 1
*/
public String getServiceId() {
return serviceId;
}
/**
* Sets the Service ID
*
* @param serviceId string of 2 characters containing Service ID (01, 02, 03, etc...)
*/
public void setServiceId(final String serviceId) {
this.serviceId = serviceId;
}
/**
* Sets the logical terminal address from the parameter BIC.
*
If the LT identifier is not provided, "A" will be set as default. If the branch code is not provided XXX will be used as default.
*
The implementation assumes the message is outgoing, and if the full logical terminal address is provided with an "X" as LT identifier, it wil be replaced by and "A".
*
* @param sender a BIC8, BIC11 or full 12 character length logical terminal address
* @see #setLogicalTerminal(LogicalTerminalAddress)
* @since 6.4
*/
public void setSender(final String sender) {
setLogicalTerminal(new LogicalTerminalAddress(sender));
}
/**
* Gets the BIC code from the LT address.
*
* @return the BIC object
* @since 7.6
*/
public BIC getBIC() {
return new BIC(this.logicalTerminal);
}
/**
* Gets the The Logical Terminal address of the sender for messages
* sent or the receiver for messages received from the SWIFT network.
*
* @return the 12 characters logical terminal address
*/
public String getLogicalTerminal() {
return logicalTerminal;
}
/**
* Sets the The Logical Terminal address with the parameter as it is given without any modification.
*
*
Beware for an outgoing message the LT identifier cannot be X and the branch code must be padded with XXX if not present in
* the BIC address. The complete logical terminal address must always be a 12 characters length alphanumeric string
*
* @param logicalTerminal should be a fixed at 12 character length string; with the BIC address, LT identifier and branch code.
*/
public void setLogicalTerminal(final String logicalTerminal) {
this.logicalTerminal = logicalTerminal;
}
/**
* Sets the LT address.
*
*
The implementation assumes the message is outgoing, and will tamper the LT identifier if necessary (changing an "X" LT identifier by and "A").
*
* @param logicalTerminal the logical terminal address to set
* @see LogicalTerminalAddress#getSenderLogicalTerminalAddress()
* @since 7.6
*/
public void setLogicalTerminal(final LogicalTerminalAddress logicalTerminal) {
this.logicalTerminal = logicalTerminal.getSenderLogicalTerminalAddress();
}
/**
* Sets the logical terminal address from the parameter BIC code with "A" as default LT identifier and XXX as default branch code.
*
* @param bic a BIC code
* @see #setLogicalTerminal(LogicalTerminalAddress)
* @since 7.6
*/
public void setLogicalTerminal(final BIC bic) {
setLogicalTerminal(new LogicalTerminalAddress(bic.getBic11()));
}
/**
* Gets the session number in block 1
*
* @return session number in block 1
*/
public String getSessionNumber() {
return sessionNumber;
}
/**
* Sets the Session number. It is generated by the user's computer.
* As appropriate, the current application session number based
* on the Login. It is padded with zeros.
*
* @param sessionNumber 4 numbers.
*/
public void setSessionNumber(final String sessionNumber) {
this.sessionNumber = sessionNumber;
}
/**
* Gets the sequence number field in block 1
*
* @return sequence number field in block 1
*/
public String getSequenceNumber() {
return sequenceNumber;
}
/**
* Sets the Sequence number that is generated by the
* user's computer.
* For all FIN messages with a Service Identifier of 01 or 05,
* this number is the next expected sequence number appropriate to
* the direction of the transmission.
* For FIN messages with a Service Identifier of 21 or 25,
* the sequence number is that of the acknowledged service message.
* It is padded with zeros.
*
* @param sequenceNumber 6 numbers
*/
public void setSequenceNumber(final String sequenceNumber) {
this.sequenceNumber = sequenceNumber;
}
/**
* Tell if this block is empty or not.
* This block is considered to be empty if all its attributes are set to null.
*
* @return true
if all fields are null and false in other case
*/
@Override
public boolean isEmpty() {
return applicationId == null
&& serviceId == null
&& logicalTerminal == null
&& sessionNumber == null
&& sequenceNumber == null;
}
/**
* Gets the fixed length block 1 value, as a result of
* concatenating its individual elements as follow:
* Application ID Service ID +
* Logical terminal (LT) address +
* Session number +
* Sequence number.
* Notice that this method does not return the "1:" string.
*/
@Override
public String getValue() {
if (isEmpty()) {
return null;
}
final StringBuilder v = new StringBuilder();
if (applicationId != null) {
v.append(applicationId);
}
if (serviceId != null) {
v.append(serviceId);
}
if (logicalTerminal != null) {
v.append(logicalTerminal);
}
if (sessionNumber != null) {
v.append(sessionNumber);
}
if (sequenceNumber != null) {
v.append(sequenceNumber);
}
return v.toString();
}
/**
* Sets the block's attributes by parsing the fixed length string argument.
*
* @param value a fixed length string containing the blocks' value (25 or 24 characters when '1:' is not indicated; 26 or 27 characters when starting string '1:' is included)
* @throws IllegalArgumentException if parameter is not between 24 and 27 characters.
* @see #setValue(String, boolean)
*/
@Override
public void setValue(final String value) {
setValue(value, false);
}
/**
* @see #getValue()
*/
@Override
public String getBlockValue() {
return getValue();
}
/**
* @see #setValue(String)
*/
@Override
public void setBlockValue(final String value) {
setValue(value);
}
/**
* Sets the block's attributes by parsing string argument with its content
* This value can be in different flavors because some fields are optional.
* For example "F01BANKBEBBAXXX2222123456" or "1:F01BANKBEBBAXXX2222123456".
*
* @param value string containing the entire blocks value
* @param lenient if true the value will be parsed with a best effort heuristic, if false it will throw a IllegalArgumentException if the value has an invalid total size
*/
public void setValue(final String value, boolean lenient) {
if (lenient) {
// leave all attributes as null (cleaning defaults)
clean();
} else {
// check parameters
Objects.requireNonNull(value, "value must not be null");
}
if (value != null) {
int offset = 0;
int slen = value.length();
// figure out the starting point
if (value.startsWith("1")) {
if (!lenient) {
Validate.isTrue(
value.startsWith("1:"),
"expected '1:' at the beginning of value and found '" + value.charAt(0) + "'");
Validate.isTrue(
slen == 26 || slen == 27,
"block value " + value
+ " cannot be parsed because it has an invalid size, expected 26 or 27 and found "
+ value.length());
}
offset = 2;
} else {
if (!lenient) {
Validate.isTrue(
slen == 24 || slen == 25,
"block value " + value
+ " cannot be parsed because it has an invalid size, expected 24 or 25 and found "
+ value.length());
}
}
// separate value fragments
int len = 1;
this.setApplicationId(this.getValuePart(value, offset, len));
offset += len;
len = 2;
this.setServiceId(this.getValuePart(value, offset, len));
offset += len;
// LT address must be fixed to 12 characters padding both the LT id and the branch with X if necessary
len = 12;
this.setLogicalTerminal(this.getValuePart(value, offset, len));
offset += len;
len = 4;
this.setSessionNumber(this.getValuePart(value, offset, len));
offset += len;
if (lenient) {
// get all remaining text
this.setSequenceNumber(this.getValuePart(value, offset));
} else {
// get text between size boundaries
len = 6;
this.setSequenceNumber(this.getValuePart(value, offset, len));
}
}
}
/**
* Sets all attributes to null
*
* @since 6.4
*/
public void clean() {
applicationId = null;
serviceId = null;
logicalTerminal = null;
sessionNumber = null;
sequenceNumber = null;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
SwiftBlock1 that = (SwiftBlock1) o;
return Objects.equals(applicationId, that.applicationId)
&& Objects.equals(serviceId, that.serviceId)
&& Objects.equals(logicalTerminal, that.logicalTerminal)
&& Objects.equals(sessionNumber, that.sessionNumber)
&& Objects.equals(sequenceNumber, that.sequenceNumber);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), applicationId, serviceId, logicalTerminal, sessionNumber, sequenceNumber);
}
/**
* Get a json representation of this object.
*
* Example:
*
* {
* "applicationId": "F",
* "serviceId": "01",
* "logicalTerminal": "FOOSEDR0AXXX",
* "sessionNumber": "0000",
* "sequenceNumber": "000000"
* }
*
*
* @return json representation
* @since 7.5
*/
public String toJson() {
final Gson gson = new GsonBuilder().create();
return gson.toJson(this);
}
/**
* Generic getter for block attributes based on qualified names from {@link SwiftBlock1Field}
*
* @param field field to get
* @return field value or null if attribute is not set
* @since 7.7
*/
public String field(SwiftBlock1Field field) {
switch (field) {
case ApplicationId:
return getApplicationId();
case ServiceId:
return getServiceId();
case LogicalTerminal:
return getLogicalTerminal();
case SessionNumber:
return getSessionNumber();
case SequenceNumber:
return getSequenceNumber();
default:
return null;
}
}
/**
* Generic setter for block attributes based on qualified names from {@link SwiftBlock1Field}
*
* @param field field to set
* @param value content to set
* @since 7.8
*/
public void setField(SwiftBlock1Field field, final String value) {
switch (field) {
case ApplicationId:
setApplicationId(value);
break;
case ServiceId:
setServiceId(value);
break;
case LogicalTerminal:
setLogicalTerminal(value);
break;
case SessionNumber:
setSessionNumber(value);
break;
case SequenceNumber:
setSequenceNumber(value);
break;
default:
log.warning("don't know how to set " + field + " to block1");
break;
}
}
/**
* Maps the service id to the service id enumeration
*
* @return the mapped enumeration or null if service id not present or cannot be mapped
* @since 7.8.3
*/
public ServiceIdType getServiceIdType() {
try {
return ServiceIdType.valueOf("_" + this.serviceId);
} catch (Exception e) {
final String text = "Block1 serviceId contains an invalid value [" + this.serviceId
+ "]. The expected values are " + Arrays.toString(ServiceIdType.values());
log.warning(text);
log.log(Level.FINEST, text, e);
return null;
}
}
}