void init(Constructor constructor) {
   for (TypeVariable param : constructor.getTypeParameters()) {
     typeParameters.add(ElementFactory.getTypeParameterElement(param));
   }
   type = TypeMirrorFactory.get(constructor);
   enclosingElement = ElementFactory.get(constructor.getDeclaringClass());
   returnType = Typ.getNoType(TypeKind.VOID);
   Type[] genericParameterTypes = constructor.getGenericParameterTypes();
   Annotation[][] parameterAnnotations = constructor.getParameterAnnotations();
   int index = 0;
   for (Type param : genericParameterTypes) {
     parameters.add(ElementFactory.getVariableElement(param, parameterAnnotations[index++]));
   }
   varArgs = constructor.isVarArgs();
   for (Type type : constructor.getGenericExceptionTypes()) {
     thrownTypes.add(TypeMirrorFactory.get(type));
   }
 }
/** @author Timo Vesalainen */
public class ExecutableElementImpl extends ElementImpl implements ExecutableElement {
  private ExecutableType type;
  private List<TypeParameterElement> typeParameters = new ArrayList<>();
  private TypeMirror returnType = Typ.getNoType(TypeKind.VOID);
  private List<VariableElement> parameters = new ArrayList<>();
  private boolean varArgs;
  private List<TypeMirror> thrownTypes = new ArrayList<>();
  private AnnotationValue defaultValue;

  @Override
  public TypeMirror asType() {
    return type;
  }

  public static class MethodBuilder extends ConstructorBuilder {
    public MethodBuilder(
        TypeElement enclosingElement,
        String name,
        List<? extends TypeMirror> classTypeArguments,
        Map<String, TypeParameterElement> classTypeParameterMap) {
      super(enclosingElement, ElementKind.METHOD, name, classTypeArguments, classTypeParameterMap);
    }

    public MethodBuilder setReturnType(Class<?> returnType, String... typeArgs) {
      return setReturnType(returnType.getCanonicalName(), typeArgs);
    }

    public MethodBuilder setReturnType(String returnType, String... typeArgs) {
      TypeMirror type = resolvType(returnType);
      if (typeArgs.length > 0 && type.getKind() == TypeKind.DECLARED) {
        DeclaredType dt = (DeclaredType) type;
        TypeElement te = (TypeElement) dt.asElement();
        TypeMirror[] args = new TypeMirror[typeArgs.length];
        for (int ii = 0; ii < args.length; ii++) {
          args[ii] = resolvType(typeArgs[ii]);
        }
        return setReturnType(Typ.getDeclaredType(te, args));
      } else {
        if (typeArgs.length > 0) {
          throw new IllegalArgumentException("type args not used");
        }
        return setReturnType(type);
      }
    }

    public MethodBuilder setReturnType(TypeMirror returnType) {
      exe.returnType = returnType;
      return this;
    }
  }

  public static class ConstructorBuilder {
    protected ExecutableElementImpl exe;
    private TypeParameterBuilder typeParamBuilder;

    public ConstructorBuilder(
        TypeElement enclosingElement,
        List<? extends TypeMirror> classTypeArguments,
        Map<String, TypeParameterElement> classTypeParameterMap) {
      this(
          enclosingElement,
          ElementKind.CONSTRUCTOR,
          "<init>",
          classTypeArguments,
          classTypeParameterMap);
    }

    public ConstructorBuilder(
        TypeElement enclosingElement,
        ElementKind kind,
        String name,
        List<? extends TypeMirror> classTypeArguments,
        Map<String, TypeParameterElement> classTypeParameterMap) {
      exe = new ExecutableElementImpl(enclosingElement, kind, name);
      typeParamBuilder =
          new TypeParameterBuilder(
              enclosingElement, exe.typeParameters, classTypeArguments, classTypeParameterMap);
    }

    public ExecutableElement getExecutableElement() {
      exe.type = new ExecutableTypeImpl(exe);
      return exe;
    }

    public ConstructorBuilder addModifier(Modifier modifier) {
      exe.modifiers.add(modifier);
      return this;
    }

    public ConstructorBuilder addModifiers(int modifier) {
      MethodFlags.setModifiers(exe.modifiers, modifier);
      return this;
    }

    public VariableBuilder addParameter(String name) {
      VariableBuilder variableBuilder =
          new VariableElementImpl.VariableBuilder(exe, name, typeParamBuilder);
      exe.parameters.add(variableBuilder.getVariableElement());
      return variableBuilder;
    }

    public ConstructorBuilder addParameter(VariableElement param) {
      exe.parameters.add(param);
      return this;
    }

    public ConstructorBuilder addThrownType(Class<?> thrownType) {
      return addThrownType(resolvType(thrownType.getCanonicalName()));
    }

    public ConstructorBuilder addThrownType(String thrownType) {
      return addThrownType(resolvType(thrownType));
    }

    public ConstructorBuilder addThrownType(TypeMirror thrownType) {
      exe.thrownTypes.add(thrownType);
      return this;
    }

    public List<TypeMirror> getTypeArguments() {
      return typeParamBuilder.getTypeArguments();
    }

    protected TypeMirror resolvType(String type) {
      return typeParamBuilder.resolvType(type);
    }

