]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4069 Deprecate BatchExtensionDisctionnary to move it outside of plugin API
authorJulien HENRY <julien.henry@sonarsource.com>
Tue, 26 Mar 2013 14:42:35 +0000 (15:42 +0100)
committerJulien HENRY <julien.henry@sonarsource.com>
Tue, 26 Mar 2013 14:42:35 +0000 (15:42 +0100)
15 files changed:
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionDictionnary.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/bootstrap/ExtensionInstaller.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java
sonar-batch/src/main/java/org/sonar/batch/phases/SensorMatcher.java
sonar-batch/src/main/java/org/sonar/batch/phases/SensorsExecutor.java
sonar-batch/src/main/java/org/sonar/batch/scan/ExtensionMatcher.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchExtensionDictionnaryTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/bootstrap/ExtensionInstallerTest.java
sonar-deprecated/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java [new file with mode: 0644]
sonar-deprecated/src/test/java/org/sonar/api/batch/BatchExtensionDictionnaryTest.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/ExtensionMatcher.java [deleted file]
sonar-plugin-api/src/test/java/org/sonar/api/batch/BatchExtensionDictionnaryTest.java [deleted file]

diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionDictionnary.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionDictionnary.java
new file mode 100644 (file)
index 0000000..e93d0a8
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.batch.bootstrap;
+
+import com.google.common.collect.Lists;
+import org.apache.commons.lang.ClassUtils;
+import org.sonar.api.BatchExtension;
+import org.sonar.api.batch.CheckProject;
+import org.sonar.api.platform.ComponentContainer;
+import org.sonar.api.resources.Project;
+import org.sonar.batch.scan.ExtensionMatcher;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @since 2.6
+ */
+public class BatchExtensionDictionnary extends org.sonar.api.batch.BatchExtensionDictionnary {
+
+  public BatchExtensionDictionnary(ComponentContainer componentContainer) {
+    super(componentContainer);
+  }
+
+  public <T> Collection<T> select(Class<T> type, Project project, boolean sort, ExtensionMatcher matcher) {
+    List<T> result = getFilteredExtensions(type, project, matcher);
+    if (sort) {
+      return sort(result);
+    }
+    return result;
+  }
+
+  private <T> List<T> getFilteredExtensions(Class<T> type, Project project, ExtensionMatcher matcher) {
+    List<T> result = Lists.newArrayList();
+    for (BatchExtension extension : getExtensions()) {
+      if (shouldKeep(type, extension, project, matcher)) {
+        result.add((T) extension);
+      }
+    }
+    return result;
+  }
+
+  private boolean shouldKeep(Class type, Object extension, Project project, ExtensionMatcher matcher) {
+    boolean keep = ClassUtils.isAssignable(extension.getClass(), type) && (matcher == null || matcher.accept(extension));
+    if (keep && project != null && ClassUtils.isAssignable(extension.getClass(), CheckProject.class)) {
+      keep = ((CheckProject) extension).shouldExecuteOnProject(project);
+    }
+    return keep;
+  }
+}
index 1de1578f043c52dc04fcc3baaa546c3c6d7504b2..27845de3b3b02705ca54825cfa6f0fc573d0d8c3 100644 (file)
@@ -22,11 +22,11 @@ package org.sonar.batch.bootstrap;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.ExtensionProvider;
 import org.sonar.api.Plugin;
-import org.sonar.api.batch.ExtensionMatcher;
 import org.sonar.api.config.Settings;
 import org.sonar.api.platform.ComponentContainer;
 import org.sonar.api.platform.PluginMetadata;
 import org.sonar.batch.bootstrapper.EnvironmentInformation;
+import org.sonar.batch.scan.ExtensionMatcher;
 
 import javax.annotation.Nullable;
 
index 78b253d4fcf344405f971087f552ca64315b0ba9..2533e3cff8898109176e1e5eddcc957c93a03a23 100644 (file)
@@ -20,7 +20,6 @@
 package org.sonar.batch.bootstrap;
 
 import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.ExtensionMatcher;
 import org.sonar.api.config.Settings;
 import org.sonar.api.platform.ComponentContainer;
 import org.sonar.api.resources.ResourceTypes;
@@ -28,6 +27,7 @@ import org.sonar.api.task.Task;
 import org.sonar.api.task.TaskDefinition;
 import org.sonar.api.task.TaskExtension;
 import org.sonar.api.utils.SonarException;
+import org.sonar.batch.scan.ExtensionMatcher;
 import org.sonar.batch.scan.ScanTask;
 import org.sonar.batch.tasks.ListTask;
 import org.sonar.batch.tasks.Tasks;
index 0bd9a9428be15ae1d0fc52c78a6165a1b5ae2127..f660fff25abd179e98c6f10bc6dd08f9a438c3c3 100644 (file)
@@ -21,9 +21,9 @@ package org.sonar.batch.phases;
 
 import org.apache.commons.lang.ClassUtils;
 import org.sonar.api.BatchExtension;
-import org.sonar.api.batch.ExtensionMatcher;
 import org.sonar.api.batch.InstantiationStrategy;
 import org.sonar.api.batch.Sensor;
