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 */ 020package org.sonar.api.batch; 021 022import com.google.common.base.Predicates; 023import com.google.common.collect.Collections2; 024import com.google.common.collect.Lists; 025import org.apache.commons.lang.ClassUtils; 026import org.sonar.api.BatchExtension; 027import org.sonar.api.batch.maven.DependsUponMavenPlugin; 028import org.sonar.api.batch.maven.MavenPluginHandler; 029import org.sonar.api.platform.ComponentContainer; 030import org.sonar.api.resources.Project; 031import org.sonar.api.utils.AnnotationUtils; 032import org.sonar.api.utils.dag.DirectAcyclicGraph; 033 034import java.lang.annotation.Annotation; 035import java.lang.reflect.Array; 036import java.lang.reflect.Method; 037import java.lang.reflect.Modifier; 038import java.util.Arrays; 039import java.util.Collection; 040import java.util.List; 041 042/** 043 * @since 1.11 044 * @deprecated since 2.6 was only used by views 045 */ 046@Deprecated 047public 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}