| /******************************************************************************* |
| * Copyright (c) 2007 - 2015 BEA Systems, Inc. and others |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Walter Harley - initial API and implementation |
| * IBM Corporation - Bug 382590 |
| *******************************************************************************/ |
| |
| package org.eclipse.jdt.compiler.apt.tests.processors.typeutils; |
| |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import javax.annotation.processing.RoundEnvironment; |
| import javax.annotation.processing.SupportedAnnotationTypes; |
| import javax.annotation.processing.SupportedSourceVersion; |
| import javax.lang.model.SourceVersion; |
| import javax.lang.model.element.Element; |
| import javax.lang.model.element.TypeElement; |
| import javax.lang.model.type.ArrayType; |
| import javax.lang.model.type.DeclaredType; |
| import javax.lang.model.type.ExecutableType; |
| 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.Types; |
| |
| import org.eclipse.jdt.compiler.apt.tests.processors.base.BaseProcessor; |
| import org.eclipse.jdt.internal.compiler.apt.model.DeclaredTypeImpl; |
| |
| /** |
| * A processor that exercises the methods on the Elements utility. To enable this processor, add |
| * -Aorg.eclipse.jdt.compiler.apt.tests.processors.typeutils.TypeUtilsProc to the command line. |
| * @since 3.3 |
| */ |
| @SupportedAnnotationTypes("*") |
| @SupportedSourceVersion(SourceVersion.RELEASE_6) |
| public class TypeUtilsProc extends BaseProcessor |
| { |
| // Always return false from this processor, because it supports "*". |
| // The return value does not signify success or failure! |
| @Override |
| public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) |
| { |
| if (roundEnv.processingOver()) { |
| // We're not interested in the postprocessing round. |
| return false; |
| } |
| Map<String, String> options = processingEnv.getOptions(); |
| if (!options.containsKey(this.getClass().getName())) { |
| // Disable this processor unless we are intentionally performing the test. |
| return false; |
| } |
| |
| try { |
| if (!examinePrimitives()) { |
| return false; |
| } |
| |
| if (!examineNoType()) { |
| return false; |
| } |
| |
| if (!examineGetDeclaredType()) { |
| return false; |
| } |
| |
| if (!examineGetDeclaredTypeParameterized()) { |
| return false; |
| } |
| |
| if (!examineGetDeclaredTypeNested()) { |
| return false; |
| } |
| |
| if (!examineGetArrayTypeParameterized()) { |
| return false; |
| } |
| |
| if (!examineTypesAsMemberOf()) { |
| return false; |
| } |
| |
| if (!examineTypesAsMemberOfSubclass()) { |
| return false; |
| } |
| } catch (RuntimeException e) { |
| StringWriter sw = new StringWriter(); |
| PrintWriter w = new PrintWriter(sw); |
| e.printStackTrace(w); |
| reportError(sw.toString()); |
| return false; |
| } |
| |
| reportSuccess(); |
| return false; |
| } |
| |
| /** |
| * Test the implementation of primitive types and the getPrimitiveType() method |
| * @return true if tests passed |
| */ |
| private boolean examinePrimitives() { |
| TypeElement integerElement = _elementUtils.getTypeElement("java.lang.Integer"); |
| if (null == integerElement) { |
| reportError("Failed to get element java.lang.Integer"); |
| return false; |
| } |
| TypeMirror integerType = integerElement.asType(); |
| if (null == integerType) { |
| reportError("Failed to get element java.lang.Integer as a type"); |
| return false; |
| } |
| TypeMirror intType = _typeUtils.getPrimitiveType(TypeKind.INT); |
| if (null == intType || intType.getKind() != TypeKind.INT) { |
| reportError("Failed to get primitive type INT"); |
| return false; |
| } |
| TypeMirror floatType = _typeUtils.getPrimitiveType(TypeKind.FLOAT); |
| if (null == floatType || floatType.getKind() != TypeKind.FLOAT) { |
| reportError("Failed to get primitive type FLOAT"); |
| return false; |
| } |
| if (!intType.equals(_typeUtils.unboxedType(integerType))) { |
| reportError("unboxedType(java.lang.Integer) is not primitive int"); |
| return false; |
| } |
| if (!_typeUtils.isAssignable(intType, floatType)) { |
| reportError("isAssignable(int, float) should be true"); |
| return false; |
| } |
| if (_typeUtils.isAssignable(floatType, intType)) { |
| reportError("isAssignable(float, int) should be false"); |
| return false; |
| } |
| |
| // TYPE IDENTITY |
| if (_typeUtils.isSameType(intType, floatType)) { |
| reportError("Primitive type int is reported to be same as float"); |
| return false; |
| } |
| if (!_typeUtils.isSameType(floatType, floatType)) { |
| reportError("Primitive type float is reported to not be same as itself"); |
| return false; |
| } |
| |
| // SUBTYPES |
| if (!_typeUtils.isSubtype(intType, intType)) { |
| reportError("Primitive type int is not a subtype of itself"); |
| return false; |
| } |
| if (!_typeUtils.isSubtype(intType, floatType)) { |
| reportError("Primitive type int is not a subtype of float"); |
| return false; |
| } |
| if (_typeUtils.isSubtype(floatType, intType)) { |
| reportError("Primitive type float is a subtype of int"); |
| return false; |
| } |
| |
| // BOXING |
| if (!_typeUtils.isAssignable(intType, integerType)) { |
| reportError("isAssignable(int, java.lang.Integer) should be true"); |
| return false; |
| } |
| if (!_typeUtils.isAssignable(integerType, intType)) { |
| reportError("isAssignable(java.lang.Integer, int) should be true"); |
| return false; |
| } |
| if (!_typeUtils.isAssignable(integerType, floatType)) { |
| reportError("isAssignable(java.lang.Integer, float) should be true"); |
| return false; |
| } |
| if (_typeUtils.isAssignable(floatType, integerType)) { |
| reportError("isAssignable(float, java.lang.Integer) should be false"); |
| return false; |
| } |
| TypeMirror erasureOfInt = _typeUtils.erasure(intType); |
| if (erasureOfInt != intType) { |
| reportError("erasure of a primitive type is the type itself"); |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Test the implementation of NoType and the getNoType() method |
| * @return true if tests passed |
| */ |
| private boolean examineNoType() { |
| NoType noType = _typeUtils.getNoType(TypeKind.NONE); |
| if (null == noType || noType.getKind() != TypeKind.NONE) { |
| reportError("getNoType() didn't return a TypeKind.NONE type"); |
| return false; |
| } |
| String noTypeString = noType.toString(); |
| if (!("none".equals(noTypeString) || "<none>".equals(noTypeString))) { |
| reportError("NoType has the wrong name: " + noTypeString); |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Test the implementation of {@link javax.lang.model.util.Types#getDeclaredType()} |
| * @return true if tests passed |
| */ |
| private boolean examineGetDeclaredType() { |
| TypeElement elementD = _elementUtils.getTypeElement("targets.model.pb.D"); |
| TypeElement elementAB = _elementUtils.getTypeElement("targets.model.pb.AB"); |
| TypeMirror typeAB = _typeUtils.getDeclaredType(elementAB); |
| if (!(typeAB instanceof DeclaredType) || typeAB.getKind() != TypeKind.DECLARED) { |
| reportError("Types.getDeclaredType(elementAB) returned bad value: " + typeAB); |
| return false; |
| } |
| TypeMirror typeDSuper = elementD.getSuperclass(); |
| if (typeDSuper == null || !_typeUtils.isSameType(typeAB, typeDSuper)) { |
| reportError("Type of AB and superclass of D are not same type"); |
| return false; |
| } |
| TypeMirror typeD = elementD.asType(); |
| if (!_typeUtils.isSubtype(typeD, typeAB)) { |
| reportError("Type of D is not a subtype of type AB"); |
| return false; |
| } |
| ArrayType arrayOfTypeD = _typeUtils.getArrayType(typeD); |
| ArrayType arrayOfTypeAB = _typeUtils.getArrayType(typeAB); |
| if (!_typeUtils.isSubtype(arrayOfTypeD, arrayOfTypeAB)) { |
| reportError("Array of type D is not a subtype of array of type AB"); |
| return false; |
| } |
| PrimitiveType typeInt = _typeUtils.getPrimitiveType(TypeKind.INT); |
| ArrayType arrayOfInt = _typeUtils.getArrayType(typeInt); |
| ArrayType arrayOfIntInt = _typeUtils.getArrayType(arrayOfInt); |
| TypeElement objectTypeElement = _elementUtils.getTypeElement("java.lang.Object"); |
| TypeMirror javaLangObject = objectTypeElement.asType(); |
| if (!_typeUtils.isSubtype(arrayOfIntInt, javaLangObject)) { |
| reportError("int[][] is not a subtype of Object"); |
| return false; |
| } |
| TypeElement stringTypeElement = _elementUtils.getTypeElement("java.lang.String"); |
| TypeMirror javaLangString = stringTypeElement.asType(); |
| ArrayType arrayOfString = _typeUtils.getArrayType(javaLangString); |
| ArrayType arrayOfObject = _typeUtils.getArrayType(javaLangObject); |
| ArrayType arrayOfObjectObject = _typeUtils.getArrayType(arrayOfObject); |
| if (_typeUtils.isSubtype(arrayOfString, arrayOfObjectObject)) { |
| reportError("String[] is a subtype of Object[][]"); |
| return false; |
| } |
| if (!_typeUtils.isSubtype(arrayOfString, arrayOfObject)) { |
| reportError("String[] is not a subtype of Object[]"); |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Test getDeclaredType() for parameterized types |
| * @return true if tests passed |
| */ |
| private boolean examineGetDeclaredTypeParameterized() { |
| TypeElement stringDecl = _elementUtils.getTypeElement(String.class.getName()); |
| TypeElement mapDecl = _elementUtils.getTypeElement(Map.class.getName()); |
| DeclaredType stringType = _typeUtils.getDeclaredType(stringDecl); |
| ArrayType stringArrayType = _typeUtils.getArrayType(stringType); |
| |
| DeclaredType decl = _typeUtils.getDeclaredType(mapDecl, stringType, stringArrayType); |
| List<? extends TypeMirror> args = decl.getTypeArguments(); |
| if (args.size() != 2) { |
| reportError("Map<String, String[]> should have two arguments but decl.getTypeArguments() returned " + args.size()); |
| return false; |
| } |
| if (!_typeUtils.isSameType(stringType, args.get(0))) { |
| reportError("First arg of Map<String, String[]> was expected to be String, but was: " + args.get(0)); |
| return false; |
| } |
| if (!_typeUtils.isSameType(stringArrayType, args.get(1))) { |
| reportError("Second arg of Map<String, String[]> was expected to be String[], but was: " + args.get(1)); |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Test getDeclaredType() for nested parameterized types (Outer<Foo>.Inner<Bar>). |
| * @return true if tests passed |
| */ |
| private boolean examineGetDeclaredTypeNested() { |
| TypeElement stringDecl = _elementUtils.getTypeElement(String.class.getName()); |
| TypeElement numberDecl = _elementUtils.getTypeElement(Number.class.getName()); |
| TypeElement elementOuter = _elementUtils.getTypeElement("targets.model.pd.Outer"); |
| TypeElement elementInner = _elementUtils.getTypeElement("targets.model.pd.Outer.Inner"); |
| DeclaredType stringType = _typeUtils.getDeclaredType(stringDecl); |
| DeclaredType numberType = _typeUtils.getDeclaredType(numberDecl); |
| ArrayType numberArrayType = _typeUtils.getArrayType(numberType); |
| |
| // Outer<T1, T2> ---> Outer<String, Number[]> |
| DeclaredType outerType = _typeUtils.getDeclaredType(elementOuter, stringType, numberArrayType); |
| |
| // Outer<T1, T2>.Inner<T2> ---> Outer<String, Number[]>.Inner<Number[]> |
| DeclaredType decl = _typeUtils.getDeclaredType(outerType, elementInner, new TypeMirror[] { numberArrayType }); |
| |
| List<? extends TypeMirror> args = decl.getTypeArguments(); |
| if (args.size() != 1) { |
| reportError("Outer<String, Number[]>.Inner<Number[]> should have one argument but decl.getTypeArguments() returned " + args.size()); |
| return false; |
| } |
| if (!_typeUtils.isSameType(numberArrayType, args.get(0))) { |
| reportError("First arg of Outer<String, Number[]>.Inner<Number[]> was expected to be Number[], but was: " + args.get(0)); |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Test getArrayType() for a parameterized type |
| * @return true if tests passed |
| */ |
| private boolean examineGetArrayTypeParameterized() { |
| TypeElement stringDecl = _elementUtils.getTypeElement(String.class.getName()); |
| TypeElement listDecl = _elementUtils.getTypeElement(List.class.getName()); |
| DeclaredType stringType = _typeUtils.getDeclaredType(stringDecl); |
| |
| // List<String> |
| DeclaredType decl = _typeUtils.getDeclaredType(listDecl, stringType); |
| |
| // List<String>[] |
| ArrayType listArray = _typeUtils.getArrayType(decl); |
| |
| TypeMirror leafType = listArray.getComponentType(); |
| if (!_typeUtils.isSameType(leafType, decl)) { |
| reportError("Leaf type of List<String>[] should be List<String>, but was: " + leafType); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Test {@link Types#asMemberOf()}. |
| * @return true if tests passed |
| */ |
| private boolean examineTypesAsMemberOf() { |
| TypeElement containerElement = _elementUtils.getTypeElement("targets.model.pc.AsMemberOf"); |
| DeclaredType longType = (DeclaredType)_elementUtils.getTypeElement("java.lang.Long").asType(); |
| DeclaredType container = _typeUtils.getDeclaredType(containerElement, longType); |
| return innerTestAsMemberOf("examineTypesAsMemberOf", container, containerElement); |
| } |
| /** |
| * Test {@link Types#asMemberOf()} where the elements are members of a superclass of the |
| * declared container. See <a href="http://bugs.eclipse.org/382590">Bug 382590</a>. Note that |
| * the asMemberOf() javadoc says the element is viewed "as a member of, or otherwise directly |
| * contained by, a given type." It is not clear what "directly" means here, but javac 1.6 |
| * supports this case, so we will too. |
| * @return true if tests passed |
| */ |
| private boolean examineTypesAsMemberOfSubclass() { |
| DeclaredType declaredContainer = (DeclaredType) _elementUtils.getTypeElement("targets.model.pc.AsMemberOfSub").asType(); |
| TypeElement containerElement = _elementUtils.getTypeElement("targets.model.pc.AsMemberOf"); |
| return innerTestAsMemberOf("examineTypesAsMemberOfSubclass", declaredContainer, containerElement); |
| } |
| |
| /** |
| * @return true if tests passed |
| */ |
| private boolean innerTestAsMemberOf(String method, DeclaredType declaredContainer, |
| TypeElement containerElement) { |
| DeclaredType longType = (DeclaredType)_elementUtils.getTypeElement("java.lang.Long").asType(); |
| Map<String, Element> members = new HashMap<String, Element>(); |
| for (Element element : containerElement.getEnclosedElements()) { |
| members.put(element.getSimpleName().toString(), element); |
| } |
| for (String m : new String[] {"f2", "m2"}) { |
| if (members.get(m) != null) { |
| reportError(method + ": Should not have found member named '" + m + "' in container " + declaredContainer); |
| return false; |
| } |
| } |
| for (String m : new String[] {"f", "m", "C", "D", "e"}) { |
| Element memberElement = members.get(m); |
| if (memberElement == null) { |
| reportError(method + ": Couldn't find member named '" + m + "' in container " + declaredContainer); |
| return false; |
| } |
| TypeMirror tm = _typeUtils.asMemberOf(declaredContainer, memberElement); |
| |
| if ("f".equals(m)) { |
| // Long field |
| if (!_typeUtils.isSameType(tm, longType)) { |
| reportError(method + ": member f should be of type Long, but was " + tm); |
| return false; |
| } |
| } else if ("m".equals(m)) { |
| // void method returning Long |
| if (tm.getKind() != TypeKind.EXECUTABLE) { |
| reportError(method + ": member m() should be executable but was " + tm.getKind()); |
| return false; |
| } |
| ExecutableType etm = (ExecutableType)tm; |
| if (!_typeUtils.isSameType(etm.getReturnType(), longType)) { |
| reportError(method + ": member m() should have Long return type, but found " + etm.getReturnType()); |
| return false; |
| } |
| } else if ("C".equals(m)) { |
| // Not clear what to expect in the case of a static nested class. Outer parameterization doesn't |
| // apply to the nested class; and the method spec doesn't say what to do when the contained |
| // member is incompletely parameterized. |
| TypeElement c = _elementUtils.getTypeElement("targets.model.pc.AsMemberOf.C"); |
| TypeMirror tmExpected = c.asType(); |
| |
| // In javac, we get A.C<T> whether C is directly contained or accessed via a subclass. |
| // In Eclipse, when accessing through a subclass, the binding to C is lazily created |
| // within ParameterizedTypeBinding.memberTypes(), so it gets created as a PTB itself, |
| // with no type arguments. Thus it is A<Long>.C instead of A.C<T>. This is probably a |
| // compiler bug, but given that asMemberOf() is documented to apply only to "directly |
| // contained" members it's probably not worth worrying about. So, we accept that case. |
| if (!_typeUtils.isSameType(tm, tmExpected) && |
| !(tm instanceof DeclaredTypeImpl && |
| "targets.model.pc.AsMemberOf<java.lang.Long>.C".equals(tm.toString()))) { |
| reportError(method + ": member C: Expected type " + tmExpected + " but found " + tm); |
| return false; |
| } |
| } else if ("D".equals(m)) { |
| // Expected type of D is AsMemberOf2<Long>.D |
| DeclaredType tmContainer = _typeUtils.getDeclaredType(containerElement, longType); |
| TypeElement d = _elementUtils.getTypeElement("targets.model.pc.AsMemberOf.D"); |
| TypeMirror tmExpected = _typeUtils.getDeclaredType(tmContainer, d); |
| if (!_typeUtils.isSameType(tm, tmExpected)) { |
| reportError(method + ": member D: Expected type " + tmExpected + |
| " but found " + tm); |
| return false; |
| } |
| } else if ("e".equals(m)) { |
| // Expected type of e is A<Long>.E<Integer> |
| DeclaredType tmContainer = _typeUtils.getDeclaredType(containerElement, longType); |
| TypeElement eTypeElem = _elementUtils.getTypeElement("targets.model.pc.AsMemberOf.E"); |
| DeclaredType tmInt = (DeclaredType) _elementUtils.getTypeElement("java.lang.Integer").asType(); |
| TypeMirror tmExpected = _typeUtils.getDeclaredType(tmContainer, eTypeElem, tmInt); |
| if (!_typeUtils.isSameType(tm, tmExpected)) { |
| reportError(method + ": member e: Expected type " + tmExpected + |
| " but found " + tm); |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| } |