Generator can generate Java Interface from property File.

REL-BRANCH-PropertyToJavaGenerator-0.0.1
Markus Kreth 7 years ago
parent 0a926855bd
commit 04c12896d1
  1. 39
      .classpath
  2. 3
      .gitignore
  3. 23
      .project
  4. 166
      pom.xml
  5. 47
      src/main/java/de/kreth/property2java/Configuration.java
  6. 59
      src/main/java/de/kreth/property2java/Generator.java
  7. 97
      src/main/java/de/kreth/property2java/cli/ArgumentConfiguration.java
  8. 52
      src/main/java/de/kreth/property2java/cli/CliConfig.java
  9. 48
      src/test/java/de/kreth/property2java/ConfigurationTest.java
  10. 175
      src/test/java/de/kreth/property2java/GeneratorTests.java

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="de.kreth.property2java"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

3
.gitignore vendored

@ -0,0 +1,3 @@
\.settings/
target

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>PropertyToJavaGenerator</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

@ -0,0 +1,166 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.kreth.property2java</groupId>
<artifactId>PropertyToJavaGenerator</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>11</java.version>
<org.junit.jupiter>5.3.0-M1</org.junit.jupiter>
<org.slf4j>1.7.21</org.slf4j>
<org.apache.logging.log4j>2.11.0</org.apache.logging.log4j>
<timestamp>${maven.build.timestamp}</timestamp>
<maven.build.timestamp.format>yyyy-MM-dd HH:mm:ss</maven.build.timestamp.format>
<!-- <sonar.login>bb362ff4cf8428c336af659ad27445944c6ba4ac</sonar.login> -->
<sonar.jacoco.reportPaths>target/surefire-reports</sonar.jacoco.reportPaths>
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
<sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin>
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
<sonar.jacoco.reportPath>${project.basedir}/target/jacoco.exec</sonar.jacoco.reportPath>
<sonar.exclusions>**/src/main/webapp/VAADIN/**/*</sonar.exclusions>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.3.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
<!-- LOGGING -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${org.apache.logging.log4j}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${org.apache.logging.log4j}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${org.apache.logging.log4j}</version>
<scope>runtime</scope>
</dependency>
<!-- TESTING -->
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-runner</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.24.5</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>2.24.5</version>
</dependency>
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>1.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>${java.version}</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.2</version>
<executions>
<execution>
<id>default-jacoco-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>default-jacoco-report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -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<String, Reader> 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;
}
}

@ -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<String, Reader> 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<String> propertyNames = (Enumeration<String>) 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();
}
}

@ -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<String, Reader> 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<String, Reader> 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<String> 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);
}
}
}

@ -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);
}
}

@ -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);
}
}

@ -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<String, Reader> 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<String> 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<String> lines, String key, String expected) {
Optional<String> 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" +
"");
}
}
Loading…
Cancel
Save