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 }