blob: 614b7c78e8ef89f617673190b37b29e3e97e8b0a [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.JsonRpcService;
import com.google.web.bindery.requestfactory.shared.Service;
import com.google.web.bindery.requestfactory.shared.ServiceName;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.MirroredTypeException;
import javax.lang.model.type.TypeMirror;
/**
* Scans a RequestContext declaration. This visitor will call out to the State
* object to validate the types that it encounters.
*/
class RequestContextScanner extends ScannerBase<Void> {
private TypeElement checkedElement;
@Override
public Void visitExecutable(ExecutableElement x, State state) {
if (shouldIgnore(x, state)) {
return null;
}
// resolve type parameters, if any
ExecutableType xType = viewIn(checkedElement, x, state);
TypeMirror returnType = xType.getReturnType();
if (state.types.isAssignable(returnType, state.requestType)) {
// Extract Request<Foo> type
DeclaredType asRequest = (DeclaredType) State.viewAs(state.requestType, returnType, state);
if (asRequest.getTypeArguments().isEmpty()) {
state.poison(x, Messages.rawType());
} else {
TypeMirror requestReturn = asRequest.getTypeArguments().get(0);
if (!state.isTransportableType(requestReturn)) {
state.poison(x, Messages.untransportableType(requestReturn));
}
}
} else if (state.types.isAssignable(returnType, state.instanceRequestType)) {
// Extract InstanceRequest<FooProxy, String>
DeclaredType asInstanceRequest =
(DeclaredType) State.viewAs(state.instanceRequestType, returnType, state);
if (asInstanceRequest.getTypeArguments().isEmpty()) {
state.poison(x, Messages.rawType());
} else {
TypeMirror instanceType = asInstanceRequest.getTypeArguments().get(0);
state.maybeScanProxy((TypeElement) state.types.asElement(instanceType));
TypeMirror requestReturn = asInstanceRequest.getTypeArguments().get(1);
if (!state.isTransportableType(requestReturn)) {
state.poison(x, Messages.untransportableType(requestReturn));
}
}
} else if (!isSetter(x, state)) {
state.poison(x, Messages.contextRequiredReturnTypes(state.requestType.asElement()
.getSimpleName(), state.instanceRequestType.asElement().getSimpleName()));
}
// 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 in ProxyScanner#visitExecutable
state.poison(x.getParameters().get(i), Messages.untransportableType(parameterType));
}
i++;
}
return null;
}
@Override
public Void visitType(TypeElement x, State state) {
checkedElement = x;
Service service = x.getAnnotation(Service.class);
ServiceName serviceName = x.getAnnotation(ServiceName.class);
JsonRpcService jsonRpcService = x.getAnnotation(JsonRpcService.class);
if (service != null) {
poisonIfAnnotationPresent(state, x, serviceName, jsonRpcService);
// See javadoc on Element.getAnnotation() for why it works this way
try {
service.value();
throw new RuntimeException("Should not reach here");
} catch (MirroredTypeException expected) {
TypeMirror type = expected.getTypeMirror();
state.addMapping(x, (TypeElement) state.types.asElement(type));
}
}
if (serviceName != null) {
poisonIfAnnotationPresent(state, x, jsonRpcService);
TypeElement domain =
state.elements.getTypeElement(BinaryName.toSourceName(serviceName.value()));
if (domain == null) {
state.warn(x, Messages.contextMissingDomainType(serviceName.value()));
}
state.addMapping(x, domain);
}
scanAllInheritedMethods(x, state);
state.checkExtraTypes(x);
return null;
}
@Override
public Void visitTypeParameter(TypeParameterElement x, State state) {
for (TypeMirror bound : x.getBounds()) {
if (!state.isTransportableType(bound)) {
state.poison(x, Messages.untransportableType(bound));
}
}
return super.visitTypeParameter(x, state);
}
}