001 /**
002 * Copyright (C) 2009 Progress Software, Inc.
003 * http://fusesource.com
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.kuali.maven.plugins.graph;
018
019 import java.io.File;
020 import java.util.ArrayList;
021 import java.util.List;
022
023 import org.apache.maven.artifact.Artifact;
024 import org.apache.maven.artifact.factory.ArtifactFactory;
025 import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
026 import org.apache.maven.artifact.repository.ArtifactRepository;
027 import org.apache.maven.artifact.resolver.ArtifactCollector;
028 import org.apache.maven.artifact.resolver.ArtifactResolver;
029 import org.apache.maven.plugin.AbstractMojo;
030 import org.apache.maven.project.MavenProject;
031 import org.apache.maven.shared.dependency.tree.DependencyNode;
032 import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder;
033 import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
034 import org.kuali.maven.plugins.graph.collector.DependencyNodeTokenCollector;
035 import org.kuali.maven.plugins.graph.collector.TokenCollector;
036 import org.kuali.maven.plugins.graph.dot.Dot;
037 import org.kuali.maven.plugins.graph.dot.GraphException;
038 import org.kuali.maven.plugins.graph.dot.GraphHelper;
039 import org.kuali.maven.plugins.graph.dot.StringGenerator;
040 import org.kuali.maven.plugins.graph.dot.edge.EdgeHandler;
041 import org.kuali.maven.plugins.graph.filter.ArtifactFilterWrapper;
042 import org.kuali.maven.plugins.graph.filter.DependencyNodeFilterWrapper;
043 import org.kuali.maven.plugins.graph.filter.DepthFilter;
044 import org.kuali.maven.plugins.graph.filter.Filter;
045 import org.kuali.maven.plugins.graph.filter.Filters;
046 import org.kuali.maven.plugins.graph.filter.MatchCondition;
047 import org.kuali.maven.plugins.graph.filter.NodeFilter;
048 import org.kuali.maven.plugins.graph.filter.NodeFilterChain;
049 import org.kuali.maven.plugins.graph.filter.ReverseNodeFilter;
050 import org.kuali.maven.plugins.graph.pojo.Direction;
051 import org.kuali.maven.plugins.graph.pojo.DotContext;
052 import org.kuali.maven.plugins.graph.pojo.Edge;
053 import org.kuali.maven.plugins.graph.pojo.Graph;
054 import org.kuali.maven.plugins.graph.pojo.GraphNode;
055 import org.kuali.maven.plugins.graph.pojo.MavenContext;
056 import org.kuali.maven.plugins.graph.tree.Node;
057 import org.kuali.maven.plugins.graph.tree.TreeHelper;
058 import org.kuali.maven.plugins.graph.tree.TreeMetaData;
059
060 /**
061 *
062 */
063 public abstract class BaseMojo extends AbstractMojo {
064 Filters filters = new Filters();
065
066 /**
067 * @required
068 * @readonly
069 * @parameter expression="${project}"
070 * @since 1.0
071 */
072 private MavenProject project;
073
074 /**
075 * @required
076 * @readonly
077 * @parameter expression="${localRepository}"
078 * @since 1.0
079 */
080 private ArtifactRepository localRepository;
081
082 /**
083 * @required
084 * @component
085 * @since 1.0
086 */
087 private ArtifactResolver artifactResolver;
088
089 /**
090 * @required
091 * @readonly
092 * @component
093 * @since 1.0
094 */
095 private ArtifactFactory artifactFactory;
096
097 /**
098 * @required
099 * @readonly
100 * @component
101 * @since 1.0
102 */
103 private ArtifactMetadataSource artifactMetadataSource;
104
105 /**
106 * @required
107 * @readonly
108 * @component
109 */
110 private ArtifactCollector artifactCollector;
111
112 /**
113 * @required
114 * @readonly
115 * @component
116 * @since 1.0
117 */
118 private DependencyTreeBuilder treeBuilder;
119
120 /**
121 * The title for the graph
122 *
123 * @parameter expression="${graph.title}" default-value="Dependency Graph for ${project.name}"
124 */
125 private String title;
126
127 /**
128 * Set to true to retain the .dot file used to draw the graph
129 *
130 * @parameter expression="${graph.keepDotFile}" default-value="false"
131 */
132 private boolean keepDotFile;
133
134 /**
135 * Set to false to show only the dependencies for the current project
136 *
137 * @parameter expression="${graph.transitive}" default-value="true"
138 */
139 private boolean transitive;
140
141 /**
142 * <p>
143 * Comma delimited list of artifact patterns to include. <code>Includes</code> works "bottom up" and can be
144 * overridden by <code>excludes</code>. If an artifact matches the <code>includes</code> criteria, it, and all of
145 * the dependencies in the direct path from it back to the root of the dependency tree are displayed.
146 * </p>
147 *
148 * The pattern syntax has the form:
149 *
150 * <pre>
151 * [groupId]:[artifactId]:[type]:[classifier]:[version]
152 * </pre>
153 *
154 * <p>
155 * Each pattern segment is optional and supports <code>*</code> wildcards. An empty pattern segment is treated as a
156 * wildcard.
157 * </p>
158 *
159 * @parameter expression="${graph.includes}"
160 */
161 private String includes;
162
163 /**
164 * <p>
165 * Comma delimited list of artifact patterns to exclude. <code>Excludes</code> overrides <code>includes</code> and
166 * works "top down". If a dependency matches the <code>excludes</code> criteria, it, and all dependencies below it,
167 * are removed from the display. If not provided, no artifacts are excluded.
168 * </p>
169 *
170 * The pattern syntax has the form:
171 *
172 * <pre>
173 * [groupId]:[artifactId]:[type]:[classifier]:[version]
174 * </pre>
175 *
176 * <p>
177 * Each pattern segment is optional and supports <code>*</code> wildcards. An empty pattern segment is treated as a
178 * wildcard.
179 * </p>
180 *
181 * @parameter expression="${graph.excludes}"
182 */
183 private String excludes;
184
185 /**
186 * <p>
187 * Comma delimited list of dependency qualifiers used for "hiding" artifacts. <code>Hide</code> overrides
188 * <code>show</code> and works "top down". If a dependency matches the <code>hide</code> criteria, it, and
189 * dependencies below it, are removed from the display. If not provided, no dependencies are hidden.
190 * </p>
191 *
192 * The pattern syntax has the form:
193 *
194 * <pre>
195 * [scope]:[optional|required]:[state]
196 * </pre>
197 *
198 * Scopes: <code>compile,provided,runtime,test,system,import</code><br>
199 * States: <code>normal,conflict,cyclic,duplicate</code><br>
200 *
201 * <p>
202 * Each pattern segment is optional and supports <code>*</code> wildcards. An empty pattern segment is treated as a
203 * wildcard.
204 * </p>
205 *
206 * @parameter expression="${graph.hide}"
207 */
208 private String hide;
209
210 /**
211 * <p>
212 * Comma delimited list of dependency qualifiers used for "showing" artifacts. <code>Show</code> works "bottom up"
213 * and can be overridden by <code>hide</code>. If a dependency matches the <code>show</code> criteria, it, and all
214 * of the dependencies in the direct path from it back to the root of the dependency tree are displayed.
215 * </p>
216 *
217 * The pattern syntax has the form:
218 *
219 * <pre>
220 * [scope]:[optional|required]:[state]
221 * </pre>
222 *
223 * Scopes: <code>compile,provided,runtime,test,system,import</code><br>
224 * States: <code>normal,conflict,cyclic,duplicate</code><br>
225 *
226 * <p>
227 * Each pattern segment is optional and supports <code>*</code> wildcards. An empty pattern segment is treated as a
228 * wildcard.
229 * </p>
230 *
231 * @parameter expression="${graph.show}"
232 */
233 private String show;
234
235 /**
236 * The direction for the graph layout. Top to bottom, left to right, bottom to top, and right to left.
237 *
238 * @parameter expression="${graph.direction}" default-value="TB"
239 */
240 private Direction direction;
241
242 @Override
243 public void execute() {
244 try {
245 String content = getDotFileContent(title, direction);
246 Dot dot = new Dot();
247 DotContext context = dot.getDotContext(getFile(), content, keepDotFile);
248 dot.execute(context);
249 } catch (Exception e) {
250 e.printStackTrace();
251 }
252 }
253
254 protected abstract File getFile();
255
256 protected abstract EdgeHandler getEdgeHandler();
257
258 /**
259 * Restricts the depth of the dependency tree. To show only the dependencies of your project, set this to 1. To show
260 * the dependencies of your project and their direct dependencies, set this to 2.
261 *
262 * @parameter expression="${graph.depth}" default-value="-1"
263 */
264 private int depth;
265
266 protected DepthFilter<MavenContext> getDepthFilter() {
267 int maxDepth = transitive ? DepthFilter.INFINITE : 1;
268 maxDepth = depth >= 0 ? depth : maxDepth;
269 return new DepthFilter<MavenContext>(maxDepth);
270 }
271
272 protected void preProcess(Node<MavenContext> node) {
273 // do nothing by default
274 }
275
276 protected void postProcess(Node<MavenContext> node, List<GraphNode> nodes, List<Edge> edges) {
277 // do nothing by default
278 }
279
280 protected String getDotFileContent(String title, Direction direction) {
281 TreeHelper helper = new TreeHelper();
282 DependencyNode mavenTree = getMavenTree();
283 Node<MavenContext> nodeTree = helper.getTree(mavenTree);
284 preProcess(nodeTree);
285 helper.sanitize(nodeTree);
286 TreeMetaData md = helper.getMetaData(nodeTree);
287 helper.show(md);
288 helper.include(nodeTree, getIncludeFilter());
289 helper.exclude(nodeTree, getExcludeFilter());
290 List<GraphNode> nodes = helper.getGraphNodes(nodeTree);
291 EdgeHandler handler = getEdgeHandler();
292 List<Edge> edges = helper.getEdges(nodeTree, handler);
293 postProcess(nodeTree, nodes, edges);
294 helper.show(nodes, edges);
295 Graph graph = new GraphHelper().getGraph(title, direction, nodes, edges);
296 return new StringGenerator().getString(graph);
297 }
298
299 protected NodeFilter<MavenContext> getShowFilter() {
300 TokenCollector<DependencyNode> collector = new DependencyNodeTokenCollector();
301 Filter<DependencyNode> filter = filters.getIncludePatternFilter(getShow(), collector);
302 return new DependencyNodeFilterWrapper(filter);
303 }
304
305 protected NodeFilter<MavenContext> getHideFilter() {
306 TokenCollector<DependencyNode> collector = new DependencyNodeTokenCollector();
307 Filter<DependencyNode> filter = filters.getExcludePatternFilter(getHide(), collector);
308 return new DependencyNodeFilterWrapper(filter);
309 }
310
311 protected NodeFilter<MavenContext> getIncludeFilter() {
312 TokenCollector<Artifact> collector = new org.kuali.maven.plugins.graph.collector.ArtifactIdTokenCollector();
313 Filter<Artifact> filter = filters.getIncludePatternFilter(getIncludes(), collector);
314 ArtifactFilterWrapper artifactFilter = new ArtifactFilterWrapper(filter);
315 List<NodeFilter<MavenContext>> filters = new ArrayList<NodeFilter<MavenContext>>();
316 NodeFilter<MavenContext> artifactQualifierFilter = getShowFilter();
317 filters.add(artifactQualifierFilter);
318 filters.add(artifactFilter);
319 return new NodeFilterChain<MavenContext>(filters, MatchCondition.ALL, true);
320 }
321
322 protected NodeFilter<MavenContext> getExcludeFilter() {
323 TokenCollector<Artifact> collector = new org.kuali.maven.plugins.graph.collector.ArtifactIdTokenCollector();
324 Filter<Artifact> filter = filters.getExcludePatternFilter(getExcludes(), collector);
325 ArtifactFilterWrapper artifactFilter = new ArtifactFilterWrapper(filter);
326 ReverseNodeFilter<MavenContext> depthFilter = new ReverseNodeFilter<MavenContext>(getDepthFilter());
327 NodeFilter<MavenContext> artifactQualifierFilter = getHideFilter();
328 List<NodeFilter<MavenContext>> filters = new ArrayList<NodeFilter<MavenContext>>();
329 filters.add(artifactQualifierFilter);
330 filters.add(artifactFilter);
331 filters.add(depthFilter);
332 return new NodeFilterChain<MavenContext>(filters, MatchCondition.ANY, false);
333 }
334
335 protected DependencyNode getMavenTree() {
336 try {
337 return getTreeBuilder().buildDependencyTree(project, localRepository, artifactFactory,
338 artifactMetadataSource, null, artifactCollector);
339 } catch (DependencyTreeBuilderException e) {
340 throw new GraphException(e);
341 }
342 }
343
344 public String getIncludes() {
345 return includes;
346 }
347
348 public void setIncludes(String includes) {
349 this.includes = includes;
350 }
351
352 public String getExcludes() {
353 return excludes;
354 }
355
356 public void setExcludes(String excludes) {
357 this.excludes = excludes;
358 }
359
360 public String getHide() {
361 return hide;
362 }
363
364 public void setHide(String hide) {
365 this.hide = hide;
366 }
367
368 public String getShow() {
369 return show;
370 }
371
372 public void setShow(String show) {
373 this.show = show;
374 }
375
376 public String getTitle() {
377 return title;
378 }
379
380 public void setTitle(String title) {
381 this.title = title;
382 }
383
384 public boolean isKeepDotFile() {
385 return keepDotFile;
386 }
387
388 public void setKeepDotFile(boolean keepDotFile) {
389 this.keepDotFile = keepDotFile;
390 }
391
392 public boolean isTransitive() {
393 return transitive;
394 }
395
396 public void setTransitive(boolean transitive) {
397 this.transitive = transitive;
398 }
399
400 public Direction getDirection() {
401 return direction;
402 }
403
404 public void setDirection(Direction direction) {
405 this.direction = direction;
406 }
407
408 public int getDepth() {
409 return depth;
410 }
411
412 public void setDepth(int depth) {
413 this.depth = depth;
414 }
415
416 public MavenProject getProject() {
417 return project;
418 }
419
420 public ArtifactRepository getLocalRepository() {
421 return localRepository;
422 }
423
424 public ArtifactResolver getArtifactResolver() {
425 return artifactResolver;
426 }
427
428 public ArtifactFactory getArtifactFactory() {
429 return artifactFactory;
430 }
431
432 public ArtifactMetadataSource getArtifactMetadataSource() {
433 return artifactMetadataSource;
434 }
435
436 public ArtifactCollector getArtifactCollector() {
437 return artifactCollector;
438 }
439
440 public DependencyTreeBuilder getTreeBuilder() {
441 return treeBuilder;
442 }
443
444 }