| /******************************************************************************* |
| * Copyright (c) 2007, 2008 BEA Systems, Inc. |
| * 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: |
| * wharley@bea.com - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.jdt.compiler.apt.tests.processors.negative; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.StringReader; |
| import java.lang.reflect.Method; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import javax.annotation.processing.AbstractProcessor; |
| import javax.annotation.processing.ProcessingEnvironment; |
| import javax.annotation.processing.RoundEnvironment; |
| import javax.annotation.processing.SupportedAnnotationTypes; |
| import javax.annotation.processing.SupportedOptions; |
| import javax.annotation.processing.SupportedSourceVersion; |
| import javax.lang.model.SourceVersion; |
| import javax.lang.model.element.AnnotationMirror; |
| import javax.lang.model.element.AnnotationValue; |
| import javax.lang.model.element.Element; |
| import javax.lang.model.element.ElementKind; |
| import javax.lang.model.element.ExecutableElement; |
| import javax.lang.model.element.TypeElement; |
| import javax.lang.model.element.VariableElement; |
| import javax.lang.model.type.DeclaredType; |
| import javax.lang.model.type.TypeKind; |
| import javax.lang.model.type.TypeMirror; |
| import javax.lang.model.util.Elements; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| |
| import org.eclipse.jdt.compiler.apt.tests.processors.base.XMLComparer; |
| import org.eclipse.jdt.compiler.apt.tests.processors.base.XMLConverter; |
| import org.w3c.dom.Document; |
| import org.xml.sax.InputSource; |
| |
| /** |
| * An annotation processor that investigates the model produced by code containing |
| * semantic errors such as missing types. To enable this processor, add |
| * -Aorg.eclipse.jdt.compiler.apt.tests.processors.negative.NegativeModelProc to the |
| * command line. |
| * |
| * Optionally, enable just a single test, by adding an integer value denoting the |
| * test to the option key. For example, to enable testNegative2, add |
| * -Aorg.eclipse.jdt.compiler.apt.tests.processors.negative.NegativeModelProc=2 |
| * to the command line. If 0 or no value is specified, all tests will be run. |
| */ |
| @SupportedAnnotationTypes("*") |
| @SupportedSourceVersion(SourceVersion.RELEASE_6) |
| @SupportedOptions({"org.eclipse.jdt.compiler.apt.tests.processors.negative.NegativeModelProc", NegativeModelProc.IGNORE_JAVAC_BUGS}) |
| public class NegativeModelProc extends AbstractProcessor |
| { |
| /** |
| * Reference model for types in Negative1 test |
| */ |
| private static final String NEGATIVE_1_MODEL = |
| "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" + |
| "<model>\n" + |
| " <type-element kind=\"CLASS\" qname=\"targets.negative.pa.Negative1\" sname=\"Negative1\">\n" + |
| " <superclass>\n" + |
| " <type-mirror kind=\"DECLARED\" to-string=\"java.lang.Object\"/>\n" + |
| " </superclass>\n" + |
| " <annotations>\n" + |
| " <annotation sname=\"A3\"/>\n" + |
| " </annotations>\n" + |
| " <executable-element kind=\"CONSTRUCTOR\" sname=\"<init>\"/>\n" + |
| " <variable-element kind=\"FIELD\" sname=\"s1\" type=\"java.lang.String\">\n" + |
| " <annotations>\n" + |
| " <annotation sname=\"Anno1\">\n" + |
| " <annotation-values>\n" + |
| " <annotation-value member=\"value\" type=\"java.lang.String\" value=\"spud\"/>\n" + |
| " </annotation-values>\n" + |
| " </annotation>\n" + |
| " </annotations>\n" + |
| " </variable-element>\n" + |
| " <variable-element kind=\"FIELD\" sname=\"m1\" type=\"Missing1\">\n" + |
| " <annotations>\n" + |
| " <annotation sname=\"A4\"/>\n" + |
| " </annotations>\n" + |
| " </variable-element>\n" + |
| " <variable-element kind=\"FIELD\" sname=\"i1\" type=\"int\">\n" + |
| " <annotations>\n" + |
| " <annotation sname=\"A5\"/>\n" + |
| " </annotations>\n" + |
| " </variable-element>\n" + |
| " <variable-element kind=\"FIELD\" sname=\"m2\" type=\"Missing2.Missing3.Missing4\">\n" + |
| " <annotations>\n" + |
| " <annotation sname=\"A8\"/>\n" + |
| " </annotations>\n" + |
| " </variable-element>\n" + |
| " </type-element>\n" + |
| "</model>\n" + |
| ""; |
| |
| /** |
| * Reference model for types in Negative4 test |
| */ |
| private static final String NEGATIVE_4_MODEL = |
| "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" + |
| "<model>\n" + |
| " <type-element kind=\"CLASS\" qname=\"targets.negative.pa.Negative4\" sname=\"Negative4\">\n" + |
| " <superclass>\n" + |
| " <type-mirror kind=\"DECLARED\" to-string=\"java.lang.Object\"/>\n" + |
| " </superclass>\n" + |
| " <executable-element kind=\"CONSTRUCTOR\" sname=\"<init>\"/>\n" + |
| " <executable-element kind=\"METHOD\" sname=\"zorkRaw\"/>\n" + |
| " <executable-element kind=\"METHOD\" sname=\"zorkOfString\"/>\n" + |
| " <executable-element kind=\"METHOD\" sname=\"ifooOfString\" optional=\"true\"/>\n" + |
| " <executable-element kind=\"METHOD\" sname=\"ibarRaw\"/>\n" + |
| " <executable-element kind=\"METHOD\" sname=\"ibarOfT1T2\" optional=\"true\"/>\n" + |
| " </type-element>\n" + |
| "</model>\n"; |
| |
| /** |
| * Reference model for types in Negative5 test |
| */ |
| private static final String NEGATIVE_5_MODEL = |
| "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" + |
| "<model>\n" + |
| " <type-element kind=\"CLASS\" qname=\"targets.negative.pa.Negative5\" sname=\"Negative5\">\n" + |
| " <superclass>\n" + |
| " <type-mirror kind=\"DECLARED\" to-string=\"java.lang.Object\"/>\n" + |
| " </superclass>\n" + |
| " <executable-element kind=\"CONSTRUCTOR\" sname=\"<init>\"/>\n" + |
| " <type-element kind=\"CLASS\" qname=\"targets.negative.pa.Negative5.C1\" sname=\"C1\">\n" + |
| " <superclass>\n" + |
| " <type-mirror kind=\"ERROR\" to-string=\"M1\"/>\n" + |
| " </superclass>\n" + |
| " <executable-element kind=\"CONSTRUCTOR\" sname=\"<init>\"/>\n" + |
| " </type-element>\n" + |
| " <type-element kind=\"CLASS\" qname=\"targets.negative.pa.Negative5.C2\" sname=\"C2\">\n" + |
| " <superclass>\n" + |
| " <type-mirror kind=\"ERROR\" to-string=\"java.lang.Object\"/>\n" + |
| " </superclass>\n" + |
| " <executable-element kind=\"CONSTRUCTOR\" sname=\"<init>\"/>\n" + |
| " </type-element>\n" + |
| " <type-element kind=\"INTERFACE\" qname=\"targets.negative.pa.Negative5.I1\" sname=\"I1\">\n" + |
| " <superclass>\n" + |
| " <type-mirror kind=\"NONE\" to-string=\"<none>\"/>\n" + |
| " </superclass>\n" + |
| " </type-element>\n" + |
| " <type-element kind=\"INTERFACE\" qname=\"targets.negative.pa.Negative5.I2\" sname=\"I2\">\n" + |
| " <superclass>\n" + |
| " <type-mirror kind=\"NONE\" to-string=\"<none>\"/>\n" + |
| " </superclass>\n" + |
| " </type-element>\n" + |
| " </type-element>\n" + |
| " <type-element kind=\"INTERFACE\" qname=\"targets.negative.pa.INegative5\" sname=\"INegative5\">\n" + |
| " <superclass>\n" + |
| " <type-mirror kind=\"NONE\" to-string=\"<none>\"/>\n" + |
| " </superclass>\n" + |
| " <type-element kind=\"CLASS\" qname=\"targets.negative.pa.INegative5.C101\" sname=\"C101\">\n" + |
| " <superclass>\n" + |
| " <type-mirror kind=\"ERROR\" to-string=\"M101\"/>\n" + |
| " </superclass>\n" + |
| " <executable-element kind=\"CONSTRUCTOR\" sname=\"<init>\"/>\n" + |
| " </type-element>\n" + |
| " <type-element kind=\"CLASS\" qname=\"targets.negative.pa.INegative5.C102\" sname=\"C102\">\n" + |
| " <superclass>\n" + |
| " <type-mirror kind=\"ERROR\" to-string=\"java.lang.Object\"/>\n" + |
| " </superclass>\n" + |
| " <executable-element kind=\"CONSTRUCTOR\" sname=\"<init>\"/>\n" + |
| " </type-element>\n" + |
| " <type-element kind=\"INTERFACE\" qname=\"targets.negative.pa.INegative5.I101\" sname=\"I101\">\n" + |
| " <superclass>\n" + |
| " <type-mirror kind=\"NONE\" to-string=\"<none>\"/>\n" + |
| " </superclass>\n" + |
| " </type-element>\n" + |
| " <type-element kind=\"INTERFACE\" qname=\"targets.negative.pa.INegative5.I102\" sname=\"I102\">\n" + |
| " <superclass>\n" + |
| " <type-mirror kind=\"NONE\" to-string=\"<none>\"/>\n" + |
| " </superclass>\n" + |
| " </type-element>\n" + |
| " </type-element>\n" + |
| "</model>"; |
| |
| /** |
| * Reference model for class Negative6. |
| */ |
| private static final String NEGATIVE_6_MODEL = |
| "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" + |
| "<model>\n" + |
| " <type-element kind=\"CLASS\" qname=\"targets.negative.pa.Negative6\" sname=\"Negative6\">\n" + |
| " <superclass>\n" + |
| " <type-mirror kind=\"DECLARED\" to-string=\"java.lang.Object\"/>\n" + |
| " </superclass>\n" + |
| " <executable-element kind=\"METHOD\" sname=\"method1\">\n" + |
| " <annotations>\n" + |
| " <annotation sname=\"M11\"/>\n" + |
| " </annotations>\n" + |
| " <variable-element kind=\"PARAMETER\" sname=\"arg0\" type=\"M16\">\n" + |
| " <annotations>\n" + |
| " <annotation sname=\"M13\"/>\n" + |
| " </annotations>\n" + |
| " </variable-element>\n" + |
| " </executable-element>\n" + |
| " <executable-element kind=\"METHOD\" sname=\"method2\">\n" + |
| " <annotations>\n" + |
| " <annotation sname=\"M21\"/>\n" + |
| " </annotations>\n" + |
| " <variable-element kind=\"PARAMETER\" sname=\"arg0\" type=\"int\">\n" + |
| " <annotations>\n" + |
| " <annotation sname=\"M22\"/>\n" + |
| " </annotations>\n" + |
| " </variable-element>\n" + |
| " </executable-element>\n" + |
| " <executable-element kind=\"METHOD\" sname=\"method3\">\n" + |
| " <variable-element kind=\"PARAMETER\" sname=\"arg0\" type=\"java.lang.String\">\n" + |
| " <annotations>\n" + |
| " <annotation sname=\"M31\"/>\n" + |
| " </annotations>\n" + |
| " </variable-element>\n" + |
| " <variable-element kind=\"PARAMETER\" sname=\"arg1\" type=\"java.lang.String\">\n" + |
| " <annotations>\n" + |
| " <annotation sname=\"M32\"/>\n" + |
| " </annotations>\n" + |
| " </variable-element>\n" + |
| " </executable-element>\n" + |
| " <executable-element kind=\"CONSTRUCTOR\" sname=\"<init>\">\n" + |
| " <annotations>\n" + |
| " <annotation sname=\"M41\"/>\n" + |
| " </annotations>\n" + |
| " <variable-element kind=\"PARAMETER\" sname=\"arg0\" type=\"M43\">\n" + |
| " <annotations>\n" + |
| " <annotation sname=\"M42\"/>\n" + |
| " </annotations>\n" + |
| " </variable-element>\n" + |
| " </executable-element>\n" + |
| " </type-element>\n" + |
| "</model>"; |
| |
| /** |
| * Reference model for class Negative7. |
| */ |
| private static final String NEGATIVE_7_MODEL = |
| "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" + |
| "<model>\n" + |
| " <type-element kind=\"INTERFACE\" qname=\"targets.negative.pa.Negative7\" sname=\"Negative7\">\n" + |
| " <superclass>\n" + |
| " <type-mirror kind=\"NONE\" to-string=\"<none>\"/>\n" + |
| " </superclass>\n" + |
| " <executable-element kind=\"METHOD\" sname=\"method1\">\n" + |
| " <annotations>\n" + |
| " <annotation sname=\"A1\"/>\n" + |
| " </annotations>\n" + |
| " <variable-element kind=\"PARAMETER\" sname=\"arg0\" type=\"int\">\n" + |
| " <annotations>\n" + |
| " <annotation sname=\"A1\"/>\n" + |
| " </annotations>\n" + |
| " </variable-element>\n" + |
| " </executable-element>\n" + |
| " </type-element>\n" + |
| " <type-element kind=\"INTERFACE\" qname=\"targets.negative.pa.Negative7A\" sname=\"Negative7A\">\n" + |
| " <superclass>\n" + |
| " <type-mirror kind=\"NONE\" to-string=\"<none>\"/>\n" + |
| " </superclass>\n" + |
| " <interfaces>\n" + |
| " <type-mirror kind=\"DECLARED\" to-string=\"java.lang.Cloneable\"/>\n" + |
| " </interfaces>\n" + |
| " <executable-element kind=\"METHOD\" sname=\"method1\">\n" + |
| " <annotations>\n" + |
| " <annotation sname=\"A1\"/>\n" + |
| " </annotations>\n" + |
| " <variable-element kind=\"PARAMETER\" sname=\"arg0\" type=\"int\">\n" + |
| " <annotations>\n" + |
| " <annotation sname=\"A1\"/>\n" + |
| " </annotations>\n" + |
| " </variable-element>\n" + |
| " </executable-element>\n" + |
| " </type-element>\n" + |
| "</model>\n"; |
| /** |
| * Reference model for class Negative8. |
| */ |
| private static final String NEGATIVE_8_MODEL = |
| "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" + |
| "<model>\n" + |
| " <type-element kind=\"CLASS\" qname=\"targets.negative.pa.Negative8a\" sname=\"Negative8a\">\n" + |
| " <superclass>\n" + |
| " <type-mirror kind=\"DECLARED\" to-string=\"java.lang.Object\"/>\n" + |
| " </superclass>\n" + |
| " <executable-element kind=\"CONSTRUCTOR\" sname=\"<init>\"/>\n" + |
| " </type-element>\n" + |
| " <type-element kind=\"INTERFACE\" qname=\"targets.negative.pa.Negative8b\" sname=\"Negative8b\">\n" + |
| " <superclass>\n" + |
| " <type-mirror kind=\"NONE\" to-string=\"<none>\"/>\n" + |
| " </superclass>\n" + |
| " </type-element>\n" + |
| " <type-element kind=\"CLASS\" qname=\"targets.negative.pa.Negative8c\" sname=\"Negative8c\">\n" + |
| " <superclass>\n" + |
| " <type-mirror kind=\"DECLARED\" to-string=\"java.lang.Object\"/>\n" + |
| " </superclass>\n" + |
| " <executable-element kind=\"CONSTRUCTOR\" sname=\"<init>\"/>\n" + |
| " </type-element>\n" + |
| " <type-element kind=\"INTERFACE\" qname=\"targets.negative.pa.Negative8d\" sname=\"Negative8d\">\n" + |
| " <superclass>\n" + |
| " <type-mirror kind=\"NONE\" to-string=\"<none>\"/>\n" + |
| " </superclass>\n" + |
| " </type-element>\n" + |
| " <type-element kind=\"INTERFACE\" qname=\"targets.negative.pa.Negative8e\" sname=\"Negative8e\">\n" + |
| " <superclass>\n" + |
| " <type-mirror kind=\"NONE\" to-string=\"<none>\"/>\n" + |
| " </superclass>\n" + |
| " <interfaces>\n" + |
| " <type-mirror kind=\"ERROR\" to-string=\"targets.negative.pa.Negative8f<T>\"/>\n" + |
| " </interfaces>\n" + |
| " </type-element>\n" + |
| " <type-element kind=\"INTERFACE\" qname=\"targets.negative.pa.Negative8f\" sname=\"Negative8f\">\n" + |
| " <superclass>\n" + |
| " <type-mirror kind=\"NONE\" to-string=\"<none>\"/>\n" + |
| " </superclass>\n" + |
| " </type-element>\n" + |
| "</model>\n"; |
| |
| /** |
| * Declare this option (-AignoreJavacBugs) to ignore failures of cases that are |
| * known to fail under javac, i.e., known bugs in javac. |
| */ |
| public static final String IGNORE_JAVAC_BUGS = "ignoreJavacBugs"; |
| |
| private static final String CLASSNAME = NegativeModelProc.class.getName(); |
| |
| private static final String[] testMethodNames = { |
| "checkNegative1", |
| "checkNegative2", |
| "checkNegative3", |
| "checkNegative4", |
| "checkNegative5", |
| "checkNegative6", |
| "checkNegative7", |
| "checkNegative8", |
| }; |
| |
| private static final Method[] testMethods = new Method[testMethodNames.length]; |
| |
| /** |
| * Report an error to the test case code. |
| * This is not the same as reporting via Messager! Use this if some API fails. |
| * @param value will be displayed in the test output, in the event of failure. |
| * Can be anything except "succeeded". |
| */ |
| public static void reportError(String value) { |
| // Uncomment for processor debugging - don't report error |
| // value = "succeeded"; |
| System.setProperty(CLASSNAME, value); |
| } |
| |
| /** |
| * Report success to the test case code |
| */ |
| public static void reportSuccess() { |
| System.setProperty(CLASSNAME, "succeeded"); |
| } |
| |
| private Elements _elementUtils; |
| |
| // 0 means run all tests; otherwise run just the (1-based) single test indicated |
| private int _oneTest; |
| |
| // Report failures on tests that are already known to be unsupported |
| private boolean _reportFailingCases = true; |
| |
| // If processor options don't include this processor's classname, don't run the proc at all. |
| private boolean _processorEnabled; |
| |
| private boolean _ignoreJavacBugs = false; |
| |
| |
| public NegativeModelProc() { |
| for (int i = 0; i < testMethodNames.length; ++i) { |
| try { |
| testMethods[i] = NegativeModelProc.class.getMethod(testMethodNames[i]); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| throw new IllegalStateException(e); |
| } |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see javax.annotation.processing.AbstractProcessor#init(javax.annotation.processing.ProcessingEnvironment) |
| */ |
| @Override |
| public synchronized void init(ProcessingEnvironment processingEnv) { |
| super.init(processingEnv); |
| _elementUtils = processingEnv.getElementUtils(); |
| |
| // parse options |
| _oneTest = -1; |
| Map<String, String> options = processingEnv.getOptions(); |
| _processorEnabled = options.containsKey(CLASSNAME); |
| String oneTestOption = options.get(CLASSNAME); |
| if (oneTestOption == null || oneTestOption.length() == 0) { |
| _oneTest = 0; |
| } |
| else { |
| try { |
| _oneTest = Integer.parseInt(oneTestOption); |
| } catch (Exception e) { |
| // report it in process(), where we have better error reporting capability |
| } |
| } |
| _ignoreJavacBugs = options.containsKey(IGNORE_JAVAC_BUGS); |
| } |
| |
| // 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 (!_processorEnabled) { |
| // Disable this processor unless we are intentionally performing the test. |
| return false; |
| } |
| if (roundEnv.processingOver()) { |
| // We're not interested in the postprocessing round. |
| return false; |
| } |
| if (_oneTest < 0 || _oneTest > testMethodNames.length) { |
| reportError("Invalid test method specified: " + processingEnv.getOptions().get(CLASSNAME)); |
| return false; |
| } |
| |
| // Reflectively invoke the specified tests. |
| try { |
| if (_oneTest == 0) { |
| for (Method testMethod : testMethods) { |
| Object success = testMethod.invoke(this); |
| if (!(success instanceof Boolean) || !(Boolean)success) { |
| return false; |
| } |
| } |
| } |
| else { |
| Object success = testMethods[_oneTest - 1].invoke(this); |
| if (!(success instanceof Boolean) || !(Boolean)success) { |
| return false; |
| } |
| } |
| } catch (Exception e) { |
| e.printStackTrace(); |
| reportError("Exception thrown while invoking test method: " + e); |
| return false; |
| } |
| |
| reportSuccess(); |
| return false; |
| } |
| |
| /** |
| * Check the model of resources/targets.negative.pa.Negative6 |
| * @return true if all tests passed |
| */ |
| public boolean checkNegative1() throws Exception { |
| |
| // Self-test of XML language model framework. |
| // TODO: this should be moved into the ModelTests test suite. |
| if (!XMLComparer.test()) { |
| reportError("XML language model comparison framework failed self-test"); |
| return false; |
| } |
| |
| // Test is failing on Linux - https://bugs.eclipse.org/bugs/show_bug.cgi?id=224424 |
| if (System.getProperty("os.name").indexOf("Windows") == -1) return true; |
| |
| // Get the root of the Negative1 model |
| TypeElement element = _elementUtils.getTypeElement("targets.negative.pa.Negative1"); |
| if (null == element || element.getKind() != ElementKind.CLASS) { |
| reportError("Element Negative1 was not found or was not a class"); |
| return false; |
| } |
| |
| return checkModel(Collections.singletonList(element), NEGATIVE_1_MODEL, "Negative1"); |
| } |
| |
| /** |
| * Check the annotations in the model of resources/targets.negative.pa.Negative2 |
| * @return true if all tests passed |
| */ |
| public boolean checkNegative2() { |
| TypeElement elementN2 = _elementUtils.getTypeElement("targets.negative.pa.Negative2"); |
| if (null == elementN2 || elementN2.getKind() != ElementKind.CLASS) { |
| reportError("Element Negative2 was not found or was not a class"); |
| return false; |
| } |
| List<? extends Element> enclosedElements = elementN2.getEnclosedElements(); |
| for (Element element : enclosedElements) { |
| String name = element.getSimpleName().toString(); |
| if ("m1".equals(name)) { |
| AnnotationMirror am2 = findAnnotation(element, "Anno2"); |
| if (_reportFailingCases && null == am2) { |
| reportError("Couldn't find annotation Anno2 on method Negative2.m1"); |
| return false; |
| } |
| } |
| else if ("m2".equals(name)) { |
| AnnotationMirror am1 = findAnnotation(element, "Anno1"); |
| if (_reportFailingCases && null == am1) { |
| reportError("Couldn't find annotation Anno1 on method Negative2.m2"); |
| return false; |
| } |
| AnnotationMirror am3 = findAnnotation(element, "FakeAnno3"); |
| if (_reportFailingCases && null == am3) { |
| reportError("Couldn't find annotation FakeAnno3 on method Negative2.m2"); |
| return false; |
| } |
| } |
| else if ("m3".equals(name)) { |
| AnnotationMirror am2 = findAnnotation(element, "Anno2"); |
| if (_reportFailingCases && null == am2) { |
| reportError("Couldn't find annotation Anno2 on method Negative2.m3"); |
| return false; |
| } |
| AnnotationMirror am3 = findAnnotation(element, "FakeAnno3"); |
| if (_reportFailingCases && null == am3) { |
| reportError("Couldn't find annotation FakeAnno3 on method Negative2.m3"); |
| return false; |
| } |
| } |
| else if ("m4".equals(name)) { |
| AnnotationMirror am4 = findAnnotation(element, "Anno4"); |
| if (_reportFailingCases && null == am4) { |
| reportError("Couldn't find annotation Anno4 on method Negative2.m4"); |
| return false; |
| } |
| Map<? extends ExecutableElement, ? extends AnnotationValue> values = am4.getElementValues(); |
| for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : values.entrySet()) { |
| if ("value".equals(entry.getKey().getSimpleName().toString())) { |
| String value = entry.getValue().getValue().toString(); |
| if (!"123".equals(value) && !"<error>".equals(value)) { |
| reportError("Unexpected value for Anno4 on Negative1.s1: " + value); |
| return false; |
| } |
| } |
| } |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Check the model of resources/targets.negative.pa.Negative3 |
| * @return true if all tests passed |
| */ |
| public boolean checkNegative3() { |
| TypeElement elementN3 = _elementUtils.getTypeElement("targets.negative.pa.Negative3"); |
| if (null == elementN3 || elementN3.getKind() != ElementKind.CLASS) { |
| reportError("Element Negative3 was not found or was not a class"); |
| return false; |
| } |
| List<? extends Element> enclosedElements = elementN3.getEnclosedElements(); |
| for (Element element : enclosedElements) { |
| String name = element.getSimpleName().toString(); |
| if ("foo".equals(name)) { |
| ElementKind kind = element.getKind(); |
| if (_reportFailingCases && ElementKind.METHOD != kind) { |
| reportError("Element 'foo' was expected to be a METHOD but was a " + kind); |
| return false; |
| } |
| List<? extends VariableElement> params = ((ExecutableElement)element).getParameters(); |
| if (_reportFailingCases && (params == null || params.size() != 1)) { |
| reportError("Expected method Negative3.foo() to have one param, but found " + |
| (params == null ? 0 : params.size())); |
| return false; |
| } |
| VariableElement param1 = params.iterator().next(); |
| TypeMirror param1Type = param1.asType(); |
| TypeKind tkind = param1Type.getKind(); |
| if (_reportFailingCases && TypeKind.ERROR != tkind && TypeKind.DECLARED != tkind) { |
| reportError("Expected the TypeKind of Negative3.foo() param to be ERROR or DECLARED, but found " + tkind); |
| return false; |
| } |
| // The behavior of TypeMirror.toString() is suggested, not required, by its javadoc. |
| // So, this is a test of whether we behave like javac, rather than whether we meet the spec. |
| String pname = param1Type.toString(); |
| if (_reportFailingCases && !"M2.M3.M4".equals(pname)) { |
| reportError("Expected toString() of the type of Negative3.foo() param to be M2.M3.M4, but found " + pname); |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Check the model of resources/targets.negative.pa.Negative4 |
| * @return true if all tests passed |
| */ |
| public boolean checkNegative4() throws Exception { |
| TypeElement elementN4 = _elementUtils.getTypeElement("targets.negative.pa.Negative4"); |
| if (null == elementN4 || elementN4.getKind() != ElementKind.CLASS) { |
| reportError("Element Negative4 was not found or was not a class"); |
| return false; |
| } |
| |
| return checkModel(Collections.singletonList(elementN4), NEGATIVE_4_MODEL, "Negative4"); |
| } |
| |
| |
| public boolean checkNegative5() throws Exception { |
| List<TypeElement> rootElements = new ArrayList<TypeElement>(); |
| TypeElement element = _elementUtils.getTypeElement("targets.negative.pa.Negative5"); |
| if (null == element) { |
| reportError("Element Negative5 was not found"); |
| return false; |
| } |
| rootElements.add(element); |
| element = _elementUtils.getTypeElement("targets.negative.pa.INegative5"); |
| if (null == element) { |
| reportError("Element INegative5 was not found"); |
| return false; |
| } |
| rootElements.add(element); |
| |
| return checkModel(rootElements, NEGATIVE_5_MODEL, "Negative5"); |
| } |
| |
| /** |
| * Check the model of resources/targets.negative.pa.Negative6 |
| * @return true if all tests passed |
| */ |
| public boolean checkNegative6() throws Exception { |
| |
| // Get the root of the Negative6 model |
| TypeElement element = _elementUtils.getTypeElement("targets.negative.pa.Negative6"); |
| if (null == element || element.getKind() != ElementKind.CLASS) { |
| reportError("Element Negative6 was not found or was not a class"); |
| return false; |
| } |
| |
| return checkModel(Collections.singletonList(element), NEGATIVE_6_MODEL, "Negative6"); |
| } |
| |
| /** |
| * Check the model of resources/targets.negative.pa.Negative7 |
| * @return true if all tests passed |
| */ |
| public boolean checkNegative7() throws Exception { |
| |
| // Get the roots of the Negative7 model |
| List<TypeElement> rootElements = new ArrayList<TypeElement>(); |
| TypeElement element = _elementUtils.getTypeElement("targets.negative.pa.Negative7"); |
| if (null == element) { |
| reportError("Element Negative7 was not found"); |
| return false; |
| } |
| rootElements.add(element); |
| element = _elementUtils.getTypeElement("targets.negative.pa.Negative7A"); |
| if (null == element) { |
| reportError("Element Negative7A was not found"); |
| return false; |
| } |
| rootElements.add(element); |
| |
| return checkModel(rootElements, NEGATIVE_7_MODEL, "Negative7"); |
| } |
| |
| /** |
| * Check the model of resources/targets.negative.pa.Negative8 |
| * @return true if all tests passed |
| */ |
| public boolean checkNegative8() throws Exception { |
| // check that all expected elements are here |
| List<TypeElement> rootElements = new ArrayList<TypeElement>(); |
| String[] suffixes = new String[] {"a", "b", "c", "d", "e", "f"}; |
| for (int i = 0, l = suffixes.length; i < l; i++) { |
| TypeElement element = _elementUtils.getTypeElement("targets.negative.pa.Negative8" + suffixes[i]); |
| if (null == element) { |
| reportError("Element Negative8" + suffixes[i] + " was not found"); |
| return false; |
| } |
| rootElements.add(element); |
| } |
| if (!checkModel(rootElements, NEGATIVE_8_MODEL, "Negative8")) { |
| return false; |
| } |
| // check that specific elements are not here |
| suffixes = new String[] {"a", "b", "c", "d"}; |
| boolean result = true; |
| String errorMessage = ""; |
| for (int i = 0, l = suffixes.length; i < l; i++) { |
| TypeElement element = _elementUtils.getTypeElement("targets.negative.pa.Negative8" + suffixes[i]); |
| if (! element.getInterfaces().isEmpty()) { |
| errorMessage += "Element Negative8" + suffixes[i] + " has extraneous interfaces\n"; |
| result = false; |
| } |
| } |
| if (!result) { |
| reportError(errorMessage); |
| } |
| return result; |
| } |
| |
| /** |
| * Compare a set of elements to a reference model, and output error information if there is a |
| * mismatch. |
| * |
| * @param rootElements |
| * @param expected |
| * a string representation of the XML reference model, as it would be serialized by |
| * XMLConverter |
| * @param name |
| * the name of the test, which is used for human-readable output |
| * @return true if the actual and expected models were equivalent |
| * @throws Exception |
| */ |
| private boolean checkModel(List<TypeElement> rootElements, String expected, String name) throws Exception { |
| Document actualModel = XMLConverter.convertModel(rootElements); |
| |
| InputSource source = new InputSource(new StringReader(expected)); |
| DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); |
| Document expectedModel = factory.newDocumentBuilder().parse(source); |
| |
| ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| StringBuilder summary = new StringBuilder(); |
| summary.append("Test ").append(name).append(" failed; see console for details. "); |
| boolean success = XMLComparer.compare(actualModel, expectedModel, out, summary, _ignoreJavacBugs); |
| if (!success) { |
| System.out.println("Test " + name + " failed. Detailed output follows:"); |
| System.out.print(out.toString()); |
| System.out.println("Cut and paste:"); |
| System.out.println(XMLConverter.xmlToCutAndPasteString(actualModel, 0, false)); |
| System.out.println("=============== end output ==============="); |
| reportError(summary.toString()); |
| } |
| return success; |
| } |
| |
| /** |
| * Find a particular annotation on a specified element. |
| * @param el the annotated element |
| * @param name the simple name of the annotation |
| * @return a mirror for the annotation, or null if the annotation was not found. |
| */ |
| private AnnotationMirror findAnnotation(Element el, String name) { |
| for (AnnotationMirror am : el.getAnnotationMirrors()) { |
| DeclaredType annoType = am.getAnnotationType(); |
| if (null != annoType) { |
| Element annoTypeElement = annoType.asElement(); |
| if (null != annoTypeElement) { |
| if (name.equals(annoTypeElement.getSimpleName().toString())) { |
| return am; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| } |