propFiles = new ArrayList<>();
-
- String packageName;
-
- public Builder setTarget(String target) {
- this.target = target;
- return this;
- }
+ public Builder setTarget(String target) {
+ this.target = target;
+ return this;
+ }
- public Builder addPropFile(String propFile) {
- this.propFiles.add(propFile);
- return this;
- }
+ public Builder addPropFile(String propFile) {
+ this.propFiles.add(propFile);
+ return this;
+ }
- public Builder setPackageName(String packageName) {
- this.packageName = packageName;
- return this;
- }
+ public Builder setPackageName(String packageName) {
+ this.packageName = packageName;
+ return this;
+ }
- public Configuration build() throws IOException {
- return new ArgumentConfiguration(this);
- }
+ public Configuration build() throws IOException {
+ return new ArgumentConfiguration(this);
}
+ }
}
diff --git a/src/main/java/de/kreth/property2java/processor/GenerateProperty2Java.java b/src/main/java/de/kreth/property2java/processor/GenerateProperty2Java.java
new file mode 100644
index 0000000..4d4862d
--- /dev/null
+++ b/src/main/java/de/kreth/property2java/processor/GenerateProperty2Java.java
@@ -0,0 +1,40 @@
+package de.kreth.property2java.processor;
+
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(TYPE)
+@Retention(RetentionPolicy.SOURCE)
+/**
+ * Für die konfigurierten Resourcen wird jeweils eine Java Klasse erzeugt. Es
+ * muss nur die Abhängigkeit eingebunden werden und die Annotation in einer
+ * Klasse verwendet werden, in deren Package die neuen Klassen generiert werden.
+ *
+ * Für die Ausgabe der Prozessornachrichten muss folgendes im maven compiler
+ * konfiguriert werden:
+ *
+ *
+<build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.8.0</version>
+ <configuration>
+ <release>${java.version}</release>
+ <showWarnings>true</showWarnings>
+ </configuration>
+ </plugin>
+ </plugins>
+</build>
+ *
+ *
+ * @author Markus
+ *
+ */
+public @interface GenerateProperty2Java {
+ String[] resources();
+}
diff --git a/src/main/java/de/kreth/property2java/processor/ProcessorConfiguration.java b/src/main/java/de/kreth/property2java/processor/ProcessorConfiguration.java
new file mode 100644
index 0000000..13e59ce
--- /dev/null
+++ b/src/main/java/de/kreth/property2java/processor/ProcessorConfiguration.java
@@ -0,0 +1,105 @@
+package de.kreth.property2java.processor;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.processing.Filer;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.tools.FileObject;
+import javax.tools.StandardLocation;
+
+import de.kreth.property2java.Configuration;
+import de.kreth.property2java.Generator;
+import de.kreth.property2java.GeneratorException;
+
+public class ProcessorConfiguration implements Configuration {
+
+ private final Filer filer;
+ private final Element element;
+ private final Map input;
+
+ ProcessorConfiguration(Builder builder) throws IOException {
+ this.filer = builder.filer;
+ this.element = builder.element;
+ this.input = new HashMap<>();
+ for (String resource : builder.resourcenames) {
+
+ FileObject ressource = filer.getResource(StandardLocation.CLASS_PATH, "",
+ resource);
+ String className = mapFilenameToClassName(resource);
+ input.put(className, ressource.openReader(false));
+ }
+ }
+
+ @Override
+ public String getPackage() {
+
+ String packageName = "";
+ if (element instanceof TypeElement) {
+ TypeElement typeElement = (TypeElement) element;
+ PackageElement packageElement = (PackageElement) typeElement.getEnclosingElement();
+ packageName = packageElement.getQualifiedName().toString();
+ }
+ return packageName;
+ }
+
+ @Override
+ public Map getInput() {
+ return input;
+ }
+
+ @Override
+ public Path getRootPath() {
+ throw new UnsupportedOperationException(
+ "For Annotation Processor this is not supported as outWriter is overwritten.");
+ }
+
+ @Override
+ public Writer outWriter(String fileName) throws IOException {
+ String packageName = getPackage();
+ if (packageName != null && !packageName.isBlank()) {
+ fileName = packageName + "." + fileName;
+ }
+ return filer.createSourceFile(fileName, element).openWriter();
+ }
+
+ static Builder builder(Filer filer, Element element) {
+ return new Builder(filer, element);
+ }
+
+ static class Builder {
+ private final Filer filer;
+ private final Element element;
+ private final List resourcenames;
+
+ private Builder(Filer filer, Element element) {
+ this.filer = filer;
+ this.element = element;
+ this.resourcenames = new ArrayList<>();
+ }
+
+ public Builder addAll(String[] resourceNames) {
+ this.resourcenames.addAll(Arrays.asList(resourceNames));
+ return this;
+ }
+
+ public Builder addAll(List resourceNames) {
+ this.resourcenames.addAll(resourceNames);
+ return this;
+ }
+
+ public void startGeneration() throws IOException, GeneratorException {
+ Generator g = new Generator(new ProcessorConfiguration(this));
+ g.start();
+ }
+ }
+}
diff --git a/src/main/java/de/kreth/property2java/processor/Property2JavaGenerator.java b/src/main/java/de/kreth/property2java/processor/Property2JavaGenerator.java
new file mode 100644
index 0000000..b89faf2
--- /dev/null
+++ b/src/main/java/de/kreth/property2java/processor/Property2JavaGenerator.java
@@ -0,0 +1,61 @@
+package de.kreth.property2java.processor;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.Set;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.annotation.processing.SupportedSourceVersion;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic.Kind;
+
+import de.kreth.property2java.GeneratorException;
+
+@SupportedAnnotationTypes({ "de.kreth.property2java.processor.GenerateProperty2Java" })
+@SupportedSourceVersion(SourceVersion.RELEASE_8)
+public class Property2JavaGenerator extends AbstractProcessor {
+
+ @Override
+ public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
+
+ if (!roundEnv.processingOver()) {
+ processingEnv.getMessager().printMessage(Kind.NOTE,
+ "Processing annotation " + GenerateProperty2Java.class);
+
+ Set extends Element> elementsAnnotatedWith = roundEnv
+ .getElementsAnnotatedWith(GenerateProperty2Java.class);
+
+ processElements(elementsAnnotatedWith);
+ } else {
+ processingEnv.getMessager().printMessage(Kind.NOTE,
+ "finished working on annotation " + GenerateProperty2Java.class);
+ }
+ return true;
+ }
+
+ private void processElements(Set extends Element> elementsAnnotatedWith) {
+ for (Element element : elementsAnnotatedWith) {
+ String[] resources = element.getAnnotation(GenerateProperty2Java.class).resources();
+ processingEnv.getMessager().printMessage(Kind.NOTE,
+ "Generating Java for " + Arrays.asList(resources));
+ try {
+ ProcessorConfiguration
+ .builder(processingEnv.getFiler(), element)
+ .addAll(resources)
+ .startGeneration();
+ } catch (IOException | GeneratorException e) {
+ StringWriter out = new StringWriter();
+ e.printStackTrace(new PrintWriter(out));
+ out.flush();
+ processingEnv.getMessager().printMessage(Kind.ERROR, "Exception " + e + "\n" + out.toString(),
+ element);
+ }
+ }
+ }
+}
diff --git a/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/src/main/resources/META-INF/services/javax.annotation.processing.Processor
new file mode 100644
index 0000000..d22a1ba
--- /dev/null
+++ b/src/main/resources/META-INF/services/javax.annotation.processing.Processor
@@ -0,0 +1 @@
+de.kreth.property2java.processor.Property2JavaGenerator
\ 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 12ece38..1229b38 100644
--- a/src/test/java/de/kreth/property2java/GeneratorTests.java
+++ b/src/test/java/de/kreth/property2java/GeneratorTests.java
@@ -29,149 +29,149 @@ 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();
- when(config.outputCharset()).thenCallRealMethod();
-
- generator = new Generator(config);
+ 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();
+ when(config.outputCharset()).thenCallRealMethod();
+
+ generator = new Generator(config);
+ }
+
+ @Test
+ void testClassDefinition() throws IOException, GeneratorException {
+
+ when(config.getPackage()).thenReturn("de.kreth.property2java");
+ when(config.mapFilenameToClassName(anyString())).thenCallRealMethod();
+
+ 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 enum")) {
+ lineClass = line;
+ }
+ if (line.contains("{")) {
+ countOpenBaces++;
+ }
+ if (line.contains("}")) {
+ countCloseBaces++;
+ }
}
- @Test
- void testClassDefinition() throws IOException, GeneratorException {
-
- when(config.getPackage()).thenReturn("de.kreth.property2java");
- when(config.mapFilenameToClassName(anyString())).thenCallRealMethod();
-
- 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 enum")) {
- 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", "enum", "Application_Properties")));
-
- }
-
- @Test
- void testOneInputGeneratesOneOutput() throws IOException, GeneratorException {
-
- 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, GeneratorException {
-
- StringWriter out = new StringWriter();
- when(config.outWriter(anyString())).thenReturn(out);
- generator.start();
-
- List lines = out.toString().lines().filter(line -> line.contains(" (\""))
- .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() - 3);
- assertEquals(expected, value, "Line \"" + line + "\" don't match expected Value \"" + expected + "\"");
-
- }
-
- private boolean keyMatches(String line, String key) {
- line = line.toLowerCase();
- key = key.toLowerCase();
- return line.contains("\t" + 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" +
- "");
- }
+ 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", "enum", "Application_Properties")));
+
+ }
+
+ @Test
+ void testOneInputGeneratesOneOutput() throws IOException, GeneratorException {
+
+ 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, GeneratorException {
+
+ StringWriter out = new StringWriter();
+ when(config.outWriter(anyString())).thenReturn(out);
+ generator.start();
+
+ List lines = out.toString().lines().filter(line -> line.contains(" (\""))
+ .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() - 3);
+ assertEquals(expected, value, "Line \"" + line + "\" don't match expected Value \"" + expected + "\"");
+
+ }
+
+ private boolean keyMatches(String line, String key) {
+ line = line.toLowerCase();
+ key = key.toLowerCase();
+ return line.contains("\t" + 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" +
+ "");
+ }
}
diff --git a/src/test/java/de/kreth/property2java/processor/Property2JavaGeneratorTest.java b/src/test/java/de/kreth/property2java/processor/Property2JavaGeneratorTest.java
new file mode 100644
index 0000000..d974371
--- /dev/null
+++ b/src/test/java/de/kreth/property2java/processor/Property2JavaGeneratorTest.java
@@ -0,0 +1,46 @@
+package de.kreth.property2java.processor;
+
+import static org.mockito.Mockito.when;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.annotation.processing.Messager;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.TypeElement;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class Property2JavaGeneratorTest {
+
+ private Property2JavaGenerator processor;
+ @Mock
+ private ProcessingEnvironment processingEnv;
+ @Mock
+ private RoundEnvironment roundEnv;
+ private Set annotations;
+ @Mock
+ private Messager messanger;
+
+ @BeforeEach
+ void initProcesor() {
+ MockitoAnnotations.initMocks(this);
+ annotations = new HashSet<>();
+
+ processor = new Property2JavaGenerator();
+ processor.init(processingEnv);
+ when(processingEnv.getMessager()).thenReturn(messanger);
+ }
+
+ @Test
+ void testGeneratorInitializedCorrectly() {
+ when(roundEnv.getElementsAnnotatedWith(ArgumentMatchers.any(Class.class)))
+ .thenReturn(annotations);
+ processor.process(annotations, roundEnv);
+ }
+}