/* * Copyright 2019 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.turbine.processing; import static java.util.Objects.requireNonNull; import com.google.common.base.Joiner; import com.google.common.base.Splitter; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.google.turbine.binder.bound.AnnotationMetadata; import com.google.turbine.binder.bound.SourceTypeBoundClass; import com.google.turbine.binder.bound.TurbineAnnotationValue; import com.google.turbine.binder.bound.TypeBoundClass; import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo; import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo; import com.google.turbine.binder.bound.TypeBoundClass.ParamInfo; import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo; import com.google.turbine.binder.lookup.PackageScope; import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.binder.sym.FieldSymbol; import com.google.turbine.binder.sym.MethodSymbol; import com.google.turbine.binder.sym.PackageSymbol; import com.google.turbine.binder.sym.ParamSymbol; import com.google.turbine.binder.sym.Symbol; import com.google.turbine.binder.sym.TyVarSymbol; import com.google.turbine.diag.TurbineError; import com.google.turbine.diag.TurbineError.ErrorKind; import com.google.turbine.model.Const; import com.google.turbine.model.Const.ArrayInitValue; import com.google.turbine.model.TurbineFlag; import com.google.turbine.tree.Tree; import com.google.turbine.tree.Tree.MethDecl; import com.google.turbine.tree.Tree.TyDecl; import com.google.turbine.tree.Tree.VarDecl; import com.google.turbine.type.AnnoInfo; import com.google.turbine.type.Type; import com.google.turbine.type.Type.ClassTy; import com.google.turbine.type.Type.ClassTy.SimpleClassTy; import com.google.turbine.type.Type.ErrorTy; import java.lang.annotation.Annotation; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.EnumSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ElementVisitor; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.Name; import javax.lang.model.element.NestingKind; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import org.checkerframework.checker.nullness.qual.Nullable; /** An {@link Element} implementation backed by a {@link Symbol}. */ @SuppressWarnings("nullness") // TODO(cushon): Address nullness diagnostics. public abstract class TurbineElement implements Element { public abstract Symbol sym(); public abstract String javadoc(); @Override public abstract int hashCode(); @Override public abstract boolean equals(@Nullable Object obj); protected final ModelFactory factory; private final Supplier> annotationMirrors; protected Supplier memoize(Supplier supplier) { return factory.memoize(supplier); } protected TurbineElement(ModelFactory factory) { this.factory = requireNonNull(factory); this.annotationMirrors = factory.memoize( new Supplier>() { @Override public ImmutableList get() { ImmutableList.Builder result = ImmutableList.builder(); for (AnnoInfo anno : annos()) { result.add(TurbineAnnotationMirror.create(factory, anno)); } return result.build(); } }); } static AnnoInfo getAnnotation(Iterable annos, ClassSymbol sym) { for (AnnoInfo anno : annos) { if (Objects.equals(anno.sym(), sym)) { return anno; } } return null; } @Override public A getAnnotation(Class annotationType) { ClassSymbol sym = new ClassSymbol(annotationType.getName().replace('.', '/')); TypeBoundClass info = factory.getSymbol(sym); if (info == null) { return null; } AnnoInfo anno = getAnnotation(annos(), sym); if (anno == null) { return null; } return TurbineAnnotationProxy.create(factory, annotationType, anno); } @Override public final A[] getAnnotationsByType(Class annotationType) { ClassSymbol sym = new ClassSymbol(annotationType.getName().replace('.', '/')); TypeBoundClass info = factory.getSymbol(sym); if (info == null) { return null; } AnnotationMetadata metadata = info.annotationMetadata(); if (metadata == null) { return null; } List result = new ArrayList<>(); for (AnnoInfo anno : annos()) { if (anno.sym().equals(sym)) { result.add(TurbineAnnotationProxy.create(factory, annotationType, anno)); continue; } if (anno.sym().equals(metadata.repeatable())) { // requireNonNull is safe because java.lang.annotation.Repeatable declares `value`. ArrayInitValue arrayValue = (ArrayInitValue) requireNonNull(anno.values().get("value")); for (Const element : arrayValue.elements()) { result.add( TurbineAnnotationProxy.create( factory, annotationType, ((TurbineAnnotationValue) element).info())); } } } return Iterables.toArray(result, annotationType); } @Override public final List getAnnotationMirrors() { return annotationMirrors.get(); } List getAllAnnotationMirrors() { return getAnnotationMirrors(); } protected abstract ImmutableList annos(); /** A {@link TypeElement} implementation backed by a {@link ClassSymbol}. */ static class TurbineTypeElement extends TurbineElement implements TypeElement { @Override public int hashCode() { return sym.hashCode(); } private final ClassSymbol sym; private final Supplier info; TurbineTypeElement(ModelFactory factory, ClassSymbol sym) { super(factory); this.sym = requireNonNull(sym); this.info = memoize( new Supplier() { @Override public TypeBoundClass get() { return factory.getSymbol(sym); } }); } @Nullable TypeBoundClass info() { return info.get(); } TypeBoundClass infoNonNull() { TypeBoundClass info = info(); if (info == null) { throw TurbineError.format(/* source= */ null, ErrorKind.SYMBOL_NOT_FOUND, sym); } return info; } @Override public NestingKind getNestingKind() { TypeBoundClass info = info(); return (info != null && info.owner() != null) ? NestingKind.MEMBER : NestingKind.TOP_LEVEL; } private final Supplier qualifiedName = memoize( new Supplier() { @Override public TurbineName get() { TypeBoundClass info = info(); if (info == null || info.owner() == null) { return new TurbineName(sym.toString()); } ClassSymbol sym = sym(); Deque flat = new ArrayDeque<>(); while (info.owner() != null) { flat.addFirst(sym.binaryName().substring(info.owner().binaryName().length() + 1)); sym = info.owner(); info = factory.getSymbol(sym); } flat.addFirst(sym.toString()); return new TurbineName(Joiner.on('.').join(flat)); } }); @Override public Name getQualifiedName() { return qualifiedName.get(); } private final Supplier superclass = memoize( new Supplier() { @Override public TypeMirror get() { TypeBoundClass info = infoNonNull(); switch (info.kind()) { case CLASS: case ENUM: if (info.superclass() != null) { return factory.asTypeMirror(info.superClassType()); } if (info instanceof SourceTypeBoundClass) { // support simple names for stuff that doesn't exist TyDecl decl = ((SourceTypeBoundClass) info).decl(); if (decl.xtnds().isPresent()) { ArrayDeque flat = new ArrayDeque<>(); for (Tree.ClassTy curr = decl.xtnds().get(); curr != null; curr = curr.base().orElse(null)) { flat.addFirst(curr.name()); } return factory.asTypeMirror(ErrorTy.create(flat)); } } return factory.noType(); case INTERFACE: case ANNOTATION: return factory.noType(); } throw new AssertionError(info.kind()); } }); @Override public TypeMirror getSuperclass() { return superclass.get(); } @Override public String toString() { return getQualifiedName().toString(); } private final Supplier> interfaces = memoize( new Supplier>() { @Override public List get() { return factory.asTypeMirrors(infoNonNull().interfaceTypes()); } }); @Override public List getInterfaces() { return interfaces.get(); } private final Supplier> typeParameters = memoize( new Supplier>() { @Override public ImmutableList get() { ImmutableList.Builder result = ImmutableList.builder(); for (TyVarSymbol p : infoNonNull().typeParameters().values()) { result.add(factory.typeParameterElement(p)); } return result.build(); } }); @Override public List getTypeParameters() { return typeParameters.get(); } private final Supplier type = memoize( new Supplier() { @Override public TypeMirror get() { return factory.asTypeMirror(asGenericType(sym)); } ClassTy asGenericType(ClassSymbol symbol) { TypeBoundClass info = info(); if (info == null) { return ClassTy.asNonParametricClassTy(symbol); } Deque simples = new ArrayDeque<>(); simples.addFirst(simple(symbol, info)); while (info.owner() != null && (info.access() & TurbineFlag.ACC_STATIC) == 0) { symbol = info.owner(); info = factory.getSymbol(symbol); simples.addFirst(simple(symbol, info)); } return ClassTy.create(ImmutableList.copyOf(simples)); } private SimpleClassTy simple(ClassSymbol sym, TypeBoundClass info) { ImmutableList.Builder args = ImmutableList.builder(); for (TyVarSymbol t : info.typeParameters().values()) { args.add(Type.TyVar.create(t, ImmutableList.of())); } return SimpleClassTy.create(sym, args.build(), ImmutableList.of()); } }); @Override public TypeMirror asType() { return type.get(); } @Override public ElementKind getKind() { TypeBoundClass info = infoNonNull(); switch (info.kind()) { case CLASS: return ElementKind.CLASS; case INTERFACE: return ElementKind.INTERFACE; case ENUM: return ElementKind.ENUM; case ANNOTATION: return ElementKind.ANNOTATION_TYPE; } throw new AssertionError(info.kind()); } @Override public Set getModifiers() { return asModifierSet(ModifierOwner.TYPE, infoNonNull().access() & ~TurbineFlag.ACC_SUPER); } private final Supplier simpleName = memoize( new Supplier() { @Override public TurbineName get() { TypeBoundClass info = info(); if (info == null || info.owner() == null) { return new TurbineName(sym.simpleName()); } return new TurbineName( sym.binaryName().substring(info.owner().binaryName().length() + 1)); } }); @Override public Name getSimpleName() { return simpleName.get(); } private final Supplier enclosing = memoize( new Supplier() { @Override public Element get() { return getNestingKind().equals(NestingKind.TOP_LEVEL) ? factory.packageElement(sym.owner()) : factory.typeElement(info().owner()); } }); @Override public Element getEnclosingElement() { return enclosing.get(); } private final Supplier> enclosed = memoize( new Supplier>() { @Override public ImmutableList get() { TypeBoundClass info = infoNonNull(); ImmutableList.Builder result = ImmutableList.builder(); for (FieldInfo field : info.fields()) { result.add(factory.fieldElement(field.sym())); } for (MethodInfo method : info.methods()) { result.add(factory.executableElement(method.sym())); } for (ClassSymbol child : info.children().values()) { result.add(factory.typeElement(child)); } return result.build(); } }); @Override public List getEnclosedElements() { return enclosed.get(); } @Override public R accept(ElementVisitor v, P p) { return v.visitType(this, p); } @Override public ClassSymbol sym() { return sym; } @Override public String javadoc() { TypeBoundClass info = info(); if (!(info instanceof SourceTypeBoundClass)) { return null; } return ((SourceTypeBoundClass) info).decl().javadoc(); } @Override public boolean equals(@Nullable Object obj) { return obj instanceof TurbineTypeElement && sym.equals(((TurbineTypeElement) obj).sym); } @Override protected ImmutableList annos() { return infoNonNull().annotations(); } @Override public final A getAnnotation(Class annotationType) { ClassSymbol sym = new ClassSymbol(annotationType.getName().replace('.', '/')); AnnoInfo anno = getAnnotation(annos(), sym); if (anno != null) { return TurbineAnnotationProxy.create(factory, annotationType, anno); } if (!isAnnotationInherited(sym)) { return null; } ClassSymbol superclass = infoNonNull().superclass(); while (superclass != null) { TypeBoundClass info = factory.getSymbol(superclass); if (info == null) { break; } anno = getAnnotation(info.annotations(), sym); if (anno != null) { return TurbineAnnotationProxy.create(factory, annotationType, anno); } superclass = info.superclass(); } return null; } @Override List getAllAnnotationMirrors() { Map result = new LinkedHashMap<>(); for (AnnoInfo anno : annos()) { result.put(anno.sym(), TurbineAnnotationMirror.create(factory, anno)); } ClassSymbol superclass = infoNonNull().superclass(); while (superclass != null) { TypeBoundClass i = factory.getSymbol(superclass); if (i == null) { break; } for (AnnoInfo anno : i.annotations()) { addAnnotationFromSuper(result, anno); } superclass = i.superclass(); } return ImmutableList.copyOf(result.values()); } private void addAnnotationFromSuper(Map result, AnnoInfo anno) { if (!isAnnotationInherited(anno.sym())) { return; } if (result.containsKey(anno.sym())) { // if the same inherited annotation is present on multiple supertypes, only return one return; } result.put(anno.sym(), TurbineAnnotationMirror.create(factory, anno)); } private boolean isAnnotationInherited(ClassSymbol sym) { TypeBoundClass annoInfo = factory.getSymbol(sym); if (annoInfo == null) { return false; } for (AnnoInfo anno : annoInfo.annotations()) { if (anno.sym().equals(ClassSymbol.INHERITED)) { return true; } } return false; } } /** A {@link TypeParameterElement} implementation backed by a {@link TyVarSymbol}. */ static class TurbineTypeParameterElement extends TurbineElement implements TypeParameterElement { @Override public int hashCode() { return sym.hashCode(); } @Override public boolean equals(@Nullable Object obj) { return obj instanceof TurbineTypeParameterElement && sym.equals(((TurbineTypeParameterElement) obj).sym); } private final TyVarSymbol sym; public TurbineTypeParameterElement(ModelFactory factory, TyVarSymbol sym) { super(factory); this.sym = sym; } private final Supplier info = memoize( new Supplier() { @Override public TyVarInfo get() { return factory.getTyVarInfo(sym); } }); @Nullable private TyVarInfo info() { return info.get(); } @Override public String toString() { return sym.name(); } @Override public Element getGenericElement() { return factory.element(sym.owner()); } @Override public List getBounds() { ImmutableList bounds = info().upperBound().bounds(); return factory.asTypeMirrors(bounds.isEmpty() ? ImmutableList.of(ClassTy.OBJECT) : bounds); } @Override public TypeMirror asType() { return factory.asTypeMirror(Type.TyVar.create(sym, info().annotations())); } @Override public ElementKind getKind() { return ElementKind.TYPE_PARAMETER; } @Override public Set getModifiers() { return ImmutableSet.of(); } @Override public Name getSimpleName() { return new TurbineName(sym.name()); } @Override public Element getEnclosingElement() { return getGenericElement(); } @Override public List getEnclosedElements() { return ImmutableList.of(); } @Override public R accept(ElementVisitor v, P p) { return v.visitTypeParameter(this, p); } @Override public TyVarSymbol sym() { return sym; } @Override public String javadoc() { return null; } @Override protected ImmutableList annos() { return info().annotations(); } } /** An {@link ExecutableElement} implementation backed by a {@link MethodSymbol}. */ static class TurbineExecutableElement extends TurbineElement implements ExecutableElement { private final MethodSymbol sym; private final Supplier info = memoize( new Supplier() { @Override public MethodInfo get() { return factory.getMethodInfo(sym); } }); @Nullable MethodInfo info() { return info.get(); } TurbineExecutableElement(ModelFactory factory, MethodSymbol sym) { super(factory); this.sym = sym; } @Override public MethodSymbol sym() { return sym; } @Override public String javadoc() { MethDecl decl = info().decl(); return decl != null ? decl.javadoc() : null; } @Override public int hashCode() { return sym.hashCode(); } @Override public boolean equals(@Nullable Object obj) { return obj instanceof TurbineExecutableElement && sym.equals(((TurbineExecutableElement) obj).sym); } @Override public List getTypeParameters() { ImmutableList.Builder result = ImmutableList.builder(); for (Map.Entry p : info().tyParams().entrySet()) { result.add(factory.typeParameterElement(p.getKey())); } return result.build(); } @Override public TypeMirror getReturnType() { return factory.asTypeMirror(info().returnType()); } private final Supplier> parameters = memoize( new Supplier>() { @Override public ImmutableList get() { ImmutableList.Builder result = ImmutableList.builder(); for (ParamInfo param : info().parameters()) { if (param.synthetic()) { // ExecutableElement#getParameters doesn't expect synthetic or mandated // parameters continue; } result.add(factory.parameterElement(param.sym())); } return result.build(); } }); @Override public List getParameters() { return parameters.get(); } @Override public String toString() { MethodInfo info = info(); StringBuilder sb = new StringBuilder(); if (!info.tyParams().isEmpty()) { sb.append('<'); Joiner.on(',').appendTo(sb, info.tyParams().keySet()); sb.append('>'); } if (getKind() == ElementKind.CONSTRUCTOR) { sb.append(info.sym().owner().simpleName()); } else { sb.append(info.sym().name()); } sb.append('('); boolean first = true; for (ParamInfo p : info.parameters()) { if (!first) { sb.append(','); } sb.append(p.type()); first = false; } sb.append(')'); return sb.toString(); } @Override public TypeMirror getReceiverType() { return info().receiver() != null ? factory.asTypeMirror(info().receiver().type()) : factory.noType(); } @Override public boolean isVarArgs() { return (info().access() & TurbineFlag.ACC_VARARGS) == TurbineFlag.ACC_VARARGS; } @Override public boolean isDefault() { return (info().access() & TurbineFlag.ACC_DEFAULT) == TurbineFlag.ACC_DEFAULT; } @Override public List getThrownTypes() { return factory.asTypeMirrors(info().exceptions()); } @Override public AnnotationValue getDefaultValue() { return info().defaultValue() != null ? TurbineAnnotationMirror.annotationValue(factory, info().defaultValue()) : null; } @Override public TypeMirror asType() { return factory.asTypeMirror(info().asType()); } @Override public ElementKind getKind() { return sym.name().equals("") ? ElementKind.CONSTRUCTOR : ElementKind.METHOD; } @Override public Set getModifiers() { return asModifierSet(ModifierOwner.METHOD, info().access()); } @Override public Name getSimpleName() { return new TurbineName(info().sym().name()); } @Override public Element getEnclosingElement() { return factory.typeElement(info().sym().owner()); } @Override public List getEnclosedElements() { return ImmutableList.of(); } @Override public R accept(ElementVisitor v, P p) { return v.visitExecutable(this, p); } @Override protected ImmutableList annos() { return info().annotations(); } } /** An {@link VariableElement} implementation backed by a {@link FieldSymbol}. */ static class TurbineFieldElement extends TurbineElement implements VariableElement { @Override public String toString() { return sym.name(); } @Override public boolean equals(@Nullable Object obj) { return obj instanceof TurbineFieldElement && sym.equals(((TurbineFieldElement) obj).sym); } @Override public int hashCode() { return sym.hashCode(); } private final FieldSymbol sym; @Override public FieldSymbol sym() { return sym; } @Override public String javadoc() { VarDecl decl = info().decl(); return decl != null ? decl.javadoc() : null; } private final Supplier info = memoize( new Supplier() { @Override public FieldInfo get() { return factory.getFieldInfo(sym); } }); @Nullable FieldInfo info() { return info.get(); } TurbineFieldElement(ModelFactory factory, FieldSymbol sym) { super(factory); this.sym = sym; } @Override public Object getConstantValue() { if (info().value() == null) { return null; } return info().value().getValue(); } @Override public TypeMirror asType() { return factory.asTypeMirror(info().type()); } @Override public ElementKind getKind() { return ((info().access() & TurbineFlag.ACC_ENUM) == TurbineFlag.ACC_ENUM) ? ElementKind.ENUM_CONSTANT : ElementKind.FIELD; } @Override public Set getModifiers() { return asModifierSet(ModifierOwner.FIELD, info().access()); } @Override public Name getSimpleName() { return new TurbineName(sym.name()); } @Override public Element getEnclosingElement() { return factory.typeElement(sym.owner()); } @Override public List getEnclosedElements() { return ImmutableList.of(); } @Override public R accept(ElementVisitor v, P p) { return v.visitVariable(this, p); } @Override protected ImmutableList annos() { return info().annotations(); } } private enum ModifierOwner { TYPE, PARAMETER, FIELD, METHOD } private static ImmutableSet asModifierSet(ModifierOwner modifierOwner, int access) { EnumSet modifiers = EnumSet.noneOf(Modifier.class); if ((access & TurbineFlag.ACC_PUBLIC) == TurbineFlag.ACC_PUBLIC) { modifiers.add(Modifier.PUBLIC); } if ((access & TurbineFlag.ACC_PROTECTED) == TurbineFlag.ACC_PROTECTED) { modifiers.add(Modifier.PROTECTED); } if ((access & TurbineFlag.ACC_PRIVATE) == TurbineFlag.ACC_PRIVATE) { modifiers.add(Modifier.PRIVATE); } if ((access & TurbineFlag.ACC_ABSTRACT) == TurbineFlag.ACC_ABSTRACT) { modifiers.add(Modifier.ABSTRACT); } if ((access & TurbineFlag.ACC_FINAL) == TurbineFlag.ACC_FINAL) { modifiers.add(Modifier.FINAL); } if ((access & TurbineFlag.ACC_DEFAULT) == TurbineFlag.ACC_DEFAULT) { modifiers.add(Modifier.DEFAULT); } if ((access & TurbineFlag.ACC_STATIC) == TurbineFlag.ACC_STATIC) { modifiers.add(Modifier.STATIC); } if ((access & TurbineFlag.ACC_TRANSIENT) == TurbineFlag.ACC_TRANSIENT) { switch (modifierOwner) { case METHOD: case PARAMETER: // varargs and transient use the same bits break; default: modifiers.add(Modifier.TRANSIENT); } } if ((access & TurbineFlag.ACC_VOLATILE) == TurbineFlag.ACC_VOLATILE) { modifiers.add(Modifier.VOLATILE); } if ((access & TurbineFlag.ACC_SYNCHRONIZED) == TurbineFlag.ACC_SYNCHRONIZED) { modifiers.add(Modifier.SYNCHRONIZED); } if ((access & TurbineFlag.ACC_NATIVE) == TurbineFlag.ACC_NATIVE) { modifiers.add(Modifier.NATIVE); } if ((access & TurbineFlag.ACC_STRICT) == TurbineFlag.ACC_STRICT) { modifiers.add(Modifier.STRICTFP); } return Sets.immutableEnumSet(modifiers); } /** A {@link PackageElement} implementation backed by a {@link PackageSymbol}. */ static class TurbinePackageElement extends TurbineElement implements PackageElement { private final PackageSymbol sym; public TurbinePackageElement(ModelFactory factory, PackageSymbol sym) { super(factory); this.sym = sym; } @Override public Name getQualifiedName() { return new TurbineName(sym.toString()); } @Override public boolean isUnnamed() { return sym.binaryName().isEmpty(); } @Override public TypeMirror asType() { return factory.packageType(sym); } @Override public ElementKind getKind() { return ElementKind.PACKAGE; } @Override public Set getModifiers() { return ImmutableSet.of(); } @Override public Name getSimpleName() { return new TurbineName(sym.binaryName().substring(sym.binaryName().lastIndexOf('/') + 1)); } @Override public Element getEnclosingElement() { // a package is not enclosed by another element return null; } @Override public List getEnclosedElements() { ImmutableSet.Builder result = ImmutableSet.builder(); PackageScope scope = factory.tli().lookupPackage(Splitter.on('/').split(sym.binaryName())); requireNonNull(scope); // the current package exists for (ClassSymbol key : scope.classes()) { if (key.binaryName().contains("$") && factory.getSymbol(key).owner() != null) { // Skip member classes: only top-level classes are enclosed by the package. // The initial check for '$' is an optimization. continue; } if (key.simpleName().equals("package-info")) { continue; } result.add(factory.typeElement(key)); } return result.build().asList(); } @Override public R accept(ElementVisitor v, P p) { return v.visitPackage(this, p); } @Override public PackageSymbol sym() { return sym; } @Override public String javadoc() { return null; } @Override public int hashCode() { return sym.hashCode(); } @Override public boolean equals(@Nullable Object obj) { return obj instanceof TurbinePackageElement && sym.equals(((TurbinePackageElement) obj).sym); } private final Supplier> annos = memoize( new Supplier>() { @Override public ImmutableList get() { TypeBoundClass info = factory.getSymbol(new ClassSymbol(sym.binaryName() + "/package-info")); return info != null ? info.annotations() : ImmutableList.of(); } }); @Override protected ImmutableList annos() { return annos.get(); } @Override public String toString() { return sym.toString(); } } /** A {@link VariableElement} implementation backed by a {@link ParamSymbol}. */ static class TurbineParameterElement extends TurbineElement implements VariableElement { @Override public ParamSymbol sym() { return sym; } @Override public String javadoc() { return null; } @Override public int hashCode() { return sym.hashCode(); } @Override public boolean equals(@Nullable Object obj) { return obj instanceof TurbineParameterElement && sym.equals(((TurbineParameterElement) obj).sym); } private final ParamSymbol sym; private final Supplier info = memoize( new Supplier() { @Override public ParamInfo get() { return factory.getParamInfo(sym); } }); @Nullable ParamInfo info() { return info.get(); } public TurbineParameterElement(ModelFactory factory, ParamSymbol sym) { super(factory); this.sym = sym; } @Override public Object getConstantValue() { return null; } private final Supplier type = memoize( new Supplier() { @Override public TypeMirror get() { return factory.asTypeMirror(info().type()); } }); @Override public TypeMirror asType() { return type.get(); } @Override public ElementKind getKind() { return ElementKind.PARAMETER; } @Override public Set getModifiers() { return asModifierSet(ModifierOwner.PARAMETER, info().access()); } @Override public Name getSimpleName() { return new TurbineName(sym.name()); } @Override public Element getEnclosingElement() { return factory.executableElement(sym.owner()); } @Override public List getEnclosedElements() { return ImmutableList.of(); } @Override public R accept(ElementVisitor v, P p) { return v.visitVariable(this, p); } @Override public String toString() { return String.valueOf(sym.name()); } @Override protected ImmutableList annos() { return info().annotations(); } } static class TurbineNoTypeElement implements TypeElement { private final ModelFactory factory; private final String name; public TurbineNoTypeElement(ModelFactory factory, String name) { this.factory = factory; this.name = requireNonNull(name); } @Override public TypeMirror asType() { return factory.noType(); } @Override public ElementKind getKind() { return ElementKind.CLASS; } @Override public Set getModifiers() { return ImmutableSet.of(); } @Override public Name getSimpleName() { return new TurbineName(name.substring(name.lastIndexOf('.') + 1)); } @Override public TypeMirror getSuperclass() { return factory.noType(); } @Override public List getInterfaces() { return ImmutableList.of(); } @Override public List getTypeParameters() { return ImmutableList.of(); } @Override public Element getEnclosingElement() { int idx = name.lastIndexOf('.'); String packageName; if (idx == -1) { packageName = ""; } else { packageName = name.substring(0, idx).replace('.', '/'); } return factory.packageElement(new PackageSymbol(packageName)); } @Override public List getEnclosedElements() { return ImmutableList.of(); } @Override public NestingKind getNestingKind() { return NestingKind.TOP_LEVEL; } @Override public Name getQualifiedName() { return new TurbineName(name); } @Override public List getAnnotationMirrors() { return ImmutableList.of(); } @Override public A getAnnotation(Class aClass) { return null; } @Override public A[] getAnnotationsByType(Class aClass) { return null; } @Override public R accept(ElementVisitor elementVisitor, P p) { return elementVisitor.visitType(this, p); } @Override public String toString() { return getSimpleName().toString(); } } }