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">
+#list>
+#if>
\ 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}