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