# HG changeset patch # User jjg # Date 1264812892 28800 # Node ID 655bba719625a10a2f15b8b4eb8580ad8b5ab4d9 # Parent a132763160d72e62bd2db3d8851ddfd9e1205110 6499119: Created package-info class file modeled improperly 6920317: package-info.java file has to be specified on the javac cmdline, else it will not be avail. Reviewed-by: darcy diff -r a132763160d7 -r 655bba719625 langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java --- a/langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java Fri Jan 29 16:06:51 2010 -0800 +++ b/langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java Fri Jan 29 16:54:52 2010 -0800 @@ -657,6 +657,11 @@ public List getAnnotationMirrors() { if (completer != null) complete(); + if (package_info != null && package_info.completer != null) { + package_info.complete(); + if (attributes_field.isEmpty()) + attributes_field = package_info.attributes_field; + } assert attributes_field != null; return attributes_field; } diff -r a132763160d7 -r 655bba719625 langtools/src/share/classes/com/sun/tools/javac/comp/Enter.java --- a/langtools/src/share/classes/com/sun/tools/javac/comp/Enter.java Fri Jan 29 16:06:51 2010 -0800 +++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Enter.java Fri Jan 29 16:54:52 2010 -0800 @@ -100,6 +100,7 @@ MemberEnter memberEnter; Types types; Lint lint; + Names names; JavaFileManager fileManager; private final Todo todo; @@ -123,6 +124,7 @@ types = Types.instance(context); annotate = Annotate.instance(context); lint = Lint.instance(context); + names = Names.instance(context); predefClassDef = make.ClassDef( make.Modifiers(PUBLIC), @@ -308,6 +310,17 @@ } } } + + for (Symbol q = tree.packge; q != null && q.kind == PCK; q = q.owner) + q.flags_field |= EXISTS; + + Name name = names.package_info; + ClassSymbol c = reader.enterClass(name, tree.packge); + c.flatname = names.fromString(tree.packge + "." + name); + c.sourcefile = tree.sourcefile; + c.completer = null; + c.members_field = new Scope(c); + tree.packge.package_info = c; } classEnter(tree.defs, env); if (addEnv) { diff -r a132763160d7 -r 655bba719625 langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java --- a/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java Fri Jan 29 16:06:51 2010 -0800 +++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java Fri Jan 29 16:54:52 2010 -0800 @@ -1994,19 +1994,14 @@ tree.packageAnnotations), name, List.nil(), null, List.nil(), List.nil()); - ClassSymbol c = reader.enterClass(name, tree.packge); - c.flatname = names.fromString(tree.packge + "." + name); - c.sourcefile = tree.sourcefile; - c.completer = null; - c.members_field = new Scope(c); - c.flags_field = flags; + ClassSymbol c = tree.packge.package_info; + c.flags_field |= flags; c.attributes_field = tree.packge.attributes_field; ClassType ctype = (ClassType) c.type; ctype.supertype_field = syms.objectType; ctype.interfaces_field = List.nil(); packageAnnotationsClass.sym = c; - translated.append(packageAnnotationsClass); } } diff -r a132763160d7 -r 655bba719625 langtools/src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java --- a/langtools/src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java Fri Jan 29 16:06:51 2010 -0800 +++ b/langtools/src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java Fri Jan 29 16:54:52 2010 -0800 @@ -67,6 +67,7 @@ import com.sun.tools.javac.tree.JCTree.*; import com.sun.tools.javac.util.Abort; import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.Convert; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Log; @@ -893,14 +894,20 @@ errorStatus = true; break runAround; } else { - ListBuffer classes = enterNewClassFiles(currentContext); + List newClasses = enterNewClassFiles(currentContext); compiler.enterTrees(roots); // annotationsPresentInSource = // collector.findAnnotations(parsedFiles); - classes.appendList(getTopLevelClasses(parsedFiles)); - topLevelClasses = classes.toList(); - packageInfoFiles = getPackageInfoFiles(parsedFiles); + ListBuffer tlc = new ListBuffer(); + tlc.appendList(getTopLevelClasses(parsedFiles)); + tlc.appendList(getTopLevelClassesFromClasses(newClasses)); + topLevelClasses = tlc.toList(); + + ListBuffer pif = new ListBuffer(); + pif.appendList(getPackageInfoFiles(parsedFiles)); + pif.appendList(getPackageInfoFilesFromClasses(newClasses)); + packageInfoFiles = pif.toList(); annotationsPresent = new LinkedHashSet(); for (ClassSymbol classSym : topLevelClasses) @@ -1026,20 +1033,30 @@ } } - private ListBuffer enterNewClassFiles(Context currentContext) { + private List enterNewClassFiles(Context currentContext) { ClassReader reader = ClassReader.instance(currentContext); Names names = Names.instance(currentContext); - ListBuffer list = new ListBuffer(); + List list = List.nil(); for (Map.Entry entry : filer.getGeneratedClasses().entrySet()) { Name name = names.fromString(entry.getKey()); JavaFileObject file = entry.getValue(); if (file.getKind() != JavaFileObject.Kind.CLASS) throw new AssertionError(file); - ClassSymbol cs = reader.enterClass(name, file); - list.append(cs); + ClassSymbol cs; + if (isPkgInfo(file, JavaFileObject.Kind.CLASS)) { + Name packageName = Convert.packagePart(name); + PackageSymbol p = reader.enterPackage(packageName); + if (p.package_info == null) + p.package_info = reader.enterClass(Convert.shortName(name), p); + cs = p.package_info; + if (cs.classfile == null) + cs.classfile = file; + } else + cs = reader.enterClass(name, file); + list = list.prepend(cs); } - return list; + return list.reverse(); } /** @@ -1066,18 +1083,44 @@ return classes.reverse(); } + private List getTopLevelClassesFromClasses(List syms) { + List classes = List.nil(); + for (ClassSymbol sym : syms) { + if (!isPkgInfo(sym)) { + classes = classes.prepend(sym); + } + } + return classes.reverse(); + } + private List getPackageInfoFiles(List units) { List packages = List.nil(); for (JCCompilationUnit unit : units) { - boolean isPkgInfo = unit.sourcefile.isNameCompatible("package-info", - JavaFileObject.Kind.SOURCE); - if (isPkgInfo) { + if (isPkgInfo(unit.sourcefile, JavaFileObject.Kind.SOURCE)) { packages = packages.prepend(unit.packge); } } return packages.reverse(); } + private List getPackageInfoFilesFromClasses(List syms) { + List packages = List.nil(); + for (ClassSymbol sym : syms) { + if (isPkgInfo(sym)) { + packages = packages.prepend((PackageSymbol) sym.owner); + } + } + return packages.reverse(); + } + + private boolean isPkgInfo(JavaFileObject fo, JavaFileObject.Kind kind) { + return fo.isNameCompatible("package-info", kind); + } + + private boolean isPkgInfo(ClassSymbol sym) { + return isPkgInfo(sym.classfile, JavaFileObject.Kind.CLASS) && (sym.packge().package_info == sym); + } + private Context contextForNextRound(Context context, boolean shareNames) throws IOException { diff -r a132763160d7 -r 655bba719625 langtools/test/tools/javac/processing/6499119/ClassProcessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/processing/6499119/ClassProcessor.java Fri Jan 29 16:54:52 2010 -0800 @@ -0,0 +1,132 @@ +/* + * Copyright 2010 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import java.io.*; +import java.util.*; +import javax.annotation.processing.*; +import javax.lang.model.element.*; +import javax.lang.model.SourceVersion; +import javax.tools.Diagnostic.Kind; + +/* + * @test + * @bug 6499119 + * @summary Created package-info class file modeled improperly + * @compile ClassProcessor.java package-info.java + * @compile/process -cp . -processor ClassProcessor -Akind=java java.lang.Object + * @compile/process -cp . -processor ClassProcessor -Akind=class java.lang.Object + */ + +@SupportedOptions({ "gen", "expect" }) +@SupportedAnnotationTypes({"*"}) +public class ClassProcessor extends AbstractProcessor { + int round = 1; + + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (round == 1) { + System.out.println("-- Round 1 --"); + createPackageFile(); + } else if (round == 2) { + boolean found_foo_A = false; + System.out.println("-- Round 2 --"); + for(Element e: roundEnv.getRootElements()) { + System.out.println("ElementKind: " + e.getKind()); + System.out.println("Modifiers: " + e.getModifiers()); + System.out.println("Annotations: " + e.getAnnotationMirrors()); + if (e.getAnnotationMirrors().toString().equals("@foo.A")) { + found_foo_A = true; + checkEqual("ElementKind", e.getKind().toString(), "PACKAGE"); + checkEqual("Modifiers", e.getModifiers().toString(), "[]"); + } + } + if (!found_foo_A) + error("did not find @foo.A"); + } + round++; + return true; + } + + private void createPackageFile() { + Filer filer = processingEnv.getFiler(); + + String kind = processingEnv.getOptions().get("kind"); + + File pkgInfo; + if (kind.equals("java")) + pkgInfo = new File(System.getProperty("test.src"), "package-info.java"); + else + pkgInfo = new File(System.getProperty("test.classes"), "foo/package-info.class"); + + byte[] bytes = new byte[(int) pkgInfo.length()]; + DataInputStream in = null; + try { + in = new DataInputStream(new FileInputStream(pkgInfo)); + in.readFully(bytes); + } catch (IOException ioe) { + error("Couldn't read package info file: " + ioe); + } finally { + if(in != null) { + try { + in.close(); + } catch (IOException e) { + error("InputStream closing failed: " + e); + } + } + } + + OutputStream out = null; + try { + if (kind.equals("java")) + out = filer.createSourceFile("foo.package-info").openOutputStream(); + else + out = filer.createClassFile("foo.package-info").openOutputStream(); + out.write(bytes, 0, bytes.length); + } catch (IOException ioe) { + error("Couldn't create package info file: " + ioe); + } finally { + if(out != null) { + try { + out.close(); + } catch (IOException e) { + error("OutputStream closing failed: " + e); + } + } + } + } + + private void checkEqual(String label, String actual, String expect) { + if (!actual.equals(expect)) { + error("Unexpected value for " + label + "; actual=" + actual + ", expected=" + expect); + } + } + + private void error(String msg) { + Messager messager = processingEnv.getMessager(); + messager.printMessage(Kind.ERROR, msg); + } +} + diff -r a132763160d7 -r 655bba719625 langtools/test/tools/javac/processing/6499119/package-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/processing/6499119/package-info.java Fri Jan 29 16:54:52 2010 -0800 @@ -0,0 +1,27 @@ +/* + * Copyright 2010 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +@A package foo; + +@interface A {} + diff -r a132763160d7 -r 655bba719625 langtools/test/tools/javac/processing/T6920317.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/processing/T6920317.java Fri Jan 29 16:54:52 2010 -0800 @@ -0,0 +1,462 @@ +/* + * Copyright 2010 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6920317 + * @summary package-info.java file has to be specified on the javac cmdline, else it will not be avail + */ + +import java.io.*; +import java.util.*; +import javax.annotation.processing.*; +import javax.lang.model.*; +import javax.lang.model.element.*; +import javax.lang.model.util.*; +import javax.tools.*; + +/** + * The test exercises different ways of providing annotations for a package. + * Each way provides an annotation with a unique argument. For each test + * case, the test verifies that the annotation with the correct argument is + * found by the compiler. + */ +public class T6920317 { + public static void main(String... args) throws Exception { + new T6920317().run(args); + } + + // Used to describe properties of files to be put on command line, source path, class path + enum Kind { + /** File is not used. */ + NONE, + /** File is used. */ + OLD, + /** Only applies to files on classpath/sourcepath, when there is another file on the + * other path of type OLD, in which case, this file must be newer than the other one. */ + NEW, + /** Only applies to files on classpath/sourcepath, when there is no file in any other + * location, in which case, this file will be generated by the annotation processor. */ + GEN + } + + void run(String... args) throws Exception { + // if no args given, all test cases are run + // if args given, they indicate the test cases to be run + for (int i = 0; i < args.length; i++) { + tests.add(Integer.valueOf(args[i])); + } + + setup(); + + // Run tests for all combinations of files on command line, source path and class path. + // Invalid combinations are skipped in the test method + for (Kind cmdLine: EnumSet.of(Kind.NONE, Kind.OLD)) { + for (Kind srcPath: Kind.values()) { + for (Kind clsPath: Kind.values()) { + try { + test(cmdLine, srcPath, clsPath); + } catch (Exception e) { + e.printStackTrace(); + error("Exception " + e); + // uncomment to stop on first failed test case + // throw e; + } + } + } + } + + if (errors > 0) + throw new Exception(errors + " errors occurred"); + } + + /** One time setup for files and directories to be used in the various test cases. */ + void setup() throws Exception { + // Annotation used in test cases to annotate package. This file is + // given on the command line in test cases. + test_java = writeFile("Test.java", "package p; @interface Test { String value(); }"); + // Compile the annotation for use later in setup + File tmpClasses = new File("tmp.classes"); + compile(tmpClasses, new String[] { }, test_java); + + // package-info file to use on the command line when requied + cl_pkgInfo_java = writeFile("cl/p/package-info.java", "@Test(\"CL\") package p;"); + + // source path containing package-info + sp_old = new File("src.old"); + writeFile("src.old/p/package-info.java", "@Test(\"SP_OLD\") package p;"); + + // class path containing package-info + cp_old = new File("classes.old"); + compile(cp_old, new String[] { "-classpath", tmpClasses.getPath() }, + writeFile("tmp.old/p/package-info.java", "@Test(\"CP_OLD\") package p;")); + + // source path containing package-info which is newer than the one in cp-old + sp_new = new File("src.new"); + File old_class = new File(cp_old, "p/package-info.class"); + writeFile("src.new/p/package-info.java", "@Test(\"SP_NEW\") package p;", old_class); + + // class path containing package-info which is newer than the one in sp-old + cp_new = new File("classes.new"); + File old_java = new File(sp_old, "p/package-info.java"); + compile(cp_new, new String[] { "-classpath", tmpClasses.getPath() }, + writeFile("tmp.new/p/package-info.java", "@Test(\"CP_NEW\") package p;", old_java)); + + // directory containing package-info.java to be "generated" later by annotation processor + sp_gen = new File("src.gen"); + writeFile("src.gen/p/package-info.java", "@Test(\"SP_GEN\") package p;"); + + // directory containing package-info.class to be "generated" later by annotation processor + cp_gen = new File("classes.gen"); + compile(cp_gen, new String[] { "-classpath", tmpClasses.getPath() }, + writeFile("tmp.gen/p/package-info.java", "@Test(\"CP_GEN\") package p;")); + } + + void test(Kind cl, Kind sp, Kind cp) throws Exception { + if (skip(cl, sp, cp)) + return; + + ++count; + // if test cases specified, skip this test case if not selected + if (tests.size() > 0 && !tests.contains(count)) + return; + + System.err.println("Test " + count + " cl:" + cl + " sp:" + sp + " cp:" + cp); + + // test specific tmp directory + File test_tmp = new File("tmp.test" + count); + test_tmp.mkdirs(); + + // build up list of options and files to be compiled + List opts = new ArrayList(); + List files = new ArrayList(); + + // expected value for annotation + String expect = null; + + opts.add("-processorpath"); + opts.add(System.getProperty("test.classes")); + opts.add("-processor"); + opts.add(Processor.class.getName()); + opts.add("-proc:only"); + opts.add("-d"); + opts.add(test_tmp.getPath()); + //opts.add("-verbose"); + files.add(test_java); + + /* + * Analyze each of cl, cp, sp, building up the options and files to + * be compiled, and determining the expected outcome fo the test case. + */ + + // command line file: either omitted or given + if (cl == Kind.OLD) { + files.add(cl_pkgInfo_java); + // command line files always supercede files on paths + expect = "CL"; + } + + // source path: + switch (sp) { + case NONE: + break; + + case OLD: + opts.add("-sourcepath"); + opts.add(sp_old.getPath()); + if (expect == null && cp == Kind.NONE) { + assert cl == Kind.NONE && cp == Kind.NONE; + expect = "SP_OLD"; + } + break; + + case NEW: + opts.add("-sourcepath"); + opts.add(sp_new.getPath()); + if (expect == null) { + assert cl == Kind.NONE && cp == Kind.OLD; + expect = "SP_NEW"; + } + break; + + case GEN: + opts.add("-Agen=" + new File(sp_gen, "p/package-info.java")); + assert cl == Kind.NONE && cp == Kind.NONE; + expect = "SP_GEN"; + break; + } + + // class path: + switch (cp) { + case NONE: + break; + + case OLD: + opts.add("-classpath"); + opts.add(cp_old.getPath()); + if (expect == null && sp == Kind.NONE) { + assert cl == Kind.NONE && sp == Kind.NONE; + expect = "CP_OLD"; + } + break; + + case NEW: + opts.add("-classpath"); + opts.add(cp_new.getPath()); + if (expect == null) { + assert cl == Kind.NONE && sp == Kind.OLD; + expect = "CP_NEW"; + } + break; + + case GEN: + opts.add("-Agen=" + new File(cp_gen, "p/package-info.class")); + assert cl == Kind.NONE && sp == Kind.NONE; + expect = "CP_GEN"; + break; + } + + // pass expected value to annotation processor + assert expect != null; + opts.add("-Aexpect=" + expect); + + // compile the files with the options that have been built up + compile(opts, files); + } + + /** + * Return true if this combination of parameters does not identify a useful test case. + */ + boolean skip(Kind cl, Kind sp, Kind cp) { + // skip if no package files required + if (cl == Kind.NONE && sp == Kind.NONE && cp == Kind.NONE) + return true; + + // skip if both sp and sp are OLD, since results may be indeterminate + if (sp == Kind.OLD && cp == Kind.OLD) + return true; + + // skip if sp or cp is NEW but the other is not OLD + if ((sp == Kind.NEW && cp != Kind.OLD) || (cp == Kind.NEW && sp != Kind.OLD)) + return true; + + // only use GEN if no other package-info files present + if (sp == Kind.GEN && !(cl == Kind.NONE && cp == Kind.NONE) || + cp == Kind.GEN && !(cl == Kind.NONE && sp == Kind.NONE)) { + return true; + } + + // remaining combinations are valid + return false; + } + + /** Write a file with a given body. */ + File writeFile(String path, String body) throws Exception { + File f = new File(path); + if (f.getParentFile() != null) + f.getParentFile().mkdirs(); + Writer out = new FileWriter(path); + try { + out.write(body); + } finally { + out.close(); + } + return f; + } + + /** Write a file with a given body, ensuring that the file is newer than a reference file. */ + File writeFile(String path, String body, File ref) throws Exception { + for (int i = 0; i < 5; i++) { + File f = writeFile(path, body); + if (f.lastModified() > ref.lastModified()) + return f; + Thread.sleep(2000); + } + throw new Exception("cannot create file " + path + " newer than " + ref); + } + + /** Compile a file to a given directory, with options provided. */ + void compile(File dir, String[] opts, File src) throws Exception { + dir.mkdirs(); + List opts2 = new ArrayList(); + opts2.addAll(Arrays.asList("-d", dir.getPath())); + opts2.addAll(Arrays.asList(opts)); + compile(opts2, Collections.singletonList(src)); + } + + /** Compile files with options provided. */ + void compile(List opts, List files) throws Exception { + System.err.println("javac: " + opts + " " + files); + List args = new ArrayList(); + args.addAll(opts); + for (File f: files) + args.add(f.getPath()); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + int rc = com.sun.tools.javac.Main.compile(args.toArray(new String[args.size()]), pw); + pw.flush(); + if (sw.getBuffer().length() > 0) + System.err.println(sw.toString()); + if (rc != 0) + throw new Exception("compilation failed: rc=" + rc); + } + + /** Report an error. */ + void error(String msg) { + System.err.println("Error: " + msg); + errors++; + } + + /** Test case counter. */ + int count; + + /** Number of errors found. */ + int errors; + + /** Optional set of test cases to be run; empty implies all test cases. */ + Set tests = new HashSet(); + + /* Files created by setup. */ + File test_java; + File sp_old; + File sp_new; + File sp_gen; + File cp_old; + File cp_new; + File cp_gen; + File cl_pkgInfo_java; + + /** Annotation processor used to verify the expected value for the + package annotations found by javac. */ + @SupportedOptions({ "gen", "expect" }) + @SupportedAnnotationTypes({"*"}) + public static class Processor extends AbstractProcessor { + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + + public boolean process(Set annots, RoundEnvironment renv) { + round++; + System.err.println("Round " + round + " annots:" + annots + " rootElems:" + renv.getRootElements()); + + // if this is the first round and the gen option is given, use the filer to create + // a copy of the file specified by the gen option. + String gen = getOption("gen"); + if (round == 1 && gen != null) { + try { + Filer filer = processingEnv.getFiler(); + JavaFileObject f; + if (gen.endsWith(".java")) + f = filer.createSourceFile("p.package-info"); + else + f = filer.createClassFile("p.package-info"); + System.err.println("copy " + gen + " to " + f.getName()); + write(f, read(new File(gen))); + } catch (IOException e) { + error("Cannot create package-info file: " + e); + } + } + + // if annotation processing is complete, verify the package annotation + // found by the compiler. + if (renv.processingOver()) { + System.err.println("final round"); + Elements eu = processingEnv.getElementUtils(); + TypeElement te = eu.getTypeElement("p.Test"); + PackageElement pe = eu.getPackageOf(te); + System.err.println("final: te:" + te + " pe:" + pe); + List annos = pe.getAnnotationMirrors(); + System.err.println("final: annos:" + annos); + if (annos.size() == 1) { + String expect = "@" + te + "(\"" + getOption("expect") + "\")"; + String actual = annos.get(0).toString(); + checkEqual("package annotations", actual, expect); + } else { + error("Wrong number of annotations found: (" + annos.size() + ") " + annos); + } + } + + return true; + } + + /** Get an option given to the annotation processor. */ + String getOption(String name) { + return processingEnv.getOptions().get(name); + } + + /** Read a file. */ + byte[] read(File file) { + byte[] bytes = new byte[(int) file.length()]; + DataInputStream in = null; + try { + in = new DataInputStream(new FileInputStream(file)); + in.readFully(bytes); + } catch (IOException e) { + error("Error reading file: " + e); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + error("Error closing file: " + e); + } + } + } + return bytes; + } + + /** Write a file. */ + void write(JavaFileObject file, byte[] bytes) { + OutputStream out = null; + try { + out = file.openOutputStream(); + out.write(bytes, 0, bytes.length); + } catch (IOException e) { + error("Error writing file: " + e); + } finally { + if (out != null) { + try { + out.close(); + } catch (IOException e) { + error("Error closing file: " + e); + } + } + } + } + + /** Check two strings are equal, and report an error if they are not. */ + private void checkEqual(String label, String actual, String expect) { + if (!actual.equals(expect)) { + error("Unexpected value for " + label + "; actual=" + actual + ", expected=" + expect); + } + } + + /** Report an error to the annotation processing system. */ + void error(String msg) { + Messager messager = processingEnv.getMessager(); + messager.printMessage(Diagnostic.Kind.ERROR, msg); + } + + int round; + } +}