diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..4721a1e
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8070aba
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+
+\.settings/
+target
diff --git a/.project b/.project
new file mode 100644
index 0000000..9c40d07
--- /dev/null
+++ b/.project
@@ -0,0 +1,23 @@
+
+
+ PropertyToJavaGenerator
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..07481a8
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,166 @@
+
+
+ 4.0.0
+ de.kreth.property2java
+ PropertyToJavaGenerator
+ 0.0.1-SNAPSHOT
+
+
+
+ UTF-8
+ UTF-8
+ 11
+ 5.3.0-M1
+ 1.7.21
+ 2.11.0
+
+ ${maven.build.timestamp}
+ yyyy-MM-dd HH:mm:ss
+
+
+ target/surefire-reports
+ jacoco
+ jacoco
+ reuseReports
+ ${project.basedir}/target/jacoco.exec
+ **/src/main/webapp/VAADIN/**/*
+
+
+
+
+
+
+ org.junit
+ junit-bom
+ 5.3.2
+ pom
+ import
+
+
+
+
+
+
+ org.apache.commons
+ commons-lang3
+ 3.8.1
+
+
+
+ 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.hamcrest
+ hamcrest-all
+ 1.3
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ test
+
+
+ org.junit.platform
+ junit-platform-launcher
+ test
+
+
+ org.junit.platform
+ junit-platform-runner
+ test
+
+
+
+ org.apache.commons
+ commons-text
+ 1.6
+
+
+ org.mockito
+ mockito-core
+ 2.24.5
+
+
+ org.mockito
+ mockito-junit-jupiter
+ 2.24.5
+
+
+ commons-cli
+ commons-cli
+ 1.4
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.0
+
+ ${java.version}
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.22.1
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.2
+
+
+ default-jacoco-prepare-agent
+
+ prepare-agent
+
+
+
+ default-jacoco-report
+ prepare-package
+
+ report
+
+
+
+
+
+
+
+
\ 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
new file mode 100644
index 0000000..7060d01
--- /dev/null
+++ b/src/main/java/de/kreth/property2java/Configuration.java
@@ -0,0 +1,47 @@
+package de.kreth.property2java;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.nio.file.Path;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.apache.commons.text.WordUtils;
+
+public interface Configuration {
+
+ static final Pattern REGEX = Pattern.compile("_[a-z]{2}(_[A-Z]{2})?\\.");
+
+ /**
+ * Package for generated Java Classes eg. "de.kreth.property2java". If null - no package line is generated.
+ * @return
+ */
+ String getPackage();
+
+ /**
+ * Filename to InputReader Entries
+ * @return
+ */
+ Map getInput();
+
+ /**
+ * Path of java source folder.
+ * @return
+ */
+ Path getRootPath();
+
+ default Writer outWriter(String fileName) throws IOException {
+ return new FileWriter(new File(getRootPath().toFile(), mapFilenameToClassName(fileName)));
+ }
+
+ default String mapFilenameToClassName(String fileName) {
+
+ String path = REGEX.matcher(fileName).replaceAll(".").replaceAll("\\.", "_").replaceAll(" ", "_");
+ path = WordUtils.capitalize(path, '_');
+ return path;
+ }
+
+}
diff --git a/src/main/java/de/kreth/property2java/Generator.java b/src/main/java/de/kreth/property2java/Generator.java
new file mode 100644
index 0000000..dd605e2
--- /dev/null
+++ b/src/main/java/de/kreth/property2java/Generator.java
@@ -0,0 +1,59 @@
+package de.kreth.property2java;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Enumeration;
+import java.util.Map.Entry;
+import java.util.Properties;
+
+import de.kreth.property2java.cli.ArgumentConfiguration;
+
+public class Generator {
+
+ private Configuration config;
+
+ public Generator(Configuration config) {
+ this.config = config;
+ }
+
+ public void start() throws IOException {
+
+ for (Entry entry : config.getInput().entrySet()) {
+ String fileName = entry.getKey();
+ Writer out = config.outWriter(fileName);
+ Properties properties = new Properties();
+ properties.load(entry.getValue());
+ generate(properties, out, fileName, config);
+ }
+ }
+
+ void generate(Properties properties, Writer out, String fileName, Configuration config) throws IOException {
+
+ @SuppressWarnings("unchecked")
+ Enumeration propertyNames = (Enumeration) properties.propertyNames();
+ String packageName = config.getPackage();
+ if (packageName != null && !packageName.isBlank()) {
+ out.write("package ");
+ out.write(packageName);
+ out.write(";\n\n");
+ }
+ out.write("public interface ");
+ out.write(config.mapFilenameToClassName(fileName));
+ out.write(" {\n");
+ while (propertyNames.hasMoreElements()) {
+ String key = propertyNames.nextElement();
+ out.write(
+ "\tpublic static String " + key.toUpperCase().replaceAll("[\\.-]", "_") + " = \"" + key + "\";\n");
+ }
+ out.write(" }\n");
+ out.flush();
+ out.close();
+ }
+
+ public static void main(String[] args) throws IOException {
+ Generator generator = new Generator(ArgumentConfiguration.parse(args));
+ generator.start();
+ }
+
+}
diff --git a/src/main/java/de/kreth/property2java/cli/ArgumentConfiguration.java b/src/main/java/de/kreth/property2java/cli/ArgumentConfiguration.java
new file mode 100644
index 0000000..88d5154
--- /dev/null
+++ b/src/main/java/de/kreth/property2java/cli/ArgumentConfiguration.java
@@ -0,0 +1,97 @@
+package de.kreth.property2java.cli;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import de.kreth.property2java.Configuration;
+
+public class ArgumentConfiguration implements Configuration {
+
+ private final String packageName;
+
+ private final Map files;
+
+ private final Path rootPath;
+
+ private ArgumentConfiguration(Builder builder) throws IOException {
+ this.packageName = builder.packageName;
+ rootPath = new File(builder.target).toPath();
+ files = new HashMap<>();
+ for (String filePath : builder.propFiles) {
+ File f = new File(filePath);
+ files.put(f.getName(), new FileReader(f));
+ }
+
+ }
+
+ @Override
+ public String getPackage() {
+ return packageName;
+ }
+
+ @Override
+ public Map getInput() {
+ return files;
+ }
+
+ @Override
+ public Path getRootPath() {
+ return rootPath;
+ }
+
+ @Override
+ public Writer outWriter(String fileName) throws IOException {
+ File dir;
+ if (packageName != null && packageName.isBlank() == false) {
+ dir = new File(rootPath.toFile(), packageName.replace('.', File.separatorChar));
+ }
+ else {
+ dir = rootPath.toFile();
+ }
+ return new FileWriter(new File(dir, mapFilenameToClassName(fileName) + ".java"), false);
+ }
+
+ public static Configuration parse(String[] args) throws IOException {
+ CliConfig cliConfig = new CliConfig();
+
+ Builder builder = new Builder();
+ cliConfig.fill(builder, args);
+ return builder.build();
+ }
+
+ static class Builder {
+ String target;
+
+ List propFiles = new ArrayList<>();
+
+ String packageName;
+
+ public Builder setTarget(String target) {
+ this.target = target;
+ return this;
+ }
+
+ public Builder addPropFile(String propFile) {
+ this.propFiles.add(propFile);
+ return this;
+ }
+
+ public Builder setPackageName(String packageName) {
+ this.packageName = packageName;
+ return this;
+ }
+
+ public Configuration build() throws IOException {
+ return new ArgumentConfiguration(this);
+ }
+ }
+}
diff --git a/src/main/java/de/kreth/property2java/cli/CliConfig.java b/src/main/java/de/kreth/property2java/cli/CliConfig.java
new file mode 100644
index 0000000..f4e5f7a
--- /dev/null
+++ b/src/main/java/de/kreth/property2java/cli/CliConfig.java
@@ -0,0 +1,52 @@
+package de.kreth.property2java.cli;
+
+import java.io.IOException;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+import de.kreth.property2java.cli.ArgumentConfiguration.Builder;
+
+public class CliConfig {
+
+ private final Options options = options();
+
+ private Options options() {
+ Options retVal = new Options();
+ retVal.addOption(Option.builder("t").longOpt("targetSourcePath").hasArg().required().build());
+ retVal.addOption(Option.builder("f").longOpt("files").hasArgs().required().valueSeparator(',').build());
+ retVal.addOption(Option.builder("p").longOpt("package").hasArg().required(false).build());
+ return retVal;
+ }
+
+ public void fill(Builder builder, String[] args) throws IOException {
+
+ CommandLineParser parser = new DefaultParser();
+ try {
+ CommandLine cmd = parser.parse(options, args);
+ builder.setTarget(cmd.getOptionValue("t", "."));
+ builder.setPackageName(cmd.getOptionValue("p"));
+ for (String value : cmd.getOptionValues("f")) {
+ builder.addPropFile(value);
+ }
+ }
+ catch (MissingOptionException e) {
+ printHelp();
+ throw new IllegalStateException(e);
+ }
+ catch (ParseException e) {
+ throw new IOException("Unable to parse Arguments", e);
+ }
+ }
+
+ public void printHelp() {
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.printHelp("Generator", options);
+ }
+}
diff --git a/src/test/java/de/kreth/property2java/ConfigurationTest.java b/src/test/java/de/kreth/property2java/ConfigurationTest.java
new file mode 100644
index 0000000..5df1040
--- /dev/null
+++ b/src/test/java/de/kreth/property2java/ConfigurationTest.java
@@ -0,0 +1,48 @@
+package de.kreth.property2java;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+import java.io.FileWriter;
+import java.io.IOException;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+class ConfigurationTest {
+
+ private Configuration config;
+
+ @BeforeEach
+ void initConfig() {
+
+ config = Mockito.mock(Configuration.class);
+ }
+
+ @Test
+ void defaultWriterIsFileWriter() throws IOException {
+
+ when(config.outWriter(anyString())).thenCallRealMethod();
+ when(config.mapFilenameToClassName(anyString())).thenCallRealMethod();
+
+ assertTrue(config.outWriter("application.properties") instanceof FileWriter);
+ }
+
+ @Test
+ void testPathMapping() {
+ String className = config.mapFilenameToClassName("application.properties");
+ assertEquals("Application_Properties", className);
+ }
+
+ @Test
+ void testPathMappingLocalized() {
+ String className = config.mapFilenameToClassName("application_de_DE.properties");
+ assertEquals("Application_Properties", className);
+ className = config.mapFilenameToClassName("application_en_US.properties");
+ assertEquals("Application_Properties", className);
+ }
+
+}
diff --git a/src/test/java/de/kreth/property2java/GeneratorTests.java b/src/test/java/de/kreth/property2java/GeneratorTests.java
new file mode 100644
index 0000000..1429197
--- /dev/null
+++ b/src/test/java/de/kreth/property2java/GeneratorTests.java
@@ -0,0 +1,175 @@
+package de.kreth.property2java;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.StringTokenizer;
+import java.util.stream.Collectors;
+
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class GeneratorTests {
+
+ private String path = "application.properties";
+
+ private Configuration config;
+
+ private Generator generator;
+
+ @BeforeEach
+ void setUp() throws Exception {
+ Map input = new HashMap<>();
+ input.put(path, testProperties());
+
+ config = mock(Configuration.class);
+ when(config.getInput()).thenReturn(input);
+ when(config.mapFilenameToClassName(anyString())).thenCallRealMethod();
+
+ generator = new Generator(config);
+ }
+
+ @Test
+ void testClassDefinition() throws IOException {
+
+ when(config.getPackage()).thenReturn("de.kreth.property2java");
+
+ StringWriter out = new StringWriter();
+ when(config.outWriter(anyString())).thenReturn(out);
+
+ generator.start();
+
+ String sourceCode = out.toString().trim();
+ StringTokenizer sourceTokenizer = new StringTokenizer(sourceCode, "\n");
+ String linePackage = null;
+ String lineClass = null;
+ int countOpenBaces = 0;
+ int countCloseBaces = 0;
+ while (sourceTokenizer.hasMoreTokens()) {
+ String line = sourceTokenizer.nextToken();
+ if (line.trim().startsWith("package")) {
+ linePackage = line;
+ }
+ else if (line.trim().startsWith("public interface")) {
+ lineClass = line;
+ }
+ if (line.contains("{")) {
+ countOpenBaces++;
+ }
+ if (line.contains("}")) {
+ countCloseBaces++;
+ }
+ }
+
+ assertEquals(countCloseBaces, countOpenBaces,
+ "Count of Braces doesn't match. Open = " + countOpenBaces + ", Close = " + countCloseBaces);
+
+ assertNotNull(linePackage);
+ assertNotNull(lineClass);
+
+ assertThat(linePackage,
+ Matchers.stringContainsInOrder(Arrays.asList("package", "de.kreth.property2java", ";")));
+
+ assertThat(lineClass,
+ Matchers.stringContainsInOrder(Arrays.asList("public", "interface", "Application_Properties")));
+
+ }
+
+ @Test
+ void testOneInputGeneratesOneOutput() throws IOException {
+
+ Writer out = mock(Writer.class);
+ Writer nonOut = mock(Writer.class);
+ when(config.outWriter(anyString())).thenReturn(out, nonOut);
+ generator.start();
+ verify(out).close();
+ verify(nonOut, never()).close();
+ verify(nonOut, never()).flush();
+ }
+
+ @Test
+ void testKeys() throws IOException {
+
+ StringWriter out = new StringWriter();
+ when(config.outWriter(anyString())).thenReturn(out);
+ generator.start();
+
+ List lines = out.toString().lines().filter(line -> line.contains(" String "))
+ .collect(Collectors.toList());
+
+ assertEquals(21, lines.size());
+ assertLineMatch(lines, "label", "label");
+ assertLineMatch(lines, "label_addarticle", "label.addarticle");
+ assertLineMatch(lines, "label_user_register", "label.user.register");
+ assertLineMatch(lines, "message_article_priceerror", "message.article.priceerror");
+ assertLineMatch(lines, "message_invoiceitem_startbeforeend", "message.invoiceitem.startbeforeend");
+ assertLineMatch(lines, "message_invoiceitem_allfieldsmustbeset",
+ "message.invoiceitem.allfieldsmustbeset");
+ }
+
+ private void assertLineMatch(List lines, String key, String expected) {
+ Optional found = lines.stream().filter(line -> keyMatches(line, key))
+ .findFirst();
+ assertTrue(found.isPresent(), "No line found with key = " + key);
+ final String line = found.get().trim();
+ int indexEquals = line.indexOf('=');
+ String value = line.substring(indexEquals + 1).trim().substring(1);
+ value = value.substring(0, value.length() - 2);
+ assertEquals(expected, value, "Line \"" + line + "\" don't match expected Value \"" + expected + "\"");
+
+ assertEquals(';', line.charAt(line.length() - 1), "Line \"" + line + "\" don't end with ;");
+ }
+
+ private boolean keyMatches(String line, String key) {
+ line = line.toLowerCase();
+ key = key.toLowerCase();
+ return line.contains(" " + key + " ");
+ }
+
+ private StringReader testProperties() {
+ return new StringReader("\r\n" +
+ "label = \r\n" +
+ "\r\n" +
+ "label.addarticle = Add Article\r\n" +
+ "label.cancel = Cancel\r\n" +
+ "label.close = Close\r\n" +
+ "label.delete = Delete\r\n" +
+ "label.discart = Discart\r\n" +
+ "label.loggedin = Logged in:\r\n" +
+ "label.logout = Logout\r\n" +
+ "label.ok = OK\r\n" +
+ "label.store = Store\r\n" +
+ "label.preview = Preview\r\n" +
+ "label.open = Open\r\n" +
+ "label.user.register = Register\r\n" +
+ "\r\n" +
+ "message.article.priceerror = Please set the price.\r\n" +
+ "message.delete.text = Delete {0}?\r\n" +
+ "message.delete.title = Really delete?\r\n" +
+ "message.invoiceitem.allfieldsmustbeset = Start, end and article must not be \\r\\n" +
+ " empty!\r\n" +
+ "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" +
+ "");
+ }
+}