blob: e102b0932c2ab00fbbad1839e0dadae400035214 [file] [log] [blame]
/*
* Copyright (c) 2011, 2012, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 7093891
* @summary support multiple task listeners
*/
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.List;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.TypeElement;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import com.sun.source.util.JavacTask;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskListener;
import com.sun.tools.javac.api.JavacTool;
public class TestSimpleAddRemove {
enum AddKind {
SET_IN_TASK,
ADD_IN_TASK,
ADD_IN_PROCESSOR,
ADD_IN_LISTENER;
}
enum RemoveKind {
REMOVE_IN_TASK,
REMOVE_IN_PROCESSOR,
REMOVE_IN_LISTENER,
}
enum CompileKind {
CALL {
void run(JavacTask t) {
if (!t.call()) throw new Error("compilation failed");
}
},
GENERATE {
void run(JavacTask t) throws IOException {
t.generate();
}
};
abstract void run(JavacTask t) throws IOException;
}
static class EventKindCounter extends EnumMap<TaskEvent.Kind, EventKindCounter.Count> {
static class Count {
int started;
int finished;
@Override
public String toString() {
return started + ":" + finished;
}
}
EventKindCounter() {
super(TaskEvent.Kind.class);
}
void inc(TaskEvent.Kind k, boolean started) {
Count c = get(k);
if (c == null)
put(k, c = new Count());
if (started)
c.started++;
else
c.finished++;
}
}
static class TestListener implements TaskListener {
EventKindCounter counter;
TestListener(EventKindCounter c) {
counter = c;
}
public void started(TaskEvent e) {
counter.inc(e.getKind(), true);
}
public void finished(TaskEvent e) {
counter.inc(e.getKind(), false);
}
}
static void addInListener(final JavacTask task, final TaskEvent.Kind kind, final TaskListener listener) {
task.addTaskListener(new TaskListener() {
public void started(TaskEvent e) {
if (e.getKind() == kind) {
task.addTaskListener(listener);
task.removeTaskListener(this);
}
}
public void finished(TaskEvent e) { }
});
}
static void removeInListener(final JavacTask task, final TaskEvent.Kind kind, final TaskListener listener) {
task.addTaskListener(new TaskListener() {
public void started(TaskEvent e) {
if (e.getKind() == kind) {
task.removeTaskListener(listener);
task.removeTaskListener(this);
}
}
public void finished(TaskEvent e) { }
});
}
@SupportedAnnotationTypes("*")
class TestProcessor extends AbstractProcessor {
AddKind ak;
RemoveKind rk;
TaskListener listener;
TestProcessor(AddKind ak, RemoveKind rk, TaskListener listener) {
this.ak = ak;
this.rk = rk;
this.listener = listener;
}
int round = 0;
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
// System.err.println("TestProcessor.process " + roundEnv);
JavacTask task = JavacTask.instance(processingEnv);
if (++round == 1) {
switch (ak) {
case ADD_IN_PROCESSOR:
task.addTaskListener(listener);
break;
case ADD_IN_LISTENER:
addInListener(task, TaskEvent.Kind.ANALYZE, listener);
break;
}
} else if (roundEnv.processingOver()) {
switch (rk) {
case REMOVE_IN_PROCESSOR:
task.removeTaskListener(listener);
break;
case REMOVE_IN_LISTENER:
removeInListener(task, TaskEvent.Kind.GENERATE, listener);
break;
}
}
return true;
}
}
static class TestSource extends SimpleJavaFileObject {
public TestSource() {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return "class Test { }";
}
}
public static void main(String... args) throws Exception {
new TestSimpleAddRemove().run();
}
JavacTool tool = (JavacTool) ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fm = tool.getStandardFileManager(null, null, null);
void run() throws Exception {
for (CompileKind ck: CompileKind.values()) {
for (AddKind ak: AddKind.values()) {
for (RemoveKind rk: RemoveKind.values()) {
test(ck, ak, rk);
}
}
}
if (errors > 0)
throw new Exception(errors + " errors occurred");
}
void test(CompileKind ck, AddKind ak, RemoveKind rk) throws IOException {
System.err.println("Test: " + ck + " " + ak + " " + rk);
File tmpDir = new File(ck + "-" + ak + "-" + rk);
tmpDir.mkdirs();
fm.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(tmpDir));
List<String> options = new ArrayList<String>();
Iterable<? extends JavaFileObject> files = Arrays.asList(new TestSource());
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
JavacTask task = tool.getTask(pw, fm, null, options, null, files);
EventKindCounter ec = new EventKindCounter();
TaskListener listener = new TestListener(ec);
boolean needProcessor = false;
switch (ak) {
case SET_IN_TASK:
task.setTaskListener(listener);
break;
case ADD_IN_TASK:
task.addTaskListener(listener);
break;
case ADD_IN_PROCESSOR:
case ADD_IN_LISTENER:
needProcessor = true;
}
switch (rk) {
case REMOVE_IN_TASK:
task.removeTaskListener(listener);
break;
case REMOVE_IN_PROCESSOR:
case REMOVE_IN_LISTENER:
needProcessor = true;
}
if (needProcessor)
task.setProcessors(Arrays.asList(new TestProcessor(ak, rk, listener)));
ck.run(task);
System.err.println(ec);
check(ck, ak, rk, ec);
System.err.println();
}
void check(CompileKind ck, AddKind ak, RemoveKind rk, EventKindCounter ec) {
// All results should be independent of ck, so we can ignore that
// Quick way to compare expected values of ec, by comparing ec.toString()
String expect = ec.toString();
String found;
switch (ak) {
// Add/set in task should record all events until the listener is removed
case SET_IN_TASK:
case ADD_IN_TASK:
switch (rk) {
case REMOVE_IN_TASK:
// Remove will succeed, meaning no events will be recorded
found = "{}";
break;
case REMOVE_IN_PROCESSOR:
found = "{PARSE=1:1, ENTER=2:2, ANNOTATION_PROCESSING=1:0, ANNOTATION_PROCESSING_ROUND=2:1}";
break;
case REMOVE_IN_LISTENER:
found = "{PARSE=1:1, ENTER=3:3, ANALYZE=1:1, GENERATE=1:0, ANNOTATION_PROCESSING=1:1, ANNOTATION_PROCESSING_ROUND=2:2}";
break;
default:
throw new IllegalStateException();
}
break;
// "Add in processor" should skip initial PARSE/ENTER events
case ADD_IN_PROCESSOR:
switch (rk) {
// Remove will fail (too early), so events to end will be recorded
case REMOVE_IN_TASK:
found = "{ENTER=2:2, ANALYZE=1:1, GENERATE=1:1, ANNOTATION_PROCESSING=0:1, ANNOTATION_PROCESSING_ROUND=1:2}";
break;
case REMOVE_IN_PROCESSOR:
found = "{ENTER=1:1, ANNOTATION_PROCESSING_ROUND=1:1}";
break;
case REMOVE_IN_LISTENER:
found = "{ENTER=2:2, ANALYZE=1:1, GENERATE=1:0, ANNOTATION_PROCESSING=0:1, ANNOTATION_PROCESSING_ROUND=1:2}";
break;
default:
throw new IllegalStateException();
}
break;
// "Add in listener" will occur during "ANALYSE.started" event
case ADD_IN_LISTENER:
switch (rk) {
// Remove will fail (too early, so events to end will be recorded
case REMOVE_IN_TASK:
case REMOVE_IN_PROCESSOR:
found = "{ANALYZE=0:1, GENERATE=1:1}";
break;
// Remove will succeed during "GENERATE.finished" event
case REMOVE_IN_LISTENER:
found = "{ANALYZE=0:1, GENERATE=1:0}";
break;
default:
throw new IllegalStateException();
}
break;
default:
throw new IllegalStateException();
}
if (!found.equals(expect)) {
System.err.println("Expected: " + expect);
System.err.println(" Found: " + found);
error("unexpected value found");
}
}
int errors;
void error(String message) {
System.err.println("Error: " + message);
errors++;
}
}