From b7d6438f4d1def8794c97e9969cb50eab0410d7b Mon Sep 17 00:00:00 2001 From: Markus Kreth Date: Tue, 6 Aug 2024 23:05:57 +0200 Subject: [PATCH] Generator Configuration supports GeneratorOptions --- pom.xml | 31 +-- pom.xml.versionsBackup | 149 +++++++++++++ .../de/kreth/property2java/Configuration.java | 5 + .../de/kreth/property2java/Generator.java | 3 + .../kreth/property2java/GeneratorOptions.java | 5 + .../enum_template_with_initializer.tpl | 8 +- .../enum_template_with_inner_properties.tpl | 11 +- ...late_with_inner_propertyresourcebundle.tpl | 8 +- .../template/parts/WithSubstitutors.tpl | 70 ++++++ .../template/parts/key_value_part.tpl | 7 + .../resources/template/parts/withOptions.tpl | 5 + .../kreth/property2java/GeneratorTests.java | 2 +- .../GeneratorWithInnerPropertiesTest.java | 8 +- .../property2java/TestPropertiesSource.java | 11 +- .../generated/GenerateTheTest.java | 47 ++++ .../Property_Loader_Options_Properties.java | 203 ++++++++++++++++++ .../generated/Property_Loader_Properties.java | 13 +- .../generated/Resource_Bundle_Properties.java | 11 +- .../generated/TestRealGenerated.java | 5 + .../generated/Unary_Operator_Properties.java | 8 +- .../parts/ReplaceLogicForTemplate.java | 65 ++++++ .../parts/ReplaceLogicForTemplateTest.java | 82 +++++++ src/test/resources/property_loader.properties | 1 + .../property_loader_options.properties | 25 +++ src/test/resources/resource_bundle.properties | 1 + src/test/resources/unary_operator.properties | 1 + 26 files changed, 735 insertions(+), 50 deletions(-) create mode 100644 pom.xml.versionsBackup create mode 100644 src/main/java/de/kreth/property2java/GeneratorOptions.java create mode 100644 src/main/resources/template/parts/WithSubstitutors.tpl create mode 100644 src/main/resources/template/parts/key_value_part.tpl create mode 100644 src/main/resources/template/parts/withOptions.tpl create mode 100644 src/test/java/de/kreth/property2java/generated/Property_Loader_Options_Properties.java create mode 100644 src/test/java/de/kreth/property2java/parts/ReplaceLogicForTemplate.java create mode 100644 src/test/java/de/kreth/property2java/parts/ReplaceLogicForTemplateTest.java create mode 100644 src/test/resources/property_loader_options.properties diff --git a/pom.xml b/pom.xml index ae36c05..30ed903 100644 --- a/pom.xml +++ b/pom.xml @@ -23,22 +23,22 @@ org.apache.commons commons-lang3 - 3.8.1 + 3.15.0 org.apache.commons commons-text - 1.10.0 + 1.12.0 commons-cli commons-cli - 1.4 + 1.8.0 org.freemarker freemarker - 2.3.28 + 2.3.33 @@ -70,51 +70,58 @@ org.junit.jupiter junit-jupiter-api - 5.3.2 + 5.11.0-RC1 test org.junit.jupiter junit-jupiter-engine - 5.3.2 + 5.11.0-RC1 test org.junit.jupiter junit-jupiter-params - 5.3.2 + 5.11.0-RC1 test org.junit.platform junit-platform-launcher - 1.5.0-M1 + 1.11.0-RC1 test org.junit.platform junit-platform-runner - 1.5.0-M1 + 1.11.0-RC1 test org.mockito mockito-core - 2.24.5 + 5.12.0 test org.mockito mockito-junit-jupiter - 2.24.5 + 5.12.0 test org.hamcrest hamcrest-library - 2.1-rc4 + 3.0 test + + org.assertj + assertj-core + 3.26.3 + test + + diff --git a/pom.xml.versionsBackup b/pom.xml.versionsBackup new file mode 100644 index 0000000..ae36c05 --- /dev/null +++ b/pom.xml.versionsBackup @@ -0,0 +1,149 @@ + + + + 4.0.0 + de.kreth.property2java + PropertyToJavaGenerator + 2.0.3-SNAPSHOT + + + + UTF-8 + UTF-8 + 11 + 5.3.0-M1 + 1.7.36 + 2.17.2 + + ${maven.build.timestamp} + yyyy-MM-dd HH:mm:ss + + + + + org.apache.commons + commons-lang3 + 3.8.1 + + + org.apache.commons + commons-text + 1.10.0 + + + commons-cli + commons-cli + 1.4 + + + org.freemarker + freemarker + 2.3.28 + + + + + org.slf4j + slf4j-api + ${org.slf4j} + + + org.apache.logging.log4j + log4j-api + ${org.apache.logging.log4j} + runtime + + + org.apache.logging.log4j + log4j-core + ${org.apache.logging.log4j} + runtime + + + org.apache.logging.log4j + log4j-slf4j-impl + ${org.apache.logging.log4j} + runtime + + + + + org.junit.jupiter + junit-jupiter-api + 5.3.2 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.3.2 + test + + + org.junit.jupiter + junit-jupiter-params + 5.3.2 + test + + + org.junit.platform + junit-platform-launcher + 1.5.0-M1 + test + + + org.junit.platform + junit-platform-runner + 1.5.0-M1 + test + + + org.mockito + mockito-core + 2.24.5 + test + + + org.mockito + mockito-junit-jupiter + 2.24.5 + test + + + org.hamcrest + hamcrest-library + 2.1-rc4 + test + + + + + + kreth.snapshots + https://nexus.kreth-development.de/repository/maven-snapshots/ + + + kreth.releases + https://nexus.kreth-development.de/repository/maven-releases/ + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + ${java.version} + -proc:none + + + + + + scm:git:https://gitea.kreth-development.de/markus/PropertyToJavaGenerator_migration.git + scm:git:https://gitea.kreth-development.de/markus/PropertyToJavaGenerator_migration.git + HEAD + + \ No newline at end of file diff --git a/src/main/java/de/kreth/property2java/Configuration.java b/src/main/java/de/kreth/property2java/Configuration.java index 93bd0ac..c4ddff7 100644 --- a/src/main/java/de/kreth/property2java/Configuration.java +++ b/src/main/java/de/kreth/property2java/Configuration.java @@ -7,6 +7,7 @@ import java.io.Reader; import java.io.Writer; import java.nio.charset.Charset; import java.nio.file.Path; +import java.util.EnumSet; import java.util.Map; import org.apache.commons.text.WordUtils; @@ -41,6 +42,10 @@ public interface Configuration { */ Path getRootPath(); + default EnumSet getOptions() { + return EnumSet.noneOf(GeneratorOptions.class); + } + default Writer outWriter(String fileName) throws IOException { return new FileWriter(new File(getRootPath().toFile(), mapFilenameToClassName(fileName) + ".java"), outputCharset()); diff --git a/src/main/java/de/kreth/property2java/Generator.java b/src/main/java/de/kreth/property2java/Generator.java index 2bb9d9f..6e15e8f 100644 --- a/src/main/java/de/kreth/property2java/Generator.java +++ b/src/main/java/de/kreth/property2java/Generator.java @@ -64,6 +64,9 @@ public class Generator { root.put("fileName", fileName); root.put("bundle_base_name", fileName.substring(0, min(fileName.length(), fileName.lastIndexOf('.')))); root.put("classname", config.mapFilenameToClassName(fileName)); + if (config.getOptions().isEmpty() == false) { + root.put("options", config.getOptions()); + } List entries = new ArrayList<>(); diff --git a/src/main/java/de/kreth/property2java/GeneratorOptions.java b/src/main/java/de/kreth/property2java/GeneratorOptions.java new file mode 100644 index 0000000..1891a42 --- /dev/null +++ b/src/main/java/de/kreth/property2java/GeneratorOptions.java @@ -0,0 +1,5 @@ +package de.kreth.property2java; + +public enum GeneratorOptions { + WithSubstitutors +} diff --git a/src/main/resources/template/enum_template_with_initializer.tpl b/src/main/resources/template/enum_template_with_initializer.tpl index 739530f..2aa682f 100644 --- a/src/main/resources/template/enum_template_with_initializer.tpl +++ b/src/main/resources/template/enum_template_with_initializer.tpl @@ -21,13 +21,7 @@ import javax.annotation.processing.Generated; function = resourceFunction; } - /** - * Represented Key in property File. - * @return key - */ - public String getValue() { - return value; - } +<#include "parts/key_value_part.tpl"> /** * Resolves the value for this key. diff --git a/src/main/resources/template/enum_template_with_inner_properties.tpl b/src/main/resources/template/enum_template_with_inner_properties.tpl index d7ea0d2..9f50344 100644 --- a/src/main/resources/template/enum_template_with_inner_properties.tpl +++ b/src/main/resources/template/enum_template_with_inner_properties.tpl @@ -23,20 +23,15 @@ import javax.annotation.processing.Generated; private final String value; - /** - * Represented Key in property File. - * @return key - */ - public String getValue() { - return value; - } +<#include "parts/key_value_part.tpl"> /** - * The Text for this Key from PropertyResourceBundle + * The text for this key from {@link Properties} * @return human readable text */ public String getText() { return properties.getProperty(value); } +<#include "parts/withOptions.tpl"> } diff --git a/src/main/resources/template/enum_template_with_inner_propertyresourcebundle.tpl b/src/main/resources/template/enum_template_with_inner_propertyresourcebundle.tpl index 7263dc3..ccc8f86 100644 --- a/src/main/resources/template/enum_template_with_inner_propertyresourcebundle.tpl +++ b/src/main/resources/template/enum_template_with_inner_propertyresourcebundle.tpl @@ -17,13 +17,7 @@ import javax.annotation.processing.Generated; this.value = value; } - /** - * Represented Key in property File. - * @return key - */ - public String getValue() { - return value; - } +<#include "parts/key_value_part.tpl"> /** * The Text for this Key from PropertyResourceBundle diff --git a/src/main/resources/template/parts/WithSubstitutors.tpl b/src/main/resources/template/parts/WithSubstitutors.tpl new file mode 100644 index 0000000..ee16707 --- /dev/null +++ b/src/main/resources/template/parts/WithSubstitutors.tpl @@ -0,0 +1,70 @@ + /** + * The text for this key from {@link Properties} with args as replacements of placeholders. + *

