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