1/* 2 * Copyright (c) 2011, 2015, 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 7093891 27 * @summary support multiple task listeners 28 * @modules jdk.compiler/com.sun.tools.javac.api 29 * jdk.compiler/com.sun.tools.javac.file 30 */ 31 32import java.io.File; 33import java.io.IOException; 34import java.io.PrintWriter; 35import java.io.StringWriter; 36import java.net.URI; 37import java.util.ArrayList; 38import java.util.Arrays; 39import java.util.EnumMap; 40import java.util.List; 41import java.util.Set; 42 43import javax.annotation.processing.AbstractProcessor; 44import javax.annotation.processing.RoundEnvironment; 45import javax.annotation.processing.SupportedAnnotationTypes; 46import javax.lang.model.element.TypeElement; 47import javax.tools.JavaFileObject; 48import javax.tools.SimpleJavaFileObject; 49import javax.tools.StandardJavaFileManager; 50import javax.tools.StandardLocation; 51import javax.tools.ToolProvider; 52 53import com.sun.source.util.JavacTask; 54import com.sun.source.util.TaskEvent; 55import com.sun.source.util.TaskListener; 56import com.sun.tools.javac.api.JavacTool; 57 58public class TestSimpleAddRemove { 59 enum AddKind { 60 SET_IN_TASK, 61 ADD_IN_TASK, 62 ADD_IN_PROCESSOR, 63 ADD_IN_LISTENER; 64 } 65 66 enum RemoveKind { 67 REMOVE_IN_TASK, 68 REMOVE_IN_PROCESSOR, 69 REMOVE_IN_LISTENER, 70 } 71 72 enum CompileKind { 73 CALL { 74 void run(JavacTask t) { 75 if (!t.call()) throw new Error("compilation failed"); 76 } 77 }, 78 GENERATE { 79 void run(JavacTask t) throws IOException { 80 t.generate(); 81 } 82 }; 83 abstract void run(JavacTask t) throws IOException; 84 } 85 86 static class EventKindCounter extends EnumMap<TaskEvent.Kind, EventKindCounter.Count> { 87 static class Count { 88 int started; 89 int finished; 90 91 @Override 92 public String toString() { 93 return started + ":" + finished; 94 } 95 } 96 97 EventKindCounter() { 98 super(TaskEvent.Kind.class); 99 } 100 101 void inc(TaskEvent.Kind k, boolean started) { 102 Count c = get(k); 103 if (c == null) 104 put(k, c = new Count()); 105 106 if (started) 107 c.started++; 108 else 109 c.finished++; 110 } 111 } 112 113 static class TestListener implements TaskListener { 114 EventKindCounter counter; 115 116 TestListener(EventKindCounter c) { 117 counter = c; 118 } 119 120 public void started(TaskEvent e) { 121 counter.inc(e.getKind(), true); 122 } 123 124 public void finished(TaskEvent e) { 125 counter.inc(e.getKind(), false); 126 } 127 } 128 129 static void addInListener(final JavacTask task, final TaskEvent.Kind kind, final TaskListener listener) { 130 task.addTaskListener(new TaskListener() { 131 public void started(TaskEvent e) { 132 if (e.getKind() == kind) { 133 task.addTaskListener(listener); 134 task.removeTaskListener(this); 135 } 136 } 137 138 public void finished(TaskEvent e) { } 139 }); 140 } 141 142 static void removeInListener(final JavacTask task, final TaskEvent.Kind kind, final TaskListener listener) { 143 task.addTaskListener(new TaskListener() { 144 public void started(TaskEvent e) { 145 if (e.getKind() == kind) { 146 task.removeTaskListener(listener); 147 task.removeTaskListener(this); 148 } 149 } 150 151 public void finished(TaskEvent e) { } 152 }); 153 } 154 155 @SupportedAnnotationTypes("*") 156 class TestProcessor extends AbstractProcessor { 157 AddKind ak; 158 RemoveKind rk; 159 TaskListener listener; 160 161 TestProcessor(AddKind ak, RemoveKind rk, TaskListener listener) { 162 this.ak = ak; 163 this.rk = rk; 164 this.listener = listener; 165 } 166 167 int round = 0; 168 169 @Override 170 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 171// System.err.println("TestProcessor.process " + roundEnv); 172 JavacTask task = JavacTask.instance(processingEnv); 173 if (++round == 1) { 174 switch (ak) { 175 case ADD_IN_PROCESSOR: 176 task.addTaskListener(listener); 177 break; 178 case ADD_IN_LISTENER: 179 addInListener(task, TaskEvent.Kind.ANALYZE, listener); 180 break; 181 } 182 } else if (roundEnv.processingOver()) { 183 switch (rk) { 184 case REMOVE_IN_PROCESSOR: 185 task.removeTaskListener(listener); 186 break; 187 case REMOVE_IN_LISTENER: 188 removeInListener(task, TaskEvent.Kind.GENERATE, listener); 189 break; 190 } 191 } 192 return true; 193 } 194 } 195 196 static class TestSource extends SimpleJavaFileObject { 197 public TestSource() { 198 super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); 199 } 200 201 @Override 202 public CharSequence getCharContent(boolean ignoreEncodingErrors) { 203 return "class Test { }"; 204 } 205 } 206 207 public static void main(String... args) throws Exception { 208 TestSimpleAddRemove t = new TestSimpleAddRemove(); 209 try { 210 t.run(); 211 } finally { 212 t.fm.close(); 213 } 214 } 215 216 JavacTool tool = (JavacTool) ToolProvider.getSystemJavaCompiler(); 217 StandardJavaFileManager fm = tool.getStandardFileManager(null, null, null); 218 219 void run() throws Exception { 220 for (CompileKind ck: CompileKind.values()) { 221 for (AddKind ak: AddKind.values()) { 222 for (RemoveKind rk: RemoveKind.values()) { 223 test(ck, ak, rk); 224 } 225 } 226 } 227 if (errors > 0) 228 throw new Exception(errors + " errors occurred"); 229 } 230 231 void test(CompileKind ck, AddKind ak, RemoveKind rk) throws IOException { 232 System.err.println("Test: " + ck + " " + ak + " " + rk); 233 234 File tmpDir = new File(ck + "-" + ak + "-" + rk); 235 tmpDir.mkdirs(); 236 fm.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(tmpDir)); 237 238 List<String> options = new ArrayList<String>(); 239 Iterable<? extends JavaFileObject> files = Arrays.asList(new TestSource()); 240 StringWriter sw = new StringWriter(); 241 PrintWriter pw = new PrintWriter(sw); 242 JavacTask task = tool.getTask(pw, fm, null, options, null, files); 243 244 EventKindCounter ec = new EventKindCounter(); 245 TaskListener listener = new TestListener(ec); 246 boolean needProcessor = false; 247 248 switch (ak) { 249 case SET_IN_TASK: 250 task.setTaskListener(listener); 251 break; 252 case ADD_IN_TASK: 253 task.addTaskListener(listener); 254 break; 255 case ADD_IN_PROCESSOR: 256 case ADD_IN_LISTENER: 257 needProcessor = true; 258 } 259 260 switch (rk) { 261 case REMOVE_IN_TASK: 262 task.removeTaskListener(listener); 263 break; 264 case REMOVE_IN_PROCESSOR: 265 case REMOVE_IN_LISTENER: 266 needProcessor = true; 267 } 268 269 if (needProcessor) 270 task.setProcessors(Arrays.asList(new TestProcessor(ak, rk, listener))); 271 272 ck.run(task); 273 System.err.println(ec); 274 275 check(ck, ak, rk, ec); 276 277 System.err.println(); 278 } 279 280 void check(CompileKind ck, AddKind ak, RemoveKind rk, EventKindCounter ec) { 281 // All results should be independent of ck, so we can ignore that 282 283 // Quick way to compare expected values of ec, by comparing ec.toString() 284 String expect = ec.toString(); 285 String found; 286 287 switch (ak) { 288 // Add/set in task should record all events until the listener is removed 289 case SET_IN_TASK: 290 case ADD_IN_TASK: 291 switch (rk) { 292 case REMOVE_IN_TASK: 293 // Remove will succeed, meaning no events will be recorded 294 found = "{}"; 295 break; 296 case REMOVE_IN_PROCESSOR: 297 if (ck == CompileKind.CALL) 298 found = "{PARSE=1:1, ENTER=2:2, ANNOTATION_PROCESSING=1:0, ANNOTATION_PROCESSING_ROUND=2:1, COMPILATION=1:0}"; 299 else 300 found = "{PARSE=1:1, ENTER=2:2, ANNOTATION_PROCESSING=1:0, ANNOTATION_PROCESSING_ROUND=2:1}"; 301 break; 302 case REMOVE_IN_LISTENER: 303 if (ck == CompileKind.CALL) 304 found = "{PARSE=1:1, ENTER=3:3, ANALYZE=1:1, GENERATE=1:0, ANNOTATION_PROCESSING=1:1, ANNOTATION_PROCESSING_ROUND=2:2, COMPILATION=1:0}"; 305 else 306 found = "{PARSE=1:1, ENTER=3:3, ANALYZE=1:1, GENERATE=1:0, ANNOTATION_PROCESSING=1:1, ANNOTATION_PROCESSING_ROUND=2:2}"; 307 break; 308 default: 309 throw new IllegalStateException(); 310 } 311 break; 312 313 // "Add in processor" should skip initial PARSE/ENTER events 314 case ADD_IN_PROCESSOR: 315 switch (rk) { 316 // Remove will fail (too early), so events to end will be recorded 317 case REMOVE_IN_TASK: 318 if (ck == CompileKind.CALL) 319 found = "{ENTER=2:2, ANALYZE=1:1, GENERATE=1:1, ANNOTATION_PROCESSING=0:1, ANNOTATION_PROCESSING_ROUND=1:2, COMPILATION=0:1}"; 320 else 321 found = "{ENTER=2:2, ANALYZE=1:1, GENERATE=1:1, ANNOTATION_PROCESSING=0:1, ANNOTATION_PROCESSING_ROUND=1:2}"; 322 break; 323 case REMOVE_IN_PROCESSOR: 324 found = "{ENTER=1:1, ANNOTATION_PROCESSING_ROUND=1:1}"; 325 break; 326 case REMOVE_IN_LISTENER: 327 found = "{ENTER=2:2, ANALYZE=1:1, GENERATE=1:0, ANNOTATION_PROCESSING=0:1, ANNOTATION_PROCESSING_ROUND=1:2}"; 328 break; 329 default: 330 throw new IllegalStateException(); 331 } 332 break; 333 334 // "Add in listener" will occur during "ANALYSE.started" event 335 case ADD_IN_LISTENER: 336 switch (rk) { 337 // Remove will fail (too early, so events to end will be recorded 338 case REMOVE_IN_TASK: 339 case REMOVE_IN_PROCESSOR: 340 if (ck == CompileKind.CALL) 341 found = "{ANALYZE=0:1, GENERATE=1:1, COMPILATION=0:1}"; 342 else 343 found = "{ANALYZE=0:1, GENERATE=1:1}"; 344 break; 345 // Remove will succeed during "GENERATE.finished" event 346 case REMOVE_IN_LISTENER: 347 found = "{ANALYZE=0:1, GENERATE=1:0}"; 348 break; 349 default: 350 throw new IllegalStateException(); 351 } 352 break; 353 default: 354 throw new IllegalStateException(); 355 } 356 357 if (!found.equals(expect)) { 358 System.err.println("Expected: " + expect); 359 System.err.println(" Found: " + found); 360 error("unexpected value found"); 361 } 362 } 363 364 int errors; 365 366 void error(String message) { 367 System.err.println("Error: " + message); 368 errors++; 369 } 370} 371