Placeholder must be defined as {0}, {1} etc. + * @return human readable text + */ + public String getText(Object...objects) { + String property = properties.getProperty(value); + return doReplacements(property, objects); + } + +<#-- From here tested substitution method --> + private String doReplacements(String property, Object...objects) { + StringBuilder text = new StringBuilder(); + int index = property.indexOf('{'); + text.append(property.substring(0, index)); + + while (index >= 0) { + index++; + int endIndex = withEndIndex(index, property); + if (endIndex >0) { + String theIndex = property.substring(index, endIndex); + int withIndex = Integer.valueOf(theIndex); + if (withIndex+1> objects.length) { + throw new IllegalStateException("No Argument for Index {" + theIndex + + "}" + " at Position=" + (index - 1) + " in \"" + property + "\""); + } + text.append(objects[withIndex].toString()); + index = property.indexOf('{', endIndex); + if (index <0) { + text.append(property.substring(endIndex + 1)); + } else { + text.append(property.substring(endIndex + 1, index)); + } + } else { + endIndex = index; + + index = property.indexOf('{', index); + if (index <0) { + text.append('{'); + text.append(property.substring(endIndex)); + } + } + } + return text.toString(); + } + + /** + * extracts the end index, if (and only if) the closing } exists and + * between the indicee an integer value exists. + * @param index + * @param property + * @return -1 if invalid or not existing + */ + private int withEndIndex(int index, String property) { + + int result = -1; + int endIndex = property.indexOf('}', index); + if (endIndex >index) { + String between = property.substring(index, endIndex); + if (between.length() > 0) { + result = endIndex; + for(int i=0; i +<#list options as option> +<#include "${option}.tpl"> + + \ No newline at end of file diff --git a/src/test/java/de/kreth/property2java/GeneratorTests.java b/src/test/java/de/kreth/property2java/GeneratorTests.java index dadcc04..f6ee6a0 100644 --- a/src/test/java/de/kreth/property2java/GeneratorTests.java +++ b/src/test/java/de/kreth/property2java/GeneratorTests.java @@ -120,7 +120,7 @@ class GeneratorTests { List lines = out.toString().lines().filter(line -> line.contains(" (\"")).collect(Collectors.toList()); - assertEquals(21, lines.size()); + assertEquals(22, lines.size()); assertLineMatch(lines, "label", "label"); assertLineMatch(lines, "label_addarticle", "label.addarticle"); assertLineMatch(lines, "label_user_register", "label.user.register"); diff --git a/src/test/java/de/kreth/property2java/GeneratorWithInnerPropertiesTest.java b/src/test/java/de/kreth/property2java/GeneratorWithInnerPropertiesTest.java index 93f5e00..658210d 100644 --- a/src/test/java/de/kreth/property2java/GeneratorWithInnerPropertiesTest.java +++ b/src/test/java/de/kreth/property2java/GeneratorWithInnerPropertiesTest.java @@ -20,6 +20,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Properties; import java.util.StringTokenizer; import java.util.stream.Collectors; @@ -119,8 +120,9 @@ public class GeneratorWithInnerPropertiesTest { while (sourceTokenizer.hasMoreTokens()) { String line = sourceTokenizer.nextToken(); if (line.contains("Properties") - && !line.contains("import") - && !line.contains("enum") + && !line.contains("import") + && !line.contains("enum") + && !line.contains("@link") && !line.contains("class")) { declaration = line; } else if (line.contains(".load")) { @@ -159,7 +161,7 @@ public class GeneratorWithInnerPropertiesTest { List lines = out.toString().lines().filter(line -> line.contains(" (\"")).collect(Collectors.toList()); - assertEquals(21, lines.size()); + assertEquals(22, lines.size()); assertLineMatch(lines, "label", "label"); assertLineMatch(lines, "label_addarticle", "label.addarticle"); assertLineMatch(lines, "label_user_register", "label.user.register"); diff --git a/src/test/java/de/kreth/property2java/TestPropertiesSource.java b/src/test/java/de/kreth/property2java/TestPropertiesSource.java index 6cf7e93..ed73950 100644 --- a/src/test/java/de/kreth/property2java/TestPropertiesSource.java +++ b/src/test/java/de/kreth/property2java/TestPropertiesSource.java @@ -25,7 +25,8 @@ public class TestPropertiesSource { + "message.invoiceitem.startbeforeend = End must be later than start.\r\n" + "message.user.create.success = Thanks {0} created!\r\n" + "message.user.loginfailure = Login Error! Wrong user or password?\r\n" - + "message.user.passwordmissmatch = Passwords don't match.\r\n" + ""); + + "message.user.passwordmissmatch = Passwords don't match.\r\n" + + "message.with.five.placeholders = Third is first{2}, then last \"{4}\", second={1}, fourth={3} and first is last={0}\r\n"); } public static void main(String[] args) throws IOException { @@ -33,13 +34,21 @@ public class TestPropertiesSource { try (FileWriter out = new FileWriter(new File(dir, GenerateTheTest.PROPERTY_LOADER_PROPERTIES))) { testProperties().transferTo(out); + System.out.println("Generated: " + GenerateTheTest.PROPERTY_LOADER_PROPERTIES); } try (FileWriter out = new FileWriter(new File(dir, GenerateTheTest.UNARY_OPERATOR_PROPERTIES))) { testProperties().transferTo(out); + System.out.println("Generated: " + GenerateTheTest.UNARY_OPERATOR_PROPERTIES); } try (FileWriter out = new FileWriter(new File(dir, GenerateTheTest.RESOURCE_BUNDLE))) { testProperties().transferTo(out); + System.out.println("Generated: " + GenerateTheTest.RESOURCE_BUNDLE); } + try (FileWriter out = new FileWriter(new File(dir, GenerateTheTest.PROPERTY_LOADER_OPTIONS_PROPERTIES))) { + testProperties().transferTo(out); + System.out.println("Generated: " + GenerateTheTest.PROPERTY_LOADER_OPTIONS_PROPERTIES); + } + } } diff --git a/src/test/java/de/kreth/property2java/generated/GenerateTheTest.java b/src/test/java/de/kreth/property2java/generated/GenerateTheTest.java index 77789b9..5517488 100644 --- a/src/test/java/de/kreth/property2java/generated/GenerateTheTest.java +++ b/src/test/java/de/kreth/property2java/generated/GenerateTheTest.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.io.Reader; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.EnumSet; import java.util.HashMap; import java.util.Map; @@ -11,11 +12,13 @@ import de.kreth.property2java.Configuration; import de.kreth.property2java.Format; import de.kreth.property2java.Generator; import de.kreth.property2java.GeneratorException; +import de.kreth.property2java.GeneratorOptions; import de.kreth.property2java.TestPropertiesSource; public class GenerateTheTest { public static final String PROPERTY_LOADER_PROPERTIES = "property_loader.properties"; + public static final String PROPERTY_LOADER_OPTIONS_PROPERTIES = "property_loader_options.properties"; public static final String UNARY_OPERATOR_PROPERTIES = "unary_operator.properties"; public static final String RESOURCE_BUNDLE = "resource_bundle.properties"; @@ -26,12 +29,14 @@ public class GenerateTheTest { withUnaryOperatorParameter(current); withInnerPropertyLoader(current); withResourceBundle(current); + withInnerPropertyLoaderAndSubstitutors(current); } private static void withResourceBundle(Path current) throws IOException, GeneratorException { Format format = Format.WithInnerPropertyResourceBundle; Map input = new HashMap<>(); input.put(RESOURCE_BUNDLE, TestPropertiesSource.testProperties()); + System.out.println("Generating: " + RESOURCE_BUNDLE); Configuration config = new Configuration() { @@ -63,6 +68,8 @@ public class GenerateTheTest { Format format = Format.WithUnaryOperatorParameter; Map input = new HashMap<>(); input.put(UNARY_OPERATOR_PROPERTIES, TestPropertiesSource.testProperties()); + + System.out.println("Generating: " + UNARY_OPERATOR_PROPERTIES); Configuration config = new Configuration() { @@ -94,6 +101,8 @@ public class GenerateTheTest { Format format = Format.WithInnerPropertyLoader; Map input = new HashMap<>(); input.put(PROPERTY_LOADER_PROPERTIES, TestPropertiesSource.testProperties()); + + System.out.println("Generating: " + PROPERTY_LOADER_PROPERTIES); Configuration config = new Configuration() { @@ -121,4 +130,42 @@ public class GenerateTheTest { generator.start(); } + private static void withInnerPropertyLoaderAndSubstitutors(Path current) throws IOException, GeneratorException { + Format format = Format.WithInnerPropertyLoader; + Map input = new HashMap<>(); + input.put(PROPERTY_LOADER_OPTIONS_PROPERTIES, TestPropertiesSource.testProperties()); + + System.out.println("Generating: " + PROPERTY_LOADER_OPTIONS_PROPERTIES); + + Configuration config = new Configuration() { + + @Override + public Path getRootPath() { + return current; + } + + @Override + public String getPackage() { + return "de.kreth.property2java.generated"; + } + + @Override + public Format getFormat() { + return format; + } + + @Override + public EnumSet getOptions() { + return EnumSet.allOf(GeneratorOptions.class); + } + + @Override + public Map getInput() { + return input; + } + }; + Generator generator = new Generator(config); + generator.start(); + } + } diff --git a/src/test/java/de/kreth/property2java/generated/Property_Loader_Options_Properties.java b/src/test/java/de/kreth/property2java/generated/Property_Loader_Options_Properties.java new file mode 100644 index 0000000..54a4b40 --- /dev/null +++ b/src/test/java/de/kreth/property2java/generated/Property_Loader_Options_Properties.java @@ -0,0 +1,203 @@ +package de.kreth.property2java.generated; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Properties; + +import javax.annotation.processing.Generated; + +/** + * Property keys from property_loader_options.properties + */ + +@Generated(date = "06.08.2024, 23:03:48", value = "de.kreth.property2java.Generator") +public enum Property_Loader_Options_Properties { + + /** + * label = "" + */ + LABEL ("label"), + /** + * label.addarticle = "Add Article" + */ + LABEL_ADDARTICLE ("label.addarticle"), + /** + * label.cancel = "Cancel" + */ + LABEL_CANCEL ("label.cancel"), + /** + * label.close = "Close" + */ + LABEL_CLOSE ("label.close"), + /** + * label.delete = "Delete" + */ + LABEL_DELETE ("label.delete"), + /** + * label.discart = "Discart" + */ + LABEL_DISCART ("label.discart"), + /** + * label.loggedin = "Logged in:" + */ + LABEL_LOGGEDIN ("label.loggedin"), + /** + * label.logout = "Logout" + */ + LABEL_LOGOUT ("label.logout"), + /** + * label.ok = "OK" + */ + LABEL_OK ("label.ok"), + /** + * label.open = "Open" + */ + LABEL_OPEN ("label.open"), + /** + * label.preview = "Preview" + */ + LABEL_PREVIEW ("label.preview"), + /** + * label.store = "Store" + */ + LABEL_STORE ("label.store"), + /** + * label.user.register = "Register" + */ + LABEL_USER_REGISTER ("label.user.register"), + /** + * message.article.priceerror = "Please set the price." + */ + MESSAGE_ARTICLE_PRICEERROR ("message.article.priceerror"), + /** + * message.delete.text = "Delete {0}?" + */ + MESSAGE_DELETE_TEXT ("message.delete.text"), + /** + * message.delete.title = "Really delete?" + */ + MESSAGE_DELETE_TITLE ("message.delete.title"), + /** + * message.invoiceitem.allfieldsmustbeset = "Start, end and article must not be + empty!" + */ + MESSAGE_INVOICEITEM_ALLFIELDSMUSTBESET ("message.invoiceitem.allfieldsmustbeset"), + /** + * message.invoiceitem.startbeforeend = "End must be later than start." + */ + MESSAGE_INVOICEITEM_STARTBEFOREEND ("message.invoiceitem.startbeforeend"), + /** + * message.user.create.success = "Thanks {0} created!" + */ + MESSAGE_USER_CREATE_SUCCESS ("message.user.create.success"), + /** + * message.user.loginfailure = "Login Error! Wrong user or password?" + */ + MESSAGE_USER_LOGINFAILURE ("message.user.loginfailure"), + /** + * message.user.passwordmissmatch = "Passwords don't match." + */ + MESSAGE_USER_PASSWORDMISSMATCH ("message.user.passwordmissmatch"), + /** + * message.with.five.placeholders = "Third is first{2}, then last "{4}", second={1}, fourth={3} and first is last={0}" + */ + MESSAGE_WITH_FIVE_PLACEHOLDERS ("message.with.five.placeholders"); + private Property_Loader_Options_Properties (String value) { + this.value = value; + } + + private static Properties properties = new Properties(); + static { + try { + properties.load(Property_Loader_Options_Properties.class.getResourceAsStream("/property_loader_options.properties")); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private final String value; + + /** + * Represented Key in property File. + * @return key + */ + public String getKey() { + return value; + } + /** + * The text for this key from {@link Properties} + * @return human readable text + */ + public String getText() { + return properties.getProperty(value); + } + + /** + * The text for this key from {@link Properties} with args as replacements of placeholders. + *

