summaryrefslogtreecommitdiffstats
path: root/sonar-deprecated/src
diff options
context:
space:
mode:
authorJulien HENRY <julien.henry@sonarsource.com>2013-03-26 15:42:35 +0100
committerJulien HENRY <julien.henry@sonarsource.com>2013-03-26 15:42:35 +0100
commitea669e56c1939f38d001fa74a7378117a948e786 (patch)
tree4edcd7c6a8bf0c477c2cb32a37ce634761a3dc34 /sonar-deprecated/src
parent9d37a7de14a8df7d02879865696510b4ebfaba5c (diff)
downloadsonarqube-ea669e56c1939f38d001fa74a7378117a948e786.tar.gz
sonarqube-ea669e56c1939f38d001fa74a7378117a948e786.zip
SONAR-4069 Deprecate BatchExtensionDisctionnary to move it outside of plugin API
Diffstat (limited to 'sonar-deprecated/src')
-rw-r--r--sonar-deprecated/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java231
-rw-r--r--sonar-deprecated/src/test/java/org/sonar/api/batch/BatchExtensionDictionnaryTest.java343
2 files changed, 574 insertions, 0 deletions
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
index 00000000000..ab57c80d7d4
--- /dev/null
+++ b/sonar-deprecated/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java
@@ -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
index 00000000000..2f37fc409b7
--- /dev/null
+++ b/sonar-deprecated/src/test/java/org/sonar/api/batch/BatchExtensionDictionnaryTest.java
@@ -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) {
+ }
+ }
+}