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 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 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 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); } } }