001    /*
002     * SonarQube, open source software quality management tool.
003     * Copyright (C) 2008-2013 SonarSource
004     * mailto:contact AT sonarsource DOT com
005     *
006     * SonarQube is free software; you can redistribute it and/or
007     * modify it under the terms of the GNU Lesser General Public
008     * License as published by the Free Software Foundation; either
009     * version 3 of the License, or (at your option) any later version.
010     *
011     * SonarQube is distributed in the hope that it will be useful,
012     * but WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014     * Lesser General Public License for more details.
015     *
016     * You should have received a copy of the GNU Lesser General Public License
017     * along with this program; if not, write to the Free Software Foundation,
018     * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019     */
020    package org.sonar.api.batch;
021    
022    import com.google.common.base.Predicates;
023    import com.google.common.collect.Collections2;
024    import com.google.common.collect.Lists;
025    import org.apache.commons.lang.ClassUtils;
026    import org.sonar.api.BatchExtension;
027    import org.sonar.api.batch.maven.DependsUponMavenPlugin;
028    import org.sonar.api.batch.maven.MavenPluginHandler;
029    import org.sonar.api.platform.ComponentContainer;
030    import org.sonar.api.resources.Project;
031    import org.sonar.api.utils.AnnotationUtils;
032    import org.sonar.api.utils.dag.DirectAcyclicGraph;
033    
034    import java.lang.annotation.Annotation;
035    import java.lang.reflect.Method;
036    import java.lang.reflect.Modifier;
037    import java.util.Arrays;
038    import java.util.Collection;
039    import java.util.List;
040    
041    /**
042     * @since 1.11
043     * @deprecated since 2.6 was only used by views
044     */
045    @Deprecated
046    public class BatchExtensionDictionnary {
047    
048      private ComponentContainer componentContainer;
049    
050      public BatchExtensionDictionnary(ComponentContainer componentContainer) {
051        this.componentContainer = componentContainer;
052      }
053    
054      public <T> Collection<T> select(Class<T> type) {
055        return select(type, null, false);
056      }
057    
058      public <T> Collection<T> select(Class<T> type, Project project, boolean sort) {
059        List<T> result = getFilteredExtensions(type, project);
060        if (sort) {
061          return sort(result);
062        }
063        return result;
064      }
065    
066      public Collection<MavenPluginHandler> selectMavenPluginHandlers(Project project) {
067        Collection<DependsUponMavenPlugin> selectedExtensions = select(DependsUponMavenPlugin.class, project, true);
068        List<MavenPluginHandler> handlers = Lists.newArrayList();
069        for (DependsUponMavenPlugin extension : selectedExtensions) {
070          MavenPluginHandler handler = extension.getMavenPluginHandler(project);
071          if (handler != null) {
072            boolean ok = true;
073            if (handler instanceof CheckProject) {
074              ok = ((CheckProject) handler).shouldExecuteOnProject(project);
075            }
076            if (ok) {
077              handlers.add(handler);
078            }
079          }
080    
081        }
082        return handlers;
083      }
084    
085      protected List<BatchExtension> getExtensions() {
086        List<BatchExtension> extensions = Lists.newArrayList();
087        completeBatchExtensions(componentContainer, extensions);
088        return extensions;
089      }
090    
091      private static void completeBatchExtensions(ComponentContainer container, List<BatchExtension> extensions) {
092        if (container != null) {
093          extensions.addAll(container.getComponentsByType(BatchExtension.class));
094          completeBatchExtensions(container.getParent(), extensions);
095        }
096      }
097    
098      private <T> List<T> getFilteredExtensions(Class<T> type, Project project) {
099        List<T> result = Lists.newArrayList();
100        for (BatchExtension extension : getExtensions()) {
101          if (shouldKeep(type, extension, project)) {
102            result.add((T) extension);
103          }
104        }
105        return result;
106      }
107    
108      private boolean shouldKeep(Class type, Object extension, Project project) {
109        boolean keep = ClassUtils.isAssignable(extension.getClass(), type);
110        if (keep && project != null && ClassUtils.isAssignable(extension.getClass(), CheckProject.class)) {
111          keep = ((CheckProject) extension).shouldExecuteOnProject(project);
112        }
113        return keep;
114      }
115    
116      public <T> Collection<T> sort(Collection<T> extensions) {
117        DirectAcyclicGraph dag = new DirectAcyclicGraph();
118    
119        for (T extension : extensions) {
120          dag.add(extension);
121          for (Object dependency : getDependencies(extension)) {
122            dag.add(extension, dependency);
123          }
124          for (Object generates : getDependents(extension)) {
125            dag.add(generates, extension);
126          }
127          completePhaseDependencies(dag, extension);
128        }
129        List sortedList = dag.sort();
130    
131        return Collections2.filter(sortedList, Predicates.in(extensions));
132      }
133    
134      /**
135       * Extension dependencies
136       */
137      private <T> List getDependencies(T extension) {
138        return evaluateAnnotatedClasses(extension, DependsUpon.class);
139      }
140    
141      /**
142       * Objects that depend upon this extension.
143       */
144      public <T> List getDependents(T extension) {
145        return evaluateAnnotatedClasses(extension, DependedUpon.class);
146      }
147    
148      private void completePhaseDependencies(DirectAcyclicGraph dag, Object extension) {
149        Phase.Name phase = evaluatePhase(extension);
150        dag.add(extension, phase);
151        for (Phase.Name name : Phase.Name.values()) {
152          if (phase.compareTo(name) < 0) {
153            dag.add(name, extension);
154          } else if (phase.compareTo(name) > 0) {
155            dag.add(extension, name);
156          }
157        }
158      }
159    
160      protected List evaluateAnnotatedClasses(Object extension, Class<? extends Annotation> annotation) {
161        List<Object> results = Lists.newArrayList();
162        Class aClass = extension.getClass();
163        while (aClass != null) {
164          evaluateClass(aClass, annotation, results);
165    
166          for (Method method : aClass.getDeclaredMethods()) {
167            if (method.getAnnotation(annotation) != null) {
168              checkAnnotatedMethod(method);
169              evaluateMethod(extension, method, results);
170            }
171          }
172          aClass = aClass.getSuperclass();
173        }
174    
175        return results;
176      }
177    
178      private void evaluateClass(Class extensionClass, Class annotationClass, List<Object> results) {
179        Annotation annotation = extensionClass.getAnnotation(annotationClass);
180        if (annotation != null) {
181          if (annotation.annotationType().isAssignableFrom(DependsUpon.class)) {
182            results.addAll(Arrays.asList(((DependsUpon) annotation).value()));
183    
184          } else if (annotation.annotationType().isAssignableFrom(DependedUpon.class)) {
185            results.addAll(Arrays.asList(((DependedUpon) annotation).value()));
186          }
187        }
188    
189        Class[] interfaces = extensionClass.getInterfaces();
190        for (Class anInterface : interfaces) {
191          evaluateClass(anInterface, annotationClass, results);
192        }
193      }
194    
195      protected Phase.Name evaluatePhase(Object extension) {
196        Phase phaseAnnotation = AnnotationUtils.getAnnotation(extension, Phase.class);
197        if (phaseAnnotation != null) {
198          return phaseAnnotation.name();
199        }
200        return Phase.Name.DEFAULT;
201      }
202    
203      private void evaluateMethod(Object extension, Method method, List<Object> results) {
204        try {
205          Object result = method.invoke(extension);
206          if (result != null) {
207            // TODO add arrays/collections of objects/classes
208            if (result instanceof Class<?>) {
209              results.addAll(componentContainer.getComponentsByType((Class<?>) result));
210    
211            } else if (result instanceof Collection<?>) {
212              results.addAll((Collection<?>) result);
213    
214            } else {
215              results.add(result);
216            }
217          }
218        } catch (Exception e) {
219          throw new IllegalStateException("Can not invoke method " + method, e);
220        }
221      }
222    
223      private void checkAnnotatedMethod(Method method) {
224        if (!Modifier.isPublic(method.getModifiers())) {
225          throw new IllegalStateException("Annotated method must be public :" + method);
226        }
227        if (method.getParameterTypes().length > 0) {
228          throw new IllegalStateException("Annotated method must not have parameters :" + method);
229        }
230      }
231    }