blob: dd1f28cf366773708103d5e7dd505254d539d618 [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 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);
}
}