package com.webcohesion.enunciate.modules.jackson.javac;
import com.webcohesion.enunciate.javac.decorations.DecoratedProcessingEnvironment;
import com.webcohesion.enunciate.javac.decorations.SourcePosition;
import com.webcohesion.enunciate.javac.decorations.adaptors.ExecutableElementAdaptor;
import com.webcohesion.enunciate.javac.decorations.adaptors.TypeElementAdaptor;
import com.webcohesion.enunciate.javac.decorations.adaptors.VariableElementAdaptor;
import com.webcohesion.enunciate.javac.decorations.type.TypeVariableContext;
import javax.lang.model.element.*;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* @author Ryan Heaton
*/
public class ParameterizedJacksonTypeElement implements TypeElementAdaptor {
private final DeclaredType root;
private final TypeElement element;
private final DecoratedProcessingEnvironment env;
private final Name fqn;
private final Name simpleName;
private final TypeVariableContext variableContext;
public ParameterizedJacksonTypeElement(DeclaredType root, DecoratedProcessingEnvironment env) {
this.root = root;
this.env = env;
this.element = (TypeElement) root.asElement();
List<? extends TypeMirror> typeArgs = root.getTypeArguments();
StringBuilder fqn = new StringBuilder(element.getQualifiedName().toString());
StringBuilder simpleName = new StringBuilder(element.getSimpleName().toString());
String nameSeparator = "Of";
for (TypeMirror typeArg : typeArgs) {
if (typeArg instanceof DeclaredType) {
TypeElement argType = (TypeElement) ((DeclaredType) typeArg).asElement();
fqn = fqn.append(nameSeparator).append(argType.getSimpleName().toString());
simpleName = simpleName.append(nameSeparator).append(argType.getSimpleName().toString());
nameSeparator = "And";
}
}
Elements elementUtils = env.getElementUtils();
this.fqn = elementUtils.getName(fqn);
this.simpleName = elementUtils.getName(simpleName);
this.variableContext = new TypeVariableContext().push(element.getTypeParameters(), root.getTypeArguments());
}
@Override
public Name getBinaryName() {
return this.fqn;
}
@Override
public String getDocComment() {
return this.env.getElementUtils().getDocComment(element);
}
@Override
public boolean isDeprecated() {
return this.env.getElementUtils().isDeprecated(element);
}
@Override
public List<? extends Element> getAllMembers() {
return decorate(env.getElementUtils().getAllMembers(element));
}
protected List<? extends Element> decorate(List<? extends Element> elements) {
ArrayList<Element> members = new ArrayList<Element>();
for (Element member : elements) {
if (member instanceof VariableElement) {
members.add(new ParameterizedVariable((VariableElement) member));
}
else if (member instanceof ExecutableElement) {
members.add(new ParameterizedExecutable((ExecutableElement) member));
}
else {
members.add(member);
}
}
return members;
}
@Override
public boolean overrides(ExecutableElement overrider, ExecutableElement overridden) {
return env.getElementUtils().overrides(overrider, overridden, element);
}
@Override
public PackageElement getPackage() {
return this.env.getElementUtils().getPackageOf(element);
}
@Override
public List<? extends AnnotationMirror> getAllAnnotationMirrors() {
return this.env.getElementUtils().getAllAnnotationMirrors(this.element);
}
@Override
public boolean hides(Element hidden) {
return false;
}
@Override
public boolean isHiddenBy(Element hider) {
return false;
}
@Override
public SourcePosition getSourcePosition() {
return new SourcePosition(null, null, -1, -1, -1);
}
@Override
public List<? extends Element> getEnclosedElements() {
return decorate(this.element.getEnclosedElements());
}
@Override
public NestingKind getNestingKind() {
return NestingKind.TOP_LEVEL;
}
@Override
public Name getQualifiedName() {
return this.fqn;
}
@Override
public Name getSimpleName() {
return this.simpleName;
}
@Override
public TypeMirror getSuperclass() {
TypeMirror superclass = this.element.getSuperclass();
if (superclass instanceof DeclaredType) {
return new ParameterizedJacksonDeclaredType((DeclaredType) superclass, env);
}
return superclass;
}
@Override
public List<? extends TypeMirror> getInterfaces() {
return this.element.getInterfaces();
}
@Override
public List<? extends TypeParameterElement> getTypeParameters() {
return Collections.emptyList();
}
@Override
public Element getEnclosingElement() {
return this.element.getEnclosingElement();
}
@Override
public TypeMirror asType() {
return new ParameterizedJacksonDeclaredType(this.root, this.env);
}
@Override
public ElementKind getKind() {
return ElementKind.CLASS;
}
@Override
public List<? extends AnnotationMirror> getAnnotationMirrors() {
return this.element.getAnnotationMirrors();
}
@Override
public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
return this.element.getAnnotation(annotationType);
}
@Override
public Set<Modifier> getModifiers() {
return this.element.getModifiers();
}
@Override
public <R, P> R accept(ElementVisitor<R, P> v, P p) {
return v.visitType(this, p);
}
private class ParameterizedVariable implements VariableElementAdaptor {
private final VariableElement variableElement;
public ParameterizedVariable(VariableElement variableElement) {
this.variableElement = variableElement;
}
@Override
public String getDocComment() {
return env.getElementUtils().getDocComment(variableElement);
}
@Override
public boolean isDeprecated() {
return env.getElementUtils().isDeprecated(variableElement);
}
@Override
public PackageElement getPackage() {
return env.getElementUtils().getPackageOf(variableElement);
}
@Override
public List<? extends AnnotationMirror> getAllAnnotationMirrors() {
return env.getElementUtils().getAllAnnotationMirrors(variableElement);
}
@Override
public boolean hides(Element hidden) {
while (hidden instanceof ParameterizedVariable) {
hidden = ((ParameterizedVariable) hidden).variableElement;
}
return env.getElementUtils().hides(variableElement, hidden);
}
@Override
public boolean isHiddenBy(Element hider) {
while (hider instanceof ParameterizedVariable) {
hider = ((ParameterizedVariable) hider).variableElement;
}
return env.getElementUtils().hides(hider, variableElement);
}
@Override
public SourcePosition getSourcePosition() {
return null;
}
@Override
public Object getConstantValue() {
return variableElement.getConstantValue();
}
@Override
public TypeMirror asType() {
return variableContext.resolveTypeVariables(variableElement.asType(), env);
}
@Override
public ElementKind getKind() {
return variableElement.getKind();
}
@Override
public List<? extends AnnotationMirror> getAnnotationMirrors() {
return variableElement.getAnnotationMirrors();
}
@Override
public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
return variableElement.getAnnotation(annotationType);
}
@Override
public Set<Modifier> getModifiers() {
return variableElement.getModifiers();
}
@Override
public Name getSimpleName() {
return variableElement.getSimpleName();
}
@Override
public Element getEnclosingElement() {
return ParameterizedJacksonTypeElement.this;
}
@Override
public List<? extends Element> getEnclosedElements() {
return variableElement.getEnclosedElements();
}
@Override
public <R, P> R accept(ElementVisitor<R, P> v, P p) {
return v.visitVariable(this, p);
}
}
private class ParameterizedExecutable implements ExecutableElementAdaptor {
private final ExecutableElement executableElement;
private ParameterizedExecutable(ExecutableElement executableElement) {
this.executableElement = executableElement;
}
@Override
public boolean overrides(ExecutableElement overridden, TypeElement scope) {
while (overridden instanceof ParameterizedExecutable) {
overridden = ((ParameterizedExecutable) overridden).executableElement;
}
return env.getElementUtils().overrides(this.executableElement, overridden, scope);
}
@Override
public boolean isOverriddenBy(ExecutableElement overrider, TypeElement type) {
while (overrider instanceof ParameterizedExecutable) {
overrider = ((ParameterizedExecutable) overrider).executableElement;
}
return env.getElementUtils().overrides(overrider, this.executableElement, type);
}
@Override
public String getDocComment() {
return env.getElementUtils().getDocComment(executableElement);
}
@Override
public boolean isDeprecated() {
return env.getElementUtils().isDeprecated(executableElement);
}
@Override
public PackageElement getPackage() {
return env.getElementUtils().getPackageOf(executableElement);
}
@Override
public List<? extends AnnotationMirror> getAllAnnotationMirrors() {
return env.getElementUtils().getAllAnnotationMirrors(executableElement);
}
@Override
public boolean hides(Element hidden) {
while (hidden instanceof ParameterizedExecutable) {
hidden = ((ParameterizedExecutable) hidden).executableElement;
}
return env.getElementUtils().hides(executableElement, hidden);
}
@Override
public boolean isHiddenBy(Element hider) {
while (hider instanceof ParameterizedExecutable) {
hider = ((ParameterizedExecutable) hider).executableElement;
}
return env.getElementUtils().hides(hider, executableElement);
}
@Override
public SourcePosition getSourcePosition() {
return null;
}
@Override
public List<? extends TypeParameterElement> getTypeParameters() {
return executableElement.getTypeParameters();
}
@Override
public TypeMirror getReturnType() {
return variableContext.resolveTypeVariables(executableElement.getReturnType(), env);
}
@Override
public List<? extends VariableElement> getParameters() {
ArrayList<VariableElement> parameters = new ArrayList<VariableElement>();
for (VariableElement variableElement : executableElement.getParameters()) {
parameters.add(new ParameterizedVariable(variableElement));
}
return parameters;
}
@Override
public boolean isVarArgs() {
return executableElement.isVarArgs();
}
@Override
public List<? extends TypeMirror> getThrownTypes() {
return executableElement.getThrownTypes();
}
@Override
public AnnotationValue getDefaultValue() {
return executableElement.getDefaultValue();
}
@Override
public Name getSimpleName() {
return executableElement.getSimpleName();
}
@Override
public TypeMirror asType() {
return variableContext.resolveTypeVariables(executableElement.asType(), env);
}
@Override
public ElementKind getKind() {
return executableElement.getKind();
}
@Override
public List<? extends AnnotationMirror> getAnnotationMirrors() {
return executableElement.getAnnotationMirrors();
}
@Override
public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
return executableElement.getAnnotation(annotationType);
}
@Override
public Set<Modifier> getModifiers() {
return executableElement.getModifiers();
}
@Override
public Element getEnclosingElement() {
return executableElement.getEnclosingElement();
}
@Override
public List<? extends Element> getEnclosedElements() {
return executableElement.getEnclosedElements();
}
@Override
public <R, P> R accept(ElementVisitor<R, P> v, P p) {
return v.visitExecutable(this, p);
}
}
}