diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..bf6e349
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 2fe2f58..24f1ec5 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -2,7 +2,7 @@
-
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 4257bea..72f48a2 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -1,14 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
@@ -19,6 +41,7 @@
@@ -32,30 +55,124 @@
- {
- "keyToString": {
- "RunOnceActivity.OpenProjectViewOnStart": "true",
- "RunOnceActivity.ShowReadmeOnStart": "true",
- "last_opened_file_path": "C:/Users/chris/Documents/HomeAuto/Confindibus",
- "project.structure.last.edited": "Artifacts",
- "project.structure.proportion": "0.0",
- "project.structure.side.proportion": "0.2"
+
+}]]>
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Confindibus.iml b/Confindibus.iml
index c90834f..a47d190 100644
--- a/Confindibus.iml
+++ b/Confindibus.iml
@@ -3,9 +3,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/META-INF/services/javax.annotation.processing.Processor b/resources/META-INF/services/javax.annotation.processing.Processor
new file mode 100644
index 0000000..fa29b95
--- /dev/null
+++ b/resources/META-INF/services/javax.annotation.processing.Processor
@@ -0,0 +1 @@
+dev.asdf00.confindibus.processor.ConfigProcessor
diff --git a/src/dev/asdf00/confindibus/ConfigurationException.java b/src/dev/asdf00/confindibus/ConfigurationException.java
index 667e372..ac16e74 100644
--- a/src/dev/asdf00/confindibus/ConfigurationException.java
+++ b/src/dev/asdf00/confindibus/ConfigurationException.java
@@ -1,4 +1,4 @@
-package dev.asdf00.confidibus;
+package dev.asdf00.confindibus;
public class ConfigurationException extends RuntimeException {
public ConfigurationException(String format, Object... params) {
diff --git a/src/dev/asdf00/confindibus/Configurator.java b/src/dev/asdf00/confindibus/Configurator.java
index 0c7bca8..48cd184 100644
--- a/src/dev/asdf00/confindibus/Configurator.java
+++ b/src/dev/asdf00/confindibus/Configurator.java
@@ -1,8 +1,8 @@
-package dev.asdf00.confidibus;
+package dev.asdf00.confindibus;
-import dev.asdf00.confidibus.annotations.Configuration;
-import dev.asdf00.confidibus.annotations.Section;
-import dev.asdf00.confidibus.annotations.Value;
+import dev.asdf00.confindibus.annotations.Configuration;
+import dev.asdf00.confindibus.annotations.Section;
+import dev.asdf00.confindibus.annotations.Value;
import java.io.IOException;
import java.io.PrintStream;
@@ -40,8 +40,7 @@ import java.util.Iterator;
* @Value(comment = "enables debug mode", standard = "false")
* public static boolean DEBUG;
* }
- * }
- *
+ * }}
*/
public class Configurator {
private final Class> _class;
diff --git a/src/dev/asdf00/confindibus/annotations/Configuration.java b/src/dev/asdf00/confindibus/annotations/Configuration.java
index fbd7ed8..f59c505 100644
--- a/src/dev/asdf00/confindibus/annotations/Configuration.java
+++ b/src/dev/asdf00/confindibus/annotations/Configuration.java
@@ -1,4 +1,4 @@
-package dev.asdf00.confidibus.annotations;
+package dev.asdf00.confindibus.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
diff --git a/src/dev/asdf00/confindibus/annotations/Section.java b/src/dev/asdf00/confindibus/annotations/Section.java
index aacae58..68e4df0 100644
--- a/src/dev/asdf00/confindibus/annotations/Section.java
+++ b/src/dev/asdf00/confindibus/annotations/Section.java
@@ -1,4 +1,4 @@
-package dev.asdf00.confidibus.annotations;
+package dev.asdf00.confindibus.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
diff --git a/src/dev/asdf00/confindibus/annotations/Value.java b/src/dev/asdf00/confindibus/annotations/Value.java
index 938182a..84dbdf7 100644
--- a/src/dev/asdf00/confindibus/annotations/Value.java
+++ b/src/dev/asdf00/confindibus/annotations/Value.java
@@ -1,4 +1,4 @@
-package dev.asdf00.confidibus.annotations;
+package dev.asdf00.confindibus.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
diff --git a/src/dev/asdf00/confindibus/processor/ConfigProcessor.java b/src/dev/asdf00/confindibus/processor/ConfigProcessor.java
new file mode 100644
index 0000000..0a8f864
--- /dev/null
+++ b/src/dev/asdf00/confindibus/processor/ConfigProcessor.java
@@ -0,0 +1,122 @@
+package dev.asdf00.confindibus.processor;
+
+import dev.asdf00.confindibus.annotations.Configuration;
+import dev.asdf00.confindibus.annotations.Section;
+import dev.asdf00.confindibus.annotations.Value;
+
+import javax.annotation.processing.*;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+import javax.tools.Diagnostic;
+import java.util.Set;
+
+@SupportedAnnotationTypes("dev.asdf00.confindibus.annotations.*")
+@SupportedSourceVersion(SourceVersion.RELEASE_17)
+public class ConfigProcessor extends AbstractProcessor {
+ @Override
+ public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ boolean processedAllAns = true;
+ for (TypeElement anno : annotations) {
+ Set extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(anno);
+ for (Element elem : annotatedElements) {
+ if (anno.getQualifiedName().contentEquals("dev.asdf00.confindibus.annotations.Configuration")) {
+ configuration(elem);
+ } else if (anno.getQualifiedName().contentEquals("dev.asdf00.confindibus.annotations.Section")) {
+ section(elem);
+ } else if (anno.getQualifiedName().contentEquals("dev.asdf00.confindibus.annotations.Value")) {
+ value(elem);
+ } else {
+ processedAllAns = false;
+ }
+ }
+ }
+ return processedAllAns;
+ }
+
+ private void configuration(Element config) {
+ if (config.getAnnotation(Section.class) == null) {
+ printError("Classes annotated with @Configuration also need to be annotated with @Section!", config);
+ }
+ }
+
+ private void section(Element section) {
+ Element inner = section;
+ Element outer = inner.getEnclosingElement();
+ while (outer.getClass().getName().equals("com.sun.tools.javac.code.Symbol$ClassSymbol")) {
+ if (outer.getAnnotation(Section.class) == null) {
+ printError("Subsection '%s' needs to be part of an outer @Section!".formatted(inner.getSimpleName()), section);
+ }
+ Set clsMods = inner.getModifiers();
+ if (!clsMods.contains(Modifier.PUBLIC) || !clsMods.contains(Modifier.STATIC)) {
+ printError("Classes that represent subsections need to be 'public static'!", section);
+ }
+ inner = outer;
+ outer = inner.getEnclosingElement();
+ }
+ if (inner.getAnnotation(Configuration.class) == null) {
+ printError("Class '%s' annotated with @Section found outside of valid configuration!".formatted(section.getSimpleName()), section);
+ }
+
+ Section sect = section.getAnnotation(Section.class);
+ if (sect.title().indexOf('{') != -1 || sect.title().indexOf('\n') != -1) {
+ printError("The title of a section must not contain '{' or '\\n'! (title = '%s')".formatted(sect.title()), section);
+ }
+ }
+
+ private void value(Element value) {
+ Set modifiers = value.getModifiers();
+ if (!modifiers.contains(Modifier.PUBLIC) || !modifiers.contains(Modifier.STATIC) || modifiers.contains(Modifier.FINAL)) {
+ printError("Field '%s' annotated with @Value needs to be exactly 'public static'!".formatted(value.getSimpleName()), value);
+ }
+ if (value.getEnclosingElement().getAnnotation(Section.class) == null) {
+ printError("Field '%s' annotated with @Value needs to be inside a class annotated with @Section!".formatted(value.getSimpleName()), value);
+ }
+
+ checkParsableAsType(value, value.getAnnotation(Value.class).standard());
+
+ Value val = value.getAnnotation(Value.class);
+ if (val.name().indexOf(':') != -1 || val.name().indexOf('\n') != -1) {
+ printError("The name of a field must not contain ':' or '\\n'! (name = '%s')".formatted(val.name()), value);
+ }
+ }
+
+ // =================================================================================================================
+ static volatile Object temp;
+
+ private void checkParsableAsType(Element elem, String value) {
+ TypeMirror type = elem.asType();
+ try {
+ String tst = type.toString();
+ if (tst.equals("byte")) {
+ temp = Byte.parseByte(value);
+ } else if (tst.equals("short")) {
+ temp = Short.parseShort(value);
+ } else if (tst.equals("int")) {
+ temp = Integer.parseInt(value);
+ } else if (tst.equals("long")) {
+ temp = Long.parseLong(value);
+ } else if (tst.equals("float")) {
+ temp = Float.parseFloat(value);
+ } else if (tst.equals("double")) {
+ temp = Double.parseDouble(value);
+ } else if (tst.equals("boolean")) {
+ temp = Boolean.parseBoolean(value);
+ } else if (tst.equals("char")) {
+ temp = value.charAt(0);
+ } else if (tst.equals("java.lang.String")) {
+ temp = value;
+ } else {
+ printError("'%s' is not a valid type for a field annotated with @Value!".formatted(type), elem);
+ }
+ } catch (NumberFormatException | IndexOutOfBoundsException e) {
+ printError("Standard value of '%s' is not parsable as %s!".formatted(value, type), elem);
+ }
+ }
+
+ private void printError(CharSequence msg, Element elem) {
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, elem);
+ }
+}
diff --git a/test/dev/asdf00/confindibus/ProcessorTest.java b/test/dev/asdf00/confindibus/ProcessorTest.java
new file mode 100644
index 0000000..9d16a4b
--- /dev/null
+++ b/test/dev/asdf00/confindibus/ProcessorTest.java
@@ -0,0 +1,67 @@
+package dev.asdf00.confindibus;
+
+import dev.asdf00.confindibus.testclasses.*;
+import org.junit.Assert;
+import org.junit.Test;
+
+import static com.sun.tools.javac.Main.compile;
+
+public class ProcessorTest {
+ @Test
+ public void testSimpleConfig() {
+ Assert.assertEquals(0, compileWithProc(SimpleTestConfig.class));
+ }
+
+ @Test
+ public void testBrokenConfig() {
+ Assert.assertEquals(1, compileWithProc(BrokenConfig.class));
+ }
+
+ @Test
+ public void testBrokenSection1() {
+ Assert.assertEquals(1, compileWithProc(BrokenSection1.class));
+ }
+
+ @Test
+ public void testBrokenSection2() {
+ Assert.assertEquals(1, compileWithProc(BrokenSection2.class));
+ }
+
+ @Test
+ public void testBrokenValue1() {
+ Assert.assertEquals(1, compileWithProc(BrokenValue1.class));
+ }
+
+ @Test
+ public void testBrokenValue2() {
+ Assert.assertEquals(1, compileWithProc(BrokenValue2.class));
+ }
+
+ @Test
+ public void testBrokenValue3() {
+ Assert.assertEquals(1, compileWithProc(BrokenValue3.class));
+ }
+
+ @Test
+ public void testBrokenValue4() {
+ Assert.assertEquals(1, compileWithProc(BrokenValue4.class));
+ }
+
+
+ // =================================================================================================================
+ private int compileWithProc(Class>... classes) {
+ int fixedArgCnt = 3;
+ String[] args = new String[classes.length + fixedArgCnt];
+ args[0] = "-proc:only";
+ args[1] = "-processor";
+ args[2] = "dev.asdf00.confindibus.processor.ConfigProcessor";
+ for (int i = fixedArgCnt; i < args.length; i++) {
+ args[i] = testClass(classes[i - fixedArgCnt]);
+ }
+ return compile(args);
+ }
+
+ private String testClass(Class cls) {
+ return "test/" + cls.getName().replace('.', '/') + ".java";
+ }
+}
diff --git a/test/dev/asdf00/confindibus/testclasses/BrokenConfig.java b/test/dev/asdf00/confindibus/testclasses/BrokenConfig.java
new file mode 100644
index 0000000..3aa7d6f
--- /dev/null
+++ b/test/dev/asdf00/confindibus/testclasses/BrokenConfig.java
@@ -0,0 +1,10 @@
+package dev.asdf00.confindibus.testclasses;
+
+import dev.asdf00.confindibus.annotations.Configuration;
+import dev.asdf00.confindibus.annotations.Value;
+
+@Configuration(path = "test")
+public class BrokenConfig {
+ @Value(standard = "value")
+ public static String VALUE;
+}
diff --git a/test/dev/asdf00/confindibus/testclasses/BrokenSection1.java b/test/dev/asdf00/confindibus/testclasses/BrokenSection1.java
new file mode 100644
index 0000000..be7e839
--- /dev/null
+++ b/test/dev/asdf00/confindibus/testclasses/BrokenSection1.java
@@ -0,0 +1,10 @@
+package dev.asdf00.confindibus.testclasses;
+
+import dev.asdf00.confindibus.annotations.Section;
+import dev.asdf00.confindibus.annotations.Value;
+
+@Section(title = "test")
+public class BrokenSection1 {
+ @Value(standard = "value")
+ public static String VALUE;
+}
diff --git a/test/dev/asdf00/confindibus/testclasses/BrokenSection2.java b/test/dev/asdf00/confindibus/testclasses/BrokenSection2.java
new file mode 100644
index 0000000..7245c6c
--- /dev/null
+++ b/test/dev/asdf00/confindibus/testclasses/BrokenSection2.java
@@ -0,0 +1,11 @@
+package dev.asdf00.confindibus.testclasses;
+
+import dev.asdf00.confindibus.annotations.Configuration;
+import dev.asdf00.confindibus.annotations.Section;
+
+@Configuration(path = "test")
+@Section(title = "sect")
+public class BrokenSection2 {
+ @Section(title = "broken subsect")
+ public class Subsect { }
+}
diff --git a/test/dev/asdf00/confindibus/testclasses/BrokenValue1.java b/test/dev/asdf00/confindibus/testclasses/BrokenValue1.java
new file mode 100644
index 0000000..28bd2ac
--- /dev/null
+++ b/test/dev/asdf00/confindibus/testclasses/BrokenValue1.java
@@ -0,0 +1,12 @@
+package dev.asdf00.confindibus.testclasses;
+
+import dev.asdf00.confindibus.annotations.Configuration;
+import dev.asdf00.confindibus.annotations.Section;
+import dev.asdf00.confindibus.annotations.Value;
+
+@Configuration(path = "test")
+@Section(title = "sect")
+public class BrokenValue1 {
+ @Value(standard = "test")
+ public String TEST;
+}
diff --git a/test/dev/asdf00/confindibus/testclasses/BrokenValue2.java b/test/dev/asdf00/confindibus/testclasses/BrokenValue2.java
new file mode 100644
index 0000000..96c8d75
--- /dev/null
+++ b/test/dev/asdf00/confindibus/testclasses/BrokenValue2.java
@@ -0,0 +1,14 @@
+package dev.asdf00.confindibus.testclasses;
+
+import dev.asdf00.confindibus.annotations.Configuration;
+import dev.asdf00.confindibus.annotations.Section;
+import dev.asdf00.confindibus.annotations.Value;
+
+@Configuration(path = "test")
+@Section(title = "sect outer")
+public class BrokenValue2 {
+ public static class InnerSection {
+ @Value(standard = "value")
+ public static String VALUE;
+ }
+}
diff --git a/test/dev/asdf00/confindibus/testclasses/BrokenValue3.java b/test/dev/asdf00/confindibus/testclasses/BrokenValue3.java
new file mode 100644
index 0000000..332195d
--- /dev/null
+++ b/test/dev/asdf00/confindibus/testclasses/BrokenValue3.java
@@ -0,0 +1,12 @@
+package dev.asdf00.confindibus.testclasses;
+
+import dev.asdf00.confindibus.annotations.Configuration;
+import dev.asdf00.confindibus.annotations.Section;
+import dev.asdf00.confindibus.annotations.Value;
+
+@Configuration(path = "test")
+@Section(title = "sect")
+public class BrokenValue3 {
+ @Value(standard = "")
+ public static Object VALUE;
+}
diff --git a/test/dev/asdf00/confindibus/testclasses/BrokenValue4.java b/test/dev/asdf00/confindibus/testclasses/BrokenValue4.java
new file mode 100644
index 0000000..ae80813
--- /dev/null
+++ b/test/dev/asdf00/confindibus/testclasses/BrokenValue4.java
@@ -0,0 +1,12 @@
+package dev.asdf00.confindibus.testclasses;
+
+import dev.asdf00.confindibus.annotations.Configuration;
+import dev.asdf00.confindibus.annotations.Section;
+import dev.asdf00.confindibus.annotations.Value;
+
+@Configuration(path = "test")
+@Section(title = "sect")
+public class BrokenValue4 {
+ @Value(standard = "256")
+ public static byte VALUE;
+}
diff --git a/test/dev/asdf00/confindibus/testclasses/SimpleTestConfig.java b/test/dev/asdf00/confindibus/testclasses/SimpleTestConfig.java
new file mode 100644
index 0000000..cc6432d
--- /dev/null
+++ b/test/dev/asdf00/confindibus/testclasses/SimpleTestConfig.java
@@ -0,0 +1,31 @@
+package dev.asdf00.confindibus.testclasses;
+
+import dev.asdf00.confindibus.annotations.Configuration;
+import dev.asdf00.confindibus.annotations.Section;
+import dev.asdf00.confindibus.annotations.Value;
+
+@Configuration(path = "test")
+@Section(title = "sect")
+public class SimpleTestConfig {
+ @Value(standard = "1")
+ public static byte BYTE;
+ @Value(standard = "1")
+ public static short SHORT;
+ @Value(standard = "1")
+ public static int INT;
+ @Value(standard = "1")
+ public static long LONG;
+ @Value(standard = "1.0")
+ public static float FLOAT;
+ @Value(standard = "1.0")
+ public static double DOUBLE;
+ @Value(standard = "true")
+ public static boolean BOOLEAN;
+ @Value(standard = "c")
+ public static char CHAR;
+ @Value(standard = "string")
+ public static String STRING;
+
+ @Section(title = "test inner section")
+ public static class SubSection { }
+}