+import org.sonar.batch.scan.ExtensionMatcher;
 
 /**
  * Allow to filter sensors that will be executed.
index b3d35338ea8e337a331147383e1db63fc60b2cfa..9428884f06846ca193e9ca8b7b6529cb15d616b0 100644 (file)
@@ -23,7 +23,6 @@ import com.google.common.collect.Lists;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.BatchComponent;
-import org.sonar.api.batch.BatchExtensionDictionnary;
 import org.sonar.api.batch.Sensor;
 import org.sonar.api.batch.SensorContext;
 import org.sonar.api.batch.maven.DependsUponMavenPlugin;
@@ -31,6 +30,7 @@ import org.sonar.api.batch.maven.MavenPluginHandler;
 import org.sonar.api.database.DatabaseSession;
 import org.sonar.api.resources.Project;
 import org.sonar.api.utils.TimeProfiler;
+import org.sonar.batch.bootstrap.BatchExtensionDictionnary;
 import org.sonar.batch.events.EventBus;
 import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
 import org.sonar.batch.scan.maven.MavenPluginExecutor;
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ExtensionMatcher.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ExtensionMatcher.java
new file mode 100644 (file)
index 0000000..231314d
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.batch.scan;
+
+/**
+ * @since 3.6
+ */
+public interface ExtensionMatcher {
+  boolean accept(Object extension);
+}
index 4cbf42f96a469d4c47366104dadc6d2dadabb9a0..ae0d6a65ccfc5962e2d05dbfc5f814a288b45317 100644 (file)
@@ -22,8 +22,6 @@ package org.sonar.batch.scan;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.BatchExtension;
-import org.sonar.api.batch.BatchExtensionDictionnary;
-import org.sonar.api.batch.ExtensionMatcher;
 import org.sonar.api.batch.InstantiationStrategy;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.platform.ComponentContainer;
@@ -39,6 +37,7 @@ import org.sonar.batch.ProfileProvider;
 import org.sonar.batch.ProjectTree;
 import org.sonar.batch.ResourceFilters;
 import org.sonar.batch.ViolationFilters;
+import org.sonar.batch.bootstrap.BatchExtensionDictionnary;
 import org.sonar.batch.bootstrap.ExtensionInstaller;
 import org.sonar.batch.bootstrap.ExtensionUtils;
 import org.sonar.batch.components.TimeMachineConfiguration;
index 6b9fa1f4fa74362d7cf878a9fb7bccc7e9098c41..ef27bf500e54d35c525dfaf1b2356276be956d83 100644 (file)
@@ -20,7 +20,6 @@
 package org.sonar.batch.scan;
 
 import org.sonar.api.BatchExtension;
-import org.sonar.api.batch.ExtensionMatcher;
 import org.sonar.api.batch.InstantiationStrategy;
 import org.sonar.api.platform.ComponentContainer;
 import org.sonar.api.resources.Project;
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchExtensionDictionnaryTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchExtensionDictionnaryTest.java
new file mode 100644 (file)
index 0000000..16077cd
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.batch.bootstrap;
+
+import org.junit.Test;
+import org.sonar.api.BatchExtension;
+import org.sonar.api.batch.Sensor;
+import org.sonar.api.batch.SensorContext;
+import org.sonar.api.platform.ComponentContainer;
+import org.sonar.api.resources.Project;
+import org.sonar.batch.scan.ExtensionMatcher;
+
+import java.util.Collection;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.internal.matchers.IsCollectionContaining.hasItem;
+
+public class BatchExtensionDictionnaryTest {
+
+  private BatchExtensionDictionnary newSelector(BatchExtension... extensions) {
+    ComponentContainer iocContainer = new ComponentContainer();
+    for (BatchExtension extension : extensions) {
+      iocContainer.addSingleton(extension);
+    }
+    return new BatchExtensionDictionnary(iocContainer);
+  }
+
+  @Test
+  public void testGetFilteredExtensionWithExtensionMatcher() {
+    final Sensor sensor1 = new FakeSensor(), sensor2 = new FakeSensor();
+
+    BatchExtensionDictionnary selector = newSelector(sensor1, sensor2);
+    Collection<Sensor> sensors = selector.select(Sensor.class, null, true, new ExtensionMatcher() {
+      @Override
+      public boolean accept(Object extension) {
+        return extension.equals(sensor1);
+      }
+    });
+
+    assertThat(sensors, hasItem(sensor1));
+    assertEquals(1, sensors.size());
+  }
+
+  class FakeSensor implements Sensor {
+
+    public void analyse(Project project, SensorContext context) {
+
+    }
+
+    public boolean shouldExecuteOnProject(Project project) {
+      return true;
+    }
+  }
+}
index d028f1f244826e009191de34e644b0e02f535b69..97941425e1a71d617af150411ace0d275774bd06 100644 (file)
@@ -26,12 +26,12 @@ import org.sonar.api.BatchExtension;
 import org.sonar.api.ExtensionProvider;
 import org.sonar.api.Plugin;
 import org.sonar.api.SonarPlugin;
-import org.sonar.api.batch.ExtensionMatcher;
 import org.sonar.api.batch.SupportedEnvironment;
 import org.sonar.api.config.Settings;
 import org.sonar.api.platform.ComponentContainer;
 import org.sonar.api.platform.PluginMetadata;
 import org.sonar.batch.bootstrapper.EnvironmentInformation;
+import org.sonar.batch.scan.ExtensionMatcher;
 
 import java.util.Arrays;
 import java.util.List;
