Skip to content

Commit

Permalink
Merge pull request #11 from brnz/incremental-annotation-processing
Browse files Browse the repository at this point in the history
Support incremental annotation processing
  • Loading branch information
KennethNickles authored May 7, 2020
2 parents 8083671 + 65a0d0c commit ebd889e
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 81 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: 0.3
version: 0.4
jobs:
build:
working_directory: ~/autoparse-json
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
* @author nathan.taylor
* @since 2015-02-27
*/
@Retention(RetentionPolicy.SOURCE)
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.PACKAGE)
public @interface JsonParserPartition {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.workday.autoparse.json.annotations.codegen;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface JsonParser {
String[] value();
}
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION_NAME=0.3
VERSION_NAME=0.4

GROUP=com.workday
POM_PACKAGING=jar
Expand All @@ -16,4 +16,4 @@ POM_DEVELOPER_NAME=Nathan Taylor

POM_DESCRIPTION=A java library built specifically for Android that uses code generation to parse JSON into custom objects in your project.

android.useAndroidX=true
android.useAndroidX=true
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

import com.workday.autoparse.json.annotations.DiscrimValue;
import com.workday.autoparse.json.annotations.JsonObject;
import com.workday.autoparse.json.annotations.JsonParserPartition;
import com.workday.autoparse.json.annotations.JsonPostCreateChild;
import com.workday.autoparse.json.annotations.JsonSelfValues;
import com.workday.autoparse.json.annotations.JsonValue;
Expand All @@ -18,22 +17,20 @@
import com.workday.autoparse.json.parser.NoJsonObjectParser;
import com.workday.meta.AnnotationUtils;
import com.workday.meta.MetaTypeNames;
import com.workday.meta.PackageTree;

import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.tools.Diagnostic;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
* This is is the root of the code generation. It scans through every class annotated with {@link JsonObject} and
Expand All @@ -46,8 +43,6 @@
*/
public class AutoparseJsonProcessor extends AbstractProcessor {

private Map<PackageElement, PartitionComponentInfo> partitionComponents = new HashMap<>();

@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new HashSet<>();
Expand All @@ -72,10 +67,6 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment

Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(JsonObject.class);

Set<PackageElement> partitionPackageElements = ElementFilter.packagesIn(
roundEnv.getElementsAnnotatedWith(JsonParserPartition.class));

PackageTree packageTree = new PackageTree(processingEnv.getElementUtils(), partitionPackageElements);
final Set<TypeElement> classesRequiringGeneratedParsers = new HashSet<>();
final Map<String, String> classNameToParserNameMap = new HashMap<>();

Expand All @@ -90,66 +81,27 @@ public void get() {

String customParserCanonicalName = customParserClassMirror.toString();
String classQualifiedName = MetaTypeNames.constructTypeName((TypeElement) element);
String codeClassQualifiedName = ((TypeElement) element).getQualifiedName().toString();

PackageElement matchingPackage = packageTree.getMatchingPackage(element);
PartitionComponentInfo partitionComponentInfo = getPartitionComponentInfoForPackage(matchingPackage);

if (isCustomParser(customParserCanonicalName)) {
addElementToMap((TypeElement) element, partitionComponentInfo.discrimValueToClassWithCustomParserMap);
classNameToParserNameMap.put(classQualifiedName, customParserCanonicalName);
partitionComponentInfo.codeClassNameToParserNameMap.put(codeClassQualifiedName,
customParserCanonicalName);
} else {
addElementToMap((TypeElement) element,
partitionComponentInfo.discrimValueToClassRequiringGeneratedParserMap);
String parserQualifiedName = classQualifiedName + GeneratedClassNames.PARSER_SUFFIX;
classNameToParserNameMap.put(classQualifiedName, parserQualifiedName);
classesRequiringGeneratedParsers.add((TypeElement) element);
partitionComponentInfo.codeClassNameToParserNameMap.put(codeClassQualifiedName, parserQualifiedName);

}
}

for (TypeElement classElement : classesRequiringGeneratedParsers) {
generateClassParser(classElement, classNameToParserNameMap);
}

generateParserMaps();
return true;
}

private PartitionComponentInfo getPartitionComponentInfoForPackage(PackageElement packageElement) {
PartitionComponentInfo partitionComponentInfo = partitionComponents.get(packageElement);
if (partitionComponentInfo == null) {
partitionComponentInfo = new PartitionComponentInfo();
partitionComponents.put(packageElement, partitionComponentInfo);
}
return partitionComponentInfo;
}

private boolean isCustomParser(String parserName) {
return !NoJsonObjectParser.class.getCanonicalName().equals(parserName);
}

private void addElementToMap(TypeElement element, Map<String, TypeElement> map) {
JsonObject annotation = element.getAnnotation(JsonObject.class);
for (String discrimValue : annotation.value()) {
if (StringUtils.isBlank(discrimValue)) {
processingEnv.getMessager()
.printMessage(Diagnostic.Kind.ERROR, "Discrimination values cannot be blank.", element);
} else {
TypeElement previousValue = map.put(discrimValue, element);
if (previousValue != null) {
String errorMessage = String.format("%s and %s both tried to map to discrimination value \"%s\"",
element.getQualifiedName(), previousValue.getQualifiedName(),
discrimValue);
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, errorMessage, element);
}
}
}
}

