/* * Copyright 2015 ArcBees 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.gwtplatform.processors.tools.domain; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static com.google.auto.common.MoreElements.getPackage; import static com.google.auto.common.MoreTypes.asArray; import static com.google.auto.common.MoreTypes.asDeclared; import static com.google.auto.common.MoreTypes.asPrimitiveType; import static com.google.auto.common.MoreTypes.isType; import static com.google.common.collect.FluentIterable.from; public class Type implements HasImports, Comparable<Type> { private static final Function<TypeMirror, Type> TYPE_MIRROR_TO_TYPE = Type::new; private final String packageName; private final String simpleName; private final String enclosingNames; private final List<Type> typeArguments; private final boolean array; public Type(TypeElement element) { this(element.asType()); } // TODO: Ok, this class is BLOATED we need a factory and some polymorphism in there... public Type(TypeMirror type) { // void is not a primitive if (type.getKind() == TypeKind.VOID) { packageName = ""; enclosingNames = ""; simpleName = "void"; typeArguments = emptyList(); array = false; } else if (type.getKind().isPrimitive()) { packageName = ""; enclosingNames = ""; simpleName = asPrimitiveType(type).toString(); typeArguments = emptyList(); array = false; } else if (type.getKind() == TypeKind.ARRAY) { packageName = ""; enclosingNames = ""; simpleName = ""; typeArguments = singletonList(new Type(asArray(type).getComponentType())); array = true; } else if (isType(type)) { DeclaredType declaredType = asDeclared(type); Element element = declaredType.asElement(); packageName = getPackage(element).getQualifiedName().toString(); simpleName = element.getSimpleName().toString(); typeArguments = from(declaredType.getTypeArguments()) .transform(TYPE_MIRROR_TO_TYPE) .toList(); array = false; StringBuilder enclosingElementNames = new StringBuilder(); Element enclosingElement = element.getEnclosingElement(); while (enclosingElement != null && enclosingElement.getKind() != ElementKind.PACKAGE) { enclosingElementNames.insert(0, enclosingElement.getSimpleName() + "."); enclosingElement = enclosingElement.getEnclosingElement(); } if (enclosingElementNames.length() != 0) { enclosingElementNames.deleteCharAt(enclosingElementNames.length() - 1); } enclosingNames = enclosingElementNames.toString(); } else { throw new IllegalArgumentException("TypeMirror must be a primitive or declared type."); } } public Type(String parameterizedQualifiedName) { enclosingNames = ""; array = false; String qualifiedName = parameterizedQualifiedName; Builder<Type> argumentsBuilder = ImmutableList.builder(); int argumentsStart = parameterizedQualifiedName.indexOf("<"); if (argumentsStart != -1) { int argumentsEnd = parameterizedQualifiedName.lastIndexOf(">"); qualifiedName = parameterizedQualifiedName.substring(0, argumentsStart); String[] arguments = parameterizedQualifiedName.substring(argumentsStart + 1, argumentsEnd).split(","); for (String argument : arguments) { argumentsBuilder.add(new Type(argument.trim())); } } typeArguments = argumentsBuilder.build(); // primitives don't have packages int lastDot = qualifiedName.lastIndexOf('.'); if (lastDot != -1) { packageName = qualifiedName.substring(0, lastDot).trim(); simpleName = qualifiedName.substring(lastDot + 1).trim(); } else { packageName = ""; simpleName = qualifiedName.trim(); } } public Type(Class<?> clazz) { this(clazz.getPackage().getName(), clazz.getSimpleName()); } public Type(Class<?> clazz, List<Type> typeArguments) { this(clazz.getPackage().getName(), clazz.getSimpleName(), typeArguments); } public Type( String packageName, String simpleName) { this(packageName, "", simpleName); } public Type( String packageName, String enclosingNames, String simpleName) { this(packageName, enclosingNames, simpleName, new ArrayList<>()); } public Type( String packageName, String simpleName, List<Type> typeArguments) { this(packageName, "", simpleName, typeArguments); } public Type( String packageName, String enclosingNames, String simpleName, List<Type> typeArguments) { this.packageName = packageName; this.enclosingNames = enclosingNames; this.simpleName = simpleName; this.typeArguments = ImmutableList.copyOf(typeArguments); this.array = false; } public String getQualifiedName() { String qualifiedName = packageName; if (!qualifiedName.isEmpty()) { qualifiedName += "."; } if (!enclosingNames.isEmpty()) { qualifiedName += enclosingNames + "."; } return qualifiedName + simpleName; } public String getParameterizedName() { if (array) { return typeArguments.get(0).getParameterizedName() + "[]"; } else { return getSimpleName() + getSimpleTypeParameters(); } } public String getQualifiedParameterizedName() { if (array) { return typeArguments.get(0).getQualifiedParameterizedName() + "[]"; } else { return getQualifiedName() + getQualifiedTypeParameters(); } } public String getPackageName() { return packageName; } public String getEnclosingNames() { return enclosingNames; } public String getSimpleName() { return simpleName; } public boolean isParameterized() { return !typeArguments.isEmpty(); } public List<Type> getTypeArguments() { return typeArguments; } public String getQualifiedTypeParameters() { return formatTypeParameters(Type::getQualifiedParameterizedName); } public String getSimpleTypeParameters() { return formatTypeParameters(Type::getParameterizedName); } @Override public Collection<String> getImports() { FluentIterable<String> imports = from(typeArguments).transformAndConcat(EXTRACT_IMPORTS_FUNCTION); if (!array) { imports = imports.append(getQualifiedName()); } return imports.toSet(); } @Override public int compareTo(Type o) { return getQualifiedParameterizedName().compareTo(o == null ? "" : o.getQualifiedParameterizedName()); } @Override public String toString() { return getQualifiedParameterizedName(); } @Override public int hashCode() { return getQualifiedParameterizedName().hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || !(obj instanceof Type)) { return false; } Type other = (Type) obj; return getQualifiedParameterizedName().equals(other.getQualifiedParameterizedName()); } private String formatTypeParameters(Function<Type, String> function) { if (typeArguments.isEmpty()) { return ""; } String qualifiedTypeParameters = from(typeArguments) .transform(function) .join(Joiner.on(", ")); return "<" + qualifiedTypeParameters + ">"; } }