1 /* 2 * Copyright (C) 2015 Square, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.squareup.javapoet; 17 18 import java.io.IOException; 19 import java.lang.reflect.GenericArrayType; 20 import java.lang.reflect.ParameterizedType; 21 import java.lang.reflect.Type; 22 import java.lang.reflect.TypeVariable; 23 import java.lang.reflect.WildcardType; 24 import java.util.ArrayList; 25 import java.util.Arrays; 26 import java.util.LinkedHashMap; 27 import java.util.List; 28 import java.util.Map; 29 import javax.lang.model.element.Modifier; 30 import javax.lang.model.element.TypeElement; 31 import javax.lang.model.element.TypeParameterElement; 32 import javax.lang.model.type.ArrayType; 33 import javax.lang.model.type.DeclaredType; 34 import javax.lang.model.type.ErrorType; 35 import javax.lang.model.type.NoType; 36 import javax.lang.model.type.PrimitiveType; 37 import javax.lang.model.type.TypeKind; 38 import javax.lang.model.type.TypeMirror; 39 import javax.lang.model.util.SimpleTypeVisitor8; 40 41 /** 42 * Any type in Java's type system, plus {@code void}. This class is an identifier for primitive 43 * types like {@code int} and raw reference types like {@code String} and {@code List}. It also 44 * identifies composite types like {@code char[]} and {@code Set<Long>}. 45 * 46 * <p>Type names are dumb identifiers only and do not model the values they name. For example, the 47 * type name for {@code java.util.List} doesn't know about the {@code size()} method, the fact that 48 * lists are collections, or even that it accepts a single type parameter. 49 * 50 * <p>Instances of this class are immutable value objects that implement {@code equals()} and {@code 51 * hashCode()} properly. 52 * 53 * <h3>Referencing existing types</h3> 54 * 55 * <p>Primitives and void are constants that you can reference directly: see {@link #INT}, {@link 56 * #DOUBLE}, and {@link #VOID}. 57 * 58 * <p>In an annotation processor you can get a type name instance for a type mirror by calling 59 * {@link #get(TypeMirror)}. In reflection code, you can use {@link #get(Type)}. 60 * 61 * <h3>Defining new types</h3> 62 * 63 * <p>Create new reference types like {@code com.example.HelloWorld} with {@link 64 * ClassName#get(String, String, String...)}. To build composite types like {@code char[]} and 65 * {@code Set<Long>}, use the factory methods on {@link ArrayTypeName}, {@link 66 * ParameterizedTypeName}, {@link TypeVariableName}, and {@link WildcardTypeName}. 67 */ 68 public class TypeName { 69 public static final TypeName VOID = new TypeName("void"); 70 public static final TypeName BOOLEAN = new TypeName("boolean"); 71 public static final TypeName BYTE = new TypeName("byte"); 72 public static final TypeName SHORT = new TypeName("short"); 73 public static final TypeName INT = new TypeName("int"); 74 public static final TypeName LONG = new TypeName("long"); 75 public static final TypeName CHAR = new TypeName("char"); 76 public static final TypeName FLOAT = new TypeName("float"); 77 public static final TypeName DOUBLE = new TypeName("double"); 78 public static final ClassName OBJECT = ClassName.get("java.lang", "Object"); 79 80 private static final ClassName BOXED_VOID = ClassName.get("java.lang", "Void"); 81 private static final ClassName BOXED_BOOLEAN = ClassName.get("java.lang", "Boolean"); 82 private static final ClassName BOXED_BYTE = ClassName.get("java.lang", "Byte"); 83 private static final ClassName BOXED_SHORT = ClassName.get("java.lang", "Short"); 84 private static final ClassName BOXED_INT = ClassName.get("java.lang", "Integer"); 85 private static final ClassName BOXED_LONG = ClassName.get("java.lang", "Long"); 86 private static final ClassName BOXED_CHAR = ClassName.get("java.lang", "Character"); 87 private static final ClassName BOXED_FLOAT = ClassName.get("java.lang", "Float"); 88 private static final ClassName BOXED_DOUBLE = ClassName.get("java.lang", "Double"); 89 90 /** The name of this type if it is a keyword, or null. */ 91 private final String keyword; 92 public final List<AnnotationSpec> annotations; 93 94 /** Lazily-initialized toString of this type name. */ 95 private String cachedString; 96 TypeName(String keyword)97 private TypeName(String keyword) { 98 this(keyword, new ArrayList<>()); 99 } 100 TypeName(String keyword, List<AnnotationSpec> annotations)101 private TypeName(String keyword, List<AnnotationSpec> annotations) { 102 this.keyword = keyword; 103 this.annotations = Util.immutableList(annotations); 104 } 105 106 // Package-private constructor to prevent third-party subclasses. TypeName(List<AnnotationSpec> annotations)107 TypeName(List<AnnotationSpec> annotations) { 108 this(null, annotations); 109 } 110 annotated(AnnotationSpec... annotations)111 public final TypeName annotated(AnnotationSpec... annotations) { 112 return annotated(Arrays.asList(annotations)); 113 } 114 annotated(List<AnnotationSpec> annotations)115 public TypeName annotated(List<AnnotationSpec> annotations) { 116 Util.checkNotNull(annotations, "annotations == null"); 117 return new TypeName(keyword, concatAnnotations(annotations)); 118 } 119 withoutAnnotations()120 public TypeName withoutAnnotations() { 121 return new TypeName(keyword); 122 } 123 concatAnnotations(List<AnnotationSpec> annotations)124 protected final List<AnnotationSpec> concatAnnotations(List<AnnotationSpec> annotations) { 125 List<AnnotationSpec> allAnnotations = new ArrayList<>(this.annotations); 126 allAnnotations.addAll(annotations); 127 return allAnnotations; 128 } 129 isAnnotated()130 public boolean isAnnotated() { 131 return !annotations.isEmpty(); 132 } 133 134 /** 135 * Returns true if this is a primitive type like {@code int}. Returns false for all other types 136 * types including boxed primitives and {@code void}. 137 */ isPrimitive()138 public boolean isPrimitive() { 139 return keyword != null && this != VOID; 140 } 141 142 /** 143 * Returns true if this is a boxed primitive type like {@code Integer}. Returns false for all 144 * other types types including unboxed primitives and {@code java.lang.Void}. 145 */ isBoxedPrimitive()146 public boolean isBoxedPrimitive() { 147 return this.equals(BOXED_BOOLEAN) 148 || this.equals(BOXED_BYTE) 149 || this.equals(BOXED_SHORT) 150 || this.equals(BOXED_INT) 151 || this.equals(BOXED_LONG) 152 || this.equals(BOXED_CHAR) 153 || this.equals(BOXED_FLOAT) 154 || this.equals(BOXED_DOUBLE); 155 } 156 157 /** 158 * Returns a boxed type if this is a primitive type (like {@code Integer} for {@code int}) or 159 * {@code void}. Returns this type if boxing doesn't apply. 160 */ box()161 public TypeName box() { 162 if (keyword == null) return this; // Doesn't need boxing. 163 if (this == VOID) return BOXED_VOID; 164 if (this == BOOLEAN) return BOXED_BOOLEAN; 165 if (this == BYTE) return BOXED_BYTE; 166 if (this == SHORT) return BOXED_SHORT; 167 if (this == INT) return BOXED_INT; 168 if (this == LONG) return BOXED_LONG; 169 if (this == CHAR) return BOXED_CHAR; 170 if (this == FLOAT) return BOXED_FLOAT; 171 if (this == DOUBLE) return BOXED_DOUBLE; 172 throw new AssertionError(keyword); 173 } 174 175 /** 176 * Returns an unboxed type if this is a boxed primitive type (like {@code int} for {@code 177 * Integer}) or {@code Void}. Returns this type if it is already unboxed. 178 * 179 * @throws UnsupportedOperationException if this type isn't eligible for unboxing. 180 */ unbox()181 public TypeName unbox() { 182 if (keyword != null) return this; // Already unboxed. 183 if (this.equals(BOXED_VOID)) return VOID; 184 if (this.equals(BOXED_BOOLEAN)) return BOOLEAN; 185 if (this.equals(BOXED_BYTE)) return BYTE; 186 if (this.equals(BOXED_SHORT)) return SHORT; 187 if (this.equals(BOXED_INT)) return INT; 188 if (this.equals(BOXED_LONG)) return LONG; 189 if (this.equals(BOXED_CHAR)) return CHAR; 190 if (this.equals(BOXED_FLOAT)) return FLOAT; 191 if (this.equals(BOXED_DOUBLE)) return DOUBLE; 192 throw new UnsupportedOperationException("cannot unbox " + this); 193 } 194 equals(Object o)195 @Override public final boolean equals(Object o) { 196 if (this == o) return true; 197 if (o == null) return false; 198 if (getClass() != o.getClass()) return false; 199 return toString().equals(o.toString()); 200 } 201 hashCode()202 @Override public final int hashCode() { 203 return toString().hashCode(); 204 } 205 toString()206 @Override public final String toString() { 207 String result = cachedString; 208 if (result == null) { 209 try { 210 StringBuilder resultBuilder = new StringBuilder(); 211 CodeWriter codeWriter = new CodeWriter(resultBuilder); 212 emit(codeWriter); 213 result = resultBuilder.toString(); 214 cachedString = result; 215 } catch (IOException e) { 216 throw new AssertionError(); 217 } 218 } 219 return result; 220 } 221 emit(CodeWriter out)222 CodeWriter emit(CodeWriter out) throws IOException { 223 if (keyword == null) throw new AssertionError(); 224 225 if (isAnnotated()) { 226 out.emit(""); 227 emitAnnotations(out); 228 } 229 return out.emitAndIndent(keyword); 230 } 231 emitAnnotations(CodeWriter out)232 CodeWriter emitAnnotations(CodeWriter out) throws IOException { 233 for (AnnotationSpec annotation : annotations) { 234 annotation.emit(out, true); 235 out.emit(" "); 236 } 237 return out; 238 } 239 240 241 /** Returns a type name equivalent to {@code mirror}. */ get(TypeMirror mirror)242 public static TypeName get(TypeMirror mirror) { 243 return get(mirror, new LinkedHashMap<>()); 244 } 245 get(TypeMirror mirror, final Map<TypeParameterElement, TypeVariableName> typeVariables)246 static TypeName get(TypeMirror mirror, 247 final Map<TypeParameterElement, TypeVariableName> typeVariables) { 248 return mirror.accept(new SimpleTypeVisitor8<TypeName, Void>() { 249 @Override public TypeName visitPrimitive(PrimitiveType t, Void p) { 250 switch (t.getKind()) { 251 case BOOLEAN: 252 return TypeName.BOOLEAN; 253 case BYTE: 254 return TypeName.BYTE; 255 case SHORT: 256 return TypeName.SHORT; 257 case INT: 258 return TypeName.INT; 259 case LONG: 260 return TypeName.LONG; 261 case CHAR: 262 return TypeName.CHAR; 263 case FLOAT: 264 return TypeName.FLOAT; 265 case DOUBLE: 266 return TypeName.DOUBLE; 267 default: 268 throw new AssertionError(); 269 } 270 } 271 272 @Override public TypeName visitDeclared(DeclaredType t, Void p) { 273 ClassName rawType = ClassName.get((TypeElement) t.asElement()); 274 TypeMirror enclosingType = t.getEnclosingType(); 275 TypeName enclosing = 276 (enclosingType.getKind() != TypeKind.NONE) 277 && !t.asElement().getModifiers().contains(Modifier.STATIC) 278 ? enclosingType.accept(this, null) 279 : null; 280 if (t.getTypeArguments().isEmpty() && !(enclosing instanceof ParameterizedTypeName)) { 281 return rawType; 282 } 283 284 List<TypeName> typeArgumentNames = new ArrayList<>(); 285 for (TypeMirror mirror : t.getTypeArguments()) { 286 typeArgumentNames.add(get(mirror, typeVariables)); 287 } 288 return enclosing instanceof ParameterizedTypeName 289 ? ((ParameterizedTypeName) enclosing).nestedClass( 290 rawType.simpleName(), typeArgumentNames) 291 : new ParameterizedTypeName(null, rawType, typeArgumentNames); 292 } 293 294 @Override public TypeName visitError(ErrorType t, Void p) { 295 return visitDeclared(t, p); 296 } 297 298 @Override public ArrayTypeName visitArray(ArrayType t, Void p) { 299 return ArrayTypeName.get(t, typeVariables); 300 } 301 302 @Override public TypeName visitTypeVariable(javax.lang.model.type.TypeVariable t, Void p) { 303 return TypeVariableName.get(t, typeVariables); 304 } 305 306 @Override public TypeName visitWildcard(javax.lang.model.type.WildcardType t, Void p) { 307 return WildcardTypeName.get(t, typeVariables); 308 } 309 310 @Override public TypeName visitNoType(NoType t, Void p) { 311 if (t.getKind() == TypeKind.VOID) return TypeName.VOID; 312 return super.visitUnknown(t, p); 313 } 314 315 @Override protected TypeName defaultAction(TypeMirror e, Void p) { 316 throw new IllegalArgumentException("Unexpected type mirror: " + e); 317 } 318 }, null); 319 } 320 321 /** Returns a type name equivalent to {@code type}. */ 322 public static TypeName get(Type type) { 323 return get(type, new LinkedHashMap<>()); 324 } 325 326 static TypeName get(Type type, Map<Type, TypeVariableName> map) { 327 if (type instanceof Class<?>) { 328 Class<?> classType = (Class<?>) type; 329 if (type == void.class) return VOID; 330 if (type == boolean.class) return BOOLEAN; 331 if (type == byte.class) return BYTE; 332 if (type == short.class) return SHORT; 333 if (type == int.class) return INT; 334 if (type == long.class) return LONG; 335 if (type == char.class) return CHAR; 336 if (type == float.class) return FLOAT; 337 if (type == double.class) return DOUBLE; 338 if (classType.isArray()) return ArrayTypeName.of(get(classType.getComponentType(), map)); 339 return ClassName.get(classType); 340 341 } else if (type instanceof ParameterizedType) { 342 return ParameterizedTypeName.get((ParameterizedType) type, map); 343 344 } else if (type instanceof WildcardType) { 345 return WildcardTypeName.get((WildcardType) type, map); 346 347 } else if (type instanceof TypeVariable<?>) { 348 return TypeVariableName.get((TypeVariable<?>) type, map); 349 350 } else if (type instanceof GenericArrayType) { 351 return ArrayTypeName.get((GenericArrayType) type, map); 352 353 } else { 354 throw new IllegalArgumentException("unexpected type: " + type); 355 } 356 } 357 358 /** Converts an array of types to a list of type names. */ 359 static List<TypeName> list(Type[] types) { 360 return list(types, new LinkedHashMap<>()); 361 } 362 363 static List<TypeName> list(Type[] types, Map<Type, TypeVariableName> map) { 364 List<TypeName> result = new ArrayList<>(types.length); 365 for (Type type : types) { 366 result.add(get(type, map)); 367 } 368 return result; 369 } 370 371 /** Returns the array component of {@code type}, or null if {@code type} is not an array. */ 372 static TypeName arrayComponent(TypeName type) { 373 return type instanceof ArrayTypeName 374 ? ((ArrayTypeName) type).componentType 375 : null; 376 } 377 378 /** Returns {@code type} as an array, or null if {@code type} is not an array. */ 379 static ArrayTypeName asArray(TypeName type) { 380 return type instanceof ArrayTypeName 381 ? ((ArrayTypeName) type) 382 : null; 383 } 384 385 } 386