|
Added
Link Here
|
| 1 |
/******************************************************************************* |
| 2 |
* Copyright (c) 2013 Jesper Steen Moller and others. |
| 3 |
* All rights reserved. This program and the accompanying materials |
| 4 |
* are made available under the terms of the Eclipse Public License v1.0 |
| 5 |
* which accompanies this distribution, and is available at |
| 6 |
* http://www.eclipse.org/legal/epl-v10.html |
| 7 |
* |
| 8 |
* Contributors: |
| 9 |
* Jesper Steen Moller - initial API and implementation |
| 10 |
* |
| 11 |
*******************************************************************************/ |
| 12 |
|
| 13 |
package org.eclipse.jdt.apt.pluggable.tests.processors.modeltester; |
| 14 |
|
| 15 |
import java.lang.reflect.InvocationTargetException; |
| 16 |
import java.lang.reflect.Method; |
| 17 |
import java.util.HashMap; |
| 18 |
import java.util.LinkedList; |
| 19 |
import java.util.List; |
| 20 |
import java.util.Map; |
| 21 |
import java.util.Set; |
| 22 |
|
| 23 |
import javax.annotation.processing.AbstractProcessor; |
| 24 |
import javax.annotation.processing.ProcessingEnvironment; |
| 25 |
import javax.annotation.processing.RoundEnvironment; |
| 26 |
import javax.annotation.processing.SupportedAnnotationTypes; |
| 27 |
import javax.annotation.processing.SupportedOptions; |
| 28 |
import javax.annotation.processing.SupportedSourceVersion; |
| 29 |
import javax.lang.model.SourceVersion; |
| 30 |
import javax.lang.model.element.Element; |
| 31 |
import javax.lang.model.element.ElementKind; |
| 32 |
import javax.lang.model.element.ExecutableElement; |
| 33 |
import javax.lang.model.element.TypeElement; |
| 34 |
import javax.lang.model.element.VariableElement; |
| 35 |
import javax.lang.model.type.DeclaredType; |
| 36 |
import javax.lang.model.type.TypeKind; |
| 37 |
import javax.lang.model.type.TypeMirror; |
| 38 |
import javax.lang.model.util.ElementFilter; |
| 39 |
|
| 40 |
import org.eclipse.jdt.apt.pluggable.tests.ProcessorTestStatus; |
| 41 |
import org.eclipse.jdt.apt.pluggable.tests.annotations.LookAt; |
| 42 |
import org.eclipse.jdt.apt.pluggable.tests.annotations.ModelTest8Trigger; |
| 43 |
|
| 44 |
/** |
| 45 |
* This processor tests features specific to JEP 118. |
| 46 |
* One processor can run many tests. The JUnit tests specify which test to run by passing its name in to the |
| 47 |
* ModelTest8Trigger annotation. |
| 48 |
* |
| 49 |
* @since 3.9 BETA_JAVA8 |
| 50 |
*/ |
| 51 |
@SupportedAnnotationTypes( { "org.eclipse.jdt.apt.pluggable.tests.annotations.ModelTest8Trigger" }) |
| 52 |
@SupportedSourceVersion(SourceVersion.RELEASE_8) |
| 53 |
@SupportedOptions( {}) |
| 54 |
public class ModelTester8Proc extends AbstractProcessor { |
| 55 |
public static final String TEST_METHOD_PARAMETERS_TYPE1_PKG = "p"; |
| 56 |
public static final String TEST_METHOD_PARAMETERS_TYPE1_CLASS = "Bar"; |
| 57 |
public static final String TEST_METHOD_PARAMETERS_TYPE1_SOURCE = |
| 58 |
"package p;\n" + |
| 59 |
"public class Bar {\n" + |
| 60 |
" public void otherStuff(final double fun, String beans) { }\n" + |
| 61 |
"}"; |
| 62 |
|
| 63 |
public static final String TEST_METHOD_PARAMETERS_TYPE2_PKG = "p"; |
| 64 |
public static final String TEST_METHOD_PARAMETERS_TYPE2_CLASS = "MyEnum"; |
| 65 |
public static final String TEST_METHOD_PARAMETERS_TYPE2_SOURCE = |
| 66 |
"package p;\n" + |
| 67 |
"\n" + |
| 68 |
"public enum MyEnum {\n" + |
| 69 |
" ONE(1), TWO(2);\n" + |
| 70 |
" \n" + |
| 71 |
" private MyEnum(final int finalIntValue) { this.var = finalIntValue; }\n" + |
| 72 |
" int var;\n" + |
| 73 |
"}\n"; |
| 74 |
|
| 75 |
public static final String TEST_METHOD_PARAMETERS_TYPE3_PKG = "p"; |
| 76 |
public static final String TEST_METHOD_PARAMETERS_TYPE3_CLASS = "Foo"; |
| 77 |
public static final String TEST_METHOD_PARAMETERS_TYPE3_SOURCE = |
| 78 |
"package p;\n" + |
| 79 |
"import org.eclipse.jdt.apt.pluggable.tests.annotations.ModelTest8Trigger;\n" + |
| 80 |
"import org.eclipse.jdt.apt.pluggable.tests.annotations.LookAt;\n" + |
| 81 |
"@ModelTest8Trigger(test = \"testMethodParameters\")" + |
| 82 |
"public class Foo {\n" + |
| 83 |
" @LookAt\n" + |
| 84 |
" public Bar doStuff(final int number, String textual) { return null; }\n" + |
| 85 |
" @LookAt\n" + |
| 86 |
" public MyEnum guess(final int isItOne) { return isItOne == 1 ? MyEnum.ONE : MyEnum.TWO; }\n" + |
| 87 |
"}"; |
| 88 |
|
| 89 |
|
| 90 |
@SuppressWarnings("unused") |
| 91 |
private ProcessingEnvironment _processingEnv; |
| 92 |
|
| 93 |
/* |
| 94 |
* (non-Javadoc) |
| 95 |
* |
| 96 |
* @see javax.annotation.processing.AbstractProcessor#init(javax.annotation.processing.ProcessingEnvironment) |
| 97 |
*/ |
| 98 |
@Override |
| 99 |
public synchronized void init(ProcessingEnvironment processingEnv) { |
| 100 |
super.init(processingEnv); |
| 101 |
_processingEnv = processingEnv; |
| 102 |
} |
| 103 |
|
| 104 |
/* |
| 105 |
* (non-Javadoc) |
| 106 |
* |
| 107 |
* @see javax.annotation.processing.AbstractProcessor#process(java.util.Set, |
| 108 |
* javax.annotation.processing.RoundEnvironment) |
| 109 |
*/ |
| 110 |
@Override |
| 111 |
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { |
| 112 |
ProcessorTestStatus.setProcessorRan(); |
| 113 |
if (!roundEnv.processingOver() && !annotations.isEmpty()) { |
| 114 |
round(annotations, roundEnv); |
| 115 |
} |
| 116 |
return true; |
| 117 |
} |
| 118 |
|
| 119 |
/** |
| 120 |
* Perform a round of processing: for a given annotation instance, determine what test method it |
| 121 |
* specifies, and invoke that method, passing in the annotated element. |
| 122 |
*/ |
| 123 |
private void round(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { |
| 124 |
TypeElement modelTesterAnno = annotations.iterator().next(); |
| 125 |
Set<? extends Element> annotatedEls = roundEnv.getElementsAnnotatedWith(modelTesterAnno); |
| 126 |
for (Element annotatedEl : annotatedEls) { |
| 127 |
ModelTest8Trigger modelTesterMirror = annotatedEl.getAnnotation(ModelTest8Trigger.class); |
| 128 |
String testMethodName = modelTesterMirror.test(); |
| 129 |
String arg0 = modelTesterMirror.arg0(); |
| 130 |
String arg1 = modelTesterMirror.arg1(); |
| 131 |
if (null != testMethodName && testMethodName.length() > 0) { |
| 132 |
try { |
| 133 |
Method testMethod = ModelTester8Proc.class.getMethod(testMethodName, |
| 134 |
RoundEnvironment.class, Element.class, String.class, String.class); |
| 135 |
testMethod.invoke(this, roundEnv, annotatedEl, arg0, arg1); |
| 136 |
} catch (Exception e) { |
| 137 |
Throwable t; |
| 138 |
t = (e instanceof InvocationTargetException) ? t = e.getCause() : e; |
| 139 |
t.printStackTrace(); |
| 140 |
// IllegalStateException probably means test method called ProcessorTestStatus.fail() |
| 141 |
String msg = (t instanceof IllegalStateException) ? |
| 142 |
t.getMessage() : |
| 143 |
t.getClass().getSimpleName() + " invoking test method " + |
| 144 |
testMethodName + " - see console for details"; |
| 145 |
ProcessorTestStatus.fail(msg); |
| 146 |
} |
| 147 |
} |
| 148 |
} |
| 149 |
} |
| 150 |
|
| 151 |
|
| 152 |
/** |
| 153 |
* Check the types of some methods (check that the annotation processing uses the parsed MethodParameters |
| 154 |
* attribute from class files according to JEP 118) |
| 155 |
* @see #TEST_METHOD_PARAMETERS_TYPE1_SOURCE |
| 156 |
* @see #TEST_METHOD_PARAMETERS_TYPE2_SOURCE |
| 157 |
* @see #TEST_METHOD_PARAMETERS_TYPE3_SOURCE |
| 158 |
*/ |
| 159 |
public void testMethodParameters(RoundEnvironment roundEnv, Element e, String arg0, String arg1) |
| 160 |
throws Exception |
| 161 |
{ |
| 162 |
Map<String, ExecutableElement> methods = new HashMap<String, ExecutableElement>(); |
| 163 |
Iterable<? extends Element> elements; |
| 164 |
|
| 165 |
elements = roundEnv.getElementsAnnotatedWith(LookAt.class); |
| 166 |
for (ExecutableElement method : ElementFilter.methodsIn(elements)) { |
| 167 |
methods.put(method.getSimpleName().toString(), method); |
| 168 |
} |
| 169 |
|
| 170 |
// Examine the easy case, the Foo.doStuff method |
| 171 |
ExecutableElement mDoStuff = methods.get("doStuff"); |
| 172 |
if (mDoStuff == null) { |
| 173 |
ProcessorTestStatus.fail("Method doStuff() was not found"); |
| 174 |
} |
| 175 |
if (mDoStuff.getKind() != ElementKind.METHOD) { |
| 176 |
ProcessorTestStatus.fail("ElementKind of method doStuff() was " + mDoStuff.getKind() + |
| 177 |
", expected METHOD"); |
| 178 |
} |
| 179 |
// Examine parameters |
| 180 |
List<? extends VariableElement> parameters = mDoStuff.getParameters(); |
| 181 |
if (parameters.size() != 2) { |
| 182 |
ProcessorTestStatus.fail("Expected two parameters for doStuff()"); |
| 183 |
} |
| 184 |
ProcessorTestStatus.assertEquals("Wrong name", "number", parameters.get(0).getSimpleName().toString()); |
| 185 |
ProcessorTestStatus.assertEquals("Wrong name", "textual", parameters.get(1).getSimpleName().toString()); |
| 186 |
|
| 187 |
/////////////////////////////////////////////////////////////////////////////////// |
| 188 |
|
| 189 |
// Cool, now check 'p.Bar.otherStuff' which is also the return type of doStuff |
| 190 |
TypeMirror returnType = mDoStuff.getReturnType(); |
| 191 |
if (returnType.getKind() != TypeKind.DECLARED) |
| 192 |
ProcessorTestStatus.fail("TypeKind of method doStuff()'s return type " + returnType.getKind() + |
| 193 |
", expected DECLARED"); |
| 194 |
|
| 195 |
DeclaredType barType = (DeclaredType) returnType; |
| 196 |
TypeElement bar = (TypeElement) barType.asElement(); |
| 197 |
|
| 198 |
for (Element method : bar.getEnclosedElements()) { |
| 199 |
if (method.getKind() == ElementKind.METHOD) |
| 200 |
methods.put(method.getSimpleName().toString(), (ExecutableElement)method); |
| 201 |
} |
| 202 |
|
| 203 |
ExecutableElement mOtherStuff = methods.get("otherStuff"); |
| 204 |
if (mOtherStuff == null) { |
| 205 |
ProcessorTestStatus.fail("Method otherStuff() was not found"); |
| 206 |
} |
| 207 |
if (mOtherStuff.getKind() != ElementKind.METHOD) { |
| 208 |
ProcessorTestStatus.fail("ElementKind of method otherStuff() was " + mOtherStuff.getKind() + |
| 209 |
", expected METHOD"); |
| 210 |
} |
| 211 |
// Examine parameters |
| 212 |
List<? extends VariableElement> otherParameters = mOtherStuff.getParameters(); |
| 213 |
if (otherParameters.size() != 2) { |
| 214 |
ProcessorTestStatus.fail("Expected two parameters for otherStuff()"); |
| 215 |
} |
| 216 |
ProcessorTestStatus.assertEquals("Wrong name", "fun", otherParameters.get(0).getSimpleName().toString()); |
| 217 |
ProcessorTestStatus.assertEquals("Wrong name", "beans", otherParameters.get(1).getSimpleName().toString()); |
| 218 |
|
| 219 |
/////////////////////////////////////////////////////////////////////////////////// |
| 220 |
|
| 221 |
// Examine the enum as returned by Foo.guess method |
| 222 |
ExecutableElement mGuess = methods.get("guess"); |
| 223 |
if (mGuess == null) { |
| 224 |
ProcessorTestStatus.fail("Method guess() was not found"); |
| 225 |
} |
| 226 |
if (mGuess.getKind() != ElementKind.METHOD) { |
| 227 |
ProcessorTestStatus.fail("ElementKind of method doStuff() was " + mGuess.getKind() + |
| 228 |
", expected METHOD"); |
| 229 |
} |
| 230 |
|
| 231 |
// Cool, now check 'p.Bar.otherStuff' which is also the return type of doStuff |
| 232 |
TypeMirror guessReturnType = mGuess.getReturnType(); |
| 233 |
if (guessReturnType.getKind() != TypeKind.DECLARED) |
| 234 |
ProcessorTestStatus.fail("TypeKind of method guess()'s return type " + guessReturnType.getKind() + |
| 235 |
", expected DECLARED"); |
| 236 |
|
| 237 |
DeclaredType myEnumType = (DeclaredType) guessReturnType; |
| 238 |
TypeElement myEnumClass = (TypeElement) myEnumType.asElement(); |
| 239 |
|
| 240 |
List<ExecutableElement> ctors = new LinkedList<ExecutableElement>(); |
| 241 |
for (Element method : myEnumClass.getEnclosedElements()) { |
| 242 |
if (method.getKind() == ElementKind.CONSTRUCTOR) { |
| 243 |
ctors.add((ExecutableElement)method); |
| 244 |
} |
| 245 |
} |
| 246 |
|
| 247 |
ProcessorTestStatus.assertEquals("Bad # of constructors for MyEnum", 1, ctors.size()); |
| 248 |
// Examine parameters |
| 249 |
List<? extends VariableElement> ctorParameters = ctors.get(0).getParameters(); |
| 250 |
ProcessorTestStatus.assertEquals("Bad # of parameters for MyEnum ctor", 1, ctorParameters.size()); |
| 251 |
ProcessorTestStatus.assertEquals("Wrong name", "finalIntValue", ctorParameters.get(0).getSimpleName().toString()); |
| 252 |
} |
| 253 |
} |