/* * Copyright 2011 Google Inc. * * 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.web.bindery.requestfactory.apt; import java.util.Set; import java.util.SortedSet; import java.util.Stack; import java.util.TreeSet; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVariable; import javax.lang.model.type.WildcardType; /** * Given a RequestFactory interface, return all RequestContext and proxy types * transitively referenced. */ class ReferredTypesCollector extends ExtraTypesScanner<Void> { /** * Finds TypeElements that we care about from TypeMirror API. This is used to * handle generic type signatures and supertypes. */ private class ElementFinder extends TypeVisitorBase<Void> { @Override public Void visitDeclared(DeclaredType x, State state) { // Some generic types don't have elements Element elt = state.types.asElement(x); if (elt != null) { ReferredTypesCollector.this.scan(elt, state); } // Capture List<FooProxy> for (TypeMirror a : x.getTypeArguments()) { a.accept(this, state); } return null; } @Override public Void visitExecutable(ExecutableType x, State state) { x.getReturnType().accept(this, state); for (TypeMirror p : x.getParameterTypes()) { p.accept(this, state); } for (TypeMirror t : x.getTypeVariables()) { t.accept(this, state); } return null; } @Override public Void visitTypeVariable(TypeVariable x, State state) { return state.types.erasure(x).accept(this, state); } @Override public Void visitWildcard(WildcardType x, State state) { return state.types.erasure(x).accept(this, state); } } /** * Collect all RequestContext and proxy types reachable from the given * RequestFactory. */ public static Set<TypeElement> collect(TypeElement requestFactory, State state) { ReferredTypesCollector c = new ReferredTypesCollector(state); c.scan(requestFactory, state); return c.seen; } private final Stack<TypeElement> currentType = new Stack<TypeElement>(); private final SortedSet<TypeElement> seen; private final State state; private ReferredTypesCollector(State state) { seen = new TreeSet<TypeElement>(new TypeComparator(state)); this.state = state; } @Override public Void visitExecutable(ExecutableElement x, State state) { if (shouldIgnore(x, state)) { return null; } ExecutableType xType = viewIn(currentType.peek(), x, state); xType.accept(new ElementFinder(), state); checkForAnnotation(x, state); return null; } @Override public Void visitType(TypeElement x, State state) { // Only look at proxies and contexts boolean isContext = state.types.isAssignable(x.asType(), state.requestContextType); boolean isFactory = state.types.isAssignable(x.asType(), state.requestFactoryType); boolean isProxy = state.types.isAssignable(x.asType(), state.baseProxyType); if (isProxy || isFactory || isContext) { currentType.push(x); try { // Ignore previously-seen types and factories if ((isContext || isProxy) && !seen.add(x)) { return null; } // Visit a proxy's supertypes if (isProxy) { x.getSuperclass().accept(new ElementFinder(), state); for (TypeMirror intf : x.getInterfaces()) { intf.accept(new ElementFinder(), state); } } // Visit methods scanAllInheritedMethods(x, state); // Scan for extra types checkForAnnotation(x, state); } finally { currentType.pop(); } } return null; } @Override protected void scanExtraType(TypeElement extraType) { scan(extraType, state); } }