Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Mapper/Reader/Writer of a Collection #10

Merged
merged 5 commits into from
Mar 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
package org.dominokit.jacksonapt.processor;

import com.google.auto.common.MoreTypes;
import com.squareup.javapoet.*;
import org.dominokit.jacksonapt.*;

import javax.lang.model.element.*;
import javax.lang.model.type.TypeMirror;
import java.io.IOException;

import static java.util.Objects.isNull;
import static org.dominokit.jacksonapt.processor.ObjectMapperProcessor.*;

/**
Expand All @@ -17,79 +13,13 @@
* @author vegegoku
* @version $Id: $Id
*/
public abstract class AbstractBeanMapperGenerator {

void generate(Element element) throws IOException {
String className = enclosingName(element, "_") + (useInterface(element) ? element.getSimpleName() : "Mapper") + "Impl";
String packageName = elementUtils.getPackageOf(element).getQualifiedName().toString();
TypeMirror beanType = getBeanType(element);
Name beanName = typeUtils.asElement(beanType).getSimpleName();

generateJsonMappers(beanType, packageName, beanName);

TypeSpec.Builder builder = TypeSpec.classBuilder(className)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.superclass(abstractObjectMapper(element))
.addField(FieldSpec.builder(ClassName.bestGuess(className), "INSTANCE")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.initializer(CodeBlock.builder().add("new $T()", ClassName.bestGuess(className)).build()).
build())
.addMethod(makeConstructor(beanName))
.addMethods(getMapperMethods(element, beanName));
if (useInterface(element))
builder.addSuperinterface(TypeName.get(element.asType()));

TypeSpec classSpec = builder
.build();

JavaFile.builder(packageName, classSpec).build().writeTo(filer);
}

private String enclosingName(Element element, String postfix) {
if (useInterface(element)) {
return element.getEnclosingElement().getSimpleName().toString() + postfix;

}
return element.getSimpleName().toString() + postfix;

}

private boolean notEnclosed(Element element) {
return ElementKind.PACKAGE.equals(element.getEnclosingElement()) || isNull(element.getEnclosingElement());
}

private TypeMirror getBeanType(Element element) {
if (useInterface(element)) {
TypeMirror objectReader = ((TypeElement) typeUtils.asElement(element.asType())).getInterfaces().get(0);
return MoreTypes.asDeclared(objectReader).getTypeArguments().get(0);
} else {
return element.asType();
}

}

private boolean useInterface(Element element) {
return Type.isAssignableFrom(element.asType(), ObjectMapper.class) || Type.isAssignableFrom(element.asType(), ObjectReader.class) || Type.isAssignableFrom(element.asType(), ObjectWriter.class);
}

private TypeName abstractObjectMapper(Element element) {
TypeMirror beanType = getBeanType(element);
return ParameterizedTypeName.get(ClassName.get(getSuperClass()),
ClassName.get(beanType));
}

private MethodSpec makeConstructor(Name beanName) {
return MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addStatement("super(\"" + beanName + "\")").build();
}

public abstract class AbstractBeanMapperGenerator extends AbstractMapperGenerator {
MethodSpec makeNewDeserializerMethod(Element element, Name beanName) {
return MethodSpec.methodBuilder("newDeserializer")
.addModifiers(Modifier.PROTECTED)
.addAnnotation(Override.class)
.returns(ParameterizedTypeName.get(ClassName.get(JsonDeserializer.class),
ClassName.get(getBeanType(element))))
ClassName.get(getElementType(element))))
.addStatement("return new " + beanName + "BeanJsonDeserializerImpl()")
.build();
}
Expand All @@ -102,29 +32,4 @@ MethodSpec makeNewSerializerMethod(Name beanName) {
.addStatement("return new " + beanName + "BeanJsonSerializerImpl()")
.build();
}

/**
* <p>getSuperClass.</p>
*
* @return a {@link java.lang.Class} object.
*/
protected abstract Class<?> getSuperClass();

/**
* <p>getMapperMethods.</p>
*
* @param element a {@link javax.lang.model.element.Element} object.
* @param beanName a {@link javax.lang.model.element.Name} object.
* @return a {@link java.lang.Iterable} object.
*/
protected abstract Iterable<MethodSpec> getMapperMethods(Element element, Name beanName);