diff --git a/sonar-deprecated/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java b/sonar-deprecated/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java
new file mode 100644 (file)
index 0000000..ab57c80
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.api.batch;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import org.apache.commons.lang.ClassUtils;
+import org.sonar.api.BatchExtension;
+import org.sonar.api.batch.maven.DependsUponMavenPlugin;
+import org.sonar.api.batch.maven.MavenPluginHandler;
+import org.sonar.api.platform.ComponentContainer;
+import org.sonar.api.resources.Project;
+import org.sonar.api.utils.AnnotationUtils;
+import org.sonar.api.utils.dag.DirectAcyclicGraph;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @since 1.11
+ * @deprecated since 2.6 was only used by views
+ */
+@Deprecated
+public class BatchExtensionDictionnary {
+
+  private ComponentContainer componentContainer;
+
+  public BatchExtensionDictionnary(ComponentContainer componentContainer) {
+    this.componentContainer = componentContainer;
+  }
+
+  public <T> Collection<T> select(Class<T> type) {
+    return select(type, null, false);
+  }
+
+  public <T> Collection<T> select(Class<T> type, Project project, boolean sort) {
+    List<T> result = getFilteredExtensions(type, project);
+    if (sort) {
+      return sort(result);
+    }
+    return result;
+  }
+
+  public Collection<MavenPluginHandler> selectMavenPluginHandlers(Project project) {
+    Collection<DependsUponMavenPlugin> selectedExtensions = select(DependsUponMavenPlugin.class, project, true);
+    List<MavenPluginHandler> handlers = Lists.newArrayList();
+    for (DependsUponMavenPlugin extension : selectedExtensions) {
+      MavenPluginHandler handler = extension.getMavenPluginHandler(project);
+      if (handler != null) {
+        boolean ok = true;
+        if (handler instanceof CheckProject) {
+          ok = ((CheckProject) handler).shouldExecuteOnProject(project);
+        }
+        if (ok) {
+          handlers.add(handler);
+        }
+      }
+
+    }
+    return handlers;
+  }
+
+  protected List<BatchExtension> getExtensions() {
+    List<BatchExtension> extensions = Lists.newArrayList();
+    completeBatchExtensions(componentContainer, extensions);
+    return extensions;
+  }
+
+  private static void completeBatchExtensions(ComponentContainer container, List<BatchExtension> extensions) {
+    if (container != null) {
+      extensions.addAll(container.getComponentsByType(BatchExtension.class));
+      completeBatchExtensions(container.getParent(), extensions);
+    }
+  }
+
+  private <T> List<T> getFilteredExtensions(Class<T> type, Project project) {
+    List<T> result = Lists.newArrayList();
+    for (BatchExtension extension : getExtensions()) {
+      if (shouldKeep(type, extension, project)) {
+        result.add((T) extension);
+      }
+    }
+    return result;
+  }
+
+  private boolean shouldKeep(Class type, Object extension, Project project) {
+    boolean keep = ClassUtils.isAssignable(extension.getClass(), type);
+    if (keep && project != null && ClassUtils.isAssignable(extension.getClass(), CheckProject.class)) {
+      keep = ((CheckProject) extension).shouldExecuteOnProject(project);
+    }
+    return keep;
+  }
+
+  public <T> Collection<T> sort(Collection<T> extensions) {
+    DirectAcyclicGraph dag = new DirectAcyclicGraph();
+
+    for (T extension : extensions) {
+      dag.add(extension);
+      for (Object dependency : getDependencies(extension)) {
+        dag.add(extension, dependency);
+      }
+      for (Object generates : getDependents(extension)) {
+        dag.add(generates, extension);
+      }
+      completePhaseDependencies(dag, extension);
+    }
+    List sortedList = dag.sort();
+
+    return Collections2.filter(sortedList, Predicates.in(extensions));
+  }
+
+  /**
+   * Extension dependencies
+   */
+  private <T> List getDependencies(T extension) {
+    return evaluateAnnotatedClasses(extension, DependsUpon.class);
+  }
+
+  /**
+   * Objects that depend upon this extension.
+   */
+  public <T> List getDependents(T extension) {
+    return evaluateAnnotatedClasses(extension, DependedUpon.class);
+  }
+
+  private void completePhaseDependencies(DirectAcyclicGraph dag, Object extension) {
+    Phase.Name phase = evaluatePhase(extension);
+    dag.add(extension, phase);
+    for (Phase.Name name : Phase.Name.values()) {
+      if (phase.compareTo(name) < 0) {
+        dag.add(name, extension);
+      } else if (phase.compareTo(name) > 0) {
+        dag.add(extension, name);
+      }
+    }
+  }
+
+  protected List evaluateAnnotatedClasses(Object extension, Class<? extends Annotation> annotation) {
+    List<Object> results = Lists.newArrayList();
+    Class aClass = extension.getClass();
+    while (aClass != null) {
+      evaluateClass(aClass, annotation, results);
+
+      for (Method method : aClass.getDeclaredMethods()) {
+        if (method.getAnnotation(annotation) != null) {
+          checkAnnotatedMethod(method);
+          evaluateMethod(extension, method, results);
+        }
+      }
+      aClass = aClass.getSuperclass();
+    }
+
+    return results;
+  }
+
+  private void evaluateClass(Class extensionClass, Class annotationClass, List<Object> results) {
+    Annotation annotation = extensionClass.getAnnotation(annotationClass);
+    if (annotation != null) {
+      if (annotation.annotationType().isAssignableFrom(DependsUpon.class)) {
+        results.addAll(Arrays.asList(((DependsUpon) annotation).value()));
+
+      } else if (annotation.annotationType().isAssignableFrom(DependedUpon.class)) {
+        results.addAll(Arrays.asList(((DependedUpon) annotation).value()));
+      }
+    }
+
+    Class[] interfaces = extensionClass.getInterfaces();
+    for (Class anInterface : interfaces) {
+      evaluateClass(anInterface, annotationClass, results);
+    }
+  }
+
+  protected Phase.Name evaluatePhase(Object extension) {
+    Phase phaseAnnotation = AnnotationUtils.getAnnotation(extension, Phase.class);
+    if (phaseAnnotation != null) {
+      return phaseAnnotation.name();
+    }
+    return Phase.Name.DEFAULT;
+  }
+
+  private void evaluateMethod(Object extension, Method method, List<Object> results) {
+    try {
+      Object result = method.invoke(extension);
+      if (result != null) {
+        // TODO add arrays/collections of objects/classes
+        if (result instanceof Class<?>) {
+          results.addAll(componentContainer.getComponentsByType((Class<?>) result));
+
+        } else if (result instanceof Collection<?>) {
+          results.addAll((Collection<?>) result);
+
+        } else {
+          results.add(result);
+        }
+      }
+    } catch (Exception e) {
+      throw new IllegalStateException("Can not invoke method " + method, e);
+    }
+  }
+
+  private void checkAnnotatedMethod(Method method) {
+    if (!Modifier.isPublic(method.getModifiers())) {
+      throw new IllegalStateException("Annotated method must be public :" + method);
+    }
+    if (method.getParameterTypes().length > 0) {
+      throw new IllegalStateException("Annotated method must not have parameters :" + method);
+    }
+  }
+}
diff --git a/sonar-deprecated/src/test/java/org/sonar/api/batch/BatchExtensionDictionnaryTest.java b/sonar-deprecated/src/test/java/org/sonar/api/batch/BatchExtensionDictionnaryTest.java
new file mode 100644 (file)
index 0000000..2f37fc4
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.api.batch;
+
+import com.google.common.collect.Lists;
+import org.junit.Test;
+import org.sonar.api.BatchExtension;
+import org.sonar.api.platform.ComponentContainer;
+import org.sonar.api.resources.Project;
+
+import java.util.Collection;
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.internal.matchers.IsCollectionContaining.hasItem;
+import static org.junit.internal.matchers.IsCollectionContaining.hasItems;
+import static org.mockito.Mockito.mock;
+
+public class BatchExtensionDictionnaryTest {
+
+  private BatchExtensionDictionnary newSelector(BatchExtension... extensions) {
+    ComponentContainer iocContainer = new ComponentContainer();
+    for (BatchExtension extension : extensions) {
+      iocContainer.addSingleton(extension);
+    }
+    return new BatchExtensionDictionnary(iocContainer);
+  }
+
+  @Test
+  public void testGetFilteredExtensions() {
+    Sensor sensor1 = new FakeSensor(), sensor2 = new FakeSensor();
+    Decorator decorator = mock(Decorator.class);
+
+    BatchExtensionDictionnary selector = newSelector(sensor1, sensor2, decorator);
+    Collection<Sensor> sensors = selector.select(Sensor.class, null, true);
+
+    assertThat(sensors, hasItem(sensor1));
+    assertThat(sensors, hasItem(sensor2));
+    assertEquals(2, sensors.size());
+  }
+
+  @Test
+  public void shouldSearchInParentContainers() {
+    BatchExtension a = new FakeSensor();
+    BatchExtension b = new FakeSensor();
+    BatchExtension c = new FakeSensor();
+
+    ComponentContainer grandParent = new ComponentContainer();
+    grandParent.addSingleton(a);
+
+    ComponentContainer parent = grandParent.createChild();
+    parent.addSingleton(b);
+
+    ComponentContainer child = parent.createChild();
+    child.addSingleton(c);
+
+    BatchExtensionDictionnary dictionnary = new BatchExtensionDictionnary(child);
+    assertThat(dictionnary.select(BatchExtension.class).size(), is(3));
+    assertThat(dictionnary.select(BatchExtension.class), hasItems(a, b, c));
+  }
+
+  @Test
+  public void sortExtensionsByDependency() {
+    BatchExtension a = new MethodDependentOf(null);
+    BatchExtension b = new MethodDependentOf(a);
+    BatchExtension c = new MethodDependentOf(b);
+
+    BatchExtensionDictionnary selector = newSelector(b, c, a);
+    List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
+
+    assertThat(extensions.size(), is(3));
+    assertThat(extensions.get(0), is(a));
+    assertThat(extensions.get(1), is(b));
+    assertThat(extensions.get(2), is(c));
+  }
+
+  @Test
+  public void useMethodAnnotationsToSortExtensions() {
+    BatchExtension a = new GeneratesSomething("foo");
+    BatchExtension b = new MethodDependentOf("foo");
+
+    BatchExtensionDictionnary selector = newSelector(a, b);
+    List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
+
+    assertThat(extensions.size(), is(2));
+    assertThat(extensions.get(0), is(a));
+    assertThat(extensions.get(1), is(b));
+
+    // different initial order
+    selector = newSelector(b, a);
+    extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
+
+    assertEquals(2, extensions.size());
+    assertEquals(a, extensions.get(0));
+    assertEquals(b, extensions.get(1));
+  }
+
+  @Test
+  public void useClassAnnotationsToSortExtensions() {
+    BatchExtension a = new ClassDependedUpon();
+    BatchExtension b = new ClassDependsUpon();
+
+    BatchExtensionDictionnary selector = newSelector(a, b);
+    List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
+
+    assertThat(extensions.size(), is(2));
+    assertThat(extensions.get(0), is(a));
+    assertThat(extensions.get(1), is(b));
+
+    // different initial order
+    selector = newSelector(b, a);
+    extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
+
+    assertEquals(2, extensions.size());
+    assertEquals(a, extensions.get(0));
+    assertEquals(b, extensions.get(1));
+  }
+
+  @Test
+  public void useClassAnnotationsOnInterfaces() {
+    BatchExtension a = new InterfaceDependedUpon() {
+    };
+    BatchExtension b = new InterfaceDependsUpon() {
+    };
+
+    BatchExtensionDictionnary selector = newSelector(a, b);
+    List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
+
+    assertThat(extensions.size(), is(2));
+    assertThat(extensions.get(0), is(a));
+    assertThat(extensions.get(1), is(b));
+
+    // different initial order
+    selector = newSelector(b, a);
+    extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
+
+    assertEquals(2, extensions.size());
+    assertEquals(a, extensions.get(0));
+    assertEquals(b, extensions.get(1));
+  }
+
+  @Test
+  public void checkProject() {
+    BatchExtension ok = new CheckProjectOK();
+    BatchExtension ko = new CheckProjectKO();
+
+    BatchExtensionDictionnary selector = newSelector(ok, ko);
+    List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, new Project("key"), true));
+
+    assertThat(extensions.size(), is(1));
+    assertThat(extensions.get(0), is(CheckProjectOK.class));
+  }
+
+  @Test
+  public void inheritAnnotations() {
+    BatchExtension a = new SubClass("foo");
+    BatchExtension b = new MethodDependentOf("foo");
+
+    BatchExtensionDictionnary selector = newSelector(b, a);
+    List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
+
+    assertEquals(2, extensions.size());
+    assertEquals(a, extensions.get(0));
+    assertEquals(b, extensions.get(1));
+
+    // change initial order
+    selector = newSelector(a, b);
+    extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
+
+    assertEquals(2, extensions.size());
+    assertEquals(a, extensions.get(0));
+    assertEquals(b, extensions.get(1));
+  }
+
+  @Test(expected = IllegalStateException.class)
+  public void annotatedMethodsCanNotBePrivate() {
+    BatchExtensionDictionnary selector = newSelector();
+    BatchExtension wrong = new BatchExtension() {
+      @DependsUpon
+      private Object foo() {
+        return "foo";
+      }
+    };
+    selector.evaluateAnnotatedClasses(wrong, DependsUpon.class);
+  }
+
+  @Test
+  public void dependsUponPhase() {
+    BatchExtension pre = new PreSensor();
+    BatchExtension analyze = new GeneratesSomething("something");
+    BatchExtension post = new PostSensor();
+
+    BatchExtensionDictionnary selector = newSelector(analyze, post, pre);
+    List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
+
+    assertEquals(3, extensions.size());
+    assertEquals(pre, extensions.get(0));
+    assertEquals(analyze, extensions.get(1));
+    assertEquals(post, extensions.get(2));
+  }
+
+  @Test
+  public void dependsUponInheritedPhase() {
+    BatchExtension pre = new PreSensorSubclass();
+    BatchExtension analyze = new GeneratesSomething("something");
+    BatchExtension post = new PostSensorSubclass();
+
+    BatchExtensionDictionnary selector = newSelector(analyze, post, pre);
+    List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
+
+    assertEquals(3, extensions.size());
+    assertEquals(pre, extensions.get(0));
+    assertEquals(analyze, extensions.get(1));
+    assertEquals(post, extensions.get(2));
+  }
+
+  @Test
+  public void buildStatusCheckersAreExecutedAfterOtherPostJobs() {
+    BuildBreaker checker = new BuildBreaker() {
+      public void executeOn(Project project, SensorContext context) {
+      }
+    };
+
+    BatchExtensionDictionnary selector = newSelector(new FakePostJob(), checker, new FakePostJob());
+    List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
+
+    assertEquals(3, extensions.size());
+    assertEquals(checker, extensions.get(2));
+  }
+
+  class FakeSensor implements Sensor {
+
+    public void analyse(Project project, SensorContext context) {
+
+    }
+
+    public boolean shouldExecuteOnProject(Project project) {
+      return true;
+    }
+  }
+
+  class MethodDependentOf implements BatchExtension {
+    private Object dep;
+
+    MethodDependentOf(Object o) {
+      this.dep = o;
+    }
+
+    @DependsUpon
+    public Object dependsUponObject() {
+      return dep;
+    }
+  }
+
+  @DependsUpon("flag")
+  class ClassDependsUpon implements BatchExtension {
+  }
+
+  @DependedUpon("flag")
+  class ClassDependedUpon implements BatchExtension {
+  }
+
+  @DependsUpon("flag")
+  interface InterfaceDependsUpon extends BatchExtension {
+  }
+
+  @DependedUpon("flag")
+  interface InterfaceDependedUpon extends BatchExtension {
+  }
+
+  class GeneratesSomething implements BatchExtension {
+    private Object gen;
+
+    GeneratesSomething(Object o) {
+      this.gen = o;
+    }
+
+    @DependedUpon
+    public Object generates() {
+      return gen;
+    }
+  }
+
+  class SubClass extends GeneratesSomething {
+    SubClass(Object o) {
+      super(o);
+    }
+  }
+
+  @Phase(name = Phase.Name.PRE)
+  class PreSensor implements BatchExtension {
+
+  }
+
+  class PreSensorSubclass extends PreSensor {
+
+  }
+
+  @Phase(name = Phase.Name.POST)
+  class PostSensor implements BatchExtension {
+
+  }
+
+  class PostSensorSubclass extends PostSensor {
+
+  }
+
+  class CheckProjectOK implements BatchExtension, CheckProject {
+    public boolean shouldExecuteOnProject(Project project) {
+      return true;
+    }
+  }
+
+  class CheckProjectKO implements BatchExtension, CheckProject {
+    public boolean shouldExecuteOnProject(Project project) {
+      return false;
+    }
+  }
+
+  private class FakePostJob implements PostJob {
+    public void executeOn(Project project, SensorContext context) {
+    }
+  }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java
deleted file mode 100644 (file)
index df5ddd2..0000000
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2012 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.api.batch;
-
-import com.google.common.base.Predicates;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.Lists;
-import org.apache.commons.lang.ClassUtils;
-import org.sonar.api.BatchExtension;
-import org.sonar.api.batch.maven.DependsUponMavenPlugin;
-import org.sonar.api.batch.maven.MavenPluginHandler;
-import org.sonar.api.platform.ComponentContainer;
-import org.sonar.api.resources.Project;
-import org.sonar.api.utils.AnnotationUtils;
-import org.sonar.api.utils.dag.DirectAcyclicGraph;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * @since 1.11
- */
-public class BatchExtensionDictionnary {
-
-  private ComponentContainer componentContainer;
-
-  public BatchExtensionDictionnary(ComponentContainer componentContainer) {
-    this.componentContainer = componentContainer;
-  }
-
-  public <T> Collection<T> select(Class<T> type) {
-    return select(type, null, false);
-  }
-
-  public <T> Collection<T> select(Class<T> type, Project project, boolean sort) {
-    return select(type, project, sort, null);
-  }
-
-  public <T> Collection<T> select(Class<T> type, Project project, boolean sort, ExtensionMatcher matcher) {
-    List<T> result = getFilteredExtensions(type, project, matcher);
-    if (sort) {
-      return sort(result);
-    }
-    return result;
-  }
-
-  public Collection<MavenPluginHandler> selectMavenPluginHandlers(Project project) {
-    Collection<DependsUponMavenPlugin> selectedExtensions = select(DependsUponMavenPlugin.class, project, true);
-    List<MavenPluginHandler> handlers = Lists.newArrayList();
-    for (DependsUponMavenPlugin extension : selectedExtensions) {
-      MavenPluginHandler handler = extension.getMavenPluginHandler(project);
-      if (handler != null) {
-        boolean ok = true;
-        if (handler instanceof CheckProject) {
-          ok = ((CheckProject) handler).shouldExecuteOnProject(project);
-        }
-        if (ok) {
-          handlers.add(handler);
-        }
-      }
-
-    }
-    return handlers;
-  }
-
-  private List<BatchExtension> getExtensions() {
-    List<BatchExtension> extensions = Lists.newArrayList();
-    completeBatchExtensions(componentContainer, extensions);
-    return extensions;
-  }
-
-  private static void completeBatchExtensions(ComponentContainer container, List<BatchExtension> extensions) {
-    if (container != null) {
-      extensions.addAll(container.getComponentsByType(BatchExtension.class));
-      completeBatchExtensions(container.getParent(), extensions);
-    }
-  }
-
-  private <T> List<T> getFilteredExtensions(Class<T> type, Project project, ExtensionMatcher matcher) {
-    List<T> result = Lists.newArrayList();
-    for (BatchExtension extension : getExtensions()) {
-      if (shouldKeep(type, extension, project, matcher)) {
-        result.add((T) extension);
-      }
-    }
-    return result;
-  }
-
-  private boolean shouldKeep(Class type, Object extension, Project project, ExtensionMatcher matcher) {
-    boolean keep = ClassUtils.isAssignable(extension.getClass(), type) && (matcher == null || matcher.accept(extension));
-    if (keep && project != null && ClassUtils.isAssignable(extension.getClass(), CheckProject.class)) {
-      keep = ((CheckProject) extension).shouldExecuteOnProject(project);
-    }
-    return keep;
-  }
-
-  public <T> Collection<T> sort(Collection<T> extensions) {
-    DirectAcyclicGraph dag = new DirectAcyclicGraph();
-
-    for (T extension : extensions) {
-      dag.add(extension);
-      for (Object dependency : getDependencies(extension)) {
-        dag.add(extension, dependency);
-      }
-      for (Object generates : getDependents(extension)) {
-        dag.add(generates, extension);
-      }
-      completePhaseDependencies(dag, extension);
-    }
-    List sortedList = dag.sort();
-
-    return Collections2.filter(sortedList, Predicates.in(extensions));
-  }
-
-  /**
-   * Extension dependencies
-   */
-  private <T> List getDependencies(T extension) {
-    return evaluateAnnotatedClasses(extension, DependsUpon.class);
-  }
-
-  /**
-   * Objects that depend upon this extension.
-   */
-  public <T> List getDependents(T extension) {
-    return evaluateAnnotatedClasses(extension, DependedUpon.class);
-  }
-
-  private void completePhaseDependencies(DirectAcyclicGraph dag, Object extension) {
-    Phase.Name phase = evaluatePhase(extension);
-    dag.add(extension, phase);
-    for (Phase.Name name : Phase.Name.values()) {
-      if (phase.compareTo(name) < 0) {
-        dag.add(name, extension);
-      } else if (phase.compareTo(name) > 0) {
-        dag.add(extension, name);
-      }
-    }
-  }
-
-  protected List evaluateAnnotatedClasses(Object extension, Class<? extends Annotation> annotation) {
-    List<Object> results = Lists.newArrayList();
-    Class aClass = extension.getClass();
-    while (aClass != null) {
-      evaluateClass(aClass, annotation, results);
-
-      for (Method method : aClass.getDeclaredMethods()) {
-        if (method.getAnnotation(annotation) != null) {
-          checkAnnotatedMethod(method);
-          evaluateMethod(extension, method, results);
-        }
-      }
-      aClass = aClass.getSuperclass();
-    }
-
-    return results;
-  }
-
-  private void evaluateClass(Class extensionClass, Class annotationClass, List<Object> results) {
-    Annotation annotation = extensionClass.getAnnotation(annotationClass);
-    if (annotation != null) {
-      if (annotation.annotationType().isAssignableFrom(DependsUpon.class)) {
-        results.addAll(Arrays.asList(((DependsUpon) annotation).value()));
-
-      } else if (annotation.annotationType().isAssignableFrom(DependedUpon.class)) {
-        results.addAll(Arrays.asList(((DependedUpon) annotation).value()));
-      }
-    }
-
-    Class[] interfaces = extensionClass.getInterfaces();
-    for (Class anInterface : interfaces) {
-      evaluateClass(anInterface, annotationClass, results);
-    }
-  }
-
-  protected Phase.Name evaluatePhase(Object extension) {
-    Phase phaseAnnotation = AnnotationUtils.getAnnotation(extension, Phase.class);
-    if (phaseAnnotation != null) {
-      return phaseAnnotation.name();
-    }
-    return Phase.Name.DEFAULT;
-  }
-
-  private void evaluateMethod(Object extension, Method method, List<Object> results) {
-    try {
-      Object result = method.invoke(extension);
-      if (result != null) {
-        // TODO add arrays/collections of objects/classes
-        if (result instanceof Class<?>) {
-          results.addAll(componentContainer.getComponentsByType((Class<?>) result));
-
-        } else if (result instanceof Collection<?>) {
-          results.addAll((Collection<?>) result);
-
-        } else {
-          results.add(result);
-        }
-      }
-    } catch (Exception e) {
-      throw new IllegalStateException("Can not invoke method " + method, e);
-    }
-  }
-
-  private void checkAnnotatedMethod(Method method) {
-    if (!Modifier.isPublic(method.getModifiers())) {
-      throw new IllegalStateException("Annotated method must be public :" + method);
-    }
-    if (method.getParameterTypes().length > 0) {
-      throw new IllegalStateException("Annotated method must not have parameters :" + method);
-    }
-  }
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/ExtensionMatcher.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/ExtensionMatcher.java
deleted file mode 100644 (file)
index 96d81ae..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2012 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.api.batch;
-
-/**
- * @since 3.6
- */
-public interface ExtensionMatcher {
-  boolean accept(Object extension);
-}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/BatchExtensionDictionnaryTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/BatchExtensionDictionnaryTest.java
deleted file mode 100644 (file)
index 61d251d..0000000
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2012 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.api.batch;
-
-import com.google.common.collect.Lists;
-import org.junit.Test;
-import org.sonar.api.BatchExtension;
-import org.sonar.api.platform.ComponentContainer;
-import org.sonar.api.resources.Project;
-
-import java.util.Collection;
-import java.util.List;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.internal.matchers.IsCollectionContaining.hasItem;
-import static org.junit.internal.matchers.IsCollectionContaining.hasItems;
-import static org.mockito.Mockito.mock;
-
-public class BatchExtensionDictionnaryTest {
-
-  private BatchExtensionDictionnary newSelector(BatchExtension... extensions) {
-    ComponentContainer iocContainer = new ComponentContainer();
-    for (BatchExtension extension : extensions) {
-      iocContainer.addSingleton(extension);
-    }
-    return new BatchExtensionDictionnary(iocContainer);
-  }
-
-  @Test
-  public void testGetFilteredExtensions() {
-    Sensor sensor1 = new FakeSensor(), sensor2 = new FakeSensor();
-    Decorator decorator = mock(Decorator.class);
-
-    BatchExtensionDictionnary selector = newSelector(sensor1, sensor2, decorator);
-    Collection<Sensor> sensors = selector.select(Sensor.class, null, true);
-
-    assertThat(sensors, hasItem(sensor1));
-    assertThat(sensors, hasItem(sensor2));
-    assertEquals(2, sensors.size());
-  }
-
-  @Test
-  public void testGetFilteredExtensionWithExtensionMatcher() {
-    final Sensor sensor1 = new FakeSensor(), sensor2 = new FakeSensor();
-
-    BatchExtensionDictionnary selector = newSelector(sensor1, sensor2);
-    Collection<Sensor> sensors = selector.select(Sensor.class, null, true, new ExtensionMatcher() {
-      @Override
-      public boolean accept(Object extension) {
-        return extension.equals(sensor1);
-      }
-    });
-
-    assertThat(sensors, hasItem(sensor1));
-    assertEquals(1, sensors.size());
-  }
-
-  @Test
-  public void shouldSearchInParentContainers() {
-    BatchExtension a = new FakeSensor();
-    BatchExtension b = new FakeSensor();
-    BatchExtension c = new FakeSensor();
-
-    ComponentContainer grandParent = new ComponentContainer();
-    grandParent.addSingleton(a);
-
-    ComponentContainer parent = grandParent.createChild();
-    parent.addSingleton(b);
-
-    ComponentContainer child = parent.createChild();
-    child.addSingleton(c);
-
-    BatchExtensionDictionnary dictionnary = new BatchExtensionDictionnary(child);
-    assertThat(dictionnary.select(BatchExtension.class).size(), is(3));
-    assertThat(dictionnary.select(BatchExtension.class), hasItems(a, b, c));
-  }
-
-  @Test
-  public void sortExtensionsByDependency() {
-    BatchExtension a = new MethodDependentOf(null);
-    BatchExtension b = new MethodDependentOf(a);
-    BatchExtension c = new MethodDependentOf(b);
-
-    BatchExtensionDictionnary selector = newSelector(b, c, a);
-    List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
-
-    assertThat(extensions.size(), is(3));
-    assertThat(extensions.get(0), is(a));
-    assertThat(extensions.get(1), is(b));
-    assertThat(extensions.get(2), is(c));
-  }
-
-  @Test
-  public void useMethodAnnotationsToSortExtensions() {
-    BatchExtension a = new GeneratesSomething("foo");
-    BatchExtension b = new MethodDependentOf("foo");
-
-    BatchExtensionDictionnary selector = newSelector(a, b);
-    List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
-
-    assertThat(extensions.size(), is(2));
-    assertThat(extensions.get(0), is(a));
-    assertThat(extensions.get(1), is(b));
-
-    // different initial order
-    selector = newSelector(b, a);
-    extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
-
-    assertEquals(2, extensions.size());
-    assertEquals(a, extensions.get(0));
-    assertEquals(b, extensions.get(1));
-  }
-
-  @Test
-  public void useClassAnnotationsToSortExtensions() {
-    BatchExtension a = new ClassDependedUpon();
-    BatchExtension b = new ClassDependsUpon();
-
-    BatchExtensionDictionnary selector = newSelector(a, b);
-    List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
-
-    assertThat(extensions.size(), is(2));
-    assertThat(extensions.get(0), is(a));
-    assertThat(extensions.get(1), is(b));
-
-    // different initial order
-    selector = newSelector(b, a);
-    extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
-
-    assertEquals(2, extensions.size());
-    assertEquals(a, extensions.get(0));
-    assertEquals(b, extensions.get(1));
-  }
-
-  @Test
-  public void useClassAnnotationsOnInterfaces() {
-    BatchExtension a = new InterfaceDependedUpon() {
-    };
-    BatchExtension b = new InterfaceDependsUpon() {
-    };
-
-    BatchExtensionDictionnary selector = newSelector(a, b);
-    List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
-
-    assertThat(extensions.size(), is(2));
-    assertThat(extensions.get(0), is(a));
-    assertThat(extensions.get(1), is(b));
-
-    // different initial order
-    selector = newSelector(b, a);
-    extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
-
-    assertEquals(2, extensions.size());
-    assertEquals(a, extensions.get(0));
-    assertEquals(b, extensions.get(1));
-  }
-
-  @Test
-  public void checkProject() {
-    BatchExtension ok = new CheckProjectOK();
-    BatchExtension ko = new CheckProjectKO();
-
-    BatchExtensionDictionnary selector = newSelector(ok, ko);
-    List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, new Project("key"), true));
-
-    assertThat(extensions.size(), is(1));
-    assertThat(extensions.get(0), is(CheckProjectOK.class));
-  }
-
-  @Test
-  public void inheritAnnotations() {
-    BatchExtension a = new SubClass("foo");
-    BatchExtension b = new MethodDependentOf("foo");
-
-    BatchExtensionDictionnary selector = newSelector(b, a);
-    List<BatchExtension> extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
-
-    assertEquals(2, extensions.size());
-    assertEquals(a, extensions.get(0));
-    assertEquals(b, extensions.get(1));
-
-    // change initial order
-    selector = newSelector(a, b);
-    extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
-
-    assertEquals(2, extensions.size());
-    assertEquals(a, extensions.get(0));
-    assertEquals(b, extensions.get(1));
-  }
-
-  @Test(expected = IllegalStateException.class)
-  public void annotatedMethodsCanNotBePrivate() {
-    BatchExtensionDictionnary selector = newSelector();
-    BatchExtension wrong = new BatchExtension() {
-      @DependsUpon
-      private Object foo() {
-        return "foo";
-      }
-    };
-    selector.evaluateAnnotatedClasses(wrong, DependsUpon.class);
-  }
-
-  @Test
-  public void dependsUponPhase() {
-    BatchExtension pre = new PreSensor();
-    BatchExtension analyze = new GeneratesSomething("something");
-    BatchExtension post = new PostSensor();
-
-    BatchExtensionDictionnary selector = newSelector(analyze, post, pre);
-    List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
-
-    assertEquals(3, extensions.size());
-    assertEquals(pre, extensions.get(0));
-    assertEquals(analyze, extensions.get(1));
-    assertEquals(post, extensions.get(2));
-  }
-
-  @Test
-  public void dependsUponInheritedPhase() {
-    BatchExtension pre = new PreSensorSubclass();
-    BatchExtension analyze = new GeneratesSomething("something");
-    BatchExtension post = new PostSensorSubclass();
-
-    BatchExtensionDictionnary selector = newSelector(analyze, post, pre);
-    List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
-
-    assertEquals(3, extensions.size());
-    assertEquals(pre, extensions.get(0));
-    assertEquals(analyze, extensions.get(1));
-    assertEquals(post, extensions.get(2));
-  }
-
-  @Test
-  public void buildStatusCheckersAreExecutedAfterOtherPostJobs() {
-    BuildBreaker checker = new BuildBreaker() {
-      public void executeOn(Project project, SensorContext context) {
-      }
-    };
-
-    BatchExtensionDictionnary selector = newSelector(new FakePostJob(), checker, new FakePostJob());
-    List extensions = Lists.newArrayList(selector.select(BatchExtension.class, null, true));
-
-    assertEquals(3, extensions.size());
-    assertEquals(checker, extensions.get(2));
-  }
-
-  class FakeSensor implements Sensor {
-
-    public void analyse(Project project, SensorContext context) {
-
-    }
-
-    public boolean shouldExecuteOnProject(Project project) {
-      return true;
-    }
-  }
-
-  class MethodDependentOf implements BatchExtension {
-    private Object dep;
-
-    MethodDependentOf(Object o) {
-      this.dep = o;
-    }
-
-    @DependsUpon
-    public Object dependsUponObject() {
-      return dep;
-    }
-  }
-
-  @DependsUpon("flag")
-  class ClassDependsUpon implements BatchExtension {
-  }
-
-  @DependedUpon("flag")
-  class ClassDependedUpon implements BatchExtension {
-  }
-
-  @DependsUpon("flag")
-  interface InterfaceDependsUpon extends BatchExtension {
-  }
-
-  @DependedUpon("flag")
-  interface InterfaceDependedUpon extends BatchExtension {
-  }
-
-  class GeneratesSomething implements BatchExtension {
-    private Object gen;
-
-    GeneratesSomething(Object o) {
-      this.gen = o;
-    }
-
-    @DependedUpon
-    public Object generates() {
-      return gen;
-    }
-  }
-
-  class SubClass extends GeneratesSomething {
-    SubClass(Object o) {
-      super(o);
-    }
-  }
-
-  @Phase(name = Phase.Name.PRE)
-  class PreSensor implements BatchExtension {
-
-  }
-
-  class PreSensorSubclass extends PreSensor {
-
-  }
-
-  @Phase(name = Phase.Name.POST)
-  class PostSensor implements BatchExtension {
-
-  }
-
-  class PostSensorSubclass extends PostSensor {
-
-  }
-
-  class CheckProjectOK implements BatchExtension, CheckProject {
-    public boolean shouldExecuteOnProject(Project project) {
-      return true;
-    }
-  }
-
-  class CheckProjectKO implements BatchExtension, CheckProject {
-    public boolean shouldExecuteOnProject(Project project) {
-      return false;
-    }
-  }
-
-  private class FakePostJob implements PostJob {
-    public void executeOn(Project project, SensorContext context) {
-    }
-  }
-}