694N/A/*
694N/A * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
694N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
694N/A *
694N/A * This code is free software; you can redistribute it and/or modify it
694N/A * under the terms of the GNU General Public License version 2 only, as
694N/A * published by the Free Software Foundation.
694N/A *
694N/A * This code is distributed in the hope that it will be useful, but WITHOUT
694N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
694N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
694N/A * version 2 for more details (a copy is included in the LICENSE file that
694N/A * accompanied this code).
694N/A *
694N/A * You should have received a copy of the GNU General Public License version
694N/A * 2 along with this work; if not, write to the Free Software Foundation,
694N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
694N/A *
694N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
694N/A * or visit www.oracle.com if you need additional information or have any
694N/A * questions.
694N/A */
694N/A
694N/A/*
694N/A * @test
695N/A * @bug 6985205 6986246
694N/A * @summary access to tree positions and doc comments may be lost across annotation processing rounds
694N/A * @build TreePosRoundsTest
694N/A * @compile -proc:only -processor TreePosRoundsTest TreePosRoundsTest.java
694N/A * @run main TreePosRoundsTest
694N/A */
694N/A
694N/Aimport java.io.*;
694N/Aimport java.util.*;
694N/Aimport javax.annotation.processing.*;
694N/Aimport javax.lang.model.*;
694N/Aimport javax.lang.model.element.*;
694N/Aimport javax.tools.*;
694N/A
694N/Aimport com.sun.source.tree.*;
694N/Aimport com.sun.source.util.*;
694N/Aimport javax.tools.JavaCompiler.CompilationTask;
694N/A
694N/A// This test is an annotation processor that performs multiple rounds of
694N/A// processing, and on each round, it checks that source positions are
694N/A// available and correct.
694N/A//
694N/A// The test can be run directly as a processor from the javac command line
694N/A// or via JSR 199 by invoking the main program.
694N/A
694N/A@SupportedAnnotationTypes("*")
694N/Apublic class TreePosRoundsTest extends AbstractProcessor {
694N/A public static void main(String... args) throws Exception {
694N/A String testSrc = System.getProperty("test.src");
694N/A String testClasses = System.getProperty("test.classes");
694N/A JavaCompiler c = ToolProvider.getSystemJavaCompiler();
694N/A StandardJavaFileManager fm = c.getStandardFileManager(null, null, null);
694N/A String thisName = TreePosRoundsTest.class.getName();
694N/A File thisFile = new File(testSrc, thisName + ".java");
694N/A Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(thisFile);
694N/A List<String> options = Arrays.asList(
694N/A "-proc:only",
694N/A "-processor", thisName,
694N/A "-processorpath", testClasses);
694N/A CompilationTask t = c.getTask(null, fm, null, options, null, files);
694N/A boolean ok = t.call();
694N/A if (!ok)
694N/A throw new Exception("processing failed");
694N/A }
694N/A
694N/A Filer filer;
694N/A Messager messager;
695N/A Trees trees;
694N/A
694N/A @Override
694N/A public SourceVersion getSupportedSourceVersion() {
694N/A return SourceVersion.latest();
694N/A }
694N/A
694N/A @Override
694N/A public void init(ProcessingEnvironment pEnv) {
694N/A super.init(pEnv);
694N/A filer = pEnv.getFiler();
694N/A messager = pEnv.getMessager();
695N/A trees = Trees.instance(pEnv);
694N/A }
694N/A
694N/A int round = 0;
694N/A
694N/A @Override
694N/A public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
694N/A round++;
694N/A
694N/A // Scan trees for elements, verifying source tree positions
694N/A for (Element e: roundEnv.getRootElements()) {
694N/A try {
694N/A TreePath p = trees.getPath(e);
694N/A new TestTreeScanner(p.getCompilationUnit(), trees).scan(trees.getPath(e), null);
694N/A } catch (IOException ex) {
694N/A messager.printMessage(Diagnostic.Kind.ERROR,
694N/A "Cannot get source: " + ex, e);
694N/A }
694N/A }
694N/A
694N/A final int MAXROUNDS = 3;
694N/A if (round < MAXROUNDS)
694N/A generateSource("Gen" + round);
694N/A
694N/A return true;
694N/A }
694N/A
694N/A void generateSource(String name) {
694N/A StringBuilder text = new StringBuilder();
694N/A text.append("class ").append(name).append("{\n");
694N/A text.append(" int one = 1;\n");
694N/A text.append(" int two = 2;\n");
694N/A text.append(" int three = one + two;\n");
694N/A text.append("}\n");
694N/A
694N/A try {
694N/A JavaFileObject fo = filer.createSourceFile(name);
694N/A Writer out = fo.openWriter();
694N/A try {
694N/A out.write(text.toString());
694N/A } finally {
694N/A out.close();
694N/A }
694N/A } catch (IOException e) {
694N/A throw new Error(e);
694N/A }
694N/A }
694N/A
694N/A class TestTreeScanner extends TreePathScanner<Void,Void> {
694N/A TestTreeScanner(CompilationUnitTree unit, Trees trees) throws IOException {
694N/A this.unit = unit;
694N/A JavaFileObject sf = unit.getSourceFile();
694N/A source = sf.getCharContent(true).toString();
694N/A sourcePositions = trees.getSourcePositions();
694N/A }
694N/A
694N/A @Override
694N/A public Void visitVariable(VariableTree tree, Void _) {
694N/A check(getCurrentPath());
694N/A return super.visitVariable(tree, _);
694N/A }
694N/A
694N/A void check(TreePath tp) {
694N/A Tree tree = tp.getLeaf();
694N/A
694N/A String expect = tree.toString();
694N/A if (tree.getKind() == Tree.Kind.VARIABLE) {
694N/A // tree.toString() does not know enough context to add ";",
694N/A // so deal with that manually...
694N/A Tree.Kind enclKind = tp.getParentPath().getLeaf().getKind();
694N/A //System.err.println(" encl: " +enclKind);
694N/A if (enclKind == Tree.Kind.CLASS || enclKind == Tree.Kind.BLOCK)
694N/A expect += ";";
694N/A }
694N/A //System.err.println("expect: " + expect);
694N/A
694N/A int start = (int)sourcePositions.getStartPosition(unit, tree);
694N/A if (start == Diagnostic.NOPOS) {
694N/A messager.printMessage(Diagnostic.Kind.ERROR, "start pos not set for " + trim(tree));
694N/A return;
694N/A }
694N/A
694N/A int end = (int)sourcePositions.getEndPosition(unit, tree);
694N/A if (end == Diagnostic.NOPOS) {
694N/A messager.printMessage(Diagnostic.Kind.ERROR, "end pos not set for " + trim(tree));
694N/A return;
694N/A }
694N/A
694N/A String found = source.substring(start, end);
694N/A //System.err.println(" found: " + found);
694N/A
694N/A // allow for long lines, in which case just compare beginning and
694N/A // end of the strings
694N/A boolean equal;
694N/A if (found.contains("\n")) {
694N/A String head = found.substring(0, found.indexOf("\n"));
694N/A String tail = found.substring(found.lastIndexOf("\n")).trim();
694N/A equal = expect.startsWith(head) && expect.endsWith(tail);
694N/A } else {
694N/A equal = expect.equals(found);
694N/A }
694N/A
694N/A if (!equal) {
694N/A messager.printMessage(Diagnostic.Kind.ERROR,
694N/A "unexpected value found: '" + found + "'; expected: '" + expect + "'");
694N/A }
694N/A }
694N/A
694N/A String trim(Tree tree) {
694N/A final int MAXLEN = 32;
694N/A String s = tree.toString().replaceAll("\\s+", " ").trim();
694N/A return (s.length() < MAXLEN) ? s : s.substring(0, MAXLEN);
694N/A
694N/A }
694N/A
694N/A CompilationUnitTree unit;
694N/A SourcePositions sourcePositions;
694N/A String source;
694N/A }
694N/A
694N/A}