| /* |
| * 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.Collection; |
| import java.util.List; |
| import java.util.Set; |
| |
| import javax.lang.model.element.ElementKind; |
| import javax.lang.model.element.TypeElement; |
| import javax.lang.model.type.DeclaredType; |
| import javax.lang.model.type.NoType; |
| import javax.lang.model.type.PrimitiveType; |
| import javax.lang.model.type.TypeKind; |
| import javax.lang.model.type.TypeMirror; |
| import javax.lang.model.type.TypeVariable; |
| import javax.lang.model.type.WildcardType; |
| |
| /** |
| * Uses information in a State object to convert client types to their domain |
| * equivalents. This types assumes that any incoming type has already been |
| * determined to be a transportable type. |
| */ |
| class ClientToDomainMapper extends TypeVisitorBase<TypeMirror> { |
| public static class UnmappedTypeException extends RuntimeException { |
| private final TypeMirror clientType; |
| |
| public UnmappedTypeException() { |
| super(); |
| clientType = null; |
| } |
| |
| public UnmappedTypeException(TypeMirror clientType) { |
| super("No domain type resolved for " + clientType.toString()); |
| this.clientType = clientType; |
| } |
| |
| public TypeMirror getClientType() { |
| return clientType; |
| } |
| } |
| |
| @Override |
| public TypeMirror visitDeclared(DeclaredType x, State state) { |
| if (x.asElement().getKind().equals(ElementKind.ENUM)) { |
| // Enums map to enums |
| return x; |
| } |
| if (state.types.isAssignable(x, state.entityProxyType) |
| || state.types.isAssignable(x, state.valueProxyType)) { |
| // FooProxy -> FooDomain |
| /* |
| * TODO(bobv): This if statement should be widened to baseProxy to support |
| * heterogenous collections of any proxy type. The BaseProxy interface |
| * would also need to be annotated with an @ProxyFor mapping. This can be |
| * done once RFIV is removed, since it only allows homogenous collections. |
| */ |
| TypeElement domainType = |
| (TypeElement) state.getClientToDomainMap().get(state.types.asElement(x)); |
| if (domainType == null) { |
| return defaultAction(x, state); |
| } |
| return domainType.asType(); |
| } |
| if (state.types.isAssignable(x, state.entityProxyIdType)) { |
| // EntityProxyId<FooProxy> -> FooDomain |
| return convertSingleParamType(x, state.entityProxyIdType, 0, state); |
| } |
| if (state.types.isAssignable(x, state.requestType)) { |
| // Request<FooProxy> -> FooDomain |
| return convertSingleParamType(x, state.requestType, 0, state); |
| } |
| if (state.types.isAssignable(x, state.instanceRequestType)) { |
| // InstanceRequest<FooProxy, X> -> FooDomain |
| return convertSingleParamType(x, state.instanceRequestType, 1, state); |
| } |
| for (DeclaredType valueType : getValueTypes(state)) { |
| if (state.types.isAssignable(x, valueType)) { |
| // Value types map straight through |
| return x; |
| } |
| } |
| if (state.types.isAssignable(x, state.findType(List.class)) |
| || state.types.isAssignable(x, state.findType(Set.class))) { |
| // Convert Set,List<FooProxy> to Set,List<FooDomain> |
| TypeMirror param = convertSingleParamType(x, state.findType(Collection.class), 0, state); |
| return state.types.getDeclaredType((TypeElement) state.types.asElement(x), param); |
| } |
| return defaultAction(x, state); |
| } |
| |
| @Override |
| public TypeMirror visitNoType(NoType x, State state) { |
| if (x.getKind().equals(TypeKind.VOID)) { |
| // Pass void through |
| return x; |
| } |
| // Here, x would be NONE or PACKAGE, neither of which make sense |
| return defaultAction(x, state); |
| } |
| |
| @Override |
| public TypeMirror visitPrimitive(PrimitiveType x, State state) { |
| // Primitives pass through |
| return x; |
| } |
| |
| @Override |
| public TypeMirror visitTypeVariable(TypeVariable x, State state) { |
| // Convert <T extends FooProxy> to FooDomain |
| return x.getUpperBound().accept(this, state); |
| } |
| |
| @Override |
| public TypeMirror visitWildcard(WildcardType x, State state) { |
| // Convert <? extends FooProxy> to FooDomain |
| return state.types.erasure(x).accept(this, state); |
| } |
| |
| /** |
| * Utility method to convert a {@code Foo<BarProxy> -> BarDomain}. The |
| * {@code param} parameter specifies the index of the type paramater to |
| * extract. |
| */ |
| protected TypeMirror convertSingleParamType(DeclaredType x, DeclaredType convertTo, int param, |
| State state) { |
| DeclaredType converted = (DeclaredType) State.viewAs(convertTo, x, state); |
| if (converted == null) { |
| return state.types.getNoType(TypeKind.NONE); |
| } |
| if (converted.getTypeArguments().isEmpty()) { |
| return defaultAction(x, state); |
| } |
| return converted.getTypeArguments().get(param).accept(this, state); |
| } |
| |
| @Override |
| protected TypeMirror defaultAction(TypeMirror x, State state) { |
| throw new UnmappedTypeException(x); |
| } |
| } |