Annotation Processor for the generator

master
Markus Kreth 3 years ago
parent 11f18a9abf
commit 1d447b2045
  1. 86
      .classpath
  2. 1
      META-INF/services/javax.annotation.processing.Processor
  3. 35
      pom.xml
  4. 74
      src/main/java/de/kreth/property2java/Configuration.java
  5. 188
      src/main/java/de/kreth/property2java/Generator.java
  6. 123
      src/main/java/de/kreth/property2java/cli/ArgumentConfiguration.java
  7. 40
      src/main/java/de/kreth/property2java/processor/GenerateProperty2Java.java
  8. 105
      src/main/java/de/kreth/property2java/processor/ProcessorConfiguration.java
  9. 61
      src/main/java/de/kreth/property2java/processor/Property2JavaGenerator.java
  10. 1
      src/main/resources/META-INF/services/javax.annotation.processing.Processor
  11. 286
      src/test/java/de/kreth/property2java/GeneratorTests.java
  12. 46
      src/test/java/de/kreth/property2java/processor/Property2JavaGeneratorTest.java

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

@ -0,0 +1 @@
de.kreth.property2java.processor.Property2JavaGenerator

@ -14,12 +14,14 @@
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>11</java.version> <java.version>11</java.version>
<org.junit.jupiter>5.3.0-M1</org.junit.jupiter> <org.junit.jupiter>5.3.0-M1</org.junit.jupiter>
<org.slf4j>1.7.21</org.slf4j> <org.slf4j>1.7.36</org.slf4j>
<org.apache.logging.log4j>2.17.1</org.apache.logging.log4j> <org.apache.logging.log4j>2.17.2</org.apache.logging.log4j>
<timestamp>${maven.build.timestamp}</timestamp> <timestamp>${maven.build.timestamp}</timestamp>
<maven.build.timestamp.format>yyyy-MM-dd HH:mm:ss</maven.build.timestamp.format> <maven.build.timestamp.format>yyyy-MM-dd HH:mm:ss</maven.build.timestamp.format>
<auto-service.version>1.0-rc2</auto-service.version>
<sonar.login>3f3a1fa86ea83226b564895f3a8503f67e855440</sonar.login> <sonar.login>3f3a1fa86ea83226b564895f3a8503f67e855440</sonar.login>
<sonar.jacoco.reportPaths>target/surefire-reports</sonar.jacoco.reportPaths> <sonar.jacoco.reportPaths>target/surefire-reports</sonar.jacoco.reportPaths>
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin> <sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
@ -51,7 +53,7 @@
<artifactId>freemarker</artifactId> <artifactId>freemarker</artifactId>
<version>2.3.28</version> <version>2.3.28</version>
</dependency> </dependency>
<!-- LOGGING --> <!-- LOGGING -->
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
@ -136,34 +138,9 @@
<version>3.8.0</version> <version>3.8.0</version>
<configuration> <configuration>
<release>${java.version}</release> <release>${java.version}</release>
<compilerArgs>-proc:none</compilerArgs>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</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> </plugins>
</build> </build>

@ -15,38 +15,46 @@ import de.kreth.property2java.config.Regex;
public interface Configuration { public interface Configuration {
/** /**
* Package for generated Java Classes eg. "de.kreth.property2java". If null - no package line is generated. * Package for generated Java Classes eg. "de.kreth.property2java". If null - no
* @return * package line is generated.
*/ *
String getPackage(); * @return
*/
/** String getPackage();
* Filename to InputReader Entries
* @return /**
*/ * Filename to InputReader Entries
Map<String, Reader> getInput(); *
* @return
/** */
* Path of java source folder. Map<String, Reader> getInput();
* @return
*/ /**
Path getRootPath(); * Path of java source folder.
*
default Writer outWriter(String fileName) throws IOException { * @return
return new FileWriter(new File(getRootPath().toFile(), mapFilenameToClassName(fileName) + ".java"), */
outputCharset()); Path getRootPath();
}
default Writer outWriter(String fileName) throws IOException {
default Charset outputCharset() { return new FileWriter(new File(getRootPath().toFile(), mapFilenameToClassName(fileName) + ".java"),
return Charset.defaultCharset(); outputCharset());
} }
default String mapFilenameToClassName(String fileName) { default Charset outputCharset() {
return Charset.defaultCharset();
String path = Regex.PATTERN.matcher(fileName).replaceAll(".").replaceAll("\\.", "_").replaceAll(" ", "_"); }
path = WordUtils.capitalize(path, '_');
return path; default String mapFilenameToClassName(String fileName) {
}
String path = Regex.PATTERN.matcher(fileName)
.replaceAll(".")
.replaceAll("\\.", "_")
.replaceAll(" ", "_")
.replaceAll("/", "_");
path = WordUtils.capitalize(path, '_');
return path;
}
} }

