Skip to content

Commit

Permalink
Merge branch 'next'; release 1.9.0
Browse files Browse the repository at this point in the history
  • Loading branch information
barend committed Apr 3, 2021
2 parents b90f1f8 + 4e5d803 commit 6c26041
Show file tree
Hide file tree
Showing 12 changed files with 250 additions and 47 deletions.
29 changes: 22 additions & 7 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
name: Automated checks
on: push
---
name: Build
"on": push

jobs:
unittests:
name: Run Unit Tests
runs-on: ubuntu-latest
strategy:
matrix:
java:
- 1.8 # LTS
- 11 # LTS
- 16 # Current
steps:
- name: Checkout Source
uses: actions/checkout@master
- name: Run Tests
uses: docker://maven:3.6-jdk-8-slim
uses: actions/checkout@v2
- name: Set up JDK ${{ matrix.java }}
uses: actions/setup-java@v1
with:
entrypoint: /usr/bin/mvn
args: --quiet --strict-checksums verify
java-version: ${{ matrix.java }}
- name: Maven Build
run: >
mvn
--batch-mode
--strict-checksums
--show-version
-Dmaven.compiler.source=${{ matrix.java }}
-Dmaven.compiler.target=${{ matrix.java }}
verify
34 changes: 29 additions & 5 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ version of the IBAN registry, but I don't plan on developing any new features.
* [Alternatives](#Alternatives)
* [Copyright and License](#Copyright-and-License)

[![Build](https://github.com/barend/java-iban/actions/workflows/main.yml/badge.svg)](https://github.com/barend/java-iban/actions/workflows/main.yml) [![CodeQL](https://github.com/barend/java-iban/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/barend/java-iban/actions/workflows/codeql-analysis.yml)

### Installation

Grab a package [from Github][download] or get it from Maven Central:
Expand All @@ -25,15 +27,15 @@ Grab a package [from Github][download] or get it from Maven Central:
<dependency>
<groupId>nl.garvelink.oss</groupId>
<artifactId>iban</artifactId>
<version>1.8.0</version>
<version>1.9.0</version>
</dependency>
```

#### Gradle

```groovy
dependencies {
compile 'nl.garvelink.oss:iban:1.8.0'
compile 'nl.garvelink.oss:iban:1.9.0'
}
```

Expand Down Expand Up @@ -73,6 +75,9 @@ Obtain an `IBAN` instance using one of the static factory methods: `valueOf( )`
String candidate = "GB29 NWBK 6016 1331 9268 19";
boolean valid = Modulo97.verifyCheckDigits( candidate ); // true

// Compose the IBAN for a country and BBAN
IBAN.compose( "BI", "201011067444" ); // BI43201011067444

// You can query whether an IBAN is of a SEPA-participating country
boolean isSepa = IBAN.parse(candidate).isSEPA(); // true

Expand All @@ -83,6 +88,11 @@ Obtain an `IBAN` instance using one of the static factory methods: `valueOf( )`
StringBuilder builder = new StringBuilder( "LU000019400644750000" );
int checkDigits = Modulo97.calculateCheckDigits( builder ); // 28

// Modulo97 API can calculate check digits, also for non-iban inputs.
// It does assume/require that the check digits are on indices 2 and 3.
Modulo97.calculateCheckDigits( "GB", "NWBK60161331926819" ); // 29
Modulo97.calculateCheckDigits( "XX", "X" ); // 50

// Get the expected IBAN length for a country code:
int length = CountryCodes.getLengthForCountryCode( "DK" );

Expand All @@ -97,8 +107,21 @@ Obtain an `IBAN` instance using one of the static factory methods: `valueOf( )`

### Version History

#### Unreleased
* Removes `@Generated` annotation from `CountryCodesData` class to avoid having a runtime dependency on newer JDK's.
#### 1.9.0: unreleased
* Compatible change: utility functions in `CountryCodes` now accept `java.lang.CharSequence` (was String).
* New API: `IBAN.compose(CharSequence, CharSequence)`.
* New API: `Modulo97.calculateCheckDigits(CharSequence, CharSequence)`.
* France (FR): add branch identifier ([#30][i30])
* Update to revision 89 of the SWIFT IBAN Registry
* Andorra (AD): is now SEPA
* Update to IBAN.com Experimental List
* No changes
* The project can now be compilead on Adopt-OpenJDK 11 HS. An outdated library used in the code generation step
prevented this.
* The `@javax.annotation.Generated` annotation has been removed from the `CountryCodesData` class. This annotation moved
into a library package in newer Java versions, and does not justify taking on a library dependency.

[i30]: https://github.com/barend/java-iban/issues/30

#### 1.8.0: 21 November 2020
* The `IBAN` class implements `java.io.Serializable` ([#23][i23]). The serialized form should stay valid across library
Expand Down Expand Up @@ -229,8 +252,9 @@ Obtain an `IBAN` instance using one of the static factory methods: `valueOf( )`
### Design Choices

I like the Joda-Time library and I try to follow the same design principles. I'm explicitly targetting Android, which
rules out some modern Java language constructs. I'm trying to keep the library as simple as I can.
at the time this library started was still on Java 1.6. I'm trying to keep the library as simple as I can.

* Easy to integrate: don't bring transitive dependencies.
* The `IBAN` objects are immutable and the IBAN therein is non-empty and valid. There is no support for partial or
invalid IBANs. Note that "valid" isn't as strict as it could be:
* It checks that the length is correct (varies per country) and that the check digits are correct.
Expand Down
79 changes: 58 additions & 21 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<groupId>nl.garvelink.oss</groupId>
<artifactId>iban</artifactId>
<version>1.8.1-SNAPSHOT</version>
<version>1.9.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>Java IBAN</name>
Expand Down Expand Up @@ -79,6 +79,8 @@

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>

<pluginRepositories>
Expand All @@ -100,7 +102,7 @@
<plugin>
<artifactId>template-maven-plugin</artifactId>
<groupId>com.github.terefang.template</groupId>
<version>2021.4.19</version>
<version>2021.4.28</version>
<executions>
<execution>
<id>generate-code</id>
Expand All @@ -115,20 +117,14 @@
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.hubspot.jinjava</groupId>
<artifactId>jinjava</artifactId>
<version>2.5.6</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>include-extra-sources-dir</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
Expand All @@ -146,8 +142,6 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<showWarnings>true</showWarnings>
<compilerArgs>
Expand Down Expand Up @@ -187,6 +181,7 @@
<artifactId>maven-release-plugin</artifactId>
<version>2.5.3</version>
<configuration>
<arguments>--strict-checksums</arguments>
<autoVersionSubmodules>true</autoVersionSubmodules>
<goals>deploy</goals>
<pushChanges>false</pushChanges>
Expand All @@ -199,6 +194,57 @@
</build>

<profiles>
<profile>
<!-- This profile adds the src/main/java11 directory to the build. -->
<id>java11-module</id>
<activation>
<!-- We cannot use the <jdk> activation rule, because that looks at the
host JVM, not at the compiler target version. We can compile for
Java 8 while running on JDK 11, and if we do, the module-info file
causes a compiler error.
So instead we evaluate the maven.compiler.target property. The
downside here is that we cannot compare against a version range.
There's no or-operator either. The result is that we only evaluate
the module-info file when targeting exactly version 11; not "any
version with module support".
This is not ideal, but at least it ensures that the CI build covers
the module-info file, so it's good enough.
Note that the compiled module-info that we ship in the JAR file
lives in src/main/resources; we don't compile the module info
as part of the release build.-->
<property>
<name>maven.compiler.target</name>
<value>11</value>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>include-extra-sources-dir</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${project.basedir}/src/main/java11</source>
<source>${project.build.directory}/generated-sources/jinjava</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>release</id>
<build>
Expand Down Expand Up @@ -246,15 +292,6 @@
</plugins>
</build>
</profile>
<profile>
<id>github</id>
<distributionManagement>
<repository>
<id>github</id>
<name>GitHub Apache Maven Packages</name>
<url>https://maven.pkg.github.com/barend/java-iban</url>
</repository>
</distributionManagement>
</profile>
</profiles>
</project>
<!-- vim: set et sw=4: -->
16 changes: 8 additions & 8 deletions src/main/java/nl/garvelink/iban/CountryCodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ static String getBranchIdentifier(IBAN iban) {
* @return the IBAN length for the given country, or -1 if the input is not a known, two-character country code.
* @throws NullPointerException if the input is null.
*/
public static int getLengthForCountryCode(String countryCode) {
int index = indexOf(countryCode);
public static int getLengthForCountryCode(CharSequence countryCode) {
int index = indexOf(countryCode.toString());
if (index > -1) {
return COUNTRY_IBAN_LENGTHS[index] & REMOVE_METADATA_MASK;
}
Expand All @@ -114,8 +114,8 @@ public static int getLengthForCountryCode(String countryCode) {
* @return true if SEPA, false if not.
* @throws NullPointerException if the input is null.
*/
public static boolean isSEPACountry(String countryCode) {
int index = indexOf(countryCode);
public static boolean isSEPACountry(CharSequence countryCode) {
int index = indexOf(countryCode.toString());
if (index > -1) {
return (COUNTRY_IBAN_LENGTHS[index] & SEPA) == SEPA;
}
Expand All @@ -128,8 +128,8 @@ public static boolean isSEPACountry(String countryCode) {
* @return true if our data is from the SWIFT IBAN Registry, false if not.
* @throws NullPointerException if the input is null.
*/
public static boolean isInSwiftRegistry(String countryCode) {
int index = indexOf(countryCode);
public static boolean isInSwiftRegistry(CharSequence countryCode) {
int index = indexOf(countryCode.toString());
if (index > -1) {
return (COUNTRY_IBAN_LENGTHS[index] & SWIFT) == SWIFT;
}
Expand All @@ -149,11 +149,11 @@ public static Collection<String> getKnownCountryCodes() {
* @param aCountryCode the string to evaluate.
* @return {@code true} if {@code aCountryCode} is a two-letter, uppercase String present in {@link #getKnownCountryCodes()}.
*/
public static boolean isKnownCountryCode(String aCountryCode) {
public static boolean isKnownCountryCode(CharSequence aCountryCode) {
if (aCountryCode == null || aCountryCode.length() != 2) {
return false;
}
return indexOf(aCountryCode) >= 0;
return indexOf(aCountryCode.toString()) >= 0;
}

/**
Expand Down
17 changes: 16 additions & 1 deletion src/main/java/nl/garvelink/iban/IBAN.java
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,22 @@ public static IBAN valueOf(CharSequence input) {
}

/**
* @deprecated invoke {@link CountryCodes#getLengthForCountryCode(String)} instead.
* Composes an IBAN from the given country code and basic bank account number.
* @param countryCode the country code.
* @param bban the BBAN.
* @return an IBAN object composed from the given parts, if valid.
* @throws IllegalArgumentException if either input is null, if the composed IBAN fails validation.
*/
public static IBAN compose(CharSequence countryCode, CharSequence bban) {
StringBuilder sb =
new StringBuilder(CountryCodes.LONGEST_IBAN_LENGTH).append(countryCode).append("00").append(bban);
int checkDigits = Modulo97.calculateCheckDigits(sb);
sb.replace(2, 4, Integer.toString(checkDigits));
return parse(sb);
}

/**
* @deprecated invoke {@link CountryCodes#getLengthForCountryCode(CharSequence)} instead.
* @param countryCode the country code for which to return the length.
* @return the length of the IBAN for the given country code, or -1 if unknown.
*/
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/nl/garvelink/iban/IBANFields.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,7 @@ public static Optional<String> getBankIdentifier(IBAN iban) {
public static Optional<String> getBranchIdentifier(IBAN iban) {
return Optional.ofNullable(CountryCodes.getBranchIdentifier(iban));
}

/** Prevent instantiation of static utility class. */
private IBANFields() { }
}
3 changes: 3 additions & 0 deletions src/main/java/nl/garvelink/iban/IBANFieldsCompat.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,7 @@ public static String getBankIdentifier(IBAN iban) {
public static String getBranchIdentifier(IBAN iban) {
return CountryCodes.getBranchIdentifier(iban);
}

/** Prevent instantiation of static utility class. */
private IBANFieldsCompat() { }
}
25 changes: 25 additions & 0 deletions src/main/java/nl/garvelink/iban/Modulo97.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,31 @@ public static int calculateCheckDigits(CharSequence input) {
return 98 - checksum(input);
}

/**
* Calculates the check digits for a given country code and BBAN.
* @param countryCode the country code. Not validated to be a known country.
* @param bban the country-specific BBAN. Not validated to required length.
* @return the check digits to be used at indices 2 and 3 to make the input MOD97 verifiable.
* @throws IllegalArgumentException if either input is null, or the country code is not two characters.
*/
public static int calculateCheckDigits(CharSequence countryCode, CharSequence bban) {
if (countryCode == null) {
throw new IllegalArgumentException("countryCode is required but is null.");
}
if (bban == null) {
throw new IllegalArgumentException("bban is required but is null.");
}
if (countryCode.length() != 2) {
throw new IllegalArgumentException(
String.format("countryCode should be length 2 but is %d", countryCode.length()));
}
if (countryCode.charAt(0) == ' ' || countryCode.charAt(1) == ' ') {
throw new IllegalArgumentException("countryCode contains space character (0x20).");
}
StringBuilder sb = new StringBuilder(countryCode).append("00").append(bban);
return calculateCheckDigits(sb);
}

/**
* Determines whether the given input has a valid MOD97 checksum.
* @param input the input to verify, it must meet the criteria defined in {@link #checksum(CharSequence)}.
Expand Down
Loading

0 comments on commit 6c26041

Please sign in to comment.