Refactor processor to make extension easier#53
Refactor processor to make extension easier#53bndbsh wants to merge 2 commits intodanielnorberg:masterfrom
Conversation
This delegates most of the field-specific processing to `FieldProcessor`, which can be implemented to customize handling of different types. Collections are handled by `CollectionProcessor`, optionals by `OptionalProcessor`. Everything else is handled by `DefaultProcessor`. A data-driven approach was taken, with `FieldProcessor` taking the descriptor and field as input and outputting Javapoet specs or statements. However, in this patch `CollectionProcessor` is dependent on `AutoMatterProcessor` for the implementation of `singular`. There is also some duplication and inefficiencies in the processor as a result of this refactor. Since doing away with them would change text order in some places and thus require edits to most of the test files, I prefer leaving that work to a followup commit to reduce review burden. Other notes: * It's not clear where common code should live. For now most of it is in default methods on `FieldProcessor`, but I'm open to suggestions. * Due to the above use of default, source level 1.8 is now required * I wasn't sure how to organize the classes, so I've left them all at top level. It might become clearer once common code is centralized and other processors are added. * Not sure if this is feasible, but it could be interesting to allow users to supply their own processors via some annotation, or to register them somehow with AutoMatter. This would allow extensions to live in separate packages.
| } | ||
|
|
||
| private FieldProcessor processorForField(Descriptor d, ExecutableElement field) throws AutoMatterProcessorException { | ||
| String prefix = fieldType(d, field).toString().split("<")[0]; |
There was a problem hiding this comment.
Use guava Splitter instead of compiling a regex for every invocation.
| private Messager messager; | ||
| private Types types; | ||
| private Map<String, FieldProcessor> processors; | ||
| private DefaultProcessor defaultProcessor = new DefaultProcessor(); |
| private Elements elements; | ||
| private Messager messager; | ||
| private Types types; | ||
| private Map<String, FieldProcessor> processors; |
There was a problem hiding this comment.
Would it make sense for this to be a Map<Class, FieldProcessor> instead?
| OptionalProcessor optionalProcessor = new OptionalProcessor(); | ||
|
|
||
| processors = ImmutableMap.of( | ||
| "java.util.Map", collectionProcessor, |
There was a problem hiding this comment.
Would be nice if we could avoid these handcrafted strings.
| } | ||
|
|
||
| private TypeName builderType(final Descriptor d) { | ||
| public static TypeName builderType(final Descriptor d) { |
There was a problem hiding this comment.
Move to a separate class and avoid circular imports?
| @@ -0,0 +1,11 @@ | |||
| package io.norberg.automatter.processor; | |||
|
|
|||
| public class BuildStatements { | |||
| package io.norberg.automatter.processor; | ||
|
|
||
| public class BuildStatements { | ||
| Iterable<Statement> statements; |
| } else { | ||
| parameters.add(fieldName(field)); | ||
| BuildStatements buildStatements = processorForField(d, field).build(d, field); | ||
| for (Statement s: buildStatements.statements) { |
| import static javax.lang.model.element.Modifier.PUBLIC; | ||
| import static javax.lang.model.type.TypeKind.DECLARED; | ||
|
|
||
| public class CollectionProcessor implements FieldProcessor { |
| public class CollectionProcessor implements FieldProcessor { | ||
| private final AutoMatterProcessor processor; | ||
|
|
||
| public CollectionProcessor(AutoMatterProcessor processor) { |
There was a problem hiding this comment.
Can we factor things to avoid this circular dependency of passing in the AutoMatterProcessor into field processors?
| BuildStatements build(Descriptor d, ExecutableElement field) throws AutoMatterProcessorException; | ||
| Iterable<Statement> valueConstructor(Descriptor d, ExecutableElement field) throws AutoMatterProcessorException; | ||
|
|
||
| default TypeName fieldType(final Descriptor d, final ExecutableElement field) throws AutoMatterProcessorException { |
There was a problem hiding this comment.
I guess these methods aren't actually part of the FieldProcessor interface. I.e., FieldProcessor users aren't expected to call them.
How about grouping and moving them to separate classes based on functionality? E.g. put the field introspection methods into e.g. a Fields class etc.
| import static io.norberg.automatter.processor.AutoMatterProcessor.builderType; | ||
| import static javax.lang.model.element.Modifier.PUBLIC; | ||
|
|
||
| public class OptionalProcessor extends DefaultProcessor { |
| @@ -0,0 +1,11 @@ | |||
| package io.norberg.automatter.processor; | |||
|
|
|||
| public class Statement { | |||
| package io.norberg.automatter.processor; | ||
|
|
||
| public class Statement { | ||
| String statement; |
|
This is a much-needed refactoring. Kudos! Looks great overall, just a few small comments.
Allowing extensions in separate packages at sounds great. AutoMatterProcessor can use |
|
Re: using |
This delegates most of the field-specific processing to
FieldProcessor,which can be implemented to customize handling of different types.
Collections are handled by
CollectionProcessor, optionals byOptionalProcessor. Everything else is handled byDefaultProcessor.A data-driven approach was taken, with
FieldProcessortaking thedescriptor and field as input and outputting Javapoet specs or
statements. However, in this patch
CollectionProcessoris dependent onAutoMatterProcessorfor the implementation ofsingular.There is also some duplication and inefficiencies in the processor as a
result of this refactor. Since doing away with them would change text
order in some places and thus require edits to most of the test files,
I prefer leaving that work to a followup commit to reduce review burden.
Other notes:
It's not clear where common code should live. For now most of it is
in default methods on
FieldProcessor, but I'm open to suggestions.Due to the above use of default, source level 1.8 is now required
I wasn't sure how to organize the classes, so I've left them all at
top level. It might become clearer once common code is centralized
and other processors are added.
Not sure if this is feasible, but it could be interesting to allow
users to supply their own processors via some annotation, or to
register them somehow with AutoMatter. This would allow extensions to
live in separate packages.