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 }