com.prowidesoftware.swift.model.SwiftMessage 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.JsonSerializable;
import com.prowidesoftware.swift.io.ConversionService;
import com.prowidesoftware.swift.io.IConversionService;
import com.prowidesoftware.swift.io.parser.SwiftParser;
import com.prowidesoftware.swift.io.parser.SwiftParserConfiguration;
import com.prowidesoftware.swift.io.parser.XMLParser;
import com.prowidesoftware.swift.io.writer.SwiftWriter;
import com.prowidesoftware.swift.io.writer.XMLWriterVisitor;
import com.prowidesoftware.swift.model.field.*;
import com.prowidesoftware.swift.model.mt.*;
import com.prowidesoftware.swift.utils.IMessageVisitor;
import java.io.IOException;
import java.io.Serializable;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.logging.Level;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder;
/**
* Base class for swift messages.
*
* This class is a generic data structure container for SWIFT messages.
*
*
This is a low level java representation of an MT. If you are looking for
* a class more suitable to be persisted see {@link MtSwiftMessage}
*
*
Instances of this class may have a list of unparsed texts (UnparsedTextList).
* For easy access, methods have been created that first ensure the lists exists (the
* real object is created and then call the base method).
* However, not all the base list methods have been implemented. If you need to use not
* exposed functionality, retrieve the underlying list with (see getUnparsedTexts method).
*/
public class SwiftMessage implements Serializable, JsonSerializable {
static final int JSON_VERSION = 2;
private static final long serialVersionUID = 8094995269559985432L;
private static final transient java.util.logging.Logger log =
java.util.logging.Logger.getLogger(SwiftMessage.class.getName());
private static final String INVALID_NAME_BLOCK = "Invalid name for User Defined Blocks (";
private static final String MESSAGE_IS_NOT_A_FRAGMENT = "message is not a fragment";
private SwiftBlock1 block1;
private SwiftBlock2 block2;
private SwiftBlock3 block3;
private SwiftBlock4 block4;
private SwiftBlock5 block5;
/**
* User defined blocks
* List of {@link SwiftBlockUser}.
*
* @since 5.0
*/
private List userBlocks;
/**
* List of unparsed texts. For performance reasons, this will be null until really needed.
*/
private UnparsedTextList unparsedTexts = null;
/**
* Default constructor.
* Must be called since here is performed default handler registration
*
* @see #SwiftMessage(boolean)
*/
public SwiftMessage() {}
/**
* Constructor that initializes blocks
*
* @param initBlocks when false the message will not have any blocks when constructed, if true blocks are created, just like with default constructor
*/
public SwiftMessage(final boolean initBlocks) {
if (initBlocks) {
this.block1 = new SwiftBlock1();
this.block2 = new SwiftBlock2Input();
this.block3 = new SwiftBlock3();
this.block4 = new SwiftBlock4();
this.block5 = new SwiftBlock5();
this.userBlocks = new ArrayList<>();
}
}
/**
* Constructor for an unparsed text list that initializes blocks
*
* @param initBlocks when false the message will not have any blocks when constructed, if true blocks are created, just like with default consturctor
* @param unparsedText the list of unparsed texts
* @see SwiftMessage#SwiftMessage()
*/
public SwiftMessage(final boolean initBlocks, final UnparsedTextList unparsedText) {
// base constructor
this(initBlocks);
// set the unparsed text list
this.unparsedTexts = unparsedText;
}
/**
* Constructor for an unparsed text list
*
* @param unparsedText the list of unparsed texts
* @see SwiftMessage#SwiftMessage()
*/
public SwiftMessage(final UnparsedTextList unparsedText) {
// base constructor
this();
// set the unparsed text list
this.unparsedTexts = unparsedText;
}
/**
* Parses a the string content into a SwiftMessage.
*
* If the file contains more than a message it will parse the first one.
* If the string is empty, does not contain any MT message, the message type is not set or
* an error occurs reading and parsing the message content; this method returns null.
*
*
The implementation uses the default parser behavior which is lenient and will do a best effort to
* read as much from the message content as possible regardless of the content and block boundaries
* being valid or not. For instance, it will read the headers even if the value length is incorrect,
* and it will read the text block (block 4) even if it is missing the closing hyphen and bracket. For
* more options check {@link SwiftParser#setConfiguration(SwiftParserConfiguration)}
*
* @param fin string a string containing a swift MT message
* @return parser message or null if string content could not be parsed
* @throws IOException if an error occurs in the parser during reading
* @since 7.8.8
*/
public static SwiftMessage parse(final String fin) throws IOException {
return new SwiftParser(fin).message();
}
/**
* Visit a Block 3 (SwiftBlock3), i.e: call the tag method for block 3
* This method is called from {@link #visit(IMessageVisitor)} but may be used independently, in such case,
* the startBlockX and endBlockX in the visitor will not be called.
*
To serialize in SWIFT native format with block boundaries check {@link SwiftWriter#writeBlock3(SwiftBlock3, java.io.Writer)}
*
* @param block the block containing the tags to visit
* @param visitor the visitor to use
* @throws IllegalArgumentException if parameter block or visitor are null
* @since 5.0
*/
public static void visit(final SwiftBlock3 block, final IMessageVisitor visitor) {
// sanity check
Objects.requireNonNull(block);
Objects.requireNonNull(visitor);
// iterate thru tags
for (final Iterator it = block.tagIterator(); it.hasNext(); ) {
visitor.tag(block, it.next());
}
}
/**
* Visit a Block 4 (SwiftBlock4), i.e: call the tag method for block 4
* This method is called from {@link #visit(IMessageVisitor)} but may be used independently, in such case,
* the startBlockX and endBlockX in the visitor will not be called.
* To serialize in SWIFT native format with block boundaries check {@link SwiftWriter#writeBlock4(SwiftBlock4, java.io.Writer)}
*
* @param block the block containing the tags to visit
* @param visitor the visitor to use
* @throws IllegalArgumentException if parameter block or visitor are null
* @since 5.0
*/
public static void visit(final SwiftBlock4 block, final IMessageVisitor visitor) {
// sanity check
Objects.requireNonNull(block);
Objects.requireNonNull(visitor);
// iterate thru tags
for (final Iterator it = block.tagIterator(); it.hasNext(); ) {
final Tag t = it.next();
visitor.tag(block, t);
}
}
/**
* Visit a Block 5 (SwiftBlock5), i.e: call the tag method for block 4
* This method is called from {@link #visit(IMessageVisitor)} but may be used independently, in such case,
* the startBlockX and endBlockX in the visitor will not be called.
* To serialize in SWIFT native format with block boundaries check {@link SwiftWriter#writeBlock5(SwiftBlock5, java.io.Writer)}
*
* @param block the block containing the tags to visit
* @param visitor the visitor to use
* @throws IllegalArgumentException if parameter block or visitor are null
* @since 5.0
*/
public static void visit(final SwiftBlock5 block, final IMessageVisitor visitor) {
// sanity check
Objects.requireNonNull(block);
Objects.requireNonNull(visitor);
// iterate thru tags
for (final Iterator it = block.tagIterator(); it.hasNext(); ) {
final Tag t = it.next();
visitor.tag(block, t);
}
}
/**
* Visit a User Defined Block (SwiftBlockUser), i.e: call the tag method for block 4
* This method is called from {@link #visit(IMessageVisitor)} but may be used independently, in such case,
* the startBlockX and endBlockX in the visitor will not be called.
*
* @param block the block containing the tags to visit
* @param visitor the visitor to use
* @throws IllegalArgumentException if parameter block or visitor are null
* @since 5.0
*/
public static void visit(final SwiftBlockUser block, final IMessageVisitor visitor) {
// sanity check
Objects.requireNonNull(block);
Objects.requireNonNull(visitor);
// iterate thru tags
for (final Iterator it = block.tagIterator(); it.hasNext(); ) {
final Tag t = it.next();
visitor.tag(block, t);
}
}
/**
* This method deserializes the JSON data into a message object.
* @param json JSON data
* @return message object
*
* @see #toJson()
* @since 7.9.8
*/
public static SwiftMessage fromJson(String json) {
final Gson gson = new GsonBuilder()
.registerTypeAdapter(SwiftMessage.class, new SwiftMessageAdapter())
.registerTypeAdapter(SwiftBlock2.class, new SwiftBlock2Adapter())
.create();
return gson.fromJson(json, SwiftMessage.class);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SwiftMessage that = (SwiftMessage) o;
return Objects.equals(block1, that.block1)
&& Objects.equals(block2, that.block2)
&& Objects.equals(block3, that.block3)
&& Objects.equals(block4, that.block4)
&& Objects.equals(block5, that.block5)
&& Objects.equals(userBlocks, that.userBlocks)
&& Objects.equals(unparsedTexts, that.unparsedTexts);
}
@Override
public int hashCode() {
return Objects.hash(block1, block2, block3, block4, block5, userBlocks, unparsedTexts);
}
/**
* Get the block number specified by b.
*
* @param b the block number to retrieve, must be greater or equal to 1 and smaller or equal to 5.
* @return the block number specified in this message
* @throws IllegalArgumentException if b < 1 or b > 5
*/
public SwiftBlock getBlock(final int b) {
// sanity check
Validate.isTrue(1 <= b && b <= 5, "block index must be 1-5 (was " + b + ")");
switch (b) {
case 1:
return this.block1;
case 2:
return this.block2;
case 3:
return this.block3;
case 4:
return this.block4;
case 5:
return this.block5;
default:
log.severe("Invalid block number " + b + ". Expected numbers are 1 to 5");
// should not be reached
return null;
}
}
/**
* Commons-lang reflection toString implementation
*/
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
/**
* Add a block to this message.
* Notes: on user blocks, no checks are done, on swift blocks, block number
* must be non null and have a value from 1-5 both inclusive
*
* @param b the block to add, may be null in which case nothing happens
* @throws IllegalArgumentException b is null or the method getInt in the block returns a value out of range (non user blocks)
*/
public void addBlock(final SwiftBlock b) {
Objects.requireNonNull(b);
// support for user blocks in this method is useful for XML parser and other code that
// takes advantages of using SwiftTagListBlock
if (b instanceof SwiftBlockUser) {
addUserBlock((SwiftBlockUser) b);
} else {
Objects.requireNonNull(b.getNumber(), "SwiftBlock.getNumber() is null");
final int index = b.getNumber();
Validate.isTrue(index >= 1 && index <= 5, "SwiftBlock.getNumber int did not return an int between 1-5");
switch (index) {
case 1:
setBlock1((SwiftBlock1) b);
break;
case 2:
setBlock2((SwiftBlock2) b);
break;
case 3:
setBlock3((SwiftBlock3) b);
break;
case 4:
setBlock4((SwiftBlock4) b);
break;
case 5:
setBlock5((SwiftBlock5) b);
break;
default:
log.severe("Invalid block number " + b + ". Expected numbers are 1 to 5");
break;
}
}
}
/**
* Tell the message type associated with this object if a block 2 is present.
*
* @return a String containing the SWIFT numeric code for the message types or null if the message does not have a block 2.
* @see SwiftBlock2#getMessageType()
*/
public String getType() {
if (this.block2 != null) {
return this.block2.getMessageType();
} else {
return null;
}
}
/**
* Visit the current message with the given visitor.
* Writes all present blocks 1 to 5, and also the user blocks if any.
*
* @param visitor the visitor to use
* @throws IllegalArgumentException if parameter visitor is null
* @see SwiftWriter#writeMessage(SwiftMessage, java.io.Writer)
*/
public void visit(final IMessageVisitor visitor) {
Objects.requireNonNull(visitor);
// start visiting
visitor.startMessage(this);
// visit block 1 and value
final SwiftBlock1 b1 = this.block1;
if (b1 != null) {
visitor.startBlock1(b1);
visitor.value(b1, b1.getValue());
visitor.endBlock1(b1);
}
// visit block 2 and value
final SwiftBlock2 b2 = this.block2;
if (b2 != null) {
visitor.startBlock2(b2);
visitor.value(b2, b2.getValue());
visitor.endBlock2(b2);
}
final SwiftBlock3 b3 = this.block3;
if (b3 != null) {
visitor.startBlock3(b3);
visit(b3, visitor);
visitor.endBlock3(b3);
}
final SwiftBlock4 b4 = this.block4;
if (b4 != null) {
visitor.startBlock4(b4);
visit(b4, visitor);
visitor.endBlock4(b4);
}
final SwiftBlock5 b5 = this.block5;
if (b5 != null) {
visitor.startBlock5(b5);
visit(b5, visitor);
visitor.endBlock5(b5);
}
// visit user defined blocks
if (this.userBlocks != null) {
// visit every user defined block
for (final SwiftBlockUser userBlock : this.userBlocks) {
if (userBlock != null) {
visitor.startBlockUser(userBlock);
visit(userBlock, visitor);
visitor.endBlockUser(userBlock);
}
}
}
// stop visiting
visitor.endMessage(this);
}
/**
* Get the number of blocks in this message, including the user blocks
*
* @return an int greater or equal to zero
*/
public int getBlockCount() {
return this.getBlockCount(Boolean.TRUE);
}
/**
* Get the number of blocks in this message, optionally including the user blocks.
* A block is summed if it is not null and is not empty.
* NOTE: that isEmpty() will be called in each block, the behavior of isEmpty is block
* dependent
*
* @param includeUserBlocks indicates whether or not user defined blocks should be counted
* @return an int greater or equal to zero
* @see SwiftBlock1#isEmpty()
* @see SwiftBlock2Input#isEmpty()
* @see SwiftBlock2Output#isEmpty()
* @see SwiftBlock3#isEmpty()
* @see SwiftBlock4#isEmpty()
* @see SwiftBlock5#isEmpty()
*/
public int getBlockCount(final Boolean includeUserBlocks) {
// count the basic blocks
int count = 0;
if (this.block1 != null && !this.block1.isEmpty()) {
count++;
}
if (this.block2 != null && !this.block2.isEmpty()) {
count++;
}
if (this.block3 != null && !this.block3.isEmpty()) {
count++;
}
if (this.block4 != null && !this.block4.isEmpty()) {
count++;
}
if (this.block5 != null && !this.block5.isEmpty()) {
count++;
}
// count user defined blocks (if requested to do so)
if (includeUserBlocks && this.userBlocks != null) {
count += this.userBlocks.size();
}
return count;
}
/**
* Get block number 1 of this message, may be null if not set
*
* @return the block 1 of the message or null
*/
public SwiftBlock1 getBlock1() {
return this.block1;
}
/**
* Set the block 1 of the message
*
* @param block1 the content of the block 1
*/
public void setBlock1(final SwiftBlock1 block1) {
this.block1 = block1;
}
/**
* Get block number 2 of this message, may be null if not set
*
* @return the block 2 of the message or null
*/
public SwiftBlock2 getBlock2() {
return this.block2;
}
/**
* Set the block 2 of the message
*
* @param block2 the content of the block 1
*/
public void setBlock2(final SwiftBlock2 block2) {
this.block2 = block2;
}
/**
* Get block number 3 of this message, may be null if not set
*
* @return the block 3 of the message or null
*/
public SwiftBlock3 getBlock3() {
return this.block3;
}
/**
* Set the block 3 of the message
*
* @param block3 the content of the block 1
*/
public void setBlock3(final SwiftBlock3 block3) {
this.block3 = block3;
}
/**
* Get block number 4 of this message, may be null if not set
*
* @return the block 4 of the message or null
*/
public SwiftBlock4 getBlock4() {
return this.block4;
}
/**
* Set the block 4 of the message
*
* @param block4 the content of the block 1
*/
public void setBlock4(final SwiftBlock4 block4) {
this.block4 = block4;
}
/**
* Get block number 5 of this message, may be null if not set
*
* @return the block 5 of the message or null
*/
public SwiftBlock5 getBlock5() {
return this.block5;
}
/**
* Set the block 5 of the message
*
* @param block5 the content of the block 5
*/
public void setBlock5(final SwiftBlock5 block5) {
this.block5 = block5;
}
/**
* Finds the position of a given User Defined Block in the internal list
*
* @param blockName the block name to find may be empty or null, in which case this method does nothing
* @return the position or -1 if not found
* @since 5.0
*/
public int getUserBlockPosition(final String blockName) {
// check parameters
if (StringUtils.isBlank(blockName)
|| // check user blocks array
this.userBlocks == null) {
return -1;
}
// start scanning the list
for (int i = 0; i < this.userBlocks.size(); i++) {
final SwiftBlockUser userBlock = this.userBlocks.get(i);
if (userBlock != null && StringUtils.equals(userBlock.getName(), blockName)) {
return i;
}
}
return -1;
}
/**
* Get the list of List of {@link SwiftBlockUser} user defined blocks.
* The requested object may be null if the message was cleared or not initialized.
*
* @return the list or user blocks or null
* @since 5.0
*/
public List getUserBlocks() {
return this.userBlocks;
}
/**
* Set the list of user defined blocks.
* This method is mainly needed for persistence services.
*
* @param userBlocks the new list of user defined blocks
* @throws IllegalArgumentException if parameter userBlocks is null
* @throws IllegalArgumentException if parameter userBlocks has elements of class other than SwiftBlockUser
* @see SwiftBlockUser
* @since 5.0
*/
public void setUserBlocks(final List userBlocks) {
// sanity check
Objects.requireNonNull(userBlocks, "parameter 'userBlocks' cannot be null");
// setup the new list
this.userBlocks = userBlocks;
}
/**
* Get a user defined block by name, may be null if not set
*
* @param blockName the name of the block to find
* @return the requested user defined block or null
* @throws IllegalArgumentException if parameter blockName is null
* @throws IllegalArgumentException if parameter blockName has an invalid block name
* @since 5.0
*/
public SwiftBlockUser getUserBlock(final String blockName) {
// sanity check
Objects.requireNonNull(blockName, "parameter 'blockName' cannot be null");
// find the block position
final int pos = getUserBlockPosition(blockName);
if (pos != -1) {
return this.userBlocks.get(pos);
}
return null;
}
/**
* Get a user defined block by number, may be null if not set
*
* @param blockNumber the number of the block to find
* @return the requested user defined block or null
* @throws IllegalArgumentException if parameter userBlock is null
* @throws IllegalArgumentException if parameter userBlock has an invalid block name
* @since 5.0
*/
public SwiftBlockUser getUserBlock(final Integer blockNumber) {
// sanity check
Objects.requireNonNull(blockNumber, "parameter 'blockNumber' cannot be null");
return this.getUserBlock(blockNumber.toString());
}
/**
* Add a user defined block to the message (if the block already exists, it is replaced)
*
* @param userBlock the user defined block
* @throws IllegalArgumentException if parameter userBlock is null
* @throws IllegalArgumentException if parameter userBlock has an invalid block name
* @since 5.0
*/
public void addUserBlock(final SwiftBlockUser userBlock) {
// sanity check
Objects.requireNonNull(userBlock);
Validate.isTrue(userBlock.isValidName(), INVALID_NAME_BLOCK + userBlock.getName() + ")");
if (this.userBlocks == null) {
this.userBlocks = new ArrayList<>();
}
// find the block position (if it's already there)
final int pos = getUserBlockPosition(userBlock.getName());
if (pos != -1) {
this.userBlocks.add(pos, userBlock);
} else {
this.userBlocks.add(userBlock);
}
}
/**
* removes a user defined block to the message (if the block does not exists, nothing is done)
*
* @param blockNumber the block number to remove
* @throws IllegalArgumentException if parameter blockNumber is null
* @throws IllegalArgumentException if parameter blockNumber is invalid
* @see SwiftBlockUser#isValidName(Integer)
* @since 5.0
*/
public void removeUserBlock(final Integer blockNumber) {
// sanity check
Objects.requireNonNull(blockNumber, "parameter 'blockNumber' cannot be null");
Validate.isTrue(SwiftBlockUser.isValidName(blockNumber), INVALID_NAME_BLOCK + blockNumber + ")");
this.removeUserBlock(blockNumber.toString());
}
/**
* removes a user defined block to the message (if the block does not exists, nothing is done)
*
* @param blockName the block name to remove
* @throws IllegalArgumentException if parameter blockName is null
* @throws IllegalArgumentException if parameter blockName is invalid
* @since 5.0
*/
public void removeUserBlock(final String blockName) {
// sanity check
Objects.requireNonNull(blockName, "parameter 'blockName' cannot be null");
Validate.isTrue(SwiftBlockUser.isValidName(blockName), INVALID_NAME_BLOCK + blockName + ")");
// find the block position (if it's there)
final int pos = getUserBlockPosition(blockName);
if (pos != -1) {
this.userBlocks.remove(pos);
}
}
/**
* remove all blocks from these message, including user blocks
*/
public void clear() {
// release all blocks
this.block1 = null;
this.block2 = null;
this.block3 = null;
this.block4 = null;
this.block5 = null;
// release user blocks
this.userBlocks = null;
}
/**
* Checks if the message is a fragment
*
* @return true if the message is a fragment
* @since 5.0
*/
public Boolean isFragment() {
// get the block 4 (if exists)
final SwiftBlock4 b4 = this.block4;
if (b4 != null) {
final String t202 = b4.getTagValue("202");
final String t203 = b4.getTagValue("203");
// if both tag exists => this is a fragment
return t202 != null && t203 != null ? Boolean.TRUE : Boolean.FALSE;
}
return Boolean.FALSE;
}
/**
* Checks if the message is the last fragment
*
* @return true if the message is the last fragment of a fragmented message
* @since 5.0
*/
public Boolean isLastFragment() {
if (!isFragment()) {
return Boolean.FALSE;
}
final Integer count = this.fragmentCount();
try {
final Integer number = this.fragmentNumber();
return count.intValue() == number.intValue() ? Boolean.TRUE : Boolean.FALSE;
} catch (final UnsupportedOperationException e) {
throw new IllegalStateException("Invalid call to islastFragment for a non fragmented message", e);
}
}
/**
* Gets the total number of fragments of a fragmented message as informed in tag 203.
*
* @return the total number of fragments or zero if the message is not fragmented
* @since 5.0
*/
public Integer fragmentCount() {
// if this is not a fragment => 0
if (!this.isFragment()) {
return 0;
}
// get the block 4 and tag 203 (they BOTH exists here)
final String t203 = this.block4.getTagValue("203");
// process the number
int _t203;
try {
_t203 = Integer.parseInt(t203, 10);
} catch (final NumberFormatException nfe) {
throw new UnsupportedOperationException(MESSAGE_IS_NOT_A_FRAGMENT);
}
return _t203;
}
/**
* Gets the number of this fragment
*
* @return the number of this fragment
* @throws UnsupportedOperationException if the message is not a part of a fragmented message
* @since 5.0
*/
public Integer fragmentNumber() {
// if this is not a fragment => 0
if (!this.isFragment()) {
throw new UnsupportedOperationException(MESSAGE_IS_NOT_A_FRAGMENT);
}
// get the block 4 and tag 203 (they BOTH exists here)
final String t202 = this.block4.getTagValue("202");
// process the number
int _t202;
try {
_t202 = Integer.parseInt(t202, 10);
} catch (final NumberFormatException nfe) {
throw new UnsupportedOperationException(MESSAGE_IS_NOT_A_FRAGMENT);
}
return _t202;
}
/**
* Gets the signature of the message (looks for an S block then the MDG tag)
*
* @return the signature of the message (or null if none exists)
* @since 7.10.4
*/
public String getSignature() {
// prepare the result
String signature = null;
// get the S block (create if it does not exist in the message)
SwiftBlockUser sBlock = getUserBlock("S");
if (sBlock != null) {
// get the MDG tag from the block
Tag mdg = sBlock.getTagByName("MDG");
if (mdg != null) {
// get the signature from the tag
signature = mdg.getValue();
}
}
return signature;
}
/**
* Sets the signature for the message (adds an S block with the MDG tag)
*
* @param signature the signature to set in block S
* @return this
* @since 7.10.4
*/
public SwiftMessage setSignature(String signature) {
// get the S block (create if it does not exist in the message)
SwiftBlockUser sBlock = getUserBlock("S");
if (sBlock == null) {
// create the block
sBlock = new SwiftBlockUser("S");
// add the block to the message
addUserBlock(sBlock);
}
// remove any MDG tag from the block (if present)
Tag mdg = sBlock.getTagByName("MDG");
if (mdg == null) {
// create the tag
mdg = new Tag();
mdg.setName("MDG");
sBlock.append(mdg);
}
// set the signature on the tag
mdg.setValue(signature);
return this;
}
/**
* verifies that the unparsed text list exists
*/
protected void unparsedTextVerify() {
if (this.unparsedTexts == null) {
this.unparsedTexts = new UnparsedTextList();
}
}
/**
* returns the unparsed text list
*
* @return the unparsed text attached to this message
*/
public UnparsedTextList getUnparsedTexts() {
// create the list if needed
unparsedTextVerify();
return this.unparsedTexts;
}
/**
* sets the list of unparsed texts
*
* @param texts the new list of unparsed texts (may be null)
*/
public void setUnparsedTexts(final UnparsedTextList texts) {
this.unparsedTexts = texts;
}
/**
* returns the size of the unparsed text list
*
* @return the count of unparsed texts attached to this message
*/
public Integer getUnparsedTextsSize() {
// no list => size is zero...
if (this.unparsedTexts == null) {
return 0;
}
return this.unparsedTexts.size();
}
/**
* decides if a specific text (by index) is likely a SWIFT FIN message. Exceptions are inherited from
* base implementation methods.
*
* @param index the unparsed text number
* @return true if the unparsed text at position index is a full SWIFT message
* @throws IllegalArgumentException if parameter index is null
* @throws IndexOutOfBoundsException if parameter index is out of bounds
*/
public Boolean unparsedTextIsMessage(final Integer index) {
// create the list if needed
unparsedTextVerify();
return this.unparsedTexts.isMessage(index);
}
/**
* get an unparsed text
*
* @param index the unparsed text number
* @return the requested text
* @throws IllegalArgumentException if parameter index is null
* @throws IndexOutOfBoundsException if parameter index is out of bounds
*/
public String unparsedTextGetText(final Integer index) {
// create the list if needed
unparsedTextVerify();
return this.unparsedTexts.getText(index);
}
/**
* get an unparsed text as a parsed swift message
*
* @param index the unparsed text number
* @return the unparsed text at position index parsed into a SwiftMessage object
* @throws IllegalArgumentException if parameter index is null
*/
public SwiftMessage unparsedTextGetAsMessage(final Integer index) {
// create the list if needed
unparsedTextVerify();
return this.unparsedTexts.getTextAsMessage(index);
}
/**
* adds a new unparsed text
*
* @param text the unparsed text to append
* @throws IllegalArgumentException if parameter text is null
*/
public void unparsedTextAddText(final String text) {
// create the list if needed
unparsedTextVerify();
this.unparsedTexts.addText(text);
}
/**
* adds a new unparsed text from a message
*
* @param message the message to be appended
* @throws IllegalArgumentException if parameter message is null
*/
public void unparsedTextAddText(final SwiftMessage message) {
// create the list if needed
unparsedTextVerify();
this.unparsedTexts.addText(message);
}
/**
* Checks if the message is a cover payment, based on the content of the User Header (block3).
*
* @return true if 119:COV is found at User Header (block3)
*/
public boolean isCOV() {
if (this.block3 != null) {
return this.block3.containsTag(Field119.tag(MTVariant.COV.name()));
}
return false;
}
/**
* Checks if the message is Straight Through Processing (STP), based on the content of the User Header (block3).
*
* @return true if 119:STP is found at User Header (block3)
*/
public boolean isSTP() {
if (this.block3 != null) {
return this.block3.containsTag(Field119.tag(MTVariant.STP.name()));
}
return false;
}
/**
* Checks if the message is a remit, based on the content of the User Header (block3).
*
* @return true if 119:REMIT is found at User Header (block3)
* @since 7.7
*/
public boolean isREMIT() {
if (this.block3 != null) {
return this.block3.containsTag(Field119.tag(MTVariant.REMIT.name()));
}
return false;
}
/**
* @see SwiftMessageUtils#sender(SwiftMessage)
* @since 9.3.19
*/
public String getSender() {
return SwiftMessageUtils.sender(this);
}
/**
* @see SwiftMessageUtils#receiver(SwiftMessage)
* @since 9.3.19
*/
public String getReceiver() {
return SwiftMessageUtils.receiver(this);
}
/**
* Get all fields with the given name in the block 4.
* Only direct naming is supported, 55a notation is NOT SUPPORTED.
*
* @param names list of names to add in fields to search
* @return a list with fields matching the given names. an empty list if none found
* @throws IllegalArgumentException if names is null
*/
public List fields(final String... names) {
Objects.requireNonNull(names, "names is null");
final List result = new ArrayList<>();
for (final String n : names) {
final Tag[] tl = this.block4.getTagsByName(n);
if (tl != null && tl.length > 0) {
for (final Tag t : tl) {
result.add(t.asField());
}
}
}
return result;
}
/**
* Checks all blocks (1 to 5) and if a block is empty, it is removed from the message.
* @return the message
*
* @since 6.4
* @since 8.0.3 returns this
*/
public SwiftMessage removeEmptyBlocks() {
if (this.block1 != null && this.block1.isEmpty()) {
this.block1 = null;
}
if (this.block2 != null && this.block2.isEmpty()) {
this.block2 = null;
}
if (this.block3 != null && this.block3.isEmpty()) {
this.block3 = null;
}
if (this.block4 != null && this.block4.isEmpty()) {
this.block4 = null;
}
if (this.block5 != null && this.block5.isEmpty()) {
this.block5 = null;
}
return this;
}
/**
* Gets message type as an integer or -1
if an error occurs or it is not set.
*
* @return the message type number or -1
if the message type is invalid or block 2 not present (for instance if the message is a service message)
* @since 6.4.1
*/
public int getTypeInt() {
if (isServiceMessage()) {
return -1;
}
try {
return Integer.parseInt(getType());
} catch (final NumberFormatException e) {
final String text = "Error converting type to int " + getType();
log.warning(text);
log.log(Level.FINEST, text, e);
return -1;
}
}
/**
* Returns the message direction from block 2 or null if block 2 is not found or incomplete.
*
* @return message direction (i.e. {@link MessageIOType#incoming} or {@link MessageIOType#outgoing})
* @since 7.0
*/
public MessageIOType getDirection() {
try {
if (this.block2 == null) {
log.fine(
"Requesting direction on a message without block2, can't determine direction. set log level to finer to view more details");
log.finest("Message: " + this);
} else {
if (this.block2.isOutput()) {
return MessageIOType.incoming;
} else if (this.block2.isInput()) {
return MessageIOType.outgoing;
}
}
} catch (final Exception e) {
log.severe("Unexpected exception occurred while determining direction from message data: " + e);
}
return null;
}
/**
* Returns true if the message is outgoing (sent to SWIFT), false other case; using the direction attribute.
* If block 2 is missign or direction cannot be determined, returns false.
*
* @return true if message is outgoing
* @since 7.8.4
*/
public boolean isOutgoing() {
return getDirection() == MessageIOType.outgoing;
}
/**
* Synonym to {@link #isOutgoing()}.
*
* @return true if message is outgoing
* @see #isOutgoing()
* @since 7.8.4
*/
public boolean isInput() {
return isOutgoing();
}
/**
* Returns true if the message is incoming (received from SWIFT), false other case; using the direction attribute.
* If block 2 is missign or direction cannot be determined, returns false.
*
* @return true if message is incoming
* @since 7.8.4
*/
public boolean isIncoming() {
return getDirection() == MessageIOType.incoming;
}
/**
* Synonym to {@link #isIncoming()}.
*
* @return true if message is incoming
* @see #isIncoming()
* @since 7.8.4
*/
public boolean isOutput() {
return isIncoming();
}
/**
* Gets PDE (Possible Duplicate Emission) flag from the trailer block or null if the trailer or the PDE field is not present
* Notice the PDE tag could hold no value, so in that case empty string is returned, meaning the flag is set but with no value.
* @return PDE
*
* @since 7.0
*/
public String getPDE() {
if (this.block5 != null) {
Optional t = this.block5.getTag(SwiftBlock5Field.PDE);
if (t.isPresent()) {
return t.get().getValue();
}
}
return null;
}
/**
* Sets the Possible Duplicate Emission tag with no value, in the trailer block (block 5),
* If the field exists, its value will be overwritten.
* @return the swift message object
*
* @since 8.0.2
*/
public SwiftMessage setPDE() {
if (this.block5 == null) {
this.block5 = new SwiftBlock5();
}
this.block5.setPDE();
return this;
}
/**
* Gets PDM from the trailer block or null if the trailer or the PDM field is not present
* @return PDM
*
* @since 7.0
*/
public String getPDM() {
if (this.block5 != null) {
Optional t = this.block5.getTag(SwiftBlock5Field.PDM);
if (t.isPresent()) {
return t.get().getValue();
}
}
return null;
}
/**
* The MIR (Message Input Reference) is a String of 28 characters, always local to the sender of the message.
* It includes the date the sender sent the message to SWIFT, followed by the full LT address of the sender of the
* message, and the sender's session and sequence to SWIFT: YYMMDD BANKBEBBAXXX 2222 123456.
* It is only available in incoming messages (received from SWIFT).
* @return MIR string
*
* @since 7.0
*/
public String getMIR() {
if (this.block2 != null && this.block2.isOutput()) {
return ((SwiftBlock2Output) this.block2).getMIR();
} else {
return null;
}
}
/**
* The MOR (Message Output Reference) is a String of 28 characters, always local to the receiver of the message.
* It includes the message output date, the address of the receiver, the output session number, and the output
* sequence number.: YYMMDD BANKBEBBAXXX 2222 123456.
* It is only available in incoming messages (received from SWIFT).
* @return MOR string
*
* @since 9.2.7
*/
public String getMOR() {
if (this.block2 != null && this.block2.isOutput()) {
SwiftBlock2Output swiftBlock2Output = (SwiftBlock2Output) this.block2;
String date = swiftBlock2Output.getReceiverOutputDate();
if (this.block1 != null) {
String logicalTerminal = this.block1.getLogicalTerminal();
String sessionNumber = this.block1.getSessionNumber();
String sequenceNumber = this.block1.getSequenceNumber();
MOR mor = new MOR(date, logicalTerminal, sessionNumber, sequenceNumber);
return mor.getMOR();
}
}
return null;
}
/**
* Gets MUR (Message User Reference) from field 108 in the user header block (block 3) or in the text block
* (block 4). Notice for user to user messages this field is located at the user header, however for system messages
* (category 0) the field is located at the text block.
*
* The MUR is the Message User Reference used by applications for reconciliation with ACK. It is a free-format
* field in which users may specify their own reference of up to 16 characters of the permitted character set.
*
* @return the value of field 108 if found, or null when not found neither in block 3 or block 4
* @since 7.0
*/
public String getMUR() {
if (this.block3 != null && this.block3.containsTag(Field108.NAME)) {
return this.block3.getTagValue(Field108.NAME);
}
if (this.block4 != null && this.block4.containsTag(Field108.NAME)) {
return this.block4.getTagValue(Field108.NAME);
}
return null;
}
/**
* Sets the MUR (Message User Reference) in the user header block.
*
If a MUR field is present, its value will be overwritten.
*
The MUR is the Message User Reference used by applications for reconciliation with ACK.
* It is a free-format field in which users may specify their own reference of up to 16 characters
* of the permitted character set, and it is contained in a 108 field at the message user header (block 3).
*
* @param mur a non blank MUR value to set, if value is blank this method does nothing
* @return this
* @since 7.10.4
*/
public SwiftMessage setMUR(String mur) {
if (StringUtils.isNotBlank(mur)) {
if (this.block3 == null) {
this.block3 = new SwiftBlock3();
}
this.block3.builder().setField108(new Field108(mur));
}
return this;
}
/**
* Gets a UUID (User Unique Identifier) for the message conformed by:
*
*
* - Direction: A single-character direction indicator; "I" for an outgoing message (input to the network) and "O" for an incoming message (output from the network). Defaults to "I".
* - The correspondent BIC 11 code; the receiver for an outgoing messages and the sender for an incoming message.
* - Message type: the 3-character number identifying the specific message.
* - Reference: field 20 or field 20C:SEME returned by {@link SwiftMessageUtils#reference(SwiftMessage)}
*
*
* Notice despite the name this identifier is unique only in the context of a specific message management platform,
* since all its values could be repeated from one installation to another. To make it completely unique in your
* application context, consider using {@link #getUID(Calendar, Long)}
*
* @return UUID
* @since 7.0
*/
public String getUUID() {
StringBuilder uuid = new StringBuilder();
if (isIncoming()) {
uuid.append("O");
} else {
uuid.append("I");
}
BIC corresp = getCorrespondentBIC();
if (corresp != null) {
uuid.append(corresp.getBic11());
}
uuid.append(StringUtils.trimToEmpty(getType()));
uuid.append(StringUtils.trimToEmpty(SwiftMessageUtils.reference(this)));
return uuid.toString();
}
/**
* Gets a UID (Unique Identifier) for the message appending a suffix to the UUID generated with {@link #getUUID()}.
*
*
The suffix is a system-generated value that can help uniquely identify a message. The suffix generated by this method is similar to the suffix
* used by SWIFT Alliance Lite. The first part is the creation date of the message in YYMMDD format, a six-digit number. The second part consists of
* 1-10 left padded digit number generated from the container application/system incremental identifier.
*
* @param created optional creation date, if provided, the YYMMDD will be appended as first part of the suffix
* @param id optional incremental identifier number from the application, if provided it will be appended as second part of the suffix
* @return the created UID
* @since 7.9.5
*/
public String getUID(final Calendar created, final Long id) {
StringBuilder suffix = new StringBuilder();
if (created != null) {
SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd");
suffix.append(sdf.format(created.getTime()));
}
if (id != null) {
suffix.append(StringUtils.leftPad(String.valueOf(id), 10, "0"));
}
if (suffix.length() == 0) {
log.warning(
"The computed suffix for message UID is blank, provide either the creation date or the numeric identifier as parameters for getUID");
}
return getUUID() + suffix;
}
/**
* return first results of fields() or null if none
*
* @param name name of field in block 4
* @return null if not found
* @see #fields(String...)
*/
public Field field(final String name) {
final List l = fields(name);
if (l.size() == 0) {
return null;
}
return l.get(0);
}
/**
* Checks if the message is linked to other message based on the presence of a LINK sequence.
*
* @return true if the message has a LINK sequence, false if it hasn't, and null if cannot determine
* @since 7.4
*/
public Boolean isLinked() {
if (this.block4 != null) {
return !this.block4.getSubBlock("LINK").isEmpty();
}
return null;
}
/**
* Return the message's LINK sequences if any.
*
* @return a block containing the found linkage sequences or null if cannot determine
* @since 7.4
*/
public List getLinkages() {
if (this.block4 != null) {
return this.block4.getSubBlocks("LINK");
}
return null;
}
/**
* Get a json representation of this object.
*
* Generated JSON string will contain additional properties with
* version number and timestamp, while the actual SwiftMessage
* serialization is put into a data element.
*
* Example:
*
* { "version": 2, "timestamp": "2016-08-26T23:57:36Z", data": {
* "block1": {
* "applicationId": "F",
* "serviceId": "01",
* "logicalTerminal": "FOOSEDR0AXXX",
* "sessionNumber": "0000",
* "sequenceNumber": "000000"
* } ,
* "block2": {
* "messageType": "103",
* "receiverAddress": "FOORECV0XXXX",
* "messagePriority": "N",
* "deliveryMonitoring": "null",
* "obsolescencePeriod": "null"
* }
* "block4": {
* "tags": [
* { "20": "REFERENCE" },
* { "23B": "CRED" },
* { "32A": "130204USD1234567,89" },
* { "50K": "/12345678901234567890\nFOOBANKXXXXX" },
* { "59": "/12345678901234567890\nJOE DOE" },
* { "71A": "OUR" }
* ]
* }
* }
*
*
* @since 7.5
*/
@Override
public String toJson() {
final Gson gson = new GsonBuilder()
.registerTypeAdapter(SwiftMessage.class, new SwiftMessageAdapter())
.registerTypeAdapter(SwiftBlock2.class, new SwiftBlock2Adapter())
.setPrettyPrinting()
.create();
return gson.toJson(this);
}
/**
* Gets a proprietary XML representation of this message.
* Notice: it is neither a standard nor the MX version of this MT.
*
* @return the MT message serialized into the proprietary XML
* @see XMLWriterVisitor
* @see XMLParser
* @since 7.8.4
*/
public final String toXml() {
final StringWriter w = new StringWriter();
visit(new XMLWriterVisitor(w, true));
final String xml = w.getBuffer().toString();
if (log.isLoggable(Level.FINEST)) {
log.finest("xml: " + xml);
}
return xml;
}
private void appendBlock(final String blockName, final StringBuilder sb, final SwiftTagListBlock b) {
sb.append("\"block" + blockName + "\" : \n");
if (b == null) {
sb.append("{ }");
} else {
sb.append(b.toJson());
}
sb.append("\n"); // block
}
/**
* Get the MTxxx instance that corresponds to the current message type.
* If you have a MT102 in a SwiftMessage, this method is the same as invoking
* new MT102(SwiftMessage)
.
*
For messages with service id 21 = GPA/FIN Message (ACK/NAK/UAK/UNK) it will
* return an instance of {@link ServiceMessage21}.
*
* @return created specific MT object or null if the message type is not set or an error occurs during message creation
*/
public AbstractMT toMT() {
final String type = getType();
if (type == null) {
if (isServiceMessage21()) {
return ServiceMessage21.newInstance(this);
}
log.warning("Cannot determine the message type from application header (block 2)");
} else {
final StringBuilder className = new StringBuilder();
className.append("com.prowidesoftware.swift.model.mt.mt");
className.append(type.charAt(0));
className.append("xx.MT");
className.append(type);
if (isSTP()) {
if (isType(102, 103)) {
className.append("_STP");
} else {
log.warning("Unexpected STP flag in MT " + getType());
}
} else if (isREMIT()) {
if (isType(103)) {
className.append("_REMIT");
} else {
log.warning("Unexpected REMIT flag in MT " + getType());
}
} else if (isCOV()) {
if (isType(202, 205)) {
className.append("COV");
} else {
log.warning("Unexpected COV flag in MT " + getType());
}
}
log.finer("About to create an instance of " + className);
try {
final Class> mtClass = Class.forName(className.toString());
return (AbstractMT) mtClass.getConstructor(SwiftMessage.class).newInstance(this);
} catch (final Exception e) {
log.warning("Could not create instance of " + className + ": " + e);
}
}
return null;
}
/**
*
Returns true if the message type is equal to the given number.
*
Notice this method only checks the message type number but can be combined with any
* message variant check such as {@link #isSTP()}, {@link #isREMIT()} or {@link #isCOV()}
* to determine the message kind precisely.
*
The implementation uses {@link #getTypeInt()}
*
* @param type message type number to check
* @return true if message type matches, false if does not match or cannot be determined because the message content is invalid
* @since 7.8.9
*/
public boolean isType(final int type) {
return getTypeInt() == type;
}
/**
* Returns true if the message type is equal to one of the given numbers.
* The implementation uses {@link #getTypeInt()}
*
* @param types message type numbers to check
* @return true if message type matches, false if does not match or cannot be determined because the message content is invalid
* @since 7.7
*/
public boolean isType(final int... types) {
final int mt = getTypeInt();
for (final int t : types) {
if (mt == t) {
return true;
}
}
return false;
}
/**
* Returns true if the message category is equal to one of the given by parameter
*
* @param categories the categories 0 to 9 to check
* @return true if message category, false if does not match or cannot be determined because the message content is invalid or the categories parameter contains values other than 0 to 9
* @since 7.8.8
*/
public final boolean isCategory(final MtCategory... categories) {
final MtCategory cat = getCategory();
for (final MtCategory t : categories) {
if (cat == t) {
return true;
}
}
return false;
}
/**
* Returns the message category from the message type.
* This implementation uses {@link #getType()} to retrieve the message type of the message.
*
* @return the category found as the first digit of the message type or null if block 2 is not found or the message type is not category number
*/
public final MtCategory getCategory() {
final String type = getType();
if (type != null) {
try {
return MtCategory.valueOf("_" + type.charAt(0));
} catch (final Exception e) {
final String text = "Error extracting category from message type " + getType();
log.warning(text);
log.log(Level.FINEST, text, e);
}
}
return null;
}
/**
* Returns true if message service id is anything but 01 = GPA/FIN Message (system and user-to-user)
*
* @return true if message is a service message, false otherwise
* @since 7.8.8
*/
public final boolean isServiceMessage() {
if (this.block1 == null) {
return false;
}
return this.block1.getServiceIdType() != ServiceIdType._01;
}
/**
* Returns true if message service id is 21 = GPA/FIN Message (ACK/NAK/UAK/UNK)
*
* @return true if it is a service message for acknowledgment, false if not or header is null and service id cannot be determined
* @since 7.8.9
*/
public boolean isServiceMessage21() {
if (this.block1 == null) {
return false;
}
return this.block1.getServiceIdType() == ServiceIdType._21;
}
/**
* Returns true if this message is an ACK.
* This is determined by testing first if it is a system message, and second
* the value of tag 451
*
* @return true if ACK, false otherwise
* @since 7.8
*/
public boolean isAck() {
if (isServiceMessage21()) {
if (this.block4 == null) {
return false;
}
return StringUtils.equals(this.block4.getTagValue(Field451.NAME), "0");
}
return false;
}
/**
* Returns true if this message is an NACK.
* This is determined by testing first if it is a system message, and second
* the value of tag 451
*
* @return true if NACK, false otherwise
* @since 7.8
*/
public boolean isNack() {
if (isServiceMessage21()) {
if (this.block4 == null) {
return false;
}
return StringUtils.equals(this.block4.getTagValue(Field451.NAME), "1");
}
return false;
}
/**
* @return the corresponding MT variant or null if flag field is not present
* @since 7.8
*/
public MTVariant getVariant() {
if (isCOV()) {
return MTVariant.COV;
} else if (isSTP()) {
return MTVariant.STP;
} else if (isREMIT()) {
return MTVariant.REMIT;
}
return null;
}
/**
* Sets or updates a variant (STP, REMIT, COV) in field 119 in block 3.
*
If the field already exists, its value will be updated; otherwise a new field will be created
*
* @param variant the variant (validation flag) to set in field 119
* @since 7.10.0
*/
public void setVariant(final MTVariant variant) {
if (!variant.isValidationFlag()) {
log.warning("Field " + Field199.NAME + " should be used only for validation flags and not for "
+ variant.name());
}
if (this.block3 == null) {
this.block3 = new SwiftBlock3();
}
this.block3.builder().setField119(new Field119(variant.name()));
}
/**
* Get a list of unique tagname contained in this message
*
* @return the list of tagnames or an empty list, does not return null ever
* @since 7.8
*/
public List getTagNames() {
if (this.block4 == null || this.block4.isEmpty()) {
return Collections.emptyList();
}
final List result = new ArrayList<>();
for (final Tag t : this.block4.getTags()) {
if (!result.contains(t.getName())) {
result.add(t.getName());
}
}
return result;
}
/**
* Returns the MT message identification.
* Composed by the business process, message type and variant.
* Example: fin.103.STP
*
* @return the constructed message id or null if message is a service message
* @since 7.8.4
*/
public MtId getMtId() {
if (isServiceMessage()) {
return null;
} else {
return new MtId(getType(), getVariant());
}
}
/**
* Returns the correspondent BIC code from the headers.
* For an outgoing message, the BIC address identifies the receiver of the message. Where for an incoming message it identifies the sender of the message.
*
* @return the correspondent BIC code or null if headers are not properly set
* @since 7.9.5
*/
public BIC getCorrespondentBIC() {
if (isOutgoing()) {
final String receiver = SwiftMessageUtils.receiver(this);
if (receiver != null) {
return new BIC(receiver);
}
}
if (isIncoming()) {
final String sender = SwiftMessageUtils.sender(this);
if (sender != null) {
return new BIC(sender);
}
}
return null;
}
/**
* Gets the Service Type Identifier (field 111 from block 3).
* This field is used by the SWIFT gpi service to track payments messages (category 1 and 2).
*
* @return the Service Type Identifier value or null if block3 or field 111 in block3 are not present
* @since 7.10.0
*/
public String getServiceTypeIdentifier() {
return this.block3 != null ? this.block3.getTagValue(Field111.NAME) : null;
}
/**
* Sets or updates the Service Type Identifier (field 111 in block 3).
*
If the field already exists, its value will be updated; otherwise a new field will be created
*
This field is used by the SWIFT gpi service to track payments messages (category 1 and 2).
*
* @param serviceTypeIdentifier the value for field 111
* @since 7.10.0
*/
public void setServiceTypeIdentifier(final String serviceTypeIdentifier) {
if (this.block3 == null) {
this.block3 = new SwiftBlock3();
}
this.block3.builder().setField111(new Field111(serviceTypeIdentifier));
}
/**
* Gets the Unique End to End Transaction Reference (field 121 from block 3).
*
This field is used by the SWIFT gpi service to track payments messages (category 1 and 2).
*
* @return the UETR value or null if block3 or field 121 in block3 are not present
* @since 7.10.0
*/
public String getUETR() {
return this.block3 != null ? this.block3.getTagValue(Field121.NAME) : null;
}
/**
* Sets or updates the Unique End to End Transaction Reference (field 121 in block 3).
*
If the field already exists, its value will be updated; otherwise a new field will be created
*
This field is used by the SWIFT gpi service to track payments messages (category 1 and 2).
*
* @param uniqueEndToEndTransactionReference the value for field 121
* @since 7.10.0
*/
public void setUETR(final String uniqueEndToEndTransactionReference) {
if (this.block3 == null) {
this.block3 = new SwiftBlock3();
}
this.block3.builder().setField121(new Field121(uniqueEndToEndTransactionReference));
}
/**
* Creates and sets the Unique End to End Transaction Reference (field 121 in block 3).
*
If the field already exists, its value will be updated
*
This field is used by the SWIFT gpi service to track payments messages (category 1 and 2).
*
* @return the UETR created (new value of field 121 in block3 after the operation)
* @since 7.10.0
*/
public String setUETR() {
String uuid36 = UETRUtils.generate();
setUETR(uuid36);
return uuid36;
}
/**
* Returns true if the message is part of the Global Payments Initiative (gpi) and thus requires the mandatory
* fields 111 and UETR for tracking within the SWIFT gpi service.
*
*
Notice this only reflects the mandatory GPI service message types for outgoing messages. More message types
* would be included as part of the GPI service if the application provider chooses to support the optional g4C and
* gFIT services.
*
* @return true if the message type is 103, 199, 299, 192, 196, 202COV or 205COV
* @see #setUETR()
* @since 7.10.0
*/
public boolean isGpi() {
return isType(103, 199, 299, 192, 196) || (isType(202, 205) && isCOV());
}
/**
* Serializes this message object into a String containing the FIN message.
*
* @return a string with the FIN format representation of the message
* @since 9.2.13
*/
public String message() {
IConversionService srv = new ConversionService();
return srv.getFIN(this);
}
}