All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.javapoet.TypeName Maven / Gradle / Ivy

There is a newer version: 6.1.6
Show newest version
/*
 * Copyright (C) 2015 Square, 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 org.springframework.javapoet;

import java.io.IOException;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.lang.model.element.Modifier;
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.ErrorType;
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.util.SimpleTypeVisitor8;

/**
 * Any type in Java's type system, plus {@code void}. This class is an identifier for primitive
 * types like {@code int} and raw reference types like {@code String} and {@code List}. It also
 * identifies composite types like {@code char[]} and {@code Set}.
 *
 * 

Type names are dumb identifiers only and do not model the values they name. For example, the * type name for {@code java.util.List} doesn't know about the {@code size()} method, the fact that * lists are collections, or even that it accepts a single type parameter. * *

Instances of this class are immutable value objects that implement {@code equals()} and {@code * hashCode()} properly. * *

Referencing existing types

* *

Primitives and void are constants that you can reference directly: see {@link #INT}, {@link * #DOUBLE}, and {@link #VOID}. * *

In an annotation processor you can get a type name instance for a type mirror by calling * {@link #get(TypeMirror)}. In reflection code, you can use {@link #get(Type)}. * *

Defining new types

* *

Create new reference types like {@code com.example.HelloWorld} with {@link * ClassName#get(String, String, String...)}. To build composite types like {@code char[]} and * {@code Set}, use the factory methods on {@link ArrayTypeName}, {@link * ParameterizedTypeName}, {@link TypeVariableName}, and {@link WildcardTypeName}. */ public class TypeName { public static final TypeName VOID = new TypeName("void"); public static final TypeName BOOLEAN = new TypeName("boolean"); public static final TypeName BYTE = new TypeName("byte"); public static final TypeName SHORT = new TypeName("short"); public static final TypeName INT = new TypeName("int"); public static final TypeName LONG = new TypeName("long"); public static final TypeName CHAR = new TypeName("char"); public static final TypeName FLOAT = new TypeName("float"); public static final TypeName DOUBLE = new TypeName("double"); public static final ClassName OBJECT = ClassName.get("java.lang", "Object"); private static final ClassName BOXED_VOID = ClassName.get("java.lang", "Void"); private static final ClassName BOXED_BOOLEAN = ClassName.get("java.lang", "Boolean"); private static final ClassName BOXED_BYTE = ClassName.get("java.lang", "Byte"); private static final ClassName BOXED_SHORT = ClassName.get("java.lang", "Short"); private static final ClassName BOXED_INT = ClassName.get("java.lang", "Integer"); private static final ClassName BOXED_LONG = ClassName.get("java.lang", "Long"); private static final ClassName BOXED_CHAR = ClassName.get("java.lang", "Character"); private static final ClassName BOXED_FLOAT = ClassName.get("java.lang", "Float"); private static final ClassName BOXED_DOUBLE = ClassName.get("java.lang", "Double"); /** The name of this type if it is a keyword, or null. */ private final String keyword; public final List annotations; /** Lazily-initialized toString of this type name. */ private String cachedString; private TypeName(String keyword) { this(keyword, new ArrayList<>()); } private TypeName(String keyword, List annotations) { this.keyword = keyword; this.annotations = Util.immutableList(annotations); } // Package-private constructor to prevent third-party subclasses. TypeName(List annotations) { this(null, annotations); } public final TypeName annotated(AnnotationSpec... annotations) { return annotated(Arrays.asList(annotations)); } public TypeName annotated(List annotations) { Util.checkNotNull(annotations, "annotations == null"); return new TypeName(keyword, concatAnnotations(annotations)); } public TypeName withoutAnnotations() { return new TypeName(keyword); } protected final List concatAnnotations(List annotations) { List allAnnotations = new ArrayList<>(this.annotations); allAnnotations.addAll(annotations); return allAnnotations; } public boolean isAnnotated() { return !annotations.isEmpty(); } /** * Returns true if this is a primitive type like {@code int}. Returns false for all other types * types including boxed primitives and {@code void}. */ public boolean isPrimitive() { return keyword != null && this != VOID; } /** * Returns true if this is a boxed primitive type like {@code Integer}. Returns false for all * other types types including unboxed primitives and {@code java.lang.Void}. */ public boolean isBoxedPrimitive() { return this.equals(BOXED_BOOLEAN) || this.equals(BOXED_BYTE) || this.equals(BOXED_SHORT) || this.equals(BOXED_INT) || this.equals(BOXED_LONG) || this.equals(BOXED_CHAR) || this.equals(BOXED_FLOAT) || this.equals(BOXED_DOUBLE); } /** * Returns a boxed type if this is a primitive type (like {@code Integer} for {@code int}) or * {@code void}. Returns this type if boxing doesn't apply. */ public TypeName box() { if (keyword == null) return this; // Doesn't need boxing. if (this == VOID) return BOXED_VOID; if (this == BOOLEAN) return BOXED_BOOLEAN; if (this == BYTE) return BOXED_BYTE; if (this == SHORT) return BOXED_SHORT; if (this == INT) return BOXED_INT; if (this == LONG) return BOXED_LONG; if (this == CHAR) return BOXED_CHAR; if (this == FLOAT) return BOXED_FLOAT; if (this == DOUBLE) return BOXED_DOUBLE; throw new AssertionError(keyword); } /** * Returns an unboxed type if this is a boxed primitive type (like {@code int} for {@code * Integer}) or {@code Void}. Returns this type if it is already unboxed. * * @throws UnsupportedOperationException if this type isn't eligible for unboxing. */ public TypeName unbox() { if (keyword != null) return this; // Already unboxed. if (this.equals(BOXED_VOID)) return VOID; if (this.equals(BOXED_BOOLEAN)) return BOOLEAN; if (this.equals(BOXED_BYTE)) return BYTE; if (this.equals(BOXED_SHORT)) return SHORT; if (this.equals(BOXED_INT)) return INT; if (this.equals(BOXED_LONG)) return LONG; if (this.equals(BOXED_CHAR)) return CHAR; if (this.equals(BOXED_FLOAT)) return FLOAT; if (this.equals(BOXED_DOUBLE)) return DOUBLE; throw new UnsupportedOperationException("cannot unbox " + this); } @Override public final boolean equals(Object o) { if (this == o) return true; if (o == null) return false; if (getClass() != o.getClass()) return false; return toString().equals(o.toString()); } @Override public final int hashCode() { return toString().hashCode(); } @Override public final String toString() { String result = cachedString; if (result == null) { try { StringBuilder resultBuilder = new StringBuilder(); CodeWriter codeWriter = new CodeWriter(resultBuilder); emit(codeWriter); result = resultBuilder.toString(); cachedString = result; } catch (IOException e) { throw new AssertionError(); } } return result; } CodeWriter emit(CodeWriter out) throws IOException { if (keyword == null) throw new AssertionError(); if (isAnnotated()) { out.emit(""); emitAnnotations(out); } return out.emitAndIndent(keyword); } CodeWriter emitAnnotations(CodeWriter out) throws IOException { for (AnnotationSpec annotation : annotations) { annotation.emit(out, true); out.emit(" "); } return out; } /** Returns a type name equivalent to {@code mirror}. */ public static TypeName get(TypeMirror mirror) { return get(mirror, new LinkedHashMap<>()); } static TypeName get(TypeMirror mirror, final Map typeVariables) { return mirror.accept(new SimpleTypeVisitor8() { @Override public TypeName visitPrimitive(PrimitiveType t, Void p) { switch (t.getKind()) { case BOOLEAN: return TypeName.BOOLEAN; case BYTE: return TypeName.BYTE; case SHORT: return TypeName.SHORT; case INT: return TypeName.INT; case LONG: return TypeName.LONG; case CHAR: return TypeName.CHAR; case FLOAT: return TypeName.FLOAT; case DOUBLE: return TypeName.DOUBLE; default: throw new AssertionError(); } } @Override public TypeName visitDeclared(DeclaredType t, Void p) { ClassName rawType = ClassName.get((TypeElement) t.asElement()); TypeMirror enclosingType = t.getEnclosingType(); TypeName enclosing = (enclosingType.getKind() != TypeKind.NONE) && !t.asElement().getModifiers().contains(Modifier.STATIC) ? enclosingType.accept(this, null) : null; if (t.getTypeArguments().isEmpty() && !(enclosing instanceof ParameterizedTypeName)) { return rawType; } List typeArgumentNames = new ArrayList<>(); for (TypeMirror mirror : t.getTypeArguments()) { typeArgumentNames.add(get(mirror, typeVariables)); } return enclosing instanceof ParameterizedTypeName ? ((ParameterizedTypeName) enclosing).nestedClass( rawType.simpleName(), typeArgumentNames) : new ParameterizedTypeName(null, rawType, typeArgumentNames); } @Override public TypeName visitError(ErrorType t, Void p) { return visitDeclared(t, p); } @Override public ArrayTypeName visitArray(ArrayType t, Void p) { return ArrayTypeName.get(t, typeVariables); } @Override public TypeName visitTypeVariable(javax.lang.model.type.TypeVariable t, Void p) { return TypeVariableName.get(t, typeVariables); } @Override public TypeName visitWildcard(javax.lang.model.type.WildcardType t, Void p) { return WildcardTypeName.get(t, typeVariables); } @Override public TypeName visitNoType(NoType t, Void p) { if (t.getKind() == TypeKind.VOID) return TypeName.VOID; return super.visitUnknown(t, p); } @Override protected TypeName defaultAction(TypeMirror e, Void p) { throw new IllegalArgumentException("Unexpected type mirror: " + e); } }, null); } /** Returns a type name equivalent to {@code type}. */ public static TypeName get(Type type) { return get(type, new LinkedHashMap<>()); } static TypeName get(Type type, Map map) { if (type instanceof Class) { Class classType = (Class) type; if (type == void.class) return VOID; if (type == boolean.class) return BOOLEAN; if (type == byte.class) return BYTE; if (type == short.class) return SHORT; if (type == int.class) return INT; if (type == long.class) return LONG; if (type == char.class) return CHAR; if (type == float.class) return FLOAT; if (type == double.class) return DOUBLE; if (classType.isArray()) return ArrayTypeName.of(get(classType.getComponentType(), map)); return ClassName.get(classType); } else if (type instanceof ParameterizedType) { return ParameterizedTypeName.get((ParameterizedType) type, map); } else if (type instanceof WildcardType) { return WildcardTypeName.get((WildcardType) type, map); } else if (type instanceof TypeVariable) { return TypeVariableName.get((TypeVariable) type, map); } else if (type instanceof GenericArrayType) { return ArrayTypeName.get((GenericArrayType) type, map); } else { throw new IllegalArgumentException("unexpected type: " + type); } } /** Converts an array of types to a list of type names. */ static List list(Type[] types) { return list(types, new LinkedHashMap<>()); } static List list(Type[] types, Map map) { List result = new ArrayList<>(types.length); for (Type type : types) { result.add(get(type, map)); } return result; } /** Returns the array component of {@code type}, or null if {@code type} is not an array. */ static TypeName arrayComponent(TypeName type) { return type instanceof ArrayTypeName ? ((ArrayTypeName) type).componentType : null; } /** Returns {@code type} as an array, or null if {@code type} is not an array. */ static ArrayTypeName asArray(TypeName type) { return type instanceof ArrayTypeName ? ((ArrayTypeName) type) : null; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy