1 package org.kuali.maven.plugins.graph.mojo;
2
3 import java.io.File;
4 import java.util.ArrayList;
5 import java.util.List;
6
7 import org.apache.maven.artifact.Artifact;
8 import org.apache.maven.shared.dependency.tree.DependencyNode;
9 import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder;
10 import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
11 import org.kuali.maven.plugins.graph.collector.ArtifactIdTokenCollector;
12 import org.kuali.maven.plugins.graph.collector.MavenContextTokenCollector;
13 import org.kuali.maven.plugins.graph.collector.TokenCollector;
14 import org.kuali.maven.plugins.graph.dot.Dot;
15 import org.kuali.maven.plugins.graph.dot.GraphHelper;
16 import org.kuali.maven.plugins.graph.dot.StringGenerator;
17 import org.kuali.maven.plugins.graph.filter.ArtifactFilterWrapper;
18 import org.kuali.maven.plugins.graph.filter.DepthFilter;
19 import org.kuali.maven.plugins.graph.filter.Filter;
20 import org.kuali.maven.plugins.graph.filter.Filters;
21 import org.kuali.maven.plugins.graph.filter.MatchCondition;
22 import org.kuali.maven.plugins.graph.filter.MavenContextFilterWrapper;
23 import org.kuali.maven.plugins.graph.filter.NodeFilter;
24 import org.kuali.maven.plugins.graph.filter.NodeFilterChain;
25 import org.kuali.maven.plugins.graph.filter.ReverseNodeFilter;
26 import org.kuali.maven.plugins.graph.pojo.Edge;
27 import org.kuali.maven.plugins.graph.pojo.Graph;
28 import org.kuali.maven.plugins.graph.pojo.GraphContext;
29 import org.kuali.maven.plugins.graph.pojo.GraphException;
30 import org.kuali.maven.plugins.graph.pojo.GraphNode;
31 import org.kuali.maven.plugins.graph.pojo.LayoutStyle;
32 import org.kuali.maven.plugins.graph.pojo.MavenContext;
33 import org.kuali.maven.plugins.graph.pojo.MojoContext;
34 import org.kuali.maven.plugins.graph.pojo.Scope;
35 import org.kuali.maven.plugins.graph.pojo.State;
36 import org.kuali.maven.plugins.graph.processor.CascadeOptionalProcessor;
37 import org.kuali.maven.plugins.graph.processor.LinkedEdgeProcessor;
38 import org.kuali.maven.plugins.graph.processor.FilteringProcessor;
39 import org.kuali.maven.plugins.graph.processor.FlatEdgeProcessor;
40 import org.kuali.maven.plugins.graph.processor.HideConflictsProcessor;
41 import org.kuali.maven.plugins.graph.processor.HideDuplicatesProcessor;
42 import org.kuali.maven.plugins.graph.processor.LabelProcessor;
43 import org.kuali.maven.plugins.graph.processor.Processor;
44 import org.kuali.maven.plugins.graph.processor.SanitizingProcessor;
45 import org.kuali.maven.plugins.graph.processor.ShowMetadataProcessor;
46 import org.kuali.maven.plugins.graph.processor.StyleProcessor;
47 import org.kuali.maven.plugins.graph.processor.ValidatingProcessor;
48 import org.kuali.maven.plugins.graph.tree.Counter;
49 import org.kuali.maven.plugins.graph.tree.Helper;
50 import org.kuali.maven.plugins.graph.tree.Node;
51 import org.kuali.maven.plugins.graph.tree.TreeHelper;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 public class MojoHelper {
56 private static final String FS = System.getProperty("file.separator");
57 private static final Logger logger = LoggerFactory.getLogger(MojoHelper.class);
58 Filters filters = new Filters();
59
60 public void execute(MojoContext mc, GraphContext gc, List<GraphContext> descriptors) {
61 try {
62 if (mc.isSkip()) {
63 logger.info("Skipping execution");
64 return;
65 }
66 List<GraphContext> descriptorsToUse = getDescriptorsToUse(mc, gc, descriptors);
67 if (Helper.isEmpty(descriptorsToUse)) {
68 logger.info("No descriptors");
69 return;
70 }
71
72 for (GraphContext descriptor : descriptorsToUse) {
73 execute(mc, descriptor);
74 }
75 } catch (Exception e) {
76 e.printStackTrace();
77 }
78 }
79
80 protected List<GraphContext> getDescriptorsToUse(MojoContext mc, GraphContext gc, List<GraphContext> descriptors) {
81 List<GraphContext> descriptorsToUse = new ArrayList<GraphContext>();
82 if (descriptors == null) {
83 descriptors = new ArrayList<GraphContext>();
84 }
85 if (mc.isUseDefaultDescriptors()) {
86 descriptorsToUse.addAll(getDefaultDescriptors(gc));
87 }
88 logger.info("descriptor count={}", descriptorsToUse.size());
89 Counter counter = new Counter(1);
90 logger.debug("global type={}", gc.getType());
91 for (GraphContext descriptor : descriptors) {
92 Helper.copyPropertiesIfNull(descriptor, gc);
93 if (descriptor.getCategory() == null) {
94 descriptor.setCategory("other");
95 }
96 if (descriptor.getLabel() == null) {
97 descriptor.setLabel(counter.increment() + "");
98 }
99 if (descriptor.getTransitive() == null) {
100 descriptor.setTransitive(true);
101 }
102 if (descriptor.getLayout() == null) {
103 descriptor.setLayout(LayoutStyle.LINKED);
104 }
105 }
106 Helper.addAll(descriptorsToUse, descriptors);
107 for (GraphContext descriptor : descriptorsToUse) {
108 String filename = getFilename(mc, descriptor);
109 File file = new File(filename);
110 descriptor.setFile(file);
111 logger.debug(file.getPath());
112 }
113 return descriptorsToUse;
114 }
115
116 protected String getFilename(MojoContext mc, GraphContext gc) {
117 String category = gc.getCategory();
118 String label = gc.getLabel();
119 String type = gc.getType();
120 return mc.getOutputDir().getAbsolutePath() + FS + category + FS + label + "." + type;
121 }
122
123 protected List<GraphContext> getDefaultDescriptors(GraphContext gc) {
124 List<GraphContext> descriptors = new ArrayList<GraphContext>();
125 descriptors.addAll(getGraphContexts(null, gc));
126 for (Scope scope : Scope.values()) {
127 descriptors.addAll(getGraphContexts(scope, gc));
128 }
129 return descriptors;
130 }
131
132 protected String getFilter(Scope scope, Boolean optional, State state) {
133 StringBuilder sb = new StringBuilder();
134 sb.append(scope == null ? "" : scope.toString());
135 sb.append(":");
136 sb.append(optional == null ? "" : (optional ? "optional" : "required"));
137 sb.append(":");
138 sb.append(state == null ? "" : state.toString());
139 return sb.toString();
140 }
141
142 protected String getLabel(Boolean optional, State state) {
143 if (optional == null && state == null) {
144 return "all";
145 }
146
147 if (optional != null && state == null) {
148 return optional ? "optional" : "required";
149 }
150
151 String o = optional == null ? "" : (optional ? "optional-" : "required-");
152 String s = state == null ? "any" : state.toString();
153
154 StringBuilder sb = new StringBuilder();
155 sb.append(o);
156 sb.append(s);
157 return sb.toString();
158 }
159
160 protected List<GraphContext> getGraphContexts(Scope scope, GraphContext context) {
161 List<GraphContext> contexts = new ArrayList<GraphContext>();
162
163
164 List<GraphContext> list1 = getGraphContexts(scope, null, context);
165
166
167 List<GraphContext> list2 = getGraphContexts(scope, true, context);
168
169
170 List<GraphContext> list3 = getGraphContexts(scope, false, context);
171
172
173 contexts.addAll(list1);
174 contexts.addAll(list2);
175 contexts.addAll(list3);
176 return contexts;
177 }
178
179 protected List<GraphContext> getGraphContexts(Scope scope, Boolean optional, GraphContext context) {
180 List<GraphContext> contexts = new ArrayList<GraphContext>();
181 String show = getFilter(scope, optional, null);
182
183
184 String label1 = getLabel(optional, null);
185 contexts.add(getGraphContext(context, scope, show, label1, true));
186
187
188 String label2 = getLabel(optional, null);
189 contexts.add(getGraphContext(context, scope, show, label2, false));
190 for (State state : State.values()) {
191 show = getFilter(scope, optional, state);
192
193
194 label1 = getLabel(optional, state);
195 contexts.add(getGraphContext(context, scope, show, label1, true));
196
197
198 label2 = getLabel(optional, state);
199 contexts.add(getGraphContext(context, scope, show, label2, false));
200 }
201 return contexts;
202 }
203
204 protected GraphContext getGraphContext(GraphContext context, Scope scope, String show, String label,
205 boolean transitive) {
206 GraphContext gc = Helper.copyProperties(GraphContext.class, context);
207 gc.setShow(show);
208 gc.setLabel(label);
209 gc.setTransitive(transitive);
210 gc.setLayout(LayoutStyle.FLAT);
211 String category = (transitive ? "transitive" : "direct") + "/" + (scope == null ? "any" : scope.toString());
212 gc.setCategory(category);
213 return gc;
214 }
215
216 protected GraphContext getGraphContext(Scope scope, Boolean transitive, LayoutStyle layout, GraphContext context) {
217 GraphContext gc = Helper.copyProperties(GraphContext.class, context);
218 gc.setTransitive(transitive);
219 gc.setCategory(transitive ? "transitive" : "direct");
220 String label = scope == null ? "all" : scope.toString();
221 String show = scope == null ? null : scope.toString();
222 if (LayoutStyle.LINKED != layout) {
223 label = label + "-" + layout.toString().toLowerCase();
224 }
225 gc.setShow(show);
226 gc.setLabel(label);
227 gc.setLayout(layout);
228 return gc;
229 }
230
231 public void execute(MojoContext mc, GraphContext gc) {
232 if (mc.isSkip()) {
233 logger.info("Skipping execution");
234 return;
235 }
236
237 try {
238 logger.info(gc.getFile().getPath());
239 GraphHelper gh = new GraphHelper();
240 String title = gh.getGraphTitle(gc);
241 gc.setTitle(title);
242 String content = getDotFileContent(mc, gc);
243 gc.setContent(content);
244 Dot dot = new Dot();
245 dot.fillInContext(gc);
246 dot.execute(gc);
247 } catch (Exception e) {
248 e.printStackTrace();
249 }
250 }
251
252 public DependencyNode getMavenTree(MojoContext c) {
253 try {
254 DependencyTreeBuilder builder = c.getTreeBuilder();
255 return builder.buildDependencyTree(c.getProject(), c.getLocalRepository(), c.getArtifactFactory(),
256 c.getArtifactMetadataSource(), null, c.getArtifactCollector());
257 } catch (DependencyTreeBuilderException e) {
258 throw new GraphException(e);
259 }
260 }
261
262 protected List<Processor> getProcessors(GraphContext gc, boolean verbose) {
263 List<Processor> processors = new ArrayList<Processor>();
264 processors.add(new ValidatingProcessor());
265 processors.add(new SanitizingProcessor());
266 processors.add(new LabelProcessor(gc));
267 if (Boolean.TRUE.equals(gc.getCascadeOptional())) {
268 processors.add(new CascadeOptionalProcessor());
269 }
270 if (verbose) {
271 processors.add(new ShowMetadataProcessor());
272 }
273 processors.add(new FilteringProcessor(gc));
274 processors.add(new StyleProcessor());
275 processors.add(getEdgeProcessor(gc.getLayout()));
276 if (!Boolean.TRUE.equals(gc.getShowDuplicates())) {
277 processors.add(new HideDuplicatesProcessor());
278 }
279 if (!Boolean.TRUE.equals(gc.getShowConflicts())) {
280 processors.add(new HideConflictsProcessor(gc.getLayout()));
281 }
282 return processors;
283 }
284
285 public String getDotFileContent(MojoContext mc, GraphContext gc) {
286 TreeHelper helper = new TreeHelper();
287 DependencyNode mavenTree = getMavenTree(mc);
288 Node<MavenContext> tree = helper.getTree(mavenTree);
289 List<Processor> processors = getProcessors(gc, mc.isVerbose());
290 for (Processor processor : processors) {
291 processor.process(tree);
292 }
293 List<GraphNode> nodes = helper.getGraphNodes(tree);
294 List<Edge> edges = helper.getEdges(tree);
295 if (mc.isVerbose()) {
296 helper.show(nodes, edges);
297 }
298 Graph graph = new GraphHelper().getGraph(gc.getTitle(), gc.getDirection(), nodes, edges);
299 return new StringGenerator().getString(graph);
300 }
301
302 protected Processor getEdgeProcessor(LayoutStyle layout) {
303 switch (layout) {
304 case LINKED:
305 return new LinkedEdgeProcessor();
306 case FLAT:
307 return new FlatEdgeProcessor();
308 default:
309 throw new IllegalStateException("Layout style " + layout + " is unknown");
310 }
311 }
312
313 protected NodeFilter<MavenContext> getShowFilter(GraphContext gc) {
314 TokenCollector<MavenContext> collector = new MavenContextTokenCollector();
315 Filter<MavenContext> filter = filters.getIncludePatternFilter(gc.getShow(), collector);
316 return new MavenContextFilterWrapper(filter);
317 }
318
319 protected NodeFilter<MavenContext> getHideFilter(GraphContext gc) {
320 TokenCollector<MavenContext> collector = new MavenContextTokenCollector();
321 Filter<MavenContext> filter = filters.getExcludePatternFilter(gc.getHide(), collector);
322 return new MavenContextFilterWrapper(filter);
323 }
324
325 public NodeFilter<MavenContext> getIncludeFilter(GraphContext gc) {
326 TokenCollector<Artifact> collector = new ArtifactIdTokenCollector();
327 Filter<Artifact> filter = filters.getIncludePatternFilter(gc.getIncludes(), collector);
328 ArtifactFilterWrapper artifactFilter = new ArtifactFilterWrapper(filter);
329 List<NodeFilter<MavenContext>> filters = new ArrayList<NodeFilter<MavenContext>>();
330 NodeFilter<MavenContext> artifactQualifierFilter = getShowFilter(gc);
331 filters.add(artifactQualifierFilter);
332 filters.add(artifactFilter);
333 return new NodeFilterChain<MavenContext>(filters, MatchCondition.ALL, true);
334 }
335
336 public NodeFilter<MavenContext> getExcludeFilter(GraphContext gc) {
337 TokenCollector<Artifact> collector = new ArtifactIdTokenCollector();
338 Filter<Artifact> filter = filters.getExcludePatternFilter(gc.getExcludes(), collector);
339 ArtifactFilterWrapper artifactFilter = new ArtifactFilterWrapper(filter);
340 ReverseNodeFilter<MavenContext> depthFilter = new ReverseNodeFilter<MavenContext>(getDepthFilter(gc));
341 NodeFilter<MavenContext> artifactQualifierFilter = getHideFilter(gc);
342 List<NodeFilter<MavenContext>> filters = new ArrayList<NodeFilter<MavenContext>>();
343 filters.add(artifactQualifierFilter);
344 filters.add(artifactFilter);
345 filters.add(depthFilter);
346 return new NodeFilterChain<MavenContext>(filters, MatchCondition.ANY, false);
347 }
348
349 protected DepthFilter<MavenContext> getDepthFilter(GraphContext gc) {
350 int maxDepth = gc.getTransitive() ? DepthFilter.INFINITE : 1;
351 maxDepth = (gc.getDepth() != null && gc.getDepth() >= 0) ? gc.getDepth() : maxDepth;
352 return new DepthFilter<MavenContext>(maxDepth);
353 }
354
355 }