blob: 5374d3cede6faee7277c5eb1d0c00311d3e210d4 [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 com.google.gwt.dev.util.Name.BinaryName;
import com.google.web.bindery.requestfactory.shared.JsonRpcProxy;
import com.google.web.bindery.requestfactory.shared.ProxyFor;
import com.google.web.bindery.requestfactory.shared.ProxyForName;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.MirroredTypeException;
import javax.lang.model.type.TypeMirror;
/**
* Examines the methods declared in a proxy interface. Also records the client
* to domain mapping for the proxy type.
*/
class ProxyScanner extends ScannerBase<Void> {
private TypeElement checkedElement;
@Override
public Void visitExecutable(ExecutableElement x, State state) {
if (shouldIgnore(x, state)) {
return null;
}
ExecutableType xType = viewIn(checkedElement, x, state);
if (isGetter(x, state)) {
TypeMirror returnType = xType.getReturnType();
if (!state.isTransportableType(returnType)) {
// XXX(t.broyer): should we really pass the "resolved" type? that could
// result in several errors being reported on the same method, but on
// the other hand tells exactly which type it is that isn't
// transportable.
// For instance, a List<T> might be transportable if T is
// java.lang.String in a sub-interface, but not if T is some
// untransportable type in another sub-interface
state.poison(x, Messages.untransportableType(returnType));
}
} else if (!isSetter(x, state)) {
state.poison(x, Messages.proxyOnlyGettersSetters());
}
// check parameters (we do not defer to visitVariable, as we need the
// resolved generics)
int i = 0;
for (TypeMirror parameterType : xType.getParameterTypes()) {
if (!state.isTransportableType(parameterType)) {
// see comments above about the returnType
state.poison(x.getParameters().get(i), Messages.untransportableType(parameterType));
}
i++;
}
return null;
}
@Override
public Void visitType(TypeElement x, State state) {
checkedElement = x;
ProxyFor proxyFor = x.getAnnotation(ProxyFor.class);
ProxyForName proxyForName = x.getAnnotation(ProxyForName.class);
JsonRpcProxy jsonRpcProxy = x.getAnnotation(JsonRpcProxy.class);
if (proxyFor != null) {
poisonIfAnnotationPresent(state, x, proxyForName, jsonRpcProxy);
// See javadoc on Element.getAnnotation() for why it works this way
try {
proxyFor.value();
throw new RuntimeException("Should not reach here");
} catch (MirroredTypeException expected) {
TypeMirror type = expected.getTypeMirror();
state.addMapping(x, (TypeElement) state.types.asElement(type));
}
}
if (proxyForName != null) {
poisonIfAnnotationPresent(state, x, jsonRpcProxy);
TypeElement domain =
state.elements.getTypeElement(BinaryName.toSourceName(proxyForName.value()));
if (domain == null) {
state.warn(x, Messages.proxyMissingDomainType(proxyForName.value()));
}
state.addMapping(x, domain);
}
scanAllInheritedMethods(x, state);
state.checkExtraTypes(x);
return null;
}
@Override
public Void visitVariable(VariableElement x, State state) {
if (!state.isTransportableType(x.asType())) {
state.poison(x, Messages.untransportableType(x.asType()));
}
return super.visitVariable(x, state);
}
@Override
protected boolean shouldIgnore(ExecutableElement x, State state) {
// Ignore overrides of stableId()
if (x.getSimpleName().contentEquals("stableId") && x.getParameters().isEmpty()) {
return true;
}
return super.shouldIgnore(x, state);
}
}