View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.repository.metadata;
20  
21  import java.util.ArrayList;
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.TreeSet;
26  import org.apache.maven.artifact.ArtifactScopeEnum;
27  
28  /**
29   * maven dependency metadata graph
30   *
31   * @author <a href="oleg@codehaus.org">Oleg Gusakov</a>
32   *
33   */
34  public class MetadataGraph {
35      public static final int DEFAULT_VERTICES = 32;
36      public static final int DEFAULT_EDGES = 64;
37  
38      // flags to indicate the granularity of vertices
39      private boolean versionedVertices = false;
40      private boolean scopedVertices = false;
41      /**
42       * the entry point we started building the graph from
43       */
44      MetadataGraphVertex entry;
45  
46      // graph vertices
47      TreeSet<MetadataGraphVertex> vertices;
48  
49      /**
50       * incident and excident edges per node
51       */
52      Map<MetadataGraphVertex, List<MetadataGraphEdge>> incidentEdges;
53  
54      Map<MetadataGraphVertex, List<MetadataGraphEdge>> excidentEdges;
55  
56      /**
57       *  null in dirty graph, actual
58       *  scope for conflict-resolved graph
59       */
60      ArtifactScopeEnum scope;
61  
62      // ------------------------------------------------------------------------
63      /**
64       * init graph
65       */
66      public MetadataGraph(int nVertices) {
67          init(nVertices, 2 * nVertices);
68      }
69  
70      public MetadataGraph(int nVertices, int nEdges) {
71          init(nVertices, nEdges);
72      }
73      // ------------------------------------------------------------------------
74      /**
75       * construct a single vertex
76       */
77      public MetadataGraph(MetadataGraphVertex entry) throws MetadataResolutionException {
78          checkVertex(entry);
79          checkVertices(1);
80  
81          entry.setCompareVersion(versionedVertices);
82          entry.setCompareScope(scopedVertices);
83  
84          vertices.add(entry);
85          this.entry = entry;
86      }
87      // ------------------------------------------------------------------------
88      /**
89       * construct graph from a "dirty" tree
90       */
91      public MetadataGraph(MetadataTreeNode tree) throws MetadataResolutionException {
92          this(tree, false, false);
93      }
94      // ------------------------------------------------------------------------
95      /**
96       * construct graph from a "dirty" tree
97       *
98       * @param tree "dirty" tree root
99       * @param versionedVertices true if graph nodes should be versioned (different versions -&gt; different nodes)
100      * @param scopedVertices true if graph nodes should be versioned and scoped
101      * (different versions and/or scopes -&gt; different nodes)
102      *
103      */
104     public MetadataGraph(MetadataTreeNode tree, boolean versionedVertices, boolean scopedVertices)
105             throws MetadataResolutionException {
106         if (tree == null) {
107             throw new MetadataResolutionException("tree is null");
108         }
109 
110         setVersionedVertices(versionedVertices);
111         setScopedVertices(scopedVertices);
112 
113         this.versionedVertices = scopedVertices || versionedVertices;
114         this.scopedVertices = scopedVertices;
115 
116         int count = countNodes(tree);
117 
118         init(count, count + (count / 2));
119 
120         processTreeNodes(null, tree, 0, 0);
121     }
122     // ------------------------------------------------------------------------
123     private void processTreeNodes(MetadataGraphVertex parentVertex, MetadataTreeNode node, int depth, int pomOrder)
124             throws MetadataResolutionException {
125         if (node == null) {
126             return;
127         }
128 
129         MetadataGraphVertex vertex = new MetadataGraphVertex(node.md, versionedVertices, scopedVertices);
130         if (!vertices.contains(vertex)) {
131             vertices.add(vertex);
132         }
133 
134         if (parentVertex != null) // then create the edge
135         {
136             ArtifactMetadata md = node.getMd();
137             MetadataGraphEdge e =
138                     new MetadataGraphEdge(md.version, md.resolved, md.artifactScope, md.artifactUri, depth, pomOrder);
139             addEdge(parentVertex, vertex, e);
140         } else {
141             entry = vertex;
142         }
143 
144         MetadataTreeNode[] kids = node.getChildren();
145         if (kids == null || kids.length < 1) {
146             return;
147         }
148 
149         for (int i = 0; i < kids.length; i++) {
150             MetadataTreeNode n = kids[i];
151             processTreeNodes(vertex, n, depth + 1, i);
152         }
153     }
154     // ------------------------------------------------------------------------
155     public MetadataGraphVertex findVertex(ArtifactMetadata md) {
156         if (md == null || vertices == null || vertices.size() < 1) {
157             return null;
158         }
159 
160         MetadataGraphVertex v = new MetadataGraphVertex(md);
161         v.setCompareVersion(versionedVertices);
162         v.setCompareScope(scopedVertices);
163 
164         for (MetadataGraphVertex gv : vertices) {
165             if (gv.equals(v)) {
166                 return gv;
167             }
168         }
169 
170         return null;
171     }
172     // ------------------------------------------------------------------------
173     public MetadataGraphVertex addVertex(ArtifactMetadata md) {
174         if (md == null) {
175             return null;
176         }
177 
178         checkVertices();
179 
180         MetadataGraphVertex v = findVertex(md);
181         if (v != null) {
182             return v;
183         }
184 
185         v = new MetadataGraphVertex(md);
186 
187         v.setCompareVersion(versionedVertices);
188         v.setCompareScope(scopedVertices);
189 
190         vertices.add(v);
191         return v;
192     }
193     // ------------------------------------------------------------------------
194     /**
195      * init graph
196      */
197     private void init(int nVertices, int nEdges) {
198         int nV = nVertices;
199         if (nVertices < 1) {
200             nV = 1;
201         }
202 
203         checkVertices(nV);
204 
205         int nE = nVertices;
206         if (nEdges <= nV) {
207             nE = 2 * nE;
208         }
209 
210         checkEdges(nE);
211     }
212 
213     private void checkVertices() {
214         checkVertices(DEFAULT_VERTICES);
215     }
216 
217     private void checkVertices(int nVertices) {
218         if (vertices == null) {
219             vertices = new TreeSet<>();
220         }
221     }
222 
223     private void checkEdges() {
224         int count = DEFAULT_EDGES;
225 
226         if (vertices != null) {
227             count = vertices.size() + vertices.size() / 2;
228         }
229 
230         checkEdges(count);
231     }
232 
233     private void checkEdges(int nEdges) {
234         if (incidentEdges == null) {
235             incidentEdges = new HashMap<>(nEdges);
236         }
237         if (excidentEdges == null) {
238             excidentEdges = new HashMap<>(nEdges);
239         }
240     }
241     // ------------------------------------------------------------------------
242     private static void checkVertex(MetadataGraphVertex v) throws MetadataResolutionException {
243         if (v == null) {
244             throw new MetadataResolutionException("null vertex");
245         }
246         if (v.getMd() == null) {
247             throw new MetadataResolutionException("vertex without metadata");
248         }
249     }
250     // ------------------------------------------------------------------------
251     private static void checkEdge(MetadataGraphEdge e) throws MetadataResolutionException {
252         if (e == null) {
253             throw new MetadataResolutionException("badly formed edge");
254         }
255     }
256     // ------------------------------------------------------------------------
257     public List<MetadataGraphEdge> getEdgesBetween(MetadataGraphVertex vFrom, MetadataGraphVertex vTo) {
258         List<MetadataGraphEdge> edges = getIncidentEdges(vTo);
259         if (edges == null || edges.isEmpty()) {
260             return null;
261         }
262 
263         List<MetadataGraphEdge> res = new ArrayList<>(edges.size());
264 
265         for (MetadataGraphEdge e : edges) {
266             if (e.getSource().equals(vFrom)) {
267                 res.add(e);
268             }
269         }
270 
271         return res;
272     }
273     // ------------------------------------------------------------------------
274     public MetadataGraph addEdge(MetadataGraphVertex vFrom, MetadataGraphVertex vTo, MetadataGraphEdge e)
275             throws MetadataResolutionException {
276         checkVertex(vFrom);
277         checkVertex(vTo);
278 
279         checkVertices();
280 
281         checkEdge(e);
282         checkEdges();
283 
284         e.setSource(vFrom);
285         e.setTarget(vTo);
286 
287         vFrom.setCompareVersion(versionedVertices);
288         vFrom.setCompareScope(scopedVertices);
289 
290         List<MetadataGraphEdge> exList = excidentEdges.computeIfAbsent(vFrom, k -> new ArrayList<>());
291 
292         if (!exList.contains(e)) {
293             exList.add(e);
294         }
295 
296         List<MetadataGraphEdge> inList = incidentEdges.computeIfAbsent(vTo, k -> new ArrayList<>());
297 
298         if (!inList.contains(e)) {
299             inList.add(e);
300         }
301 
302         return this;
303     }
304     // ------------------------------------------------------------------------
305     public MetadataGraph removeVertex(MetadataGraphVertex v) {
306         if (vertices != null && v != null) {
307             vertices.remove(v);
308         }
309 
310         if (incidentEdges != null) {
311             incidentEdges.remove(v);
312         }
313 
314         if (excidentEdges != null) {
315             excidentEdges.remove(v);
316         }
317 
318         return this;
319     }
320     // ------------------------------------------------------------------------
321     private static int countNodes(MetadataTreeNode tree) {
322         if (tree == null) {
323             return 0;
324         }
325 
326         int count = 1;
327         MetadataTreeNode[] kids = tree.getChildren();
328         if (kids == null || kids.length < 1) {
329             return count;
330         }
331         for (MetadataTreeNode n : kids) {
332             count += countNodes(n);
333         }
334 
335         return count;
336     }
337 
338     // ------------------------------------------------------------------------
339     public MetadataGraphVertex getEntry() {
340         return entry;
341     }
342 
343     public void setEntry(MetadataGraphVertex entry) {
344         this.entry = entry;
345     }
346 
347     public TreeSet<MetadataGraphVertex> getVertices() {
348         return vertices;
349     }
350 
351     public List<MetadataGraphEdge> getIncidentEdges(MetadataGraphVertex vertex) {
352         checkEdges();
353         return incidentEdges.get(vertex);
354     }
355 
356     public List<MetadataGraphEdge> getExcidentEdges(MetadataGraphVertex vertex) {
357         checkEdges();
358         return excidentEdges.get(vertex);
359     }
360 
361     public boolean isVersionedVertices() {
362         return versionedVertices;
363     }
364 
365     public void setVersionedVertices(boolean versionedVertices) {
366         this.versionedVertices = versionedVertices;
367     }
368 
369     public boolean isScopedVertices() {
370         return scopedVertices;
371     }
372 
373     public void setScopedVertices(boolean scopedVertices) {
374         this.scopedVertices = scopedVertices;
375 
376         // scoped graph is versioned by definition
377         if (scopedVertices) {
378             versionedVertices = true;
379         }
380     }
381 
382     public ArtifactScopeEnum getScope() {
383         return scope;
384     }
385 
386     public void setScope(ArtifactScopeEnum scope) {
387         this.scope = scope;
388     }
389 
390     // ------------------------------------------------------------------------
391     public boolean isEmpty() {
392         return entry == null || vertices == null || vertices.isEmpty();
393     }
394 
395     // ------------------------------------------------------------------------
396     public boolean isEmptyEdges() {
397         return isEmpty() || incidentEdges == null || incidentEdges.isEmpty();
398     }
399     // ------------------------------------------------------------------------
400     @Override
401     public String toString() {
402         StringBuilder sb = new StringBuilder(512);
403         if (isEmpty()) {
404             return "empty";
405         }
406         for (MetadataGraphVertex v : vertices) {
407             sb.append("Vertex:  ").append(v.getMd().toString()).append('\n');
408             List<MetadataGraphEdge> ins = getIncidentEdges(v);
409             if (ins != null) {
410                 for (MetadataGraphEdge e : ins) {
411                     sb.append("       from :  ").append(e.toString()).append('\n');
412                 }
413             } else {
414                 sb.append("      no entries\n");
415             }
416 
417             List<MetadataGraphEdge> outs = getExcidentEdges(v);
418             if (outs != null) {
419                 for (MetadataGraphEdge e : outs) {
420                     sb.append("        to :  ").append(e.toString()).append('\n');
421                 }
422             } else {
423                 sb.append("      no exit\n");
424             }
425 
426             sb.append("-------------------------------------------------\n");
427         }
428         sb.append("=============================================================\n");
429         return sb.toString();
430     }
431 
432     // ------------------------------------------------------------------------
433     // ------------------------------------------------------------------------
434 }