@ -1,8 +1,10 @@
package de.kreth.property2java; package de.kreth.property2java;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.io.Writer; import java.io.Writer;
import java.net.URL;
import java.text.DateFormat; import java.text.DateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -20,114 +22,134 @@ import freemarker.template.TemplateException;
public class Generator { public class Generator {
private static final DateFormat dateTimeInstance = DateFormat.getDateTimeInstance(); private static final DateFormat dateTimeInstance = DateFormat.getDateTimeInstance();
private final Configuration config; private final Configuration config;
private final Template template; private final Template template;
public Generator(Configuration config) { public Generator(Configuration config) {
this.config = config; this.config = config;
try { try {
template = FreemarkerConfig.INSTANCE.getTemplate(); template = FreemarkerConfig.INSTANCE.getTemplate();
} } catch (IOException e) {
catch (IOException e) { throw new IllegalStateException("Unable to load freemarker template", e);
throw new IllegalStateException("Unable to load freemarker template", e);
}
} }
}
public void start() throws IOException, GeneratorException {
public void start() throws IOException, GeneratorException { for (Map.Entry<String, Reader> entry : config.getInput().entrySet()) {
String fileName = entry.getKey();
for (Map.Entry<String, Reader> entry : config.getInput().entrySet()) { try (Writer out = config.outWriter(fileName)) {
String fileName = entry.getKey();
try (Writer out = config.outWriter(fileName)) { Properties properties = new Properties();
properties.load(entry.getValue());
Properties properties = new Properties(); try {
properties.load(entry.getValue()); generate(properties, out, fileName, config);
try { } catch (TemplateException e) {
generate(properties, out, fileName, config); throw new GeneratorException("Error configuring Engine", e);
}
catch (TemplateException e) {
throw new GeneratorException("Error configuring Engine", e);
}
}
} }
}
} }
}
void generate(Properties properties, Writer out, String fileName, Configuration config) void generate(Properties properties, Writer out, String fileName, Configuration config)
throws IOException, TemplateException { throws IOException, TemplateException {
Map<String, Object> root = new HashMap<>(); Map<String, Object> root = new HashMap<>();
root.put("generator_name", getClass().getName()); root.put("generator_name", getClass().getName());
root.put("generation_date", dateTimeInstance.format(new Date())); root.put("generation_date", dateTimeInstance.format(new Date()));
root.put("package", config.getPackage()); root.put("package", config.getPackage());
root.put("fileName", fileName); root.put("fileName", fileName);
root.put("classname", config.mapFilenameToClassName(fileName)); root.put("classname", config.mapFilenameToClassName(fileName));
List<Entry> entries = new ArrayList<>(); List<Entry> entries = new ArrayList<>();
root.put("entries", entries); root.put("entries", entries);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
List<String> propertyNames = Collections.list((Enumeration<String>) properties.propertyNames()); List<String> propertyNames = Collections.list((Enumeration<String>) properties.propertyNames());
Collections.sort(propertyNames); Collections.sort(propertyNames);
for (String propertyKeyString : propertyNames) { for (String propertyKeyString : propertyNames) {
final String propertyValue = properties.getProperty(propertyKeyString); final String propertyValue = properties.getProperty(propertyKeyString);
entries.add(new Entry(propertyKeyString.toUpperCase().replaceAll("[\\.-]", "_"), propertyKeyString, entries.add(new Entry(propertyKeyString.toUpperCase().replaceAll("[\\.-]", "_"), propertyKeyString,
propertyValue)); propertyValue));
}
template.process(root, out);
} }
template.process(root, out);
}
public static void main(String[] args) throws IOException, GeneratorException { public static void main(String[] args) throws IOException, GeneratorException {
Generator generator = new Generator(ArgumentConfiguration.parse(args)); Generator generator = new Generator(ArgumentConfiguration.parse(args));
generator.start(); generator.start();
} }
/** public static void generateFor(Class<?> locationClass, List<URL> rescources, String relativeTargetDir)
* Represents an Property Entry for the generated java class. throws IOException, GeneratorException {
* @author markus
*
*/
public class Entry {
public final String constant; ArgumentConfiguration.Builder config = new ArgumentConfiguration.Builder();
public final String key; rescources
.stream()
.map(URL::getFile)
.map(File::new)
.map(File::getAbsolutePath)
.forEach(config::addPropFile);
public final String value; config.setPackageName(locationClass.getPackageName())
.setTarget(relativeTargetDir);
/** Generator g = new Generator(config.build());
* Creates Property Entry data for the generated java class. g.start();
* @param constant name for the created constant. }
* @param key property key
* @param value property value
*/
public Entry(String constant, String key, String value) {
super();
this.constant = constant;
this.key = key;
this.value = value;
}
public String getConstant() { /**
return constant; * Represents an Property Entry for the generated java class.
} *
* @author markus
*
*/
public class Entry {
public String getKey() { public final String constant;
return key;
}
public String getValue() { public final String key;
return value;
}
@Override public final String value;
public String toString() {
return "Entry [constant=" + constant + ", key=" + key + ", value=" + value + "]"; /**
} * Creates Property Entry data for the generated java class.
*
* @param constant name for the created constant.
* @param key property key
* @param value property value
*/
public Entry(String constant, String key, String value) {
super();
this.constant = constant;
this.key = key;
this.value = value;
}
public String getConstant() {
return constant;
}
public String getKey() {
return key;
}
public String getValue() {
return value;
} }
@Override
public String toString() {
return "Entry [constant=" + constant + ", key=" + key + ", value=" + value + "]";
}
}
} }

@ -16,82 +16,81 @@ import de.kreth.property2java.Configuration;
public class ArgumentConfiguration implements Configuration { public class ArgumentConfiguration implements Configuration {
private final String packageName; private final String packageName;
private final Map<String, Reader> files; private final Map<String, Reader> files;
private final Path rootPath; private final Path rootPath;
private ArgumentConfiguration(Builder builder) throws IOException { private ArgumentConfiguration(Builder builder) throws IOException {
this.packageName = builder.packageName; this.packageName = builder.packageName;
rootPath = new File(builder.target).toPath(); rootPath = new File(builder.target).toPath();
files = new HashMap<>(); files = new HashMap<>();
for (String filePath : builder.propFiles) { for (String filePath : builder.propFiles) {
File f = new File(filePath); File f = new File(filePath);
files.put(f.getName(), new FileReader(f)); files.put(f.getName(), new FileReader(f));
}
}
@Override
public String getPackage() {
return packageName;
} }
@Override }
public Map<String, Reader> getInput() {
return files; @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);
}
@Override public static Configuration parse(String[] args) throws IOException {
public Path getRootPath() { CliConfig cliConfig = new CliConfig();
return rootPath;
}
@Override Builder builder = new Builder();
public Writer outWriter(String fileName) throws IOException { cliConfig.fill(builder, args);
File dir; return builder.build();
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 { public static class Builder {
CliConfig cliConfig = new CliConfig(); String target;
Builder builder = new Builder(); List<String> propFiles = new ArrayList<>();
cliConfig.fill(builder, args);
return builder.build();
}
static class Builder { String packageName;
String target;
List<String> propFiles = new ArrayList<>(); public Builder setTarget(String target) {
this.target = target;
String packageName; return this;
}
public Builder setTarget(String target) {
this.target = target;
return this;
}
public Builder addPropFile(String propFile) { public Builder addPropFile(String propFile) {
this.propFiles.add(propFile); this.propFiles.add(propFile);
return this; return this;
} }
public Builder setPackageName(String packageName) { public Builder setPackageName(String packageName) {
this.packageName = packageName; this.packageName = packageName;
return this; return this;
} }
public Configuration build() throws IOException { public Configuration build() throws IOException {
return new ArgumentConfiguration(this); return new ArgumentConfiguration(this);
}
} }
}
} }

@ -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:
*
* <pre>
&lt;build&gt;
&lt;plugins&gt;
&lt;plugin&gt;
&lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
&lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;
&lt;version&gt;3.8.0&lt;/version&gt;
&lt;configuration&gt;
&lt;release&gt;${java.version}&lt;/release&gt;
<b>&lt;showWarnings&gt;true&lt;/showWarnings&gt;</b>
&lt;/configuration&gt;
&lt;/plugin&gt;
&lt;/plugins&gt;
&lt;/build&gt;
* </pre>
*
* @author Markus
*
*/
public @interface GenerateProperty2Java {
String[] resources();
}

@ -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<String, Reader> 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<String, Reader> 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<String> 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<String> resourceNames) {
this.resourcenames.addAll(resourceNames);
return this;
}
public void startGeneration() throws IOException, GeneratorException {
Generator g = new Generator(new ProcessorConfiguration(this));
g.start();
}
}
}

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

