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