diff --git a/Application_Properties.java b/Application_Properties.java new file mode 100644 index 0000000..e69de29 diff --git a/src/main/java/de/kreth/property2java/Generator.java b/src/main/java/de/kreth/property2java/Generator.java index 6e15e8f..29cc63c 100644 --- a/src/main/java/de/kreth/property2java/Generator.java +++ b/src/main/java/de/kreth/property2java/Generator.java @@ -7,6 +7,7 @@ import java.io.Writer; import java.net.URL; import java.text.DateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.Enumeration; @@ -14,6 +15,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.stream.Collectors; import de.kreth.property2java.cli.ArgumentConfiguration; import de.kreth.property2java.config.FreemarkerConfig; @@ -66,6 +68,13 @@ public class Generator { root.put("classname", config.mapFilenameToClassName(fileName)); if (config.getOptions().isEmpty() == false) { root.put("options", config.getOptions()); + List imports = config.getOptions().stream() + .map(GeneratorOptions::getAdditionalImport) + .flatMap(Arrays::stream) + .collect(Collectors.toList()); + if (imports.isEmpty() == false) { + root.put("imports", imports); + } } List entries = new ArrayList<>(); diff --git a/src/main/java/de/kreth/property2java/GeneratorOptions.java b/src/main/java/de/kreth/property2java/GeneratorOptions.java index 1891a42..b72b374 100644 --- a/src/main/java/de/kreth/property2java/GeneratorOptions.java +++ b/src/main/java/de/kreth/property2java/GeneratorOptions.java @@ -1,5 +1,24 @@ package de.kreth.property2java; +import java.text.MessageFormat; + public enum GeneratorOptions { - WithSubstitutors + /** + * + */ + WithSubstitutors, + /** + * Add a format Method, which uses and supports {@link MessageFormat}. + */ + WithMessageFormatter("java.text.MessageFormat"); + + private final String[] additionalImport; + + private GeneratorOptions(String... additionalImport) { + this.additionalImport = additionalImport!= null ? additionalImport : new String[] {}; + } + + String[] getAdditionalImport() { + return additionalImport; + } } diff --git a/src/main/java/de/kreth/property2java/processor/GenerateProperty2Java.java b/src/main/java/de/kreth/property2java/processor/GenerateProperty2Java.java index 1d0d49c..52dda7e 100644 --- a/src/main/java/de/kreth/property2java/processor/GenerateProperty2Java.java +++ b/src/main/java/de/kreth/property2java/processor/GenerateProperty2Java.java @@ -7,6 +7,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import de.kreth.property2java.Format; +import de.kreth.property2java.GeneratorOptions; @Target(TYPE) @Retention(RetentionPolicy.SOURCE) @@ -43,4 +44,6 @@ public @interface GenerateProperty2Java { String[] resources(); Format format() default Format.WithUnaryOperatorParameter; + + GeneratorOptions[] options() default {}; } diff --git a/src/main/java/de/kreth/property2java/processor/GenerateResourceBundleProperty2Java.java b/src/main/java/de/kreth/property2java/processor/GenerateResourceBundleProperty2Java.java index 9e15a2b..a528d08 100644 --- a/src/main/java/de/kreth/property2java/processor/GenerateResourceBundleProperty2Java.java +++ b/src/main/java/de/kreth/property2java/processor/GenerateResourceBundleProperty2Java.java @@ -8,6 +8,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import de.kreth.property2java.Format; +import de.kreth.property2java.GeneratorOptions; @Target(TYPE) @Retention(RetentionPolicy.SOURCE) @@ -45,4 +46,6 @@ public @interface GenerateResourceBundleProperty2Java { String resource(); Format format(); + + GeneratorOptions[] options() default {}; } diff --git a/src/main/java/de/kreth/property2java/processor/ProcessorConfiguration.java b/src/main/java/de/kreth/property2java/processor/ProcessorConfiguration.java index 8691a74..ce423e7 100644 --- a/src/main/java/de/kreth/property2java/processor/ProcessorConfiguration.java +++ b/src/main/java/de/kreth/property2java/processor/ProcessorConfiguration.java @@ -6,6 +6,7 @@ import java.io.Writer; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; +import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -22,6 +23,7 @@ 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; public class ProcessorConfiguration implements Configuration { @@ -29,11 +31,13 @@ public class ProcessorConfiguration implements Configuration { private final Element element; private final Map input; private final Format format; + private final GeneratorOptions[] options; ProcessorConfiguration(Builder builder) throws IOException { this.filer = builder.filer; this.element = builder.element; this.format = Objects.requireNonNullElse(builder.format, Format.WithUnaryOperatorParameter); + this.options = builder.options; this.input = new HashMap<>(); for (String resource : builder.resourcenames) { FileObject ressource = filer.getResource(StandardLocation.CLASS_PATH, "", resource); @@ -63,6 +67,11 @@ public class ProcessorConfiguration implements Configuration { return input; } + @Override + public EnumSet getOptions() { + return EnumSet.copyOf(Arrays.asList(options)); + } + @Override public Path getRootPath() { throw new UnsupportedOperationException( @@ -83,6 +92,7 @@ public class ProcessorConfiguration implements Configuration { } static class Builder { + public GeneratorOptions[] options; private final Filer filer; private final Element element; private final List resourcenames; @@ -99,6 +109,11 @@ public class ProcessorConfiguration implements Configuration { return this; } + public Builder withOptions(GeneratorOptions[] options) { + this.options = options; + return this; + } + public Builder addAll(String[] resourceNames) { this.resourcenames.addAll(Arrays.asList(resourceNames)); return this; diff --git a/src/main/java/de/kreth/property2java/processor/Property2JavaGenerator.java b/src/main/java/de/kreth/property2java/processor/Property2JavaGenerator.java index c119438..576eb4e 100644 --- a/src/main/java/de/kreth/property2java/processor/Property2JavaGenerator.java +++ b/src/main/java/de/kreth/property2java/processor/Property2JavaGenerator.java @@ -18,6 +18,7 @@ import javax.tools.Diagnostic.Kind; import de.kreth.property2java.Format; import de.kreth.property2java.GeneratorException; +import de.kreth.property2java.GeneratorOptions; @SupportedAnnotationTypes({ "de.kreth.property2java.processor.GenerateProperty2Java", "de.kreth.property2java.processor.GenerateResourceBundleProperty2Javas", @@ -46,7 +47,8 @@ public class Property2JavaGenerator extends AbstractProcessor { GenerateProperty2Java generateAnnotation = element.getAnnotation(GenerateProperty2Java.class); String[] resources = generateAnnotation.resources(); Format format = generateAnnotation.format(); - generateElementProperties(element, Arrays.asList(resources), format); + GeneratorOptions[] options = generateAnnotation.options(); + generateElementProperties(element, Arrays.asList(resources), format, options); } } @@ -63,15 +65,19 @@ public class Property2JavaGenerator extends AbstractProcessor { .getAnnotation(GenerateResourceBundleProperty2Javas.class).value(); for (GenerateResourceBundleProperty2Java generateResourceBundleProperty2Java : value) { List resources = Arrays.asList(generateResourceBundleProperty2Java.resource()); - generateElementProperties(element, resources, generateResourceBundleProperty2Java.format()); + generateElementProperties(element, resources, generateResourceBundleProperty2Java.format(), generateResourceBundleProperty2Java.options()); } } } - private void generateElementProperties(Element element, List resources, Format format) { + private void generateElementProperties(Element element, List resources, Format format, GeneratorOptions[] options) { processingEnv.getMessager().printMessage(Kind.NOTE, "Generating Java for " + Arrays.asList(resources)); try { - ProcessorConfiguration.builder(processingEnv.getFiler(), element).addAll(resources).withFormat(format) + ProcessorConfiguration + .builder(processingEnv.getFiler(), element) + .addAll(resources) + .withFormat(format) + .withOptions(options) .startGeneration(); } catch (IOException | GeneratorException e) { StringWriter out = new StringWriter(); 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 9f50344..2b9409c 100644 --- a/src/main/resources/template/enum_template_with_inner_properties.tpl +++ b/src/main/resources/template/enum_template_with_inner_properties.tpl @@ -3,6 +3,7 @@ import java.io.UncheckedIOException; import java.util.Properties; import javax.annotation.processing.Generated; +<#include "parts/additionalImports.tpl"> /** * Property keys from ${fileName} 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 ccc8f86..70756cd 100644 --- a/src/main/resources/template/enum_template_with_inner_propertyresourcebundle.tpl +++ b/src/main/resources/template/enum_template_with_inner_propertyresourcebundle.tpl @@ -2,6 +2,7 @@ import java.util.ResourceBundle; import javax.annotation.processing.Generated; +<#include "parts/additionalImports.tpl"> /** * Property keys from ${fileName} @@ -27,4 +28,5 @@ import javax.annotation.processing.Generated; return bundle.getString(value); } +<#include "parts/withOptions.tpl"> } diff --git a/src/main/resources/template/parts/WithMessageFormatter.tpl b/src/main/resources/template/parts/WithMessageFormatter.tpl new file mode 100644 index 0000000..6e3e6e4 --- /dev/null +++ b/src/main/resources/template/parts/WithMessageFormatter.tpl @@ -0,0 +1,8 @@ + + private MessageFormat messageFormat = null; + public String format(Object...objects) { + if (messageFormat == null) { + messageFormat = new MessageFormat(getText()); + } + return messageFormat.format(objects, new StringBuffer(), null).toString(); + } \ No newline at end of file diff --git a/src/main/resources/template/parts/WithSubstitutors.tpl b/src/main/resources/template/parts/WithSubstitutors.tpl index ee16707..6e10629 100644 --- a/src/main/resources/template/parts/WithSubstitutors.tpl +++ b/src/main/resources/template/parts/WithSubstitutors.tpl @@ -4,7 +4,7 @@ * @return human readable text */ public String getText(Object...objects) { - String property = properties.getProperty(value); + String property = getText(); return doReplacements(property, objects); } diff --git a/src/main/resources/template/parts/additionalImports.tpl b/src/main/resources/template/parts/additionalImports.tpl new file mode 100644 index 0000000..93c0ae8 --- /dev/null +++ b/src/main/resources/template/parts/additionalImports.tpl @@ -0,0 +1,4 @@ +<#if imports??><#list imports as e> +import ${e}; +; + \ No newline at end of file diff --git a/src/test/java/de/kreth/property2java/generated/GenerateTheTest.java b/src/test/java/de/kreth/property2java/generated/GenerateTheTest.java index 5517488..026949b 100644 --- a/src/test/java/de/kreth/property2java/generated/GenerateTheTest.java +++ b/src/test/java/de/kreth/property2java/generated/GenerateTheTest.java @@ -18,7 +18,9 @@ import de.kreth.property2java.TestPropertiesSource; public class GenerateTheTest { public static final String PROPERTY_LOADER_PROPERTIES = "property_loader.properties"; + public static final String PROPERTY_LOADER_ALLOPTIONS_PROPERTIES = "property_loader_alloptions.properties"; public static final String PROPERTY_LOADER_OPTIONS_PROPERTIES = "property_loader_options.properties"; + public static final String PROPERTY_LOADER_FORMAT_PROPERTIES = "property_loader_format.properties"; public static final String UNARY_OPERATOR_PROPERTIES = "unary_operator.properties"; public static final String RESOURCE_BUNDLE = "resource_bundle.properties"; @@ -29,7 +31,9 @@ public class GenerateTheTest { withUnaryOperatorParameter(current); withInnerPropertyLoader(current); withResourceBundle(current); + withInnerResourceBundleAllOptions(current); withInnerPropertyLoaderAndSubstitutors(current); + withInnerResourceBundleAndFormat(current); } private static void withResourceBundle(Path current) throws IOException, GeneratorException { @@ -156,7 +160,84 @@ public class GenerateTheTest { @Override public EnumSet getOptions() { - return EnumSet.allOf(GeneratorOptions.class); + return EnumSet.of(GeneratorOptions.WithSubstitutors); + } + + @Override + public Map getInput() { + return input; + } + }; + Generator generator = new Generator(config); + generator.start(); + } + + private static void withInnerResourceBundleAllOptions(Path current) throws IOException, GeneratorException { + Format format = Format.WithInnerPropertyLoader; + Map input = new HashMap<>(); + input.put(PROPERTY_LOADER_ALLOPTIONS_PROPERTIES, TestPropertiesSource.testProperties()); + + System.out.println("Generating: " + PROPERTY_LOADER_ALLOPTIONS_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.of(GeneratorOptions.WithSubstitutors); + } + + @Override + public Map getInput() { + return input; + } + }; + Generator generator = new Generator(config); + generator.start(); + } + + + private static void withInnerResourceBundleAndFormat(Path current) throws IOException, GeneratorException { + Format format = Format.WithInnerPropertyResourceBundle; + Map input = new HashMap<>(); + input.put(PROPERTY_LOADER_FORMAT_PROPERTIES, TestPropertiesSource.testProperties()); + + System.out.println("Generating: " + PROPERTY_LOADER_FORMAT_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.of(GeneratorOptions.WithMessageFormatter); } @Override diff --git a/src/test/java/de/kreth/property2java/generated/Property_Loader_Alloptions_Properties.java b/src/test/java/de/kreth/property2java/generated/Property_Loader_Alloptions_Properties.java new file mode 100644 index 0000000..49830e6 --- /dev/null +++ b/src/test/java/de/kreth/property2java/generated/Property_Loader_Alloptions_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_alloptions.properties + */ + +@Generated(date = "25.01.2025, 23:42:57", value = "de.kreth.property2java.Generator") +public enum Property_Loader_Alloptions_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_Alloptions_Properties (String value) { + this.value = value; + } + + private static Properties properties = new Properties(); + static { + try { + properties.load(Property_Loader_Alloptions_Properties.class.getResourceAsStream("/property_loader_alloptions.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