    public ConstructorBuilder addTypeParameter(String name, Class<?>... bounds) {
      exe.typeParameters.add(typeParamBuilder.addTypeParameter(name, bounds));
      return this;
    }

    public ConstructorBuilder addTypeParameter(String name, CharSequence... bounds) {
      exe.typeParameters.add(typeParamBuilder.addTypeParameter(name, bounds));
      return this;
    }

    public ConstructorBuilder addTypeParameter(String name, TypeElement... bounds) {
      exe.typeParameters.add(typeParamBuilder.addTypeParameter(name, bounds));
      return this;
    }

    public ConstructorBuilder addTypeParameter(String name, TypeMirror... bounds) {
      exe.typeParameters.add(typeParamBuilder.addTypeParameter(name, bounds));
      return this;
    }

    public ConstructorBuilder addTypeParameter(TypeParameterElement param) {
      exe.typeParameters.add(typeParamBuilder.addTypeParameter(param));
      return this;
    }
  }

  public ExecutableElementImpl(Element enclosingElement, ElementKind kind, String name) {
    super(kind, name);
    this.enclosingElement = enclosingElement;
  }

  ExecutableElementImpl(Constructor constructor) {
    super(ElementKind.CONSTRUCTOR, constructor, constructor.getModifiers(), "<init>");
  }

  void init(Constructor constructor) {
    for (TypeVariable param : constructor.getTypeParameters()) {
      typeParameters.add(ElementFactory.getTypeParameterElement(param));
    }
    type = TypeMirrorFactory.get(constructor);
    enclosingElement = ElementFactory.get(constructor.getDeclaringClass());
    returnType = Typ.getNoType(TypeKind.VOID);
    Type[] genericParameterTypes = constructor.getGenericParameterTypes();
    Annotation[][] parameterAnnotations = constructor.getParameterAnnotations();
    int index = 0;
    for (Type param : genericParameterTypes) {
      parameters.add(ElementFactory.getVariableElement(param, parameterAnnotations[index++]));
    }
    varArgs = constructor.isVarArgs();
    for (Type type : constructor.getGenericExceptionTypes()) {
      thrownTypes.add(TypeMirrorFactory.get(type));
    }
  }

  ExecutableElementImpl(Method method) {
    super(ElementKind.METHOD, method, method.getModifiers(), method.getName());
  }

  void init(Method method) {
    for (TypeVariable param : method.getTypeParameters()) {
      typeParameters.add(ElementFactory.getTypeParameterElement(param));
    }
    type = TypeMirrorFactory.get(method);
    enclosingElement = ElementFactory.get(method.getDeclaringClass());
    returnType = TypeMirrorFactory.get(method.getGenericReturnType());
    Type[] genericParameterTypes = method.getGenericParameterTypes();
    Annotation[][] parameterAnnotations = method.getParameterAnnotations();
    int index = 0;
    for (Type param : genericParameterTypes) {
      parameters.add(ElementFactory.getVariableElement(param, parameterAnnotations[index++]));
    }
    varArgs = method.isVarArgs();
    for (Type type : method.getGenericExceptionTypes()) {
      thrownTypes.add(TypeMirrorFactory.get(type));
    }
    Object defValue = method.getDefaultValue();
    if (defValue != null) {
      defaultValue = new AnnotationValueImpl(defValue);
    }
  }

  @Override
  public List<? extends TypeParameterElement> getTypeParameters() {
    return typeParameters;
  }

  @Override
  @SuppressWarnings("unchecked")
  public List<? extends Element> getEnclosedElements() {
    return Collections.EMPTY_LIST;
  }

  @Override
  public <R, P> R accept(ElementVisitor<R, P> v, P p) {
    return v.visitExecutable(this, p);
  }

  @Override
  public TypeMirror getReturnType() {
    return returnType;
  }

  @Override
  public List<? extends VariableElement> getParameters() {
    return parameters;
  }

  @Override
  public boolean isVarArgs() {
    return varArgs;
  }

  @Override
  public List<? extends TypeMirror> getThrownTypes() {
    return thrownTypes;
  }

  @Override
  public AnnotationValue getDefaultValue() {
    return defaultValue;
  }

  @Override
  public int hashCode() {
    int hash = 5;
    return hash;
  }

  @Override
  public boolean equals(Object obj) {
    if (obj == null) {
      return false;
    }
    if (getClass() != obj.getClass()) {
      return false;
    }
    final ExecutableElementImpl other = (ExecutableElementImpl) obj;
    if (!Objects.equals(this.typeParameters, other.typeParameters)) {
      return false;
    }
    if (!Objects.equals(this.enclosingElement, other.enclosingElement)) {
      return false;
    }
    if (!Objects.equals(this.returnType, other.returnType)) {
      return false;
    }
    if (!Objects.equals(this.parameters, other.parameters)) {
      return false;
    }
    if (this.varArgs != other.varArgs) {
      return false;
    }
    if (!Objects.equals(this.thrownTypes, other.thrownTypes)) {
      return false;
    }
    if (!Objects.equals(this.defaultValue, other.defaultValue)) {
      return false;
    }
    return true;
  }

  @Override
  public String toString() {
    return "ExecutableElementImpl{" + getSimpleName() + '}';
  }
}