| /* | |
| * 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); | |
| } | |
| } |