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.eclipse.aether.util.graph.visitor;
20  
21  import java.io.File;
22  import java.util.ArrayList;
23  import java.util.IdentityHashMap;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  
28  import org.eclipse.aether.artifact.Artifact;
29  import org.eclipse.aether.graph.Dependency;
30  import org.eclipse.aether.graph.DependencyNode;
31  import org.eclipse.aether.graph.DependencyVisitor;
32  
33  /**
34   * Abstract base class for depth first dependency tree traversers. Subclasses of this visitor will visit each node
35   * exactly once regardless how many paths within the dependency graph lead to the node such that the resulting node
36   * sequence is free of duplicates.
37   * <p>
38   * Actual vertex ordering (preorder, inorder, postorder) needs to be defined by subclasses through appropriate
39   * implementations for {@link #visitEnter(org.eclipse.aether.graph.DependencyNode)} and
40   * {@link #visitLeave(org.eclipse.aether.graph.DependencyNode)}
41   */
42  abstract class AbstractDepthFirstNodeListGenerator implements DependencyVisitor {
43  
44      private final Map<DependencyNode, Object> visitedNodes;
45  
46      protected final List<DependencyNode> nodes;
47  
48      AbstractDepthFirstNodeListGenerator() {
49          nodes = new ArrayList<>(128);
50          visitedNodes = new IdentityHashMap<>(512);
51      }
52  
53      /**
54       * Gets the list of dependency nodes that was generated during the graph traversal.
55       *
56       * @return The list of dependency nodes, never {@code null}.
57       */
58      public List<DependencyNode> getNodes() {
59          return nodes;
60      }
61  
62      /**
63       * Gets the dependencies seen during the graph traversal.
64       *
65       * @param includeUnresolved Whether unresolved dependencies shall be included in the result or not.
66       * @return The list of dependencies, never {@code null}.
67       */
68      public List<Dependency> getDependencies(boolean includeUnresolved) {
69          List<Dependency> dependencies = new ArrayList<>(getNodes().size());
70  
71          for (DependencyNode node : getNodes()) {
72              Dependency dependency = node.getDependency();
73              if (dependency != null) {
74                  if (includeUnresolved || dependency.getArtifact().getFile() != null) {
75                      dependencies.add(dependency);
76                  }
77              }
78          }
79  
80          return dependencies;
81      }
82  
83      /**
84       * Gets the artifacts associated with the list of dependency nodes generated during the graph traversal.
85       *
86       * @param includeUnresolved Whether unresolved artifacts shall be included in the result or not.
87       * @return The list of artifacts, never {@code null}.
88       */
89      public List<Artifact> getArtifacts(boolean includeUnresolved) {
90          List<Artifact> artifacts = new ArrayList<>(getNodes().size());
91  
92          for (DependencyNode node : getNodes()) {
93              if (node.getDependency() != null) {
94                  Artifact artifact = node.getDependency().getArtifact();
95                  if (includeUnresolved || artifact.getFile() != null) {
96                      artifacts.add(artifact);
97                  }
98              }
99          }
100 
101         return artifacts;
102     }
103 
104     /**
105      * Gets the files of resolved artifacts seen during the graph traversal.
106      *
107      * @return The list of artifact files, never {@code null}.
108      */
109     public List<File> getFiles() {
110         List<File> files = new ArrayList<>(getNodes().size());
111 
112         for (DependencyNode node : getNodes()) {
113             if (node.getDependency() != null) {
114                 File file = node.getDependency().getArtifact().getFile();
115                 if (file != null) {
116                     files.add(file);
117                 }
118             }
119         }
120 
121         return files;
122     }
123 
124     /**
125      * Gets a class path by concatenating the artifact files of the visited dependency nodes. Nodes with unresolved
126      * artifacts are automatically skipped.
127      *
128      * @return The class path, using the platform-specific path separator, never {@code null}.
129      */
130     public String getClassPath() {
131         StringBuilder buffer = new StringBuilder(1024);
132 
133         for (Iterator<DependencyNode> it = getNodes().iterator(); it.hasNext(); ) {
134             DependencyNode node = it.next();
135             if (node.getDependency() != null) {
136                 Artifact artifact = node.getDependency().getArtifact();
137                 if (artifact.getFile() != null) {
138                     buffer.append(artifact.getFile().getAbsolutePath());
139                     if (it.hasNext()) {
140                         buffer.append(File.pathSeparatorChar);
141                     }
142                 }
143             }
144         }
145 
146         return buffer.toString();
147     }
148 
149     /**
150      * Marks the specified node as being visited and determines whether the node has been visited before.
151      *
152      * @param node The node being visited, must not be {@code null}.
153      * @return {@code true} if the node has not been visited before, {@code false} if the node was already visited.
154      */
155     protected boolean setVisited(DependencyNode node) {
156         return visitedNodes.put(node, Boolean.TRUE) == null;
157     }
158 
159     @Override
160     public abstract boolean visitEnter(DependencyNode node);
161 
162     @Override
163     public abstract boolean visitLeave(DependencyNode node);
164 }