001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2009 SonarSource SA 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar 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 * Sonar 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 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020 package org.sonar.api.batch; 021 022 import com.google.common.base.Predicates; 023 import com.google.common.collect.Collections2; 024 import org.apache.commons.lang.ClassUtils; 025 import org.picocontainer.MutablePicoContainer; 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.resources.Project; 030 import org.sonar.api.utils.AnnotationUtils; 031 import org.sonar.api.utils.IocContainer; 032 import org.sonar.api.utils.dag.DirectAcyclicGraph; 033 034 import java.lang.reflect.Method; 035 import java.lang.reflect.Modifier; 036 import java.util.ArrayList; 037 import java.util.Collection; 038 import java.util.List; 039 040 /** 041 * @since 1.11 042 */ 043 public class BatchExtensionDictionnary { 044 045 private MutablePicoContainer picoContainer; 046 047 public BatchExtensionDictionnary(IocContainer iocContainer) { 048 this.picoContainer = iocContainer.getPicoContainer(); 049 } 050 051 public BatchExtensionDictionnary(MutablePicoContainer picoContainer) { 052 this.picoContainer = picoContainer; 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 Collection<DependsUponMavenPlugin> selectedExtensions = select(DependsUponMavenPlugin.class, project, true); 069 List<MavenPluginHandler> handlers = new ArrayList<MavenPluginHandler>(); 070 for (DependsUponMavenPlugin extension : selectedExtensions) { 071 MavenPluginHandler handler = extension.getMavenPluginHandler(project); 072 if (handler != null) { 073 boolean ok = true; 074 if (handler instanceof CheckProject) { 075 ok = ((CheckProject) handler).shouldExecuteOnProject(project); 076 } 077 if (ok) { 078 handlers.add(handler); 079 } 080 } 081 082 } 083 return handlers; 084 } 085 086 private List<BatchExtension> getExtensions() { 087 return picoContainer.getComponents(BatchExtension.class); 088 } 089 090 private <T> List<T> getFilteredExtensions(Class<T> type, Project project) { 091 List<T> result = new ArrayList<T>(); 092 for (BatchExtension extension : getExtensions()) { 093 if (shouldKeep(type, extension, project)) { 094 result.add((T) extension); 095 } 096 } 097 return result; 098 } 099 100 private boolean shouldKeep(Class type, Object extension, Project project) { 101 boolean keep = ClassUtils.isAssignable(extension.getClass(), type); 102 if (keep && project != null && ClassUtils.isAssignable(extension.getClass(), CheckProject.class)) { 103 keep = ((CheckProject) extension).shouldExecuteOnProject(project); 104 } 105 return keep; 106 } 107 108 public <T> Collection<T> sort(Collection<T> extensions) { 109 DirectAcyclicGraph dag = new DirectAcyclicGraph(); 110 111 for (T extension : extensions) { 112 dag.add(extension); 113 for (Object dependency : getDependencies(extension)) { 114 dag.add(extension, dependency); 115 } 116 for (Object generates : getDependents(extension)) { 117 dag.add(generates, extension); 118 } 119 completePhaseDependencies(dag, extension); 120 } 121 List sortedList = dag.sort(); 122 123 return (Collection<T>) Collections2.filter(sortedList, Predicates.in(extensions)); 124 } 125 126 /** 127 * Extension dependencies 128 */ 129 private <T> List getDependencies(T extension) { 130 return evaluateAnnotatedMethods(extension, DependsUpon.class); 131 } 132 133 /** 134 * Objects that depend upon this extension. 135 */ 136 public <T> List getDependents(T extension) { 137 return evaluateAnnotatedMethods(extension, DependedUpon.class); 138 } 139 140 private void completePhaseDependencies(DirectAcyclicGraph dag, Object extension) { 141 Phase.Name phase = evaluatePhase(extension); 142 dag.add(extension, phase); 143 for (Phase.Name name : Phase.Name.values()) { 144 if (phase.compareTo(name) < 0) { 145 dag.add(name, extension); 146 } else if (phase.compareTo(name) > 0) { 147 dag.add(extension, name); 148 } 149 } 150 } 151 152 153 protected List evaluateAnnotatedMethods(Object extension, Class annotation) { 154 List results = new ArrayList(); 155 Class aClass = extension.getClass(); 156 while (aClass != null) { 157 for (Method method : aClass.getDeclaredMethods()) { 158 if (method.getAnnotation(annotation) != null) { 159 checkAnnotatedMethod(method); 160 evaluateMethod(extension, method, results); 161 } 162 } 163 aClass = aClass.getSuperclass(); 164 } 165 166 return results; 167 } 168 169 protected Phase.Name evaluatePhase(Object extension) { 170 Phase phaseAnnotation = AnnotationUtils.getClassAnnotation(extension, Phase.class); 171 if (phaseAnnotation != null) { 172 return phaseAnnotation.name(); 173 } 174 return Phase.Name.DEFAULT; 175 } 176 177 private void evaluateMethod(Object extension, Method method, List results) { 178 try { 179 Object result = method.invoke(extension); 180 if (result != null) { 181 //TODO add arrays/collections of objects/classes 182 if (result instanceof Class) { 183 results.addAll(picoContainer.getComponents((Class) result)); 184 185 } else if (result instanceof Collection) { 186 results.addAll((Collection) result); 187 188 } else { 189 results.add(result); 190 } 191 } 192 } catch (Exception e) { 193 throw new IllegalStateException("Can not invoke method " + method, e); 194 } 195 } 196 197 private void checkAnnotatedMethod(Method method) { 198 if (!Modifier.isPublic(method.getModifiers())) { 199 throw new IllegalStateException("Annotated method must be public :" + method); 200 } 201 if (method.getParameterTypes().length > 0) { 202 throw new IllegalStateException("Annotated method must not have parameters :" + method); 203 } 204 } 205 }