gson-compile/gson-compile-processor/src/main/java/io/gitlab/jfronny/gson/compile/processor/util/valueprocessor/ValueCreator.java

155 lines
5.8 KiB
Java

package io.gitlab.jfronny.gson.compile.processor.util.valueprocessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.*;
import javax.lang.model.util.ElementFilter;
import java.util.*;
public class ValueCreator {
private final ProcessingEnvironment env;
public ValueCreator(ProcessingEnvironment env) {
this.env = env;
}
public Value from(Element element) throws ElementException {
return from(element, false);
}
/**
* Creates a [Value] from the given element. This element can be the [TypeElement] of the target class, or a
* specific constructor or factory method. If [isBuilder] is true, then the element represents the builder class,
* constructor or factory method.
*/
public Value from(Element element, boolean isBuilder) throws ElementException {
if (element instanceof TypeElement tel) {
return isBuilder ? fromBuilderClass(tel) : fromClass(tel);
} else if (element instanceof ExecutableElement xel) {
if (xel.getKind() == ElementKind.CONSTRUCTOR) {
return isBuilder ? fromBuilderConstructor(xel) : fromConstructor(xel);
} else {
return isBuilder ? fromBuilderFactory(xel) : fromFactory(xel);
}
} else throw new IllegalArgumentException("Expected TypeElement or ExecutableElement but got: " + element);
}
/**
* Creates a [Value] from the given constructor element. ex:
* ```
* public class Value {
* > public Value(int arg1) { ... }
* }
* ```
*/
public Value fromConstructor(ExecutableElement constructor) {
checkKind(constructor, ElementKind.CONSTRUCTOR);
return create(new ConstructionSource.Constructor(constructor));
}
/**
* Creates a [Value] from the given builder's constructor element. ex:
* ```
* public class Builder {
* > public Builder() { ... }
* public Value build() { ... }
* }
* ```
*/
public Value fromBuilderConstructor(ExecutableElement constructor) {
checkKind(constructor, ElementKind.CONSTRUCTOR);
return create(new ConstructionSource.BuilderConstructor(env.getTypeUtils(), constructor));
}
/**
* Creates a [Value] from the given factory method element. ex:
* ```
* public class Value {
* > public static Value create(int arg) { ... }
* }
* ```
*/
public Value fromFactory(ExecutableElement factory) {
checkKind(factory, ElementKind.METHOD);
return create(new ConstructionSource.Factory(env.getTypeUtils(), factory));
}
/**
* Creates a [Value] from the given builder factory method element. ex:
* ```
* public class Value {
* > public static Builder builder() { ... }
* public static class Builder { ... }
* }
* ```
*/
public Value fromBuilderFactory(ExecutableElement builderFactory) {
checkKind(builderFactory, ElementKind.METHOD);
return create(new ConstructionSource.BuilderFactory(env.getTypeUtils(), builderFactory));
}
/**
* Creates a [Value] from the given class. ex:
* ```
* > public class Value { ... }
* ```
*/
public Value fromClass(TypeElement targetClass) throws ElementException {
ExecutableElement creator = findConstructorOrFactory(targetClass);
return creator.getKind() == ElementKind.CONSTRUCTOR ? fromConstructor(creator) : fromFactory(creator);
}
/**
* Creates a [Value] from the given builder class. ex:
* ```
* > public class Builder {
* public Value build() { ... }
* }
* ```
*/
public Value fromBuilderClass(TypeElement builderClass) throws ElementException {
ExecutableElement creator = findConstructorOrFactory(builderClass);
return creator.getKind() == ElementKind.CONSTRUCTOR ? fromBuilderConstructor(creator) : fromBuilderFactory(creator);
}
private Value create(ConstructionSource constructionSource) {
return new Value(env, constructionSource);
}
private static ExecutableElement findConstructorOrFactory(TypeElement klazz) throws ElementException {
ExecutableElement noArgConstructor = null;
List<ExecutableElement> constructors = ElementFilter.constructorsIn(klazz.getEnclosedElements());
if (constructors.size() == 1) {
ExecutableElement constructor = constructors.get(0);
if (constructor.getParameters().isEmpty()) {
noArgConstructor = constructor;
constructors.remove(0);
}
}
for (ExecutableElement method : ElementFilter.methodsIn(klazz.getEnclosedElements())) {
Set<Modifier> modifiers = method.getModifiers();
if (modifiers.contains(Modifier.STATIC)
&& !modifiers.contains(Modifier.PRIVATE)
&& method.getReturnType().equals(klazz.asType())) {
constructors.add(method);
}
}
if (constructors.isEmpty() && noArgConstructor != null) {
constructors.add(noArgConstructor);
}
if (constructors.size() == 1) {
return constructors.get(0);
} else {
List<ElementException.Message> messages = new ArrayList<>();
messages.add(new ElementException.Message("More than one constructor or factory method found.", klazz));
constructors.stream().map(s -> new ElementException.Message(" " + s, s)).forEach(messages::add);
throw new ElementException(messages);
}
}
private static void checkKind(Element element, ElementKind kind) {
if (element.getKind() != kind) {
throw new IllegalArgumentException("Expected " + kind + " but got: " + element);
}
}
}