Placeholder must be defined as {0}, {1} etc. + * @return human readable text + */ + public String getText(Object...objects) { + String property = properties.getProperty(value); + return doReplacements(property, objects); + } + + private String doReplacements(String property, Object...objects) { + StringBuilder text = new StringBuilder(); + int index = property.indexOf('{'); + text.append(property.substring(0, index)); + + while (index >= 0) { + index++; + int endIndex = withEndIndex(index, property); + if (endIndex >0) { + String theIndex = property.substring(index, endIndex); + int withIndex = Integer.valueOf(theIndex); + if (withIndex+1> objects.length) { + throw new IllegalStateException("No Argument for Index {" + theIndex + + "}" + " at Position=" + (index - 1) + " in \"" + property + "\""); + } + text.append(objects[withIndex].toString()); + index = property.indexOf('{', endIndex); + if (index <0) { + text.append(property.substring(endIndex + 1)); + } else { + text.append(property.substring(endIndex + 1, index)); + } + } else { + endIndex = index; + + index = property.indexOf('{', index); + if (index <0) { + text.append('{'); + text.append(property.substring(endIndex)); + } + } + } + return text.toString(); + } + + /** + * extracts the end index, if (and only if) the closing } exists and + * between the indicee an integer value exists. + * @param index + * @param property + * @return -1 if invalid or not existing + */ + private int withEndIndex(int index, String property) { + + int result = -1; + int endIndex = property.indexOf('}', index); + if (endIndex >index) { + String between = property.substring(index, endIndex); + if (between.length() > 0) { + result = endIndex; + for(int i=0; i= 0) { + index++; + int endIndex = withEndIndex(index, property); + if (endIndex >0) { + String theIndex = property.substring(index, endIndex); + int withIndex = Integer.valueOf(theIndex); + if (withIndex+1> objects.length) { + throw new IllegalStateException("No Argument for Index {" + theIndex + + "}" + " at Position=" + (index - 1) + " in \"" + property + "\""); + } + text.append(objects[withIndex].toString()); + index = property.indexOf('{', endIndex); + if (index <0) { + text.append(property.substring(endIndex + 1)); + } else { + text.append(property.substring(endIndex + 1, index)); + } + } else { + endIndex = index; + + index = property.indexOf('{', index); + if (index <0) { + text.append('{'); + text.append(property.substring(endIndex)); + } + } + } + return text.toString(); + } + + /** + * extracts the end index, if (and only if) the closing } exists and + * between the indicee an integer value exists. + * @param index + * @param property + * @return -1 if invalid or not existing + */ + private static int withEndIndex(int index, String property) { + + int result = -1; + int endIndex = property.indexOf('}', index); + if (endIndex >index) { + String between = property.substring(index, endIndex); + if (between.length() > 0) { + result = endIndex; + for(int i=0; i doReplacements(property, "|0|")) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("{1}") // Orignal Placeholder + .hasMessageContaining("Position=5") // Index of missing Placeholder + .hasMessageContaining(property); // Orignal Text + } + +} diff --git a/src/test/resources/property_loader.properties b/src/test/resources/property_loader.properties index 84cf35a..5546f9e 100644 --- a/src/test/resources/property_loader.properties +++ b/src/test/resources/property_loader.properties @@ -22,3 +22,4 @@ message.invoiceitem.startbeforeend = End must be later than start. message.user.create.success = Thanks {0} created! message.user.loginfailure = Login Error! Wrong user or password? message.user.passwordmissmatch = Passwords don't match. +message.with.five.placeholders = Third is first{2}, then last "{4}", second={1}, fourth={3} and first is last={0} diff --git a/src/test/resources/property_loader_options.properties b/src/test/resources/property_loader_options.properties new file mode 100644 index 0000000..5546f9e --- /dev/null +++ b/src/test/resources/property_loader_options.properties @@ -0,0 +1,25 @@ + +label = + +label.addarticle = Add Article +label.cancel = Cancel +label.close = Close +label.delete = Delete +label.discart = Discart +label.loggedin = Logged in: +label.logout = Logout +label.ok = OK +label.store = Store +label.preview = Preview +label.open = Open +label.user.register = Register + +message.article.priceerror = Please set the price. +message.delete.text = Delete {0}? +message.delete.title = Really delete? +message.invoiceitem.allfieldsmustbeset = Start, end and article must not be \r\n empty! +message.invoiceitem.startbeforeend = End must be later than start. +message.user.create.success = Thanks {0} created! +message.user.loginfailure = Login Error! Wrong user or password? +message.user.passwordmissmatch = Passwords don't match. +message.with.five.placeholders = Third is first{2}, then last "{4}", second={1}, fourth={3} and first is last={0} diff --git a/src/test/resources/resource_bundle.properties b/src/test/resources/resource_bundle.properties index 84cf35a..5546f9e 100644 --- a/src/test/resources/resource_bundle.properties +++ b/src/test/resources/resource_bundle.properties @@ -22,3 +22,4 @@ message.invoiceitem.startbeforeend = End must be later than start. message.user.create.success = Thanks {0} created! message.user.loginfailure = Login Error! Wrong user or password? message.user.passwordmissmatch = Passwords don't match. +message.with.five.placeholders = Third is first{2}, then last "{4}", second={1}, fourth={3} and first is last={0} diff --git a/src/test/resources/unary_operator.properties b/src/test/resources/unary_operator.properties index 84cf35a..5546f9e 100644 --- a/src/test/resources/unary_operator.properties +++ b/src/test/resources/unary_operator.properties @@ -22,3 +22,4 @@ message.invoiceitem.startbeforeend = End must be later than start. message.user.create.success = Thanks {0} created! message.user.loginfailure = Login Error! Wrong user or password? message.user.passwordmissmatch = Passwords don't match. +message.with.five.placeholders = Third is first{2}, then last "{4}", second={1}, fourth={3} and first is last={0}