1 /*
   2  * Copyright (c) 2006, 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 6380018 6453386 6457283
  27  * @summary Test that the constraints guaranteed by the Filer and maintained
  28  * @author  Joseph D. Darcy
  29  * @build TestFilerConstraints
  30  * @compile -encoding iso-8859-1 -processor TestFilerConstraints -proc:only TestFilerConstraints.java
  31  */
  32 
  33 import java.util.Set;
  34 import javax.annotation.processing.*;
  35 import javax.lang.model.SourceVersion;
  36 import static javax.lang.model.SourceVersion.*;
  37 import javax.lang.model.element.*;
  38 import javax.lang.model.util.*;
  39 import static javax.lang.model.util.ElementFilter.*;
  40 import static javax.tools.Diagnostic.Kind.*;
  41 import static javax.tools.StandardLocation.*;
  42 
  43 import java.io.*;
  44 import java.nio.charset.Charset;
  45 
  46 /**
  47  * A processor that verifies the explicit and implicit constraints in
  48  * the Filer contract are maintained:
  49  *
  50  * <blockquote>
  51  *
  52  *  During each run of an annotation processing tool, a file with a
  53  *  given pathname may be created only once. If that file already
  54  *  exists before the first attempt to create it, the old contents
  55  *  will be deleted. Any subsequent attempt to create the same file
  56  *  during a run will throw a FilerException, as will attempting to
  57  *  open both a class file and source file for the same type name.
  58  *
  59  * </blockquote>
  60  *
  61  * Specific checks will include:
  62  *
  63  * <ul>
  64  *
  65  * <li> Source and class files can be written to from either a Writer or an OutputStream.
  66  *
  67  * <li> Calling close multiple times does not re-register the file for
  68  * processing.
  69  *
  70  * </ul>
  71  */
  72 @SupportedAnnotationTypes("*")
  73 public class TestFilerConstraints extends AbstractProcessor {
  74     private int round = 0;
  75     private Messager messager;
  76     private Filer filer;
  77 
  78     private PrintWriter  pw_src1 = null;
  79     private PrintWriter  pw_src2 = null;
  80     private OutputStream os_classFile1 = null;
  81     private      Writer  pw_classFile2 = null;
  82 
  83     public boolean process(Set<? extends TypeElement> annotations,
  84                            RoundEnvironment roundEnv) {
  85         round++;
  86 
  87         try {
  88             switch(round) {
  89                 // Open two source files
  90             case 1:
  91                 pw_src1 = new PrintWriter(filer.createSourceFile("Src1").openWriter());
  92                 pw_src1.println("class Src1 {}");
  93                 pw_src1.close();
  94 
  95                 // Hold open across rounds
  96                 pw_src2 = new PrintWriter(new OutputStreamWriter(filer.createSourceFile("Src2").openOutputStream()));
  97                 break;
  98 
  99             case 2:
 100                 testExpectedType(roundEnv, "Src1");
 101 
 102                 // Close Src1 a second time
 103                 pw_src1.close();
 104 
 105                 pw_src2.println("class Src2 {}");
 106                 pw_src2.close();
 107 
 108                 break;
 109 
 110             case 3:
 111                 testExpectedType(roundEnv, "Src2");
 112 
 113                 // Close Src2 a second time
 114                 pw_src2.close();
 115 
 116                 os_classFile1 = filer.createClassFile("ClassFile1").openOutputStream();
 117                 for (int value : classFile1Bytes)
 118                     os_classFile1.write((byte)value);
 119                 os_classFile1.close();
 120 
 121                 break;
 122 
 123             case 4:
 124                 testExpectedType(roundEnv, "ClassFile1");
 125 
 126                 // Close a second time
 127                 os_classFile1.close();
 128 
 129                 testReopening();
 130 
 131                 pw_classFile2 = new PrintWriter(filer.createClassFile("ClassFile2",
 132                                                                       (Element[])null).openWriter());
 133 
 134                 for(int byteVal : classFile2Bytes) {
 135                     // int value = (0xff00 & (classFile2Bytes[i]<<8)) | classFile2Bytes[i+1];
 136                     // System.out.print(Integer.toHexString(value));
 137                     //if ((i % 4) == 0)
 138                     // System.out.println();
 139                     pw_classFile2.write((char) (0xff & byteVal));
 140                 }
 141                 pw_classFile2.close();
 142 
 143                 break;
 144 
 145 
 146 
 147             case 5:
 148                 testExpectedType(roundEnv, "ClassFile2");
 149                 // Close a second time
 150                 pw_classFile2.close();
 151 
 152 
 153                 break;
 154 
 155             case 6:
 156                 if (!roundEnv.processingOver() && !roundEnv.errorRaised())
 157                     throw new RuntimeException("Bad round state: " + roundEnv);
 158                 break;
 159 
 160             default:
 161                 throw new RuntimeException("Unexpected round number!");
 162             }
 163         } catch (IOException ioe) {
 164             throw new RuntimeException(ioe);
 165         }
 166 
 167         return true;
 168     }
 169 
 170     public SourceVersion getSupportedSourceVersion() {
 171         return SourceVersion.latest();
 172     }
 173 
 174     public void init(ProcessingEnvironment processingEnv) {
 175         super.init(processingEnv);
 176         messager = processingEnv.getMessager();
 177         filer    = processingEnv.getFiler();
 178 
 179     }
 180 
 181     /**
 182      * Test that the single expected expected type, name, is the root
 183      * element.
 184      */
 185     private void testExpectedType(RoundEnvironment roundEnv, String name) {
 186         if (!roundEnv.getRootElements().isEmpty()) {
 187             for(TypeElement type : typesIn(roundEnv.getRootElements())) {
 188                 if (!name.contentEquals(type.getSimpleName()))
 189                     throw new RuntimeException("Unexpected type " +  type.getSimpleName());
 190             }
 191         } else
 192             throw new RuntimeException("Unexpected empty root elements.");
 193     }
 194 
 195     private void testReopening() throws IOException {
 196         String[] names = {"Src1", "Src2", "ClassFile1"};
 197         for (String name : names) {
 198             try {
 199                 filer.createSourceFile(name);
 200                 throw new RuntimeException("Opened a source file for type " + name);
 201             } catch (FilerException fe) {;}
 202 
 203             try {
 204                 filer.createClassFile(name);
 205                 throw new RuntimeException("Opened a class file for type " + name);
 206             } catch (FilerException fe) {;}
 207         }
 208 
 209         // Try to open a resource over a source file
 210         try {
 211             filer.createResource(SOURCE_OUTPUT, "", "Src1.java");
 212             throw new RuntimeException("Opened a text file over Src1.java!");
 213         } catch (FilerException fe) {;}
 214 
 215         // Try to open a resource over a class file
 216         try {
 217             filer.createResource(CLASS_OUTPUT, "", "ClassFile1.class");
 218             throw new RuntimeException("Opened a text file over Src1.java!");
 219         } catch (FilerException fe) {;}
 220 
 221     }
 222 
 223     private int[] classFile1Bytes =
 224     {202, 254, 186, 190,   0,   0,   0,  50,
 225        0,  13,  10,   0,   3,   0,  10,   7,
 226        0,  11,   7,   0,  12,   1,   0,   6,
 227       60, 105, 110, 105, 116,  62,   1,   0,
 228        3,  40,  41,  86,   1,   0,   4,  67,
 229      111, 100, 101,   1,   0,  15,  76, 105,
 230      110, 101,  78, 117, 109,  98, 101, 114,
 231       84,  97,  98, 108, 101,   1,   0,  10,
 232       83, 111, 117, 114,  99, 101,  70, 105,
 233      108, 101,   1,   0,  15,  67, 108,  97,
 234      115, 115,  70, 105, 108, 101,  49,  46,
 235      106,  97, 118,  97,  12,   0,   4,   0,
 236        5,   1,   0,  10,  67, 108,  97, 115,
 237      115,  70, 105, 108, 101,  49,   1,   0,
 238       16, 106,  97, 118,  97,  47, 108,  97,
 239      110, 103,  47,  79,  98, 106, 101,  99,
 240      116,   0,  33,   0,   2,   0,   3,   0,
 241        0,   0,   0,   0,   1,   0,   1,   0,
 242        4,   0,   5,   0,   1,   0,   6,   0,
 243        0,   0,  29,   0,   1,   0,   1,   0,
 244        0,   0,   5,  42, 183,   0,   1, 177,
 245        0,   0,   0,   1,   0,   7,   0,   0,
 246        0,   6,   0,   1,   0,   0,   0,   1,
 247        0,   1,   0,   8,   0,   0,   0,   2,
 248        0,   9,};
 249 
 250     private int[] classFile2Bytes =
 251     {202, 254, 186, 190,   0,   0,   0,  50,
 252        0,  13,  10,   0,   3,   0,  10,   7,
 253        0,  11,   7,   0,  12,   1,   0,   6,
 254       60, 105, 110, 105, 116,  62,   1,   0,
 255        3,  40,  41,  86,   1,   0,   4,  67,
 256      111, 100, 101,   1,   0,  15,  76, 105,
 257      110, 101,  78, 117, 109,  98, 101, 114,
 258       84,  97,  98, 108, 101,   1,   0,  10,
 259       83, 111, 117, 114,  99, 101,  70, 105,
 260      108, 101,   1,   0,  15,  67, 108,  97,
 261      115, 115,  70, 105, 108, 101,  50,  46,
 262      106,  97, 118,  97,  12,   0,   4,   0,
 263        5,   1,   0,  10,  67, 108,  97, 115,
 264      115,  70, 105, 108, 101,  50,   1,   0,
 265       16, 106,  97, 118,  97,  47, 108,  97,
 266      110, 103,  47,  79,  98, 106, 101,  99,
 267      116,   0,  33,   0,   2,   0,   3,   0,
 268        0,   0,   0,   0,   1,   0,   1,   0,
 269        4,   0,   5,   0,   1,   0,   6,   0,
 270        0,   0,  29,   0,   1,   0,   1,   0,
 271        0,   0,   5,  42, 183,   0,   1, 177,
 272        0,   0,   0,   1,   0,   7,   0,   0,
 273        0,   6,   0,   1,   0,   0,   0,   1,
 274        0,   1,   0,   8,   0,   0,   0,   2,
 275        0,   9,};
 276 }