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.api.services;
20  
21  import static org.apache.maven.api.services.BaseRequest.nonNull;
22  
23  import java.util.ArrayList;
24  import java.util.Collection;
25  import java.util.Collections;
26  import java.util.List;
27  import java.util.Optional;
28  import org.apache.maven.api.Artifact;
29  import org.apache.maven.api.DependencyCoordinate;
30  import org.apache.maven.api.Project;
31  import org.apache.maven.api.Session;
32  import org.apache.maven.api.annotations.Experimental;
33  import org.apache.maven.api.annotations.Immutable;
34  import org.apache.maven.api.annotations.Nonnull;
35  import org.apache.maven.api.annotations.NotThreadSafe;
36  import org.apache.maven.api.annotations.Nullable;
37  
38  /**
39   * A request to collect the transitive dependencies and to build a dependency graph from them. There are three ways to
40   * create a dependency graph. First, only the root dependency can be given. Second, a root dependency and direct
41   * dependencies can be specified in which case the specified direct dependencies are merged with the direct dependencies
42   * retrieved from the artifact descriptor of the root dependency. And last, only direct dependencies can be specified in
43   * which case the root node of the resulting graph has no associated dependency.
44   *
45   * @since 4.0
46   * @see DependencyCollector#collect(DependencyCollectorRequest)
47   */
48  @Experimental
49  @Immutable
50  public interface DependencyCollectorRequest {
51  
52      @Nonnull
53      Session getSession();
54  
55      @Nonnull
56      Optional<Artifact> getRootArtifact();
57  
58      @Nonnull
59      Optional<DependencyCoordinate> getRoot();
60  
61      @Nonnull
62      Collection<DependencyCoordinate> getDependencies();
63  
64      @Nonnull
65      Collection<DependencyCoordinate> getManagedDependencies();
66  
67      boolean getVerbose();
68  
69      @Nonnull
70      static DependencyCollectorRequest build(@Nonnull Session session, Artifact root) {
71          return builder()
72                  .session(nonNull(session, "session cannot be null"))
73                  .rootArtifact(nonNull(root, "root cannot be null"))
74                  .build();
75      }
76  
77      @Nonnull
78      static DependencyCollectorRequest build(@Nonnull Session session, @Nonnull DependencyCoordinate root) {
79          return builder()
80                  .session(nonNull(session, "session cannot be null"))
81                  .root(nonNull(root, "root cannot be null"))
82                  .build();
83      }
84  
85      @Nonnull
86      static DependencyCollectorRequest build(@Nonnull Session session, @Nonnull Project project) {
87          return builder()
88                  .session(nonNull(session, "session cannot be null"))
89                  .rootArtifact(nonNull(project, "project cannot be null").getArtifact())
90                  .dependencies(project.getDependencies())
91                  .managedDependencies(project.getManagedDependencies())
92                  .build();
93      }
94  
95      @Nonnull
96      static DependencyCollectorRequestBuilder builder() {
97          return new DependencyCollectorRequestBuilder();
98      }
99  
100     @NotThreadSafe
101     class DependencyCollectorRequestBuilder {
102 
103         Session session;
104         Artifact rootArtifact;
105         DependencyCoordinate root;
106         List<DependencyCoordinate> dependencies = Collections.emptyList();
107         List<DependencyCoordinate> managedDependencies = Collections.emptyList();
108         boolean verbose;
109 
110         DependencyCollectorRequestBuilder() {}
111 
112         @Nonnull
113         public DependencyCollectorRequestBuilder session(@Nonnull Session session) {
114             this.session = session;
115             return this;
116         }
117 
118         /**
119          * Sets the root artifact for the dependency graph.
120          * This must not be confused with {@link #root(DependencyCoordinate)}: The root <em>dependency</em>, like any
121          * other specified dependency, will be subject to dependency collection/resolution, i.e. should have an artifact
122          * descriptor and a corresponding artifact file. The root <em>artifact</em> on the other hand is only used
123          * as a label for the root node of the graph in case no root dependency was specified. As such, the configured
124          * root artifact is ignored if {@link #root(DependencyCoordinate)} has been set.
125          *
126          * @param rootArtifact the root artifact for the dependency graph, may be {@code null}
127          * @return this request for chaining, never {@code null}
128          */
129         @Nonnull
130         public DependencyCollectorRequestBuilder rootArtifact(@Nullable Artifact rootArtifact) {
131             this.rootArtifact = rootArtifact;
132             return this;
133         }
134 
135         /**
136          * @param root The root dependency
137          * @return this request for chaining, never {@code null}
138          */
139         @Nonnull
140         public DependencyCollectorRequestBuilder root(@Nonnull DependencyCoordinate root) {
141             this.root = root;
142             return this;
143         }
144 
145         /**
146          * Sets the direct dependencies. If both a root dependency and direct dependencies are given in the request, the
147          * direct dependencies from the request will be merged with the direct dependencies from the root dependency's
148          * artifact descriptor, giving higher priority to the dependencies from the request.
149          *
150          * @param dependencies the direct dependencies, may be {@code null}
151          * @return this request for chaining, never {@code null}
152          */
153         @Nonnull
154         public DependencyCollectorRequestBuilder dependencies(@Nullable List<DependencyCoordinate> dependencies) {
155             this.dependencies = (dependencies != null) ? dependencies : Collections.emptyList();
156             return this;
157         }
158 
159         /**
160          * Adds the specified direct dependency.
161          *
162          * @param dependency the dependency to add, may be {@code null}
163          * @return this request for chaining, never {@code null}
164          */
165         @Nonnull
166         public DependencyCollectorRequestBuilder dependency(@Nullable DependencyCoordinate dependency) {
167             if (dependency != null) {
168                 if (this.dependencies.isEmpty()) {
169                     this.dependencies = new ArrayList<>();
170                 }
171                 this.dependencies.add(dependency);
172             }
173             return this;
174         }
175 
176         /**
177          * Sets the dependency management to apply to transitive dependencies. To clarify, this management does not
178          * apply to
179          * the direct dependencies of the root node.
180          *
181          * @param managedDependencies the dependency management, may be {@code null}
182          * @return this request for chaining, never {@code null}
183          */
184         @Nonnull
185         public DependencyCollectorRequestBuilder managedDependencies(
186                 @Nullable List<DependencyCoordinate> managedDependencies) {
187             this.managedDependencies = (managedDependencies != null) ? managedDependencies : Collections.emptyList();
188             return this;
189         }
190 
191         /**
192          * Adds the specified managed dependency.
193          *
194          * @param managedDependency The managed dependency to add, may be {@code null} in which case the call
195          *                          will have no effect.
196          * @return this request for chaining, never {@code null}
197          */
198         @Nonnull
199         public DependencyCollectorRequestBuilder managedDependency(@Nullable DependencyCoordinate managedDependency) {
200             if (managedDependency != null) {
201                 if (this.managedDependencies.isEmpty()) {
202                     this.managedDependencies = new ArrayList<>();
203                 }
204                 this.managedDependencies.add(managedDependency);
205             }
206             return this;
207         }
208 
209         /**
210          * Specifies that the collection should be verbose.
211          *
212          * @param verbose whether the collection should be verbose or not
213          * @return this request for chaining, never {@code null}
214          */
215         @Nonnull
216         public DependencyCollectorRequestBuilder verbose(boolean verbose) {
217             this.verbose = verbose;
218             return this;
219         }
220 
221         @Nonnull
222         public DependencyCollectorRequest build() {
223             return new DefaultDependencyCollectorRequest(
224                     session, rootArtifact, root, dependencies, managedDependencies, verbose);
225         }
226 
227         static class DefaultDependencyCollectorRequest extends BaseRequest implements DependencyCollectorRequest {
228             private final Artifact rootArtifact;
229             private final DependencyCoordinate root;
230             private final Collection<DependencyCoordinate> dependencies;
231             private final Collection<DependencyCoordinate> managedDependencies;
232             private final boolean verbose;
233 
234             /**
235              * Creates a request with the specified properties.
236              *
237              * @param session      {@link Session}
238              * @param rootArtifact The root dependency whose transitive dependencies should be collected, may be {@code
239              *                     null}.
240              */
241             DefaultDependencyCollectorRequest(
242                     @Nonnull Session session,
243                     @Nullable Artifact rootArtifact,
244                     @Nullable DependencyCoordinate root,
245                     @Nonnull Collection<DependencyCoordinate> dependencies,
246                     @Nonnull Collection<DependencyCoordinate> managedDependencies,
247                     boolean verbose) {
248                 super(session);
249                 this.rootArtifact = rootArtifact;
250                 this.root = root;
251                 this.dependencies = unmodifiable(nonNull(dependencies, "dependencies cannot be null"));
252                 this.managedDependencies =
253                         unmodifiable(nonNull(managedDependencies, "managedDependencies cannot be null"));
254                 this.verbose = verbose;
255             }
256 
257             @Nonnull
258             @Override
259             public Optional<Artifact> getRootArtifact() {
260                 return Optional.ofNullable(rootArtifact);
261             }
262 
263             @Nonnull
264             @Override
265             public Optional<DependencyCoordinate> getRoot() {
266                 return Optional.ofNullable(root);
267             }
268 
269             @Nonnull
270             @Override
271             public Collection<DependencyCoordinate> getDependencies() {
272                 return dependencies;
273             }
274 
275             @Nonnull
276             @Override
277             public Collection<DependencyCoordinate> getManagedDependencies() {
278                 return managedDependencies;
279             }
280 
281             @Override
282             public boolean getVerbose() {
283                 return verbose;
284             }
285 
286             @Nonnull
287             @Override
288             public String toString() {
289                 return getRoot() + " -> " + getDependencies();
290             }
291         }
292     }
293 }