@ -0,0 +1 @@
de.kreth.property2java.processor.Property2JavaGenerator

@ -29,149 +29,149 @@ import org.junit.jupiter.api.Test;
class GeneratorTests { class GeneratorTests {
private String path = "application.properties"; private String path = "application.properties";
private Configuration config; private Configuration config;
private Generator generator; private Generator generator;
@BeforeEach @BeforeEach
void setUp() throws Exception { void setUp() throws Exception {
Map<String, Reader> input = new HashMap<>(); Map<String, Reader> input = new HashMap<>();
input.put(path, testProperties()); input.put(path, testProperties());
config = mock(Configuration.class); config = mock(Configuration.class);
when(config.getInput()).thenReturn(input); when(config.getInput()).thenReturn(input);
when(config.mapFilenameToClassName(anyString())).thenCallRealMethod(); when(config.mapFilenameToClassName(anyString())).thenCallRealMethod();
when(config.outputCharset()).thenCallRealMethod(); when(config.outputCharset()).thenCallRealMethod();
generator = new Generator(config); 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 assertEquals(countCloseBaces, countOpenBaces,
void testClassDefinition() throws IOException, GeneratorException { "Count of Braces doesn't match. Open = " + countOpenBaces + ", Close = " + countCloseBaces);
when(config.getPackage()).thenReturn("de.kreth.property2java"); assertNotNull(linePackage);
when(config.mapFilenameToClassName(anyString())).thenCallRealMethod(); assertNotNull(lineClass);
StringWriter out = new StringWriter(); assertThat(linePackage,
when(config.outWriter(anyString())).thenReturn(out); Matchers.stringContainsInOrder(Arrays.asList("package", "de.kreth.property2java", ";")));
generator.start(); assertThat(lineClass,
Matchers.stringContainsInOrder(Arrays.asList("public", "enum", "Application_Properties")));
String sourceCode = out.toString().trim();
StringTokenizer sourceTokenizer = new StringTokenizer(sourceCode, "\n"); }
String linePackage = null;
String lineClass = null; @Test
int countOpenBaces = 0; void testOneInputGeneratesOneOutput() throws IOException, GeneratorException {
int countCloseBaces = 0;
while (sourceTokenizer.hasMoreTokens()) { Writer out = mock(Writer.class);
String line = sourceTokenizer.nextToken(); Writer nonOut = mock(Writer.class);
if (line.trim().startsWith("package")) { when(config.outWriter(anyString())).thenReturn(out, nonOut);
linePackage = line; generator.start();
} verify(out).close();
else if (line.trim().startsWith("public enum")) { verify(nonOut, never()).close();
lineClass = line; verify(nonOut, never()).flush();
} }
if (line.contains("{")) {
countOpenBaces++; @Test
} void testKeys() throws IOException, GeneratorException {
if (line.contains("}")) {
countCloseBaces++; StringWriter out = new StringWriter();
} when(config.outWriter(anyString())).thenReturn(out);
} generator.start();
assertEquals(countCloseBaces, countOpenBaces, List<String> lines = out.toString().lines().filter(line -> line.contains(" (\""))
"Count of Braces doesn't match. Open = " + countOpenBaces + ", Close = " + countCloseBaces); .collect(Collectors.toList());
assertNotNull(linePackage); assertEquals(21, lines.size());
assertNotNull(lineClass); assertLineMatch(lines, "label", "label");
assertLineMatch(lines, "label_addarticle", "label.addarticle");
assertThat(linePackage, assertLineMatch(lines, "label_user_register", "label.user.register");
Matchers.stringContainsInOrder(Arrays.asList("package", "de.kreth.property2java", ";"))); assertLineMatch(lines, "message_article_priceerror", "message.article.priceerror");
assertLineMatch(lines, "message_invoiceitem_startbeforeend",
assertThat(lineClass, "message.invoiceitem.startbeforeend");
Matchers.stringContainsInOrder(Arrays.asList("public", "enum", "Application_Properties"))); assertLineMatch(lines, "message_invoiceitem_allfieldsmustbeset",
"message.invoiceitem.allfieldsmustbeset");
} }
@Test private void assertLineMatch(List<String> lines, String key, String expected) {
void testOneInputGeneratesOneOutput() throws IOException, GeneratorException { Optional<String> found = lines.stream().filter(line -> keyMatches(line, key))
.findFirst();
Writer out = mock(Writer.class);
Writer nonOut = mock(Writer.class); assertTrue(found.isPresent(), "No line found with key = " + key);
when(config.outWriter(anyString())).thenReturn(out, nonOut); final String line = found.get().trim();
generator.start(); int indexEquals = line.indexOf('(');
verify(out).close(); String value = line.substring(indexEquals + 1).trim().substring(1);
verify(nonOut, never()).close(); value = value.substring(0, value.length() - 3);
verify(nonOut, never()).flush(); assertEquals(expected, value, "Line \"" + line + "\" don't match expected Value \"" + expected + "\"");
}
}
@Test
void testKeys() throws IOException, GeneratorException { private boolean keyMatches(String line, String key) {
line = line.toLowerCase();
StringWriter out = new StringWriter(); key = key.toLowerCase();
when(config.outWriter(anyString())).thenReturn(out); return line.contains("\t" + key + " ");
generator.start(); }
List<String> lines = out.toString().lines().filter(line -> line.contains(" (\"")) private StringReader testProperties() {
.collect(Collectors.toList()); return new StringReader("\r\n" +
"label = \r\n" +
assertEquals(21, lines.size()); "\r\n" +
assertLineMatch(lines, "label", "label"); "label.addarticle = Add Article\r\n" +
assertLineMatch(lines, "label_addarticle", "label.addarticle"); "label.cancel = Cancel\r\n" +
assertLineMatch(lines, "label_user_register", "label.user.register"); "label.close = Close\r\n" +
assertLineMatch(lines, "message_article_priceerror", "message.article.priceerror"); "label.delete = Delete\r\n" +
assertLineMatch(lines, "message_invoiceitem_startbeforeend", "label.discart = Discart\r\n" +
"message.invoiceitem.startbeforeend"); "label.loggedin = Logged in:\r\n" +
assertLineMatch(lines, "message_invoiceitem_allfieldsmustbeset", "label.logout = Logout\r\n" +
"message.invoiceitem.allfieldsmustbeset"); "label.ok = OK\r\n" +
} "label.store = Store\r\n" +
"label.preview = Preview\r\n" +
private void assertLineMatch(List<String> lines, String key, String expected) { "label.open = Open\r\n" +
Optional<String> found = lines.stream().filter(line -> keyMatches(line, key)) "label.user.register = Register\r\n" +
.findFirst(); "\r\n" +
assertTrue(found.isPresent(), "No line found with key = " + key); "message.article.priceerror = Please set the price.\r\n" +
final String line = found.get().trim(); "message.delete.text = Delete {0}?\r\n" +
int indexEquals = line.indexOf('('); "message.delete.title = Really delete?\r\n" +
String value = line.substring(indexEquals + 1).trim().substring(1); "message.invoiceitem.allfieldsmustbeset = Start, end and article must not be \\r\\n" +
value = value.substring(0, value.length() - 3); " empty!\r\n" +
assertEquals(expected, value, "Line \"" + line + "\" don't match expected Value \"" + expected + "\""); "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" +
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" +
"");
}
} }

@ -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<TypeElement> 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);
}
}
Loading…
Cancel
Save