Skip to content

Commit 8879fc7

Browse files
committed
Create @EnumExt and EnumExtProcessor
1 parent 5308d6a commit 8879fc7

File tree

10 files changed

+377
-84
lines changed

10 files changed

+377
-84
lines changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
GROUP=com.importre.kotlin
2-
VERSION_NAME=0.2.0
2+
VERSION_NAME=0.3.0
33
VCS_URL=https://github.com/importre/kotlin-enumerize.git

modules/enumerize-processor/src/main/java/com/importre/kotlin/BaseProcessor.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import com.squareup.kotlinpoet.FileSpec;
44

5+
import java.io.File;
6+
import java.io.IOException;
57
import java.io.PrintWriter;
68
import java.io.StringWriter;
79
import java.lang.annotation.Annotation;
@@ -44,7 +46,24 @@ private boolean processImpl(
4446
return true;
4547
}
4648

47-
protected abstract void generateFiles();
49+
protected void generateFiles() {
50+
String pathname = processingEnv
51+
.getOptions()
52+
.get("kapt.kotlin.generated");
53+
54+
if (pathname == null) {
55+
error("Please set `kapt.kotlin.generated` in your gradle project.");
56+
return;
57+
}
58+
59+
fileSpecs.forEach(fileSpec -> {
60+
try {
61+
fileSpec.writeTo(new File(pathname));
62+
} catch (IOException e) {
63+
fatalError(e);
64+
}
65+
});
66+
}
4867

