Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Prowide Core - CHANGELOG

#### 10.3.20 - SNAPSHOT
* Feat: new `IsoUtils#getCountryName(code)` with the ISO 3166-1 English short name of a country, looked up by its alpha-2, alpha-3 or numeric code
* Feat: `IsoUtils#isUserAssignedCountryCode(code)` is now public, for the ISO 3166-1 user assigned range XA to XZ; it no longer accepts an upper case letter of another alphabet after the X, so `isValidISOCountry` rejects codes such as "XЖ"

#### 10.3.19 - August 2026
* (PW-3433) Fix: `OptionJPartyField.getValueByCodeword` no longer shifts the codeword/value pairs that follow a codeword with a blank value (for example "/CITY/" followed by "/USFW/021000018" returned "USFW" for CITY and null for USFW); a codeword present with a blank value now returns an empty string
* (GH-341) Fix: `SwiftMessageUtils.money` revised for category 7: amount added for MT744, MT760, MT765 and MT786; changed to field 32B for MT750 and MT752, and to 34B for MT769; removed for MT707
Expand Down
110 changes: 108 additions & 2 deletions src/main/java/com/prowidesoftware/swift/utils/IsoUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
*/
package com.prowidesoftware.swift.utils;

import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
Expand All @@ -27,6 +29,9 @@
* change or add new values. This can be particularly helpful when the application is not running on
* the latest Java version and a currency change or addition has not yet been updated in the used JRE.
*
* <p>The ISO 3166-1 country names are also available, by alpha-2, alpha-3 or numeric code, with
* {@link #getCountryName(String)}.
*
* @since 7.9.2
*/
public final class IsoUtils {
Expand Down Expand Up @@ -96,8 +101,109 @@ public boolean isValidISOCountry(String countryCode) {
return false;
}

private boolean isUserAssignedCountryCode(String code) {
return code.charAt(0) == 'X' && Character.isUpperCase(code.charAt(1));
/**
* Checks if the country code belongs to the ISO 3166-1 user assigned range XA to XZ, for example XK or XX.
*
* <p>These codes are accepted by {@link #isValidISOCountry(String)} regardless of the country codes list. The other
* ISO 3166-1 user assigned codes (AA, QM to QZ and ZZ) are not part of this range.
*
* @param countryCode a two letters capitalized country code, example: XK
* @return true if the parameter is an X followed by a letter in the A-Z range, false otherwise, including null
* @since 10.3.20
*/
public boolean isUserAssignedCountryCode(String countryCode) {
return StringUtils.length(countryCode) == 2
&& countryCode.charAt(0) == 'X'
&& countryCode.charAt(1) >= 'A'
&& countryCode.charAt(1) <= 'Z';
}

/**
* Gets the English short name of a country from its ISO 3166-1 code.
*
* <p>The parameter can be the alpha-2 code (US), the alpha-3 code (USA) or the numeric code (840). The lookup is
* case-insensitive, ignores surrounding whitespace, accepts a numeric code with fewer or more leading zeros than
* the three digits of the ISO code (28 or 0028 for Antigua and Barbuda, whose ISO numeric code is 028), and does
* not depend on the default locale.
*
* <p>The names are the English short names of the officially assigned codes, in the comma-inverted form of the
* ISO 3166-1 lists (for example "Bolivia, Plurinational State of"). This catalog is independent of the country
* codes list: a code added with {@link #addCountry(String)} has no name, and the user assigned codes (the XA to
* XZ range) have no name either.
*
* @param code an alpha-2, alpha-3 or numeric ISO 3166-1 country code
* @return the English short name of the country, or null if the parameter is null, blank or not an officially
* assigned code
* @since 10.3.20
*/
public String getCountryName(String code) {
if (code == null) {
return null;
}
String key = code.trim().toUpperCase(Locale.ROOT);
if (isAsciiDigits(key)) {
String digits = stripLeadingZeros(key);
if (digits.length() > 3) {
return null;
}
key = "000".substring(digits.length()) + digits;
}
return CountryNames.BY_CODE.get(key);
}

private static boolean isAsciiDigits(String s) {
if (s.isEmpty()) {
return false;
}
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c < '0' || c > '9') {
return false;
}
}
return true;
}

private static String stripLeadingZeros(String digits) {
int i = 0;
while (i < digits.length() && digits.charAt(i) == '0') {
i++;
}
return digits.substring(i);
}

/**
* Lazily loaded ISO 3166-1 names, keyed by the alpha-2, alpha-3 and numeric codes.
*
* <p>The data comes from the IsoCountries.txt resource, one country per line as alpha-2;alpha-3;numeric;name,
* with lines starting with # ignored.
*/
private static final class CountryNames {
private static final Map<String, String> BY_CODE = load();

private static Map<String, String> load() {
Map<String, String> map = new HashMap<>();
try {
String content = Lib.readResource("IsoCountries.txt", "UTF-8", IsoUtils.class);
for (String line : content.split("\\R")) {
if (line.isEmpty() || line.charAt(0) == '#') {
continue;
}
String[] fields = line.split(";", 4);
if (fields.length == 4) {
map.put(fields[0], fields[3]);
map.put(fields[1], fields[3]);
map.put(fields[2], fields[3]);
}
}
} catch (IOException e) {
log.log(Level.SEVERE, "Could not load the ISO 3166-1 country names", e);
}
if (map.isEmpty()) {
log.severe("The ISO 3166-1 country names resource IsoCountries.txt is missing or empty");
}
return map;
}
}

/**
Expand Down
250 changes: 250 additions & 0 deletions src/main/resources/IsoCountries.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
# ISO 3166-1 officially assigned countries: alpha-2;alpha-3;numeric;English short name
AD;AND;020;Andorra
AE;ARE;784;United Arab Emirates
AF;AFG;004;Afghanistan
AG;ATG;028;Antigua and Barbuda
AI;AIA;660;Anguilla
AL;ALB;008;Albania
AM;ARM;051;Armenia
AO;AGO;024;Angola
AQ;ATA;010;Antarctica
AR;ARG;032;Argentina
AS;ASM;016;American Samoa
AT;AUT;040;Austria
AU;AUS;036;Australia
AW;ABW;533;Aruba
AX;ALA;248;Åland Islands
AZ;AZE;031;Azerbaijan
BA;BIH;070;Bosnia and Herzegovina
BB;BRB;052;Barbados
BD;BGD;050;Bangladesh
BE;BEL;056;Belgium
BF;BFA;854;Burkina Faso
BG;BGR;100;Bulgaria
BH;BHR;048;Bahrain
BI;BDI;108;Burundi
BJ;BEN;204;Benin
BL;BLM;652;Saint Barthélemy
BM;BMU;060;Bermuda
BN;BRN;096;Brunei Darussalam
BO;BOL;068;Bolivia, Plurinational State of
BQ;BES;535;Bonaire, Sint Eustatius and Saba
BR;BRA;076;Brazil
BS;BHS;044;Bahamas
BT;BTN;064;Bhutan
BV;BVT;074;Bouvet Island
BW;BWA;072;Botswana
BY;BLR;112;Belarus
BZ;BLZ;084;Belize
CA;CAN;124;Canada
CC;CCK;166;Cocos (Keeling) Islands
CD;COD;180;Congo, Democratic Republic of the
CF;CAF;140;Central African Republic
CG;COG;178;Congo
CH;CHE;756;Switzerland
CI;CIV;384;Côte d'Ivoire
CK;COK;184;Cook Islands
CL;CHL;152;Chile
CM;CMR;120;Cameroon
CN;CHN;156;China
CO;COL;170;Colombia
CR;CRI;188;Costa Rica
CU;CUB;192;Cuba
CV;CPV;132;Cabo Verde
CW;CUW;531;Curaçao
CX;CXR;162;Christmas Island
CY;CYP;196;Cyprus
CZ;CZE;203;Czechia
DE;DEU;276;Germany
DJ;DJI;262;Djibouti
DK;DNK;208;Denmark
DM;DMA;212;Dominica
DO;DOM;214;Dominican Republic
DZ;DZA;012;Algeria
EC;ECU;218;Ecuador
EE;EST;233;Estonia
EG;EGY;818;Egypt
EH;ESH;732;Western Sahara
ER;ERI;232;Eritrea
ES;ESP;724;Spain
ET;ETH;231;Ethiopia
FI;FIN;246;Finland
FJ;FJI;242;Fiji
FK;FLK;238;Falkland Islands (Malvinas)
FM;FSM;583;Micronesia, Federated States of
FO;FRO;234;Faroe Islands
FR;FRA;250;France
GA;GAB;266;Gabon
GB;GBR;826;United Kingdom of Great Britain and Northern Ireland
GD;GRD;308;Grenada
GE;GEO;268;Georgia
GF;GUF;254;French Guiana
GG;GGY;831;Guernsey
GH;GHA;288;Ghana
GI;GIB;292;Gibraltar
GL;GRL;304;Greenland
GM;GMB;270;Gambia
GN;GIN;324;Guinea
GP;GLP;312;Guadeloupe
GQ;GNQ;226;Equatorial Guinea
GR;GRC;300;Greece
GS;SGS;239;South Georgia and the South Sandwich Islands
GT;GTM;320;Guatemala
GU;GUM;316;Guam
GW;GNB;624;Guinea-Bissau
GY;GUY;328;Guyana
HK;HKG;344;Hong Kong
HM;HMD;334;Heard Island and McDonald Islands
HN;HND;340;Honduras
HR;HRV;191;Croatia
HT;HTI;332;Haiti
HU;HUN;348;Hungary
ID;IDN;360;Indonesia
IE;IRL;372;Ireland
IL;ISR;376;Israel
IM;IMN;833;Isle of Man
IN;IND;356;India
IO;IOT;086;British Indian Ocean Territory
IQ;IRQ;368;Iraq
IR;IRN;364;Iran, Islamic Republic of
IS;ISL;352;Iceland
IT;ITA;380;Italy
JE;JEY;832;Jersey
JM;JAM;388;Jamaica
JO;JOR;400;Jordan
JP;JPN;392;Japan
KE;KEN;404;Kenya
KG;KGZ;417;Kyrgyzstan
KH;KHM;116;Cambodia
KI;KIR;296;Kiribati
KM;COM;174;Comoros
KN;KNA;659;Saint Kitts and Nevis
KP;PRK;408;Korea, Democratic People's Republic of
KR;KOR;410;Korea, Republic of
KW;KWT;414;Kuwait
KY;CYM;136;Cayman Islands
KZ;KAZ;398;Kazakhstan
LA;LAO;418;Lao People's Democratic Republic
LB;LBN;422;Lebanon
LC;LCA;662;Saint Lucia
LI;LIE;438;Liechtenstein
LK;LKA;144;Sri Lanka
LR;LBR;430;Liberia
LS;LSO;426;Lesotho
LT;LTU;440;Lithuania
LU;LUX;442;Luxembourg
LV;LVA;428;Latvia
LY;LBY;434;Libya
MA;MAR;504;Morocco
MC;MCO;492;Monaco
MD;MDA;498;Moldova, Republic of
ME;MNE;499;Montenegro
MF;MAF;663;Saint Martin (French part)
MG;MDG;450;Madagascar
MH;MHL;584;Marshall Islands
MK;MKD;807;North Macedonia
ML;MLI;466;Mali
MM;MMR;104;Myanmar
MN;MNG;496;Mongolia
MO;MAC;446;Macao
MP;MNP;580;Northern Mariana Islands
MQ;MTQ;474;Martinique
MR;MRT;478;Mauritania
MS;MSR;500;Montserrat
MT;MLT;470;Malta
MU;MUS;480;Mauritius
MV;MDV;462;Maldives
MW;MWI;454;Malawi
MX;MEX;484;Mexico
MY;MYS;458;Malaysia
MZ;MOZ;508;Mozambique
NA;NAM;516;Namibia
NC;NCL;540;New Caledonia
NE;NER;562;Niger
NF;NFK;574;Norfolk Island
NG;NGA;566;Nigeria
NI;NIC;558;Nicaragua
NL;NLD;528;Netherlands, Kingdom of the
NO;NOR;578;Norway
NP;NPL;524;Nepal
NR;NRU;520;Nauru
NU;NIU;570;Niue
NZ;NZL;554;New Zealand
OM;OMN;512;Oman
PA;PAN;591;Panama
PE;PER;604;Peru
PF;PYF;258;French Polynesia
PG;PNG;598;Papua New Guinea
PH;PHL;608;Philippines
PK;PAK;586;Pakistan
PL;POL;616;Poland
PM;SPM;666;Saint Pierre and Miquelon
PN;PCN;612;Pitcairn
PR;PRI;630;Puerto Rico
PS;PSE;275;Palestine, State of
PT;PRT;620;Portugal
PW;PLW;585;Palau
PY;PRY;600;Paraguay
QA;QAT;634;Qatar
RE;REU;638;Réunion
RO;ROU;642;Romania
RS;SRB;688;Serbia
RU;RUS;643;Russian Federation
RW;RWA;646;Rwanda
SA;SAU;682;Saudi Arabia
SB;SLB;090;Solomon Islands
SC;SYC;690;Seychelles
SD;SDN;729;Sudan
SE;SWE;752;Sweden
SG;SGP;702;Singapore
SH;SHN;654;Saint Helena, Ascension and Tristan da Cunha
SI;SVN;705;Slovenia
SJ;SJM;744;Svalbard and Jan Mayen
SK;SVK;703;Slovakia
SL;SLE;694;Sierra Leone
SM;SMR;674;San Marino
SN;SEN;686;Senegal
SO;SOM;706;Somalia
SR;SUR;740;Suriname
SS;SSD;728;South Sudan
ST;STP;678;Sao Tome and Principe
SV;SLV;222;El Salvador
SX;SXM;534;Sint Maarten (Dutch part)
SY;SYR;760;Syrian Arab Republic
SZ;SWZ;748;Eswatini
TC;TCA;796;Turks and Caicos Islands
TD;TCD;148;Chad
TF;ATF;260;French Southern Territories
TG;TGO;768;Togo
TH;THA;764;Thailand
TJ;TJK;762;Tajikistan
TK;TKL;772;Tokelau
TL;TLS;626;Timor-Leste
TM;TKM;795;Turkmenistan
TN;TUN;788;Tunisia
TO;TON;776;Tonga
TR;TUR;792;Türkiye
TT;TTO;780;Trinidad and Tobago
TV;TUV;798;Tuvalu
TW;TWN;158;Taiwan, Province of China
TZ;TZA;834;Tanzania, United Republic of
UA;UKR;804;Ukraine
UG;UGA;800;Uganda
UM;UMI;581;United States Minor Outlying Islands
US;USA;840;United States of America
UY;URY;858;Uruguay
UZ;UZB;860;Uzbekistan
VA;VAT;336;Holy See
VC;VCT;670;Saint Vincent and the Grenadines
VE;VEN;862;Venezuela, Bolivarian Republic of
VG;VGB;092;Virgin Islands (British)
VI;VIR;850;Virgin Islands (U.S.)
VN;VNM;704;Viet Nam
VU;VUT;548;Vanuatu
WF;WLF;876;Wallis and Futuna
WS;WSM;882;Samoa
YE;YEM;887;Yemen
YT;MYT;175;Mayotte
ZA;ZAF;710;South Africa
ZM;ZMB;894;Zambia
ZW;ZWE;716;Zimbabwe
Loading
Loading