private void generateClassParser(TypeElement classElement, Map<String, String> classNameToParserNameMap) {
try {
new JsonObjectParserGenerator(processingEnv, classElement, classNameToParserNameMap).generateParser();
Expand All @@ -158,22 +110,4 @@ private void generateClassParser(TypeElement classElement, Map<String, String> c
}

}

private void generateParserMaps() {
for (Map.Entry<PackageElement, PartitionComponentInfo> packageMapEntry : partitionComponents.entrySet()) {
final PackageElement packageElement = packageMapEntry.getKey();
final PartitionComponentInfo partitionComponentInfo = packageMapEntry.getValue();
try {
new InstanceUpdaterTableGenerator(processingEnv, partitionComponentInfo.codeClassNameToParserNameMap,
packageElement).generateTable();
new JsonObjectParserTableGenerator(processingEnv,
partitionComponentInfo
.discrimValueToClassRequiringGeneratedParserMap,
partitionComponentInfo.discrimValueToClassWithCustomParserMap,
packageElement).generateParserMap();
} catch (IOException e) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@

import com.squareup.javawriter.JavaWriter;
import com.workday.autoparse.json.annotations.DiscrimValue;
import com.workday.autoparse.json.annotations.JsonObject;
import com.workday.autoparse.json.annotations.JsonPostCreateChild;
import com.workday.autoparse.json.annotations.JsonSelfValues;
import com.workday.autoparse.json.annotations.JsonValue;
import com.workday.autoparse.json.annotations.codegen.JsonParser;
import com.workday.autoparse.json.context.ContextHolder;
import com.workday.autoparse.json.context.GeneratedClassNames;
import com.workday.autoparse.json.context.JsonParserContext;
Expand Down Expand Up @@ -40,6 +42,7 @@
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
Expand All @@ -52,6 +55,7 @@
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;

/**
* Generates code for {@link JsonObjectParser}s.
Expand Down Expand Up @@ -142,14 +146,28 @@ private Collection<String> getDiscriminationValueAssignmentPatterns(ValueAssigne
public void generateParser() throws IOException {
String parserName = MetaTypeNames.constructTypeName(classElement, GeneratedClassNames.PARSER_SUFFIX);

JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(parserName);
JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(parserName, classElement);

JavaWriter writer = new JavaWriter(sourceFile.openWriter());
writer.setIndent(" ");
writer.emitPackage(processingEnv.getElementUtils().getPackageOf(classElement).getQualifiedName().toString());
writer.emitImports(getStandardImports());
writer.emitEmptyLine();

JsonObject jsonObject = classElement.getAnnotation(JsonObject.class);
if (jsonObject != null) {
Object[] values = Arrays
.stream(jsonObject.value())
.map(new Function<String, Object>() {
@Override
public Object apply(String data) {
return JavaWriter.stringLiteral(data);
}
})
.toArray();
writer.emitAnnotation("JsonParser", values);
}

parsedClassName = writer.compressType(classElement.getQualifiedName().toString());
String jsonObjectParserInterfaceName = JavaWriter.type(JsonObjectParser.class, parsedClassName);
String fromMapUpdaterInterfaceName = JavaWriter.type(InstanceUpdater.class, parsedClassName);
Expand Down Expand Up @@ -209,6 +227,7 @@ private Set<String> getStandardImports() {
results.add(IOException.class.getCanonicalName());
results.add(Map.class.getCanonicalName());
results.add(MapValueGetter.class.getCanonicalName());
results.add(JsonParser.class.getCanonicalName());
return results;
}

Expand Down
Loading

0 comments on commit ebd889e

Please sign in to comment.