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.plugins.dependency.tree;
20  
21  import java.util.ArrayList;
22  import java.util.List;
23  import java.util.Stack;
24  
25  import org.apache.maven.artifact.Artifact;
26  import org.apache.maven.model.Exclusion;
27  import org.apache.maven.shared.dependency.graph.DependencyNode;
28  import org.apache.maven.shared.dependency.graph.traversal.DependencyNodeVisitor;
29  
30  /**
31   * A dependency node visitor that clones visited nodes into a new dependency tree. This can be used in conjunction with
32   * a dependency node filter to construct subtrees.
33   *
34   * @author <a href="mailto:markhobson@gmail.com">Mark Hobson</a>
35   */
36  public class BuildingDependencyNodeVisitor implements DependencyNodeVisitor {
37      // fields -----------------------------------------------------------------
38  
39      /**
40       * The dependency node visitor to apply on the resultant dependency tree, or <code>null</code> for none.
41       */
42      private final DependencyNodeVisitor visitor;
43  
44      /**
45       * The resultant tree parent nodes for the currently visited node.
46       */
47      private final Stack<DependencyNode> parentNodes;
48  
49      /**
50       * The root node of the resultant tree.
51       */
52      private DependencyNode rootNode;
53  
54      // constructors -----------------------------------------------------------
55  
56      /**
57       * Creates a dependency node visitor that clones visited nodes into a new dependency tree.
58       */
59      public BuildingDependencyNodeVisitor() {
60          this(null);
61      }
62  
63      /**
64       * Creates a dependency node visitor that clones visited nodes into a new dependency tree, and then applies the
65       * specified dependency node visitor on the resultant dependency tree.
66       *
67       * @param visitor the dependency node visitor to apply on the resultant dependency tree, or <code>null</code> for
68       *            none
69       */
70      public BuildingDependencyNodeVisitor(DependencyNodeVisitor visitor) {
71          this.visitor = visitor;
72  
73          parentNodes = new Stack<>();
74      }
75  
76      // DependencyNodeVisitor methods ------------------------------------------
77  
78      /**
79       * {@inheritDoc}
80       */
81      @Override
82      public boolean visit(DependencyNode node) {
83          // clone the node
84          WrapperNode newNode = new WrapperNode(
85                  parentNodes.isEmpty() ? null : parentNodes.peek(),
86                  node.getArtifact(),
87                  node.getPremanagedVersion(),
88                  node.getPremanagedScope(),
89                  node.getVersionConstraint(),
90                  node.getOptional(),
91                  node.getExclusions(),
92                  node.toNodeString());
93          newNode.setChildren(new ArrayList<>());
94  
95          if (parentNodes.empty()) {
96              rootNode = newNode;
97          } else {
98              DependencyNode parentNode = parentNodes.peek();
99              parentNode.getChildren().add(newNode);
100         }
101 
102         parentNodes.push(newNode);
103 
104         return true;
105     }
106 
107     /**
108      * {@inheritDoc}
109      */
110     @Override
111     public boolean endVisit(DependencyNode node) {
112         parentNodes.pop();
113 
114         // apply the visitor to the resultant tree on the last visit
115         if (parentNodes.empty() && visitor != null) {
116             rootNode.accept(visitor);
117         }
118 
119         return true;
120     }
121 
122     // public methods ---------------------------------------------------------
123 
124     /**
125      * Gets the dependency node visitor that this visitor applies on the resultant dependency tree.
126      *
127      * @return the dependency node visitor, or <code>null</code> for none
128      */
129     public DependencyNodeVisitor getDependencyNodeVisitor() {
130         return visitor;
131     }
132 
133     /**
134      * Gets the root node of the resultant dependency tree constructed by this visitor.
135      *
136      * @return the root node, or <code>null</code> if the source tree has not yet been visited
137      */
138     public DependencyNode getDependencyTree() {
139         return rootNode;
140     }
141 
142     private static class WrapperNode implements DependencyNode {
143 
144         private final Artifact artifact;
145 
146         private final DependencyNode parent;
147 
148         private final String premanagedVersion;
149 
150         private final String premanagedScope;
151 
152         private final String versionConstraint;
153 
154         private List<DependencyNode> children;
155 
156         private final Boolean optional;
157 
158         private final List<Exclusion> exclusions;
159 
160         private final String nodeString;
161 
162         private WrapperNode(
163                 DependencyNode parent,
164                 Artifact artifact,
165                 String premanagedVersion,
166                 String premanagedScope,
167                 String versionConstraint,
168                 Boolean optional,
169                 List<Exclusion> exclusions,
170                 String nodeString) {
171             this.artifact = artifact;
172             this.parent = parent;
173             this.premanagedVersion = premanagedVersion;
174             this.premanagedScope = premanagedScope;
175             this.versionConstraint = versionConstraint;
176             this.optional = optional;
177             this.exclusions = exclusions;
178             this.nodeString = nodeString;
179         }
180 
181         @Override
182         public Artifact getArtifact() {
183             return artifact;
184         }
185 
186         @Override
187         public List<DependencyNode> getChildren() {
188             return children;
189         }
190 
191         @Override
192         public boolean accept(DependencyNodeVisitor visitor) {
193             if (visitor.visit(this)) {
194                 for (DependencyNode child : getChildren()) {
195                     if (!child.accept(visitor)) {
196                         break;
197                     }
198                 }
199             }
200 
201             return visitor.endVisit(this);
202         }
203 
204         @Override
205         public DependencyNode getParent() {
206             return parent;
207         }
208 
209         @Override
210         public String getPremanagedVersion() {
211             return premanagedVersion;
212         }
213 
214         @Override
215         public String getPremanagedScope() {
216             return premanagedScope;
217         }
218 
219         @Override
220         public String getVersionConstraint() {
221             return versionConstraint;
222         }
223 
224         @Override
225         public String toNodeString() {
226             return nodeString;
227         }
228 
229         @Override
230         public Boolean getOptional() {
231             return optional;
232         }
233 
234         @Override
235         public List<Exclusion> getExclusions() {
236             return exclusions;
237         }
238 
239         public void setChildren(List<DependencyNode> children) {
240             this.children = children;
241         }
242     }
243 }