1 /*
   2  * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 8038455
  27  * @summary Verify that annotation processor can overwrite source and class files it generated
  28  *          during previous compilations, and that the Symbols are updated appropriately.
  29  * @library /tools/lib /tools/javac/lib/
  30  * @modules jdk.compiler/com.sun.tools.javac.api
  31  *          jdk.compiler/com.sun.tools.javac.main
  32  *          jdk.compiler/com.sun.tools.javac.processing
  33  *          jdk.compiler/com.sun.tools.javac.util
  34  *          jdk.jdeps/com.sun.tools.javap
  35  * @clean *
  36  * @build toolbox.ToolBox toolbox.JavacTask
  37  * @build OverwriteBetweenCompilations JavacTestingAbstractProcessor
  38  * @compile/ref=OverwriteBetweenCompilations_1.out -XDaccessInternalAPI -processor OverwriteBetweenCompilations -Apass=1 -parameters -XDrawDiagnostics OverwriteBetweenCompilationsSource.java
  39  * @compile/ref=OverwriteBetweenCompilations_2.out -XDaccessInternalAPI -processor OverwriteBetweenCompilations -Apass=2 -parameters -XDrawDiagnostics OverwriteBetweenCompilationsSource.java
  40  * @compile/ref=OverwriteBetweenCompilations_3.out -XDaccessInternalAPI -processor OverwriteBetweenCompilations -Apass=3 -parameters -XDrawDiagnostics OverwriteBetweenCompilationsSource.java
  41  */
  42 
  43 import java.io.*;
  44 import java.util.*;
  45 
  46 import javax.annotation.processing.*;
  47 import javax.lang.model.element.*;
  48 import javax.tools.*;
  49 
  50 import com.sun.tools.javac.processing.JavacProcessingEnvironment;
  51 import com.sun.tools.javac.processing.PrintingProcessor.PrintingElementVisitor;
  52 import com.sun.tools.javac.util.Log;
  53 import com.sun.tools.javac.util.Log.WriterKind;
  54 
  55 import toolbox.JavacTask;
  56 import toolbox.ToolBox;
  57 
  58 @SupportedOptions("pass")
  59 public class OverwriteBetweenCompilations extends JavacTestingAbstractProcessor {
  60     int round = 1;
  61     @Override
  62     public boolean process(Set<? extends TypeElement> annotations,
  63                            RoundEnvironment roundEnv) {
  64         Log log = Log.instance(((JavacProcessingEnvironment) processingEnv).getContext());
  65         PrintWriter pw = log.getWriter(WriterKind.NOTICE);
  66 
  67         pw.println("round: " + round);
  68 
  69         TypeElement generatedSource =
  70                 processingEnv.getElementUtils().getTypeElement("GeneratedSource");
  71 
  72         if (generatedSource != null) {
  73             new PrintingElementVisitor(pw, processingEnv.getElementUtils()).visit(generatedSource);
  74             pw.flush();
  75         }
  76 
  77         TypeElement generatedClass =
  78                 processingEnv.getElementUtils().getTypeElement("GeneratedClass");
  79 
  80         if (generatedClass != null) {
  81             new PrintingElementVisitor(pw, processingEnv.getElementUtils()).visit(generatedClass);
  82             pw.flush();
  83         }
  84 
  85         int pass = Integer.parseInt(processingEnv.getOptions().get("pass"));
  86 
  87         if (round++ == 1) {
  88             try (Writer out = filer.createSourceFile("GeneratedSource").openWriter()) {
  89                 String code = pass != 2 ? GENERATED_INIT : GENERATED_UPDATE;
  90                 code = code.replace("NAME", "GeneratedSource");
  91                 out.write(code);
  92             } catch (IOException e) {
  93                 processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.toString());
  94             }
  95             try (OutputStream out = filer.createClassFile("GeneratedClass").openOutputStream()) {
  96                 String code = pass != 2 ? GENERATED_INIT : GENERATED_UPDATE;
  97                 code = code.replace("NAME", "GeneratedClass");
  98 
  99                 ToolBox tb = new ToolBox();
 100                 ToolBox.MemoryFileManager mfm = new ToolBox.MemoryFileManager();
 101                 new JavacTask(tb)
 102                         .fileManager(mfm)
 103                         .options("-parameters")
 104                         .sources(code)
 105                         .run();
 106 
 107                 out.write(mfm.getFileBytes(StandardLocation.CLASS_OUTPUT, "GeneratedClass"));
 108             } catch (IOException e) {
 109                 processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.toString());
 110             }
 111         }
 112 
 113         return false;
 114     }
 115 
 116     //the initial generated class - "NAME" will be replaced with either "GeneratedSource" or
 117     //"GeneratedClass" while generating the class:
 118     private static final String GENERATED_INIT =
 119             "@Deprecated\n" +
 120             "public class NAME<T extends CharSequence> extends java.util.ArrayList<String>\n" +
 121             "                                          implements Runnable {\n" +
 122             "    public void test(int a) { }\n" +
 123             "    public void run() { }\n" +
 124             "}";
 125 
 126     //generated class update- "NAME" will be replaced with either "GeneratedSource" or
 127     //"GeneratedClass" while generating the class:
 128     private static final String GENERATED_UPDATE =
 129             "@javax.annotation.processing.SupportedAnnotationTypes(\"*\")\n" +
 130             "public abstract class NAME<E extends Number> extends java.util.LinkedList<Number>" +
 131             "                                             implements Runnable, CharSequence {\n" +
 132             "    public void test(long a) { }\n" +
 133             "}";
 134 }