diff options
author | Julien HENRY <julien.henry@sonarsource.com> | 2013-03-26 15:42:35 +0100 |
---|---|---|
committer | Julien HENRY <julien.henry@sonarsource.com> | 2013-03-26 15:42:35 +0100 |
commit | ea669e56c1939f38d001fa74a7378117a948e786 (patch) | |
tree | 4edcd7c6a8bf0c477c2cb32a37ce634761a3dc34 /sonar-deprecated/src | |
parent | 9d37a7de14a8df7d02879865696510b4ebfaba5c (diff) | |
download | sonarqube-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.java | 231 | ||||
-rw-r--r-- | sonar-deprecated/src/test/java/org/sonar/api/batch/BatchExtensionDictionnaryTest.java | 343 |
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) { + } + } +} |