1 /*
2  * Copyright (C) 2014 Google, 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 static com.google.common.truth.Truth.assertThat;
19 import static com.google.testing.compile.CompilationSubject.assertThat;
20 import static com.google.testing.compile.Compiler.javac;
21 import static javax.lang.model.util.ElementFilter.fieldsIn;
22 import static org.junit.Assert.*;
23 
24 import com.google.testing.compile.Compilation;
25 import com.google.testing.compile.JavaFileObjects;
26 import java.io.Serializable;
27 import java.lang.annotation.Annotation;
28 import java.nio.charset.Charset;
29 import java.util.Collections;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Set;
33 import javax.annotation.processing.AbstractProcessor;
34 import javax.annotation.processing.RoundEnvironment;
35 import javax.lang.model.element.AnnotationMirror;
36 import javax.lang.model.element.Element;
37 import javax.lang.model.element.TypeElement;
38 import javax.lang.model.element.TypeParameterElement;
39 import javax.lang.model.element.VariableElement;
40 import javax.lang.model.type.DeclaredType;
41 import javax.lang.model.type.ErrorType;
42 import javax.lang.model.type.TypeKind;
43 import javax.lang.model.type.TypeMirror;
44 import javax.lang.model.type.TypeVisitor;
45 import javax.lang.model.type.WildcardType;
46 import javax.lang.model.util.Elements;
47 import javax.lang.model.util.Types;
48 import javax.tools.JavaFileObject;
49 
50 import org.junit.Test;
51 
52 public abstract class AbstractTypesTest {
getElements()53   protected abstract Elements getElements();
getTypes()54   protected abstract Types getTypes();
55 
getElement(Class<?> clazz)56   private TypeElement getElement(Class<?> clazz) {
57     return getElements().getTypeElement(clazz.getCanonicalName());
58   }
59 
getMirror(Class<?> clazz)60   private TypeMirror getMirror(Class<?> clazz) {
61     return getElement(clazz).asType();
62   }
63 
getBasicTypeMirror()64   @Test public void getBasicTypeMirror() {
65     assertThat(TypeName.get(getMirror(Object.class)))
66         .isEqualTo(ClassName.get(Object.class));
67     assertThat(TypeName.get(getMirror(Charset.class)))
68         .isEqualTo(ClassName.get(Charset.class));
69     assertThat(TypeName.get(getMirror(AbstractTypesTest.class)))
70         .isEqualTo(ClassName.get(AbstractTypesTest.class));
71   }
72 
getParameterizedTypeMirror()73   @Test public void getParameterizedTypeMirror() {
74     DeclaredType setType =
75         getTypes().getDeclaredType(getElement(Set.class), getMirror(Object.class));
76     assertThat(TypeName.get(setType))
77         .isEqualTo(ParameterizedTypeName.get(ClassName.get(Set.class), ClassName.OBJECT));
78   }
79 
errorTypes()80   @Test public void errorTypes() {
81     JavaFileObject hasErrorTypes =
82         JavaFileObjects.forSourceLines(
83             "com.squareup.tacos.ErrorTypes",
84             "package com.squareup.tacos;",
85             "",
86             "@SuppressWarnings(\"hook-into-compiler\")",
87             "class ErrorTypes {",
88             "  Tacos tacos;",
89             "  Ingredients.Guacamole guacamole;",
90             "}");
91     Compilation compilation = javac().withProcessors(new AbstractProcessor() {
92       @Override
93       public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
94         TypeElement classFile =
95             processingEnv.getElementUtils().getTypeElement("com.squareup.tacos.ErrorTypes");
96         List<VariableElement> fields = fieldsIn(classFile.getEnclosedElements());
97         ErrorType topLevel = (ErrorType) fields.get(0).asType();
98         ErrorType member = (ErrorType) fields.get(1).asType();
99 
100         assertThat(TypeName.get(topLevel)).isEqualTo(ClassName.get("", "Tacos"));
101         assertThat(TypeName.get(member)).isEqualTo(ClassName.get("Ingredients", "Guacamole"));
102         return false;
103       }
104 
105       @Override
106       public Set<String> getSupportedAnnotationTypes() {
107         return Collections.singleton("*");
108       }
109     }).compile(hasErrorTypes);
110 
111     assertThat(compilation).failed();
112   }
113 
114   static class Parameterized<
115       Simple,
116       ExtendsClass extends Number,
117       ExtendsInterface extends Runnable,
118       ExtendsTypeVariable extends Simple,
119       Intersection extends Number & Runnable,
120       IntersectionOfInterfaces extends Runnable & Serializable> {}
121 
getTypeVariableTypeMirror()122   @Test public void getTypeVariableTypeMirror() {
123     List<? extends TypeParameterElement> typeVariables =
124         getElement(Parameterized.class).getTypeParameters();
125 
126     // Members of converted types use ClassName and not Class<?>.
127     ClassName number = ClassName.get(Number.class);
128     ClassName runnable = ClassName.get(Runnable.class);
129     ClassName serializable = ClassName.get(Serializable.class);
130 
131     assertThat(TypeName.get(typeVariables.get(0).asType()))
132         .isEqualTo(TypeVariableName.get("Simple"));
133     assertThat(TypeName.get(typeVariables.get(1).asType()))
134         .isEqualTo(TypeVariableName.get("ExtendsClass", number));
135     assertThat(TypeName.get(typeVariables.get(2).asType()))
136         .isEqualTo(TypeVariableName.get("ExtendsInterface", runnable));
137     assertThat(TypeName.get(typeVariables.get(3).asType()))
138         .isEqualTo(TypeVariableName.get("ExtendsTypeVariable", TypeVariableName.get("Simple")));
139     assertThat(TypeName.get(typeVariables.get(4).asType()))
140         .isEqualTo(TypeVariableName.get("Intersection", number, runnable));
141     assertThat(TypeName.get(typeVariables.get(5).asType()))
142         .isEqualTo(TypeVariableName.get("IntersectionOfInterfaces", runnable, serializable));
143     assertThat(((TypeVariableName) TypeName.get(typeVariables.get(4).asType())).bounds)
144         .containsExactly(number, runnable);
145   }
146 
147   static class Recursive<T extends Map<List<T>, Set<T[]>>> {}
148 
149   @Test
getTypeVariableTypeMirrorRecursive()150   public void getTypeVariableTypeMirrorRecursive() {
151     TypeMirror typeMirror = getElement(Recursive.class).asType();
152     ParameterizedTypeName typeName = (ParameterizedTypeName) TypeName.get(typeMirror);
153     String className = Recursive.class.getCanonicalName();
154     assertThat(typeName.toString()).isEqualTo(className + "<T>");
155 
156     TypeVariableName typeVariableName = (TypeVariableName) typeName.typeArguments.get(0);
157 
158     try {
159       typeVariableName.bounds.set(0, null);
160       fail("Expected UnsupportedOperationException");
161     } catch (UnsupportedOperationException expected) {
162     }
163 
164     assertThat(typeVariableName.toString()).isEqualTo("T");
165     assertThat(typeVariableName.bounds.toString())
166         .isEqualTo("[java.util.Map<java.util.List<T>, java.util.Set<T[]>>]");
167   }
168 
getPrimitiveTypeMirror()169   @Test public void getPrimitiveTypeMirror() {
170     assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.BOOLEAN)))
171         .isEqualTo(TypeName.BOOLEAN);
172     assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.BYTE)))
173         .isEqualTo(TypeName.BYTE);
174     assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.SHORT)))
175         .isEqualTo(TypeName.SHORT);
176     assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.INT)))
177         .isEqualTo(TypeName.INT);
178     assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.LONG)))
179         .isEqualTo(TypeName.LONG);
180     assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.CHAR)))
181         .isEqualTo(TypeName.CHAR);
182     assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.FLOAT)))
183         .isEqualTo(TypeName.FLOAT);
184     assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.DOUBLE)))
185         .isEqualTo(TypeName.DOUBLE);
186   }
187 
getArrayTypeMirror()188   @Test public void getArrayTypeMirror() {
189     assertThat(TypeName.get(getTypes().getArrayType(getMirror(Object.class))))
190         .isEqualTo(ArrayTypeName.of(ClassName.OBJECT));
191   }
192 
getVoidTypeMirror()193   @Test public void getVoidTypeMirror() {
194     assertThat(TypeName.get(getTypes().getNoType(TypeKind.VOID)))
195         .isEqualTo(TypeName.VOID);
196   }
197 
getNullTypeMirror()198   @Test public void getNullTypeMirror() {
199     try {
200       TypeName.get(getTypes().getNullType());
201       fail();
202     } catch (IllegalArgumentException expected) {
203     }
204   }
205 
parameterizedType()206   @Test public void parameterizedType() throws Exception {
207     ParameterizedTypeName type = ParameterizedTypeName.get(Map.class, String.class, Long.class);
208     assertThat(type.toString()).isEqualTo("java.util.Map<java.lang.String, java.lang.Long>");
209   }
210 
arrayType()211   @Test public void arrayType() throws Exception {
212     ArrayTypeName type = ArrayTypeName.of(String.class);
213     assertThat(type.toString()).isEqualTo("java.lang.String[]");
214   }
215 
wildcardExtendsType()216   @Test public void wildcardExtendsType() throws Exception {
217     WildcardTypeName type = WildcardTypeName.subtypeOf(CharSequence.class);
218     assertThat(type.toString()).isEqualTo("? extends java.lang.CharSequence");
219   }
220 
wildcardExtendsObject()221   @Test public void wildcardExtendsObject() throws Exception {
222     WildcardTypeName type = WildcardTypeName.subtypeOf(Object.class);
223     assertThat(type.toString()).isEqualTo("?");
224   }
225 
wildcardSuperType()226   @Test public void wildcardSuperType() throws Exception {
227     WildcardTypeName type = WildcardTypeName.supertypeOf(String.class);
228     assertThat(type.toString()).isEqualTo("? super java.lang.String");
229   }
230 
wildcardMirrorNoBounds()231   @Test public void wildcardMirrorNoBounds() throws Exception {
232     WildcardType wildcard = getTypes().getWildcardType(null, null);
233     TypeName type = TypeName.get(wildcard);
234     assertThat(type.toString()).isEqualTo("?");
235   }
236 
wildcardMirrorExtendsType()237   @Test public void wildcardMirrorExtendsType() throws Exception {
238     Types types = getTypes();
239     Elements elements = getElements();
240     TypeMirror charSequence = elements.getTypeElement(CharSequence.class.getName()).asType();
241     WildcardType wildcard = types.getWildcardType(charSequence, null);
242     TypeName type = TypeName.get(wildcard);
243     assertThat(type.toString()).isEqualTo("? extends java.lang.CharSequence");
244   }
245 
wildcardMirrorSuperType()246   @Test public void wildcardMirrorSuperType() throws Exception {
247     Types types = getTypes();
248     Elements elements = getElements();
249     TypeMirror string = elements.getTypeElement(String.class.getName()).asType();
250     WildcardType wildcard = types.getWildcardType(null, string);
251     TypeName type = TypeName.get(wildcard);
252     assertThat(type.toString()).isEqualTo("? super java.lang.String");
253   }
254 
typeVariable()255   @Test public void typeVariable() throws Exception {
256     TypeVariableName type = TypeVariableName.get("T", CharSequence.class);
257     assertThat(type.toString()).isEqualTo("T"); // (Bounds are only emitted in declaration.)
258   }
259 
box()260   @Test public void box() throws Exception {
261     assertThat(TypeName.INT.box()).isEqualTo(ClassName.get(Integer.class));
262     assertThat(TypeName.VOID.box()).isEqualTo(ClassName.get(Void.class));
263     assertThat(ClassName.get(Integer.class).box()).isEqualTo(ClassName.get(Integer.class));
264     assertThat(ClassName.get(Void.class).box()).isEqualTo(ClassName.get(Void.class));
265     assertThat(TypeName.OBJECT.box()).isEqualTo(TypeName.OBJECT);
266     assertThat(ClassName.get(String.class).box()).isEqualTo(ClassName.get(String.class));
267   }
268 
unbox()269   @Test public void unbox() throws Exception {
270     assertThat(TypeName.INT).isEqualTo(TypeName.INT.unbox());
271     assertThat(TypeName.VOID).isEqualTo(TypeName.VOID.unbox());
272     assertThat(ClassName.get(Integer.class).unbox()).isEqualTo(TypeName.INT.unbox());
273     assertThat(ClassName.get(Void.class).unbox()).isEqualTo(TypeName.VOID.unbox());
274     try {
275       TypeName.OBJECT.unbox();
276       fail();
277     } catch (UnsupportedOperationException expected) {
278     }
279     try {
280       ClassName.get(String.class).unbox();
281       fail();
282     } catch (UnsupportedOperationException expected) {
283     }
284   }
285 }
286