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.analyzer.Analyzer; 028 import org.sonar.api.batch.analyzer.internal.DefaultAnalyzerDescriptor; 029 import org.sonar.api.batch.maven.DependsUponMavenPlugin; 030 import org.sonar.api.batch.maven.MavenPluginHandler; 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(), Analyzer.class)) { 152 DefaultAnalyzerDescriptor descriptor = new DefaultAnalyzerDescriptor(); 153 ((Analyzer) 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(), Analyzer.class)) { 166 DefaultAnalyzerDescriptor descriptor = new DefaultAnalyzerDescriptor(); 167 ((Analyzer) 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 }