/* * Copyright (C) 2012 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 fr.lteconsulting.hexa.databinding.annotation.processor; import java.util.List; import javax.lang.model.element.Element; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.WildcardType; import javax.lang.model.util.SimpleTypeVisitor6; import javax.lang.model.util.Types; /** * A class from the Google Auto project on Github * Special thanks to the authors ! */ final public class TypeSimplifier { private final Types typeUtils; public TypeSimplifier( Types typeUtils ) { this.typeUtils = typeUtils; } String simplify( TypeMirror type ) { return type.accept( TO_STRING_TYPE_VISITOR, new StringBuilder() ).toString(); } // From the Google Auto project, thanks ! // The formal type parameters of the given type. // It will return the angle-bracket part of: // Foo<SomeClass> // Foo<T extends SomeClass> // Foo<T extends Number> // Foo<E extends Enum<E>> // Foo<K, V extends Comparable<? extends K>> // Type variables need special treatment because we only want to include // their bounds when they // are declared, not when they are referenced. We don't want to include the // bounds of the second E // in <E extends Enum<E>> or of the second K in <K, V extends Comparable<? // extends K>>. That's // why we put the "extends" handling here and not in ToStringTypeVisitor. public String formalTypeParametersString( TypeElement type ) { List<? extends TypeParameterElement> typeParameters = type.getTypeParameters(); if( typeParameters.isEmpty() ) { return ""; } else { StringBuilder sb = new StringBuilder( "<" ); String sep = ""; for( TypeParameterElement typeParameter : typeParameters ) { sb.append( sep ); sep = ", "; appendTypeParameterWithBounds( sb, typeParameter ); } return sb.append( ">" ).toString(); } } // From the Google Auto project, thanks ! // The actual type parameters of the given type. // If we have a class Foo<T extends Something> extends Foo<T>. // <T extends Something> is the formal type parameter list and // <T> is the actual type parameter list, which is what this method returns. static public String actualTypeParametersString( TypeElement type ) { List<? extends TypeParameterElement> typeParameters = type.getTypeParameters(); if( typeParameters.isEmpty() ) { return ""; } else { StringBuilder sb = new StringBuilder(); sb.append( "<" ); String sep = ""; for( TypeParameterElement typeP : typeParameters ) { sb.append( sep ); sep = ", "; sb.append( typeP.getSimpleName() ); } sb.append( ">" ); return sb.toString(); } } private void appendTypeParameterWithBounds( StringBuilder sb, TypeParameterElement typeParameter ) { sb.append( typeParameter.getSimpleName() ); String sep = " extends "; for( TypeMirror bound : typeParameter.getBounds() ) { if( !bound.toString().equals( "java.lang.Object" ) ) { sb.append( sep ); sep = " & "; bound.accept( TO_STRING_TYPE_VISITOR, sb ); } } } private final ToStringTypeVisitor TO_STRING_TYPE_VISITOR = new ToStringTypeVisitor(); /** * Visitor that produces a string representation of a type for use in * generated code. The visitor takes into account the imports defined by * {@link #typesToImport} and will use the short names of those types. * * <p> * A simpler alternative would be just to use TypeMirror.toString() and * regular expressions to pick apart the type references and replace * fully-qualified types where possible. That depends on unspecified * behaviour of TypeMirror.toString(), though, and is vulnerable to * formatting quirks such as the way it omits the space after the comma in * {@code java.util.Map<java.lang.String, java.lang.String>}. */ private class ToStringTypeVisitor extends SimpleTypeVisitor6<StringBuilder, StringBuilder> { @Override protected StringBuilder defaultAction( TypeMirror type, StringBuilder sb ) { return sb.append( type ); } @Override public StringBuilder visitArray( ArrayType type, StringBuilder sb ) { return visit( type.getComponentType(), sb ).append( "[]" ); } @Override public StringBuilder visitDeclared( DeclaredType type, StringBuilder sb ) { TypeElement typeElement = (TypeElement) typeUtils.asElement( type ); String typeString = typeElement.getQualifiedName().toString(); sb.append( typeString ); appendTypeArguments( type, sb ); return sb; } void appendTypeArguments( DeclaredType type, StringBuilder sb ) { List<? extends TypeMirror> arguments = type.getTypeArguments(); if( !arguments.isEmpty() ) { sb.append( "<" ); String sep = ""; for( TypeMirror argument : arguments ) { sb.append( sep ); sep = ", "; visit( argument, sb ); } sb.append( ">" ); } } @Override public StringBuilder visitWildcard( WildcardType type, StringBuilder sb ) { sb.append( "?" ); TypeMirror extendsBound = type.getExtendsBound(); TypeMirror superBound = type.getSuperBound(); if( superBound != null ) { sb.append( " super " ); visit( superBound, sb ); } else if( extendsBound != null ) { sb.append( " extends " ); visit( extendsBound, sb ); } return sb; } } /** * Returns the name of the given type, including any enclosing types but not * the package. */ static String classNameOf( TypeElement type ) { String name = type.getQualifiedName().toString(); String pkgName = packageNameOf( type ); return pkgName.isEmpty() ? name : name.substring( pkgName.length() + 1 ); } /** * Returns the name of the package that the given type is in. If the type is * in the default (unnamed) package then the name is the empty string. */ static String packageNameOf( TypeElement type ) { while( true ) { Element enclosing = type.getEnclosingElement(); if( enclosing instanceof PackageElement ) { return ((PackageElement) enclosing).getQualifiedName().toString(); } type = (TypeElement) enclosing; } } static String simpleNameOf( String s ) { if( s.contains( "." ) ) { return s.substring( s.lastIndexOf( '.' ) + 1 ); } else { return s; } } /** * Returns the qualified name of a TypeMirror. */ public static String getTypeQualifiedName(TypeMirror type) throws CodeGenerationIncompleteException { if(type.toString().equals("<any>")) { throw new CodeGenerationIncompleteException("Type reported as <any> is likely a not-yet " + "generated parameterized type."); } switch( type.getKind() ) { case ARRAY: return getTypeQualifiedName( ((ArrayType) type).getComponentType() ) + "[]"; case BOOLEAN: return "boolean"; case BYTE: return "byte"; case CHAR: return "char"; case DOUBLE: return "double"; case FLOAT: return "float"; case INT: return "int"; case LONG: return "long"; case SHORT: return "short"; case DECLARED: StringBuilder b = new StringBuilder(); b.append( ((TypeElement) ((DeclaredType) type).asElement()).getQualifiedName().toString() ); if( !((DeclaredType) type).getTypeArguments().isEmpty() ) { b.append( "<" ); boolean addComa = false; for( TypeMirror pType : ((DeclaredType) type).getTypeArguments() ) { if( addComa ) b.append( ", " ); else addComa = true; b.append( getTypeQualifiedName( pType ) ); } b.append( ">" ); } return b.toString(); default: return type.toString(); } } }