View Javadoc
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         // optional or required
164         List<GraphContext> list1 = getGraphContexts(scope, null, context);
165 
166         // optional only
167         List<GraphContext> list2 = getGraphContexts(scope, true, context);
168 
169         // required only
170         List<GraphContext> list3 = getGraphContexts(scope, false, context);
171 
172         // Add them to the list
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         // transitive
184         String label1 = getLabel(optional, null);
185         contexts.add(getGraphContext(context, scope, show, label1, true));
186 
187         // non-transitive
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             // transitive
194             label1 = getLabel(optional, state);
195             contexts.add(getGraphContext(context, scope, show, label1, true));
196 
197             // non-transitive
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 }