blob: a8eb5e1cc938a9fd4a9145693477e9eda66f293a [file] [log] [blame]
/*
* 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);
}
}