diff --git a/.gitignore b/.gitignore index ba96765..7a5a921 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,7 @@ pom.xml.next pom.xml.releaseBackup pom.xml.tag release.properties + +# Python-based code generator +venv/ +.python-version diff --git a/codegen.py b/codegen.py new file mode 100644 index 0000000..9f1008e --- /dev/null +++ b/codegen.py @@ -0,0 +1,62 @@ +#! /usr/bin/env python3 +# Copyright 2021 Barend Garvelink +# +# 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. + +""" +Code generator for the country codes file. +""" +import os +import json +from pathlib import Path + +from jinja2 import FileSystemLoader, Environment +from yaml import safe_load as yaml_load + + +def expand_templates(basedir: Path, context: dict): + template_dir = basedir.joinpath("src/main/jinja2") + target_dir = basedir.joinpath("target/generated-sources/jinja2") + + def flat_get(obj, path: str, def_val=None): + for elem in path.split("."): + if elem not in obj: + return def_val if def_val is not None else environment.undefined(name=f"'{path}' (at '{elem}')") + obj = obj[elem] + return obj + + environment = Environment(loader=FileSystemLoader(template_dir), autoescape=False) + environment.filters["date_time_format"] = lambda dt, pat: dt.strftime(pat) + environment.filters["escape_java_string"] = lambda s: json.dumps(s).strip("\"") + environment.filters["flat_get"] = flat_get + + for template in environment.list_templates(): + template_path = Path(template) + output_path = Path(target_dir).joinpath(template_path.parent) + output_path.mkdir(parents=True, exist_ok=True) + output_fqfn = output_path.joinpath(template_path.with_suffix("").name) + with open(output_fqfn, "w") as outfile: + print(f"Render {template} to {output_fqfn}") + print(environment.get_template(template).render(context), file=outfile) + + +def load_context(basedir: Path) -> dict: + data_file = basedir.joinpath("src/main/resources/nl/garvelink/iban/IBAN.yml") + with open(data_file, "r") as df: + return yaml_load(df) + + +if __name__ == "__main__": + basedir = Path(os.getcwd()) + context = load_context(basedir) + expand_templates(basedir, context) diff --git a/codegen.sh b/codegen.sh new file mode 100755 index 0000000..2060f0f --- /dev/null +++ b/codegen.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -eu + +if [ ! -d "venv" ]; then + /usr/bin/env python3 -m venv venv +fi +venv/bin/pip3 install --progress-bar off --require-hashes -r requirements.txt +venv/bin/python codegen.py diff --git a/docs/index.md b/docs/index.md index 69eb09e..f870719 100644 --- a/docs/index.md +++ b/docs/index.md @@ -107,6 +107,11 @@ Obtain an `IBAN` instance using one of the static factory methods: `valueOf( )` ### Version History +#### 1.9.1: Unreleased +* Drop `template-maven-plugin`. It has proven to make the CI build very flaky. This also removes the build-time + dependency on a third-party artifact repository. The downside is that the build now requires Python 3 and a bourne + shell. It builds on WSL2 just fine, but I have no idea how to build this natively on Windows. + #### 1.9.0: 3 April 2021 * Compatible change: utility functions in `CountryCodes` now accept `java.lang.CharSequence` (was String). * New API method: `IBAN.compose(CharSequence, CharSequence)`. diff --git a/pom.xml b/pom.xml index 6c6301c..15734b2 100644 --- a/pom.xml +++ b/pom.xml @@ -83,56 +83,23 @@ 1.8 - - - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - - jitpack.io - https://jitpack.io - - - - - template-maven-plugin - com.github.terefang.template - 2021.4.28 - - - generate-code - generate-sources - - jinjava-standard - - - src/main/resources/nl/garvelink/iban/IBAN.yml - src/main/jinjava/ - ${project.build.directory}/generated-sources/jinjava - - - - org.codehaus.mojo - build-helper-maven-plugin - 3.2.0 + exec-maven-plugin + 3.0.0 - include-extra-sources-dir + generate-code generate-sources - add-source + exec - - ${project.build.directory}/generated-sources/jinjava - + codegen.sh + ${project.build.directory}/generated-sources/jinja2 + true @@ -236,7 +203,7 @@ ${project.basedir}/src/main/java11 - ${project.build.directory}/generated-sources/jinjava + ${project.build.directory}/generated-sources/jinja2 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..d6f8740 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +Jinja2==3.0.1 \ + --hash=sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4 +MarkupSafe==2.0.1 \ + --hash=sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a +PyYAML==5.4.1 \ + --hash=sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e diff --git a/src/main/jinjava/nl/garvelink/iban/CountryCodesData.java.j2 b/src/main/jinja2/nl/garvelink/iban/CountryCodesData.java.j2 similarity index 77% rename from src/main/jinjava/nl/garvelink/iban/CountryCodesData.java.j2 rename to src/main/jinja2/nl/garvelink/iban/CountryCodesData.java.j2 index c95e35a..bd25b24 100755 --- a/src/main/jinjava/nl/garvelink/iban/CountryCodesData.java.j2 +++ b/src/main/jinja2/nl/garvelink/iban/CountryCodesData.java.j2 @@ -21,17 +21,17 @@ import java.util.Collections; /** * Contains information about IBAN country codes. This is a generated file. - * Updated to SWIFT IBAN Registry version {{ context.meta.iban_registry_version | escape }} on {{ context.meta.last_update | datetimeformat("%Y-%m-%d") | escape }}. + * Updated to SWIFT IBAN Registry version {{ meta.iban_registry_version | escape }} on {{ meta.last_update | date_time_format("%Y-%m-%d") | escape }}. */ abstract class CountryCodesData { /** * The "yyyy-MM-dd" datestamp that the embedded IBAN data was updated. */ - static final String LAST_UPDATE_DATE = "{{ context.meta.last_update | datetimeformat("%Y-%m-%d") | escapejson }}"; + static final String LAST_UPDATE_DATE = "{{ meta.last_update | date_time_format("%Y-%m-%d") | escape_java_string }}"; /** * The revision of the SWIFT IBAN Registry to which the embedded IBAN data was updated. */ - static final String LAST_UPDATE_REV = "{{ context.meta.iban_registry_version | escapejson }}"; + static final String LAST_UPDATE_REV = "{{ meta.iban_registry_version | escape_java_string }}"; static final int SEPA = 1 << 8; static final int SWIFT = 1 << 9; @@ -44,12 +44,12 @@ abstract class CountryCodesData { static final int BRANCH_IDENTIFIER_END_SHIFT = 24; static final int BRANCH_IDENTIFIER_END_MASK = 0xFF << BRANCH_IDENTIFIER_END_SHIFT; -/** + /** * Known country codes, this list must be sorted to allow binary search. All other lists in this file must use the * same indices for the same countries. */ static final String[] COUNTRY_CODES = { -{%- for iban in context.ibans %} +{%- for iban in ibans %} "{{ iban.country_code }}"{% if not loop.last %},{% endif %}{# #}{% endfor %} }; @@ -60,7 +60,7 @@ abstract class CountryCodesData { * whether the record is listed in the SWIFT IBAN Registry. */ static final int[] COUNTRY_IBAN_LENGTHS = { -{%- for iban in context.ibans %} +{%- for iban in ibans %} /* {{ iban.country_code }} */ {{ iban.length }}{% if iban.flags.in_swift_registry %} | SWIFT{% endif %}{% if iban.flags.sepa_country %} | SEPA{% endif %}{# #}{% if not loop.last %},{% endif %}{# #}{% endfor %} @@ -78,12 +78,12 @@ abstract class CountryCodesData { * */ static final int[] BANK_CODE_BRANCH_CODE = { -{%- for iban in context.ibans %} +{%- for iban in ibans %} /* {{ iban.country_code }} */ - {{ iban.embeds.bank_code.position | default(0) }} - | ({{ iban.embeds.bank_code.position | default(0) }} + {{ iban.embeds.bank_code.length | default(0) }}) << BANK_IDENTIFIER_END_SHIFT - | {{ iban.embeds.branch_code.position | default(0) }} << BRANCH_IDENTIFIER_BEGIN_SHIFT - | ({{ iban.embeds.branch_code.position | default(0) }} + {{ iban.embeds.branch_code.length | default(0) }}) << BRANCH_IDENTIFIER_END_SHIFT{# + {{ iban | flat_get("embeds.bank_code.position", 0) }} + | ({{ iban | flat_get("embeds.bank_code.position", 0) }} + {{ iban | flat_get("embeds.bank_code.length", 0) }}) << BANK_IDENTIFIER_END_SHIFT + | {{ iban | flat_get("embeds.branch_code.position", 0) }} << BRANCH_IDENTIFIER_BEGIN_SHIFT + | ({{ iban | flat_get("embeds.branch_code.position", 0) }} + {{ iban | flat_get("embeds.branch_code.length", 0) }}) << BRANCH_IDENTIFIER_END_SHIFT{# #}{% if not loop.last %},{% endif %} {% endfor %} };