4968
protected abstract void process(RoundEnvironment roundEnv);
5069

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package com.importre.kotlin.enumerize;
2+
3+
import com.google.auto.service.AutoService;
4+
import com.importre.kotlin.BaseProcessor;
5+
import com.squareup.kotlinpoet.AnnotationSpec;
6+
import com.squareup.kotlinpoet.FileSpec;
7+
import com.squareup.kotlinpoet.FunSpec;
8+
import com.squareup.kotlinpoet.PropertySpec;
9+
import com.squareup.kotlinpoet.TypeName;
10+
import com.squareup.kotlinpoet.TypeNames;
11+
12+
import java.lang.annotation.Annotation;
13+
import java.util.LinkedHashSet;
14+
import java.util.List;
15+
import java.util.Set;
16+
import java.util.stream.Collectors;
17+
18+
import javax.annotation.processing.Processor;
19+
import javax.annotation.processing.RoundEnvironment;
20+
import javax.annotation.processing.SupportedOptions;
21+
import javax.lang.model.element.Element;
22+
import javax.lang.model.element.ElementKind;
23+
import javax.lang.model.element.Modifier;
24+
import javax.lang.model.element.Name;
25+
import javax.lang.model.element.TypeElement;
26+
import javax.lang.model.type.TypeMirror;
27+
28+
import kotlin.jvm.JvmName;
29+
30+
import static com.google.common.base.CaseFormat.LOWER_CAMEL;
31+
import static com.google.common.base.CaseFormat.UPPER_CAMEL;
32+
import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
33+
import static javax.lang.model.element.ElementKind.ENUM;
34+
import static javax.lang.model.element.ElementKind.ENUM_CONSTANT;
35+
36+
@SupportedOptions("debug")
37+
@AutoService(Processor.class)
38+
public class EnumExtProcessor extends BaseProcessor {
39+
40+
@Override
41+
protected void process(RoundEnvironment roundEnv) {
42+
roundEnv
43+
.getElementsAnnotatedWith(EnumExt.class)
44+
.forEach(element -> {
45+
log("--> process @EnumExt");
46+
47+
if (element.getModifiers().contains(Modifier.STATIC)) {
48+
error("Static field is not supported", element);
49+
return;
50+
}
51+
52+
// init package
53+
String packageName = processingEnv
54+
.getElementUtils()
55+
.getPackageOf(element)
56+
.getQualifiedName()
57+
.toString();
58+
log("packageName: " + packageName);
59+
60+
// receiver type
61+
TypeElement parentElement = (TypeElement) element.getEnclosingElement();
62+
TypeName receiverType = TypeNames.get(parentElement.asType());
63+
Name receiverName = parentElement.getSimpleName();
64+
log("receiverName: " + receiverName);
65+
66+
// annotated field type
67+
TypeMirror fieldType = element.asType();
68+
log("fieldType: " + fieldType);
69+
70+
String definedFieldName = element.getSimpleName().toString();
71+
TypeElement fieldTypeElement = processingEnv
72+
.getElementUtils()
73+
.getTypeElement(fieldType.toString());
74+
ElementKind kindOfElementType = fieldTypeElement
75+
.getKind();
76+
77+
if (kindOfElementType != ENUM) {
78+
String format = String.format("`%s` Must be String Type", definedFieldName);
79+
error(format, element);
80+
return;
81+
}
82+
83+
// annotated field name
84+
String fieldName = LOWER_CAMEL.to(UPPER_CAMEL, definedFieldName);
85+
log("fieldName: " + fieldName);
86+
87+
// ext class name
88+
String extClassName = receiverName + "Ext";
89+
log("extClassName: " + receiverName);
90+
91+
// file builder
92+
FileSpec.Builder fileSpecBuilder = FileSpec
93+
.builder(packageName, extClassName)
94+
.addAnnotation(
95+
AnnotationSpec.builder(JvmName.class)
96+
.addMember("%S", extClassName)
97+
.build()
98+
);
99+
100+
List<? extends Element> enumValues = fieldTypeElement.getEnclosedElements()
101+
.stream()
102+
.filter(it -> it.getKind() == ENUM_CONSTANT)
103+
.collect(Collectors.toList());
104+
105+
enumValues.forEach(it -> {
106+
String propName = UPPER_UNDERSCORE.to(UPPER_CAMEL, it.getSimpleName().toString());
107+
FunSpec getter = FunSpec
108+
.getterBuilder()
109+
.addStatement("return %T.%L == %L", fieldType, it, definedFieldName)
110+
.build();
111+
fileSpecBuilder.addProperty(
112+
PropertySpec
113+
.builder("is" + propName, TypeNames.BOOLEAN)
114+
.receiver(receiverType)
115+
.getter(getter)
116+
.build()
117+
);
118+
});
119+
120+
// add file spec to fileSpecs
121+
fileSpecs.add(
122+
fileSpecBuilder
123+
.addComment("Generated by kotlin-enumerize")
124+
.build()
125+
);
126+
});
127+
}
128+
129+
@Override
130+
protected Set<Class<? extends Annotation>> getSupportedAnnotations() {
131+
Set<Class<? extends Annotation>> annotations = new LinkedHashSet<>();
132+
annotations.add(EnumExt.class);
133+
return annotations;
134+
}
135+
}

modules/enumerize-processor/src/main/java/com/importre/kotlin/enumerize/EnumerizeProcessor.java

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
import com.squareup.kotlinpoet.TypeSpec;
1212
import com.squareup.kotlinpoet.TypeVariableName;
1313