/**
* <p>generateJsonMappers.</p>
*
* @param beanType a {@link javax.lang.model.type.TypeMirror} object.
* @param packageName a {@link java.lang.String} object.
* @param beanName a {@link javax.lang.model.element.Name} object.
*/
protected abstract void generateJsonMappers(TypeMirror beanType, String packageName, Name beanName);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package org.dominokit.jacksonapt.processor;

import static org.dominokit.jacksonapt.processor.AbstractMapperProcessor.elementUtils;
import static org.dominokit.jacksonapt.processor.AbstractMapperProcessor.typeUtils;
import static org.dominokit.jacksonapt.processor.ObjectMapperProcessor.DEFAULT_WILDCARD;

import java.util.List;

import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.Name;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;

import org.dominokit.jacksonapt.JsonDeserializer;
import org.dominokit.jacksonapt.JsonSerializer;
import org.dominokit.jacksonapt.processor.deserialization.FieldDeserializersChainBuilder;
import org.dominokit.jacksonapt.processor.serialization.FieldSerializerChainBuilder;

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The unused import needs to be removed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I will fix unused imports.

import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;


public abstract class AbstractCollectionMapperGenerator extends AbstractMapperGenerator {
protected MethodSpec makeNewDeserializerMethod(Element element, Name beanName) {
return MethodSpec.methodBuilder("newDeserializer")
.addModifiers(Modifier.PROTECTED)
.addAnnotation(Override.class)
.returns(ParameterizedTypeName.get(ClassName.get(JsonDeserializer.class),
ClassName.get(getElementType(element))))
.addStatement("return $L", new FieldDeserializersChainBuilder(getElementType(element)).getInstance(getElementType(element)))
.build();
}

protected MethodSpec makeNewSerializerMethod(Element element, Name beanName) {
return MethodSpec.methodBuilder("newSerializer")
.addModifiers(Modifier.PROTECTED)
.addAnnotation(Override.class)
.returns(ParameterizedTypeName.get(ClassName.get(JsonSerializer.class), DEFAULT_WILDCARD))
.addStatement("return $L", new FieldSerializerChainBuilder(getElementType(element)).getInstance(getElementType(element)))
.build();
}


@Override
protected void generateJsonMappers(TypeMirror beanType, String packageName, Name beanName) {
List<? extends TypeMirror> typeArguments = ((DeclaredType)beanType).getTypeArguments();
for (TypeMirror typeParamType:typeArguments){
if(Type.isCollection(typeParamType) || Type.isMap(typeParamType))
generateJsonMappers(typeParamType, packageName, typeUtils.asElement(typeParamType).getSimpleName());
else if (Type.isBasicType(typeParamType))
//In case of basic type, no need to generate serializer
return;
else {
Element typeParamElement = typeUtils.asElement(typeParamType);
String typeParamPackageName = elementUtils.getPackageOf(typeParamElement).getQualifiedName().toString();

generateDeserializer(typeParamType, typeParamPackageName, typeParamElement.getSimpleName());
generateSerializer(typeParamType, typeParamPackageName, typeParamElement.getSimpleName());
}
}
}

protected void generateSerializer(TypeMirror beanType, String packageName, Name beanName) {
}

protected void generateDeserializer(TypeMirror beanType, String packageName, Name beanName) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ private MethodSpec targetTypeMethod() {
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.returns(ClassName.get(Class.class))
.addStatement("return $T.class", TypeName.get(beanType))
.addStatement("return $T.class", TypeName.get(ObjectMapperProcessor.typeUtils.erasure(beanType)))
.build();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package org.dominokit.jacksonapt.processor;

import static org.dominokit.jacksonapt.processor.AbstractMapperProcessor.elementUtils;
import static org.dominokit.jacksonapt.processor.AbstractMapperProcessor.filer;
import static org.dominokit.jacksonapt.processor.AbstractMapperProcessor.typeUtils;

import java.io.IOException;

import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;

import org.dominokit.jacksonapt.ObjectMapper;
import org.dominokit.jacksonapt.ObjectReader;
import org.dominokit.jacksonapt.ObjectWriter;

import com.google.auto.common.MoreTypes;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;

public abstract class AbstractMapperGenerator implements MapperGenerator {

@Override
public void generate(Element element) throws IOException {
String className = enclosingName(element, "_") + (useInterface(element) ? element.getSimpleName() : "Mapper") + "Impl";
String packageName = elementUtils.getPackageOf(element).getQualifiedName().toString();
TypeMirror beanType = getElementType(element);
Name beanName = typeUtils.asElement(beanType).getSimpleName();

generateJsonMappers(beanType, packageName, beanName);

TypeSpec.Builder builder = TypeSpec.classBuilder(className)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.superclass(abstractObjectMapper(element))
.addField(FieldSpec.builder(ClassName.bestGuess(className), "INSTANCE")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.initializer(CodeBlock.builder().add("new $T()", ClassName.bestGuess(className)).build()).
build())
.addMethod(makeConstructor(beanName))
.addMethods(getMapperMethods(element, beanName));
if (useInterface(element))
builder.addSuperinterface(TypeName.get(element.asType()));

TypeSpec classSpec = builder
.build();

JavaFile.builder(packageName, classSpec).build().writeTo(filer);
}

protected static TypeMirror getElementType(Element element) {
if(useInterface(element)){
TypeMirror objectReader = ((TypeElement) typeUtils.asElement(element.asType())).getInterfaces().get(0);
return MoreTypes.asDeclared(objectReader).getTypeArguments().get(0);
}else{
return element.asType();
}

}

protected static boolean useInterface(Element element) {
return
Type.isAssignableFrom(element.asType(), ObjectMapper.class)
|| Type.isAssignableFrom(element.asType(), ObjectReader.class)
|| Type.isAssignableFrom(element.asType(), ObjectWriter.class);
}

protected String enclosingName(Element element, String postfix) {
if (useInterface(element)) {
return element.getEnclosingElement().getSimpleName().toString() + postfix;

}
return element.getSimpleName().toString() + postfix;

}

protected TypeName abstractObjectMapper(Element element) {
TypeMirror beanType = getElementType(element);
return ParameterizedTypeName.get(ClassName.get(getSuperClass()),
ClassName.get(beanType));
}

protected MethodSpec makeConstructor(Name beanName) {
return MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addStatement("super(\"" + beanName + "\")").build();
}

/**
* <p>getSuperClass.</p>
*
* @return a {@link java.lang.Class} object.
*/
protected abstract Class<?> getSuperClass();

/**
* <p>getMapperMethods.</p>
*
* @param element a {@link javax.lang.model.element.Element} object.
* @param beanName a {@link javax.lang.model.element.Name} object.
* @return a {@link java.lang.Iterable} object.
*/
protected abstract Iterable<MethodSpec> getMapperMethods(Element element, Name beanName);

/**
* <p>generateJsonMappers.</p>
*
* @param beanType a {@link javax.lang.model.type.TypeMirror} object.
* @param packageName a {@link java.lang.String} object.
* @param beanName a {@link javax.lang.model.element.Name} object.
*/
protected abstract void generateJsonMappers(TypeMirror beanType, String packageName, Name beanName);

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ protected Class<AbstractObjectMapper> getSuperClass() {
@Override
protected Iterable<MethodSpec> getMapperMethods(Element element, Name beanName) {
return Stream.of(makeNewDeserializerMethod(element, beanName), makeNewSerializerMethod(beanName))
.collect(Collectors.toSet());
.collect(Collectors.toList());
}

/** {@inheritDoc} */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ protected Class<AbstractObjectWriter> getSuperClass() {
@Override
protected Iterable<MethodSpec> getMapperMethods(Element element, Name beanName) {
return Stream.of(makeNewSerializerMethod(beanName))
.collect(Collectors.toSet());
.collect(Collectors.toList());
}

/** {@inheritDoc} */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.dominokit.jacksonapt.processor;

import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.lang.model.element.Element;
import javax.lang.model.element.Name;
import javax.lang.model.type.TypeMirror;

import org.dominokit.jacksonapt.AbstractObjectMapper;

import com.squareup.javapoet.MethodSpec;

public class CollectionMapperGenerator extends AbstractCollectionMapperGenerator {
@Override
protected Class<?> getSuperClass() {
return AbstractObjectMapper.class;
}

@Override
protected Iterable<MethodSpec> getMapperMethods(Element element, Name beanName) {
return Stream.of(makeNewDeserializerMethod(element, beanName), makeNewSerializerMethod(element, beanName))
.collect(Collectors.toList());
}

@Override
protected void generateSerializer(TypeMirror beanType, String packageName, Name beanName) {
new SerializerGenerator().generate(beanType, packageName, beanName);
}

@Override
protected void generateDeserializer(TypeMirror beanType, String packageName, Name beanName) {
new DeserializerGenerator().generate(beanType, packageName, beanName);
}
}
Loading