001    /*
002     * SonarQube, open source software quality management tool.
003     * Copyright (C) 2008-2014 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.Array;
036    import java.lang.reflect.Method;
037    import java.lang.reflect.Modifier;
038    import java.util.Arrays;
039    import java.util.Collection;
040    import java.util.List;
041    
042    /**
043     * @since 1.11
044     * @deprecated since 2.6 was only used by views
045     */
046    @Deprecated
047    public class BatchExtensionDictionnary {
048    
049      private final ComponentContainer componentContainer;
050    
051      public BatchExtensionDictionnary(ComponentContainer componentContainer) {
052        this.componentContainer = componentContainer;
053      }
054    
055      public <T> Collection<T> select(Class<T> type) {
056        return select(type, null, false);
057      }
058    
059      public <T> Collection<T> select(Class<T> type, Project project, boolean sort) {
060        List<T> result = getFilteredExtensions(type, project);
061        if (sort) {
062          return sort(result);
063        }
064        return result;
065      }
066    
067      public Collection<MavenPluginHandler> selectMavenPluginHandlers(Project project) {
068        List<DependsUponMavenPlugin> selectedExtensions = Lists.newArrayList();
069        for (BatchExtension extension : getExtensions()) {
070          if (ClassUtils.isAssignable(extension.getClass(), DependsUponMavenPlugin.class)) {
071            selectedExtensions.add((DependsUponMavenPlugin) extension);
072          }
073        }
074        List<MavenPluginHandler> handlers = Lists.newArrayList();
075        for (DependsUponMavenPlugin extension : selectedExtensions) {
076          MavenPluginHandler handler = extension.getMavenPluginHandler(project);
077          if (handler != null) {
078            boolean ok = true;
079            if (handler instanceof CheckProject) {
080              ok = ((CheckProject) handler).shouldExecuteOnProject(project);
081            }
082            if (ok) {
083              handlers.add(handler);
084            }
085          }
086    
087        }
088        return handlers;
089      }
090    
091      protected List<BatchExtension> getExtensions() {
092        List<BatchExtension> extensions = Lists.newArrayList();
093        completeBatchExtensions(componentContainer, extensions);
094        return extensions;
095      }
096    
097      private static void completeBatchExtensions(ComponentContainer container, List<BatchExtension> extensions) {
098        if (container != null) {
099          extensions.addAll(container.getComponentsByType(BatchExtension.class));
100          completeBatchExtensions(container.getParent(), extensions);
101        }
102      }
103    
104      private <T> List<T> getFilteredExtensions(Class<T> type, Project project) {
105        List<T> result = Lists.newArrayList();
106        for (BatchExtension extension : getExtensions()) {
107          if (shouldKeep(type, extension, project)) {
108            result.add((T) extension);
109          }
110        }
111        return result;
112      }
113    
114      private boolean shouldKeep(Class type, Object extension, Project project) {
115        boolean keep = ClassUtils.isAssignable(extension.getClass(), type);
116        if (keep && project != null && ClassUtils.isAssignable(extension.getClass(), CheckProject.class)) {
117          keep = ((CheckProject) extension).shouldExecuteOnProject(project);
118        }
119        return keep;
120      }
121    
122      public <T> Collection<T> sort(Collection<T> extensions) {
123        DirectAcyclicGraph dag = new DirectAcyclicGraph();
124    
125        for (T extension : extensions) {
126          dag.add(extension);
127          for (Object dependency : getDependencies(extension)) {
128            dag.add(extension, dependency);
129          }
130          for (Object generates : getDependents(extension)) {
131            dag.add(generates, extension);
132          }
133          completePhaseDependencies(dag, extension);
134        }
135        List sortedList = dag.sort();
136    
137        return Collections2.filter(sortedList, Predicates.in(extensions));
138      }
139    
140      /**
141       * Extension dependencies
142       */
143      private <T> List getDependencies(T extension) {
144        return evaluateAnnotatedClasses(extension, DependsUpon.class);
145      }
146    
147      /**
148       * Objects that depend upon this extension.
149       */
150      public <T> List getDependents(T extension) {
151        return evaluateAnnotatedClasses(extension, DependedUpon.class);
152      }
153    
154      private void completePhaseDependencies(DirectAcyclicGraph dag, Object extension) {
155        Phase.Name phase = evaluatePhase(extension);
156        dag.add(extension, phase);
157        for (Phase.Name name : Phase.Name.values()) {
158          if (phase.compareTo(name) < 0) {
159            dag.add(name, extension);
160          } else if (phase.compareTo(name) > 0) {
161            dag.add(extension, name);
162          }
163        }
164      }
165    
166      protected List evaluateAnnotatedClasses(Object extension, Class<? extends Annotation> annotation) {
167        List<Object> results = Lists.newArrayList();
168        Class aClass = extension.getClass();
169        while (aClass != null) {
170          evaluateClass(aClass, annotation, results);
171    
172          for (Method method : aClass.getDeclaredMethods()) {
173            if (method.getAnnotation(annotation) != null) {
174              checkAnnotatedMethod(method);
175              evaluateMethod(extension, method, results);
176            }
177          }
178          aClass = aClass.getSuperclass();
179        }
180    
181        return results;
182      }
183    
184      private void evaluateClass(Class extensionClass, Class annotationClass, List<Object> results) {
185        Annotation annotation = extensionClass.getAnnotation(annotationClass);
186        if (annotation != null) {
187          if (annotation.annotationType().isAssignableFrom(DependsUpon.class)) {
188            results.addAll(Arrays.asList(((DependsUpon) annotation).value()));
189    
190          } else if (annotation.annotationType().isAssignableFrom(DependedUpon.class)) {
191            results.addAll(Arrays.asList(((DependedUpon) annotation).value()));
192          }
193        }
194    
195        Class[] interfaces = extensionClass.getInterfaces();
196        for (Class anInterface : interfaces) {
197          evaluateClass(anInterface, annotationClass, results);
198        }
199      }
200    
201      protected Phase.Name evaluatePhase(Object extension) {
202        Phase phaseAnnotation = AnnotationUtils.getAnnotation(extension, Phase.class);
203        if (phaseAnnotation != null) {
204          return phaseAnnotation.name();
205        }
206        return Phase.Name.DEFAULT;
207      }
208    
209      private void evaluateMethod(Object extension, Method method, List<Object> results) {
210        try {
211          Object result = method.invoke(extension);
212          if (result != null) {
213            if (result instanceof Class<?>) {
214              results.addAll(componentContainer.getComponentsByType((Class<?>) result));
215    
216            } else if (result instanceof Collection<?>) {
217              results.addAll((Collection<?>) result);
218    
219            } else if (result.getClass().isArray()) {
220              for (int i = 0; i < Array.getLength(result); i++) {
221                results.add(Array.get(result, i));
222              }
223    
224            } else {
225              results.add(result);
226            }
227          }
228        } catch (Exception e) {
229          throw new IllegalStateException("Can not invoke method " + method, e);
230        }
231      }
232    
233      private void checkAnnotatedMethod(Method method) {
234        if (!Modifier.isPublic(method.getModifiers())) {
235          throw new IllegalStateException("Annotated method must be public:" + method);
236        }
237        if (method.getParameterTypes().length > 0) {
238          throw new IllegalStateException("Annotated method must not have parameters:" + method);
239        }
240      }
241    }