14-
import java.io.File;
15-
import java.io.IOException;
1614
import java.lang.annotation.Annotation;
1715
import java.util.Arrays;
1816
import java.util.LinkedHashSet;
@@ -38,26 +36,6 @@
3836
@AutoService(Processor.class)
3937
public class EnumerizeProcessor extends BaseProcessor {
4038

41-
@Override
42-
protected void generateFiles() {
43-
String pathname = processingEnv
44-
.getOptions()
45-
.get("kapt.kotlin.generated");
46-
47-
if (pathname == null) {
48-
error("Please set `kapt.kotlin.generated` in your gradle project.");
49-
return;
50-
}
51-
52-
fileSpecs.forEach(fileSpec -> {
53-
try {
54-
fileSpec.writeTo(new File(pathname));
55-
} catch (IOException e) {
56-
fatalError(e);
57-
}
58-
});
59-
}
60-
6139
@Override
6240
protected void process(RoundEnvironment roundEnv) {
6341
roundEnv
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package com.importre.kotlin.enumerize;
2+
3+
import com.google.common.base.Joiner;
4+
import com.google.common.io.Files;
5+
import com.google.testing.compile.JavaFileObjects;
6+
import com.squareup.kotlinpoet.AnnotationSpec;
7+
import com.squareup.kotlinpoet.FileSpec;
8+
import com.squareup.kotlinpoet.FunSpec;
9+
import com.squareup.kotlinpoet.PropertySpec;
10+
import com.squareup.kotlinpoet.TypeNames;
11+
import com.squareup.kotlinpoet.TypeVariableName;
12+
13+
import org.intellij.lang.annotations.Language;
14+
import org.jetbrains.annotations.NotNull;
15+
import org.junit.Test;
16+
17+
import java.io.File;
18+
import java.io.IOException;
19+
import java.nio.charset.Charset;
20+
21+
import javax.tools.JavaFileObject;
22+
23+
import kotlin.jvm.JvmName;
24+
25+
import static com.google.common.truth.Truth.assertThat;
26+
import static com.google.testing.compile.Compiler.javac;
27+
28+
public class EnumExtTest {
29+
30+
@Test
31+
public void testGenerateFile() throws IOException {
32+
33+
JavaFileObject sourceObject = getSourceObject();
34+
File tempDir = new File(System.getProperty("java.io.tmpdir"));
35+
File file = new File(tempDir, "test/LogExt.kt");
36+
37+
javac()
38+
.withOptions("-Akapt.kotlin.generated=" + tempDir)
39+
.withProcessors(new EnumExtProcessor())
40+
.compile(sourceObject)
41+
.generatedFiles();
42+
43+
String actual = Joiner.on("\n")
44+
.join(Files.readLines(file, Charset.forName("UTF-8")))
45+
.trim();
46+
String expected = buildExpectedGeneratedFile();
47+
assertThat(actual).isEqualTo(expected);
48+
file.delete();
49+
}
50+
51+
@NotNull
52+
private JavaFileObject getSourceObject() {
53+
@Language("java")
54+
String source = "package test;\n" +
55+
"\n" +
56+
"import com.importre.kotlin.enumerize.EnumExt;\n" +
57+
"\n" +
58+
"class Log {\n" +
59+
" \n" +
60+
" @EnumExt\n" +
61+
" private Level level;\n" +
62+
"\n" +
63+
" enum Level {\n" +
64+
" DEBUG,\n" +
65+
" ERROR,\n" +
66+
" }\n" +
67+
"}\n";
68+
69+
return JavaFileObjects
70+
.forSourceString("test.Log", source);
71+
}
72+
73+
@NotNull
74+
private String buildExpectedGeneratedFile() {
75+
return FileSpec
76+
.builder("test", "LogLevel")
77+
.addComment("Generated by kotlin-enumerize")
78+
.addAnnotation(
79+
AnnotationSpec.builder(JvmName.class)
80+
.addMember("%S", "LogExt")
81+
.build()
82+
)
83+
.addProperty(
84+
PropertySpec
85+
.builder("isDebug", TypeNames.BOOLEAN)
86+
.receiver(TypeVariableName.get("Log"))
87+
.getter(
88+
FunSpec.getterBuilder()
89+
.addStatement("return Log.Level.DEBUG == level")
90+
.build()
91+
)
92+
.build()
93+
)
94+
.addProperty(
95+
PropertySpec
96+
.builder("isError", TypeNames.BOOLEAN)
97+
.receiver(TypeVariableName.get("Log"))
98+
.getter(
99+
FunSpec.getterBuilder()
100+
.addStatement("return Log.Level.ERROR == level")
101+
.build()
102+
)
103+
.build()
104+
)
105+
.build()
106+
.toString()
107+
.trim();
108+
}
109+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.importre.kotlin.enumerize;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
/**
9+
* Generate {@link Enum} extensions.
10+
*/
11+
@Retention(RetentionPolicy.CLASS)
12+
@Target(ElementType.FIELD)
13+
public @interface EnumExt {
14+
}

0 commit comments

Comments
 (0)