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