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.impl;
20  
21  import java.util.ArrayList;
22  import java.util.List;
23  import java.util.Objects;
24  import java.util.Optional;
25  
26  import org.apache.maven.api.Artifact;
27  import org.apache.maven.api.Dependency;
28  import org.apache.maven.api.Node;
29  import org.apache.maven.api.RemoteRepository;
30  import org.apache.maven.api.annotations.Nonnull;
31  import org.eclipse.aether.graph.DependencyNode;
32  import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
33  import org.eclipse.aether.util.graph.transformer.ConflictResolver;
34  
35  public class DefaultNode extends AbstractNode {
36  
37      protected final @Nonnull InternalSession session;
38      protected final @Nonnull org.eclipse.aether.graph.DependencyNode node;
39      protected final boolean verbose;
40  
41      public DefaultNode(
42              @Nonnull InternalSession session, @Nonnull org.eclipse.aether.graph.DependencyNode node, boolean verbose) {
43          this.session = session;
44          this.node = node;
45          this.verbose = verbose;
46      }
47  
48      @Override
49      DependencyNode getDependencyNode() {
50          return node;
51      }
52  
53      @Override
54      public Artifact getArtifact() {
55          return node.getArtifact() != null ? session.getArtifact(node.getArtifact()) : null;
56      }
57  
58      @Override
59      public Dependency getDependency() {
60          return node.getDependency() != null ? session.getDependency(node.getDependency()) : null;
61      }
62  
63      @Override
64      public List<Node> getChildren() {
65          return new MappedList<>(node.getChildren(), n -> session.getNode(n, verbose));
66      }
67  
68      @Override
69      public List<RemoteRepository> getRemoteRepositories() {
70          return new MappedList<>(node.getRepositories(), session::getRemoteRepository);
71      }
72  
73      @Override
74      public Optional<RemoteRepository> getRepository() {
75          // TODO: v4: implement
76          throw new UnsupportedOperationException("Not implemented yet");
77      }
78  
79      /**
80       * Returns a detailed string representation of this dependency node.
81       * <p>
82       * When verbose mode is disabled, returns the basic string representation in the format:
83       * {@code groupId:artifactId:version[:scope]}
84       * <p>
85       * When verbose mode is enabled, additional details are included with the following format:
86       * <ul>
87       *   <li>For included dependencies: {@code groupId:artifactId:version[:scope] (details)}</li>
88       *   <li>For omitted dependencies: {@code (groupId:artifactId:version[:scope] - details)}</li>
89       * </ul>
90       * Where details may include:
91       * <ul>
92       *   <li>Version management information (if the version was managed from a different version)</li>
93       *   <li>Scope management information (if the scope was managed from a different scope)</li>
94       *   <li>Scope updates (if the scope was changed during resolution)</li>
95       *   <li>Conflict resolution information (if the dependency was omitted due to conflicts or duplicates)</li>
96       * </ul>
97       *
98       * @return a string representation of this dependency node with optional detailed information
99       */
100     @Nonnull
101     @Override
102     public String asString() {
103         StringBuilder sb = new StringBuilder();
104         DependencyNode node = getDependencyNode();
105         org.eclipse.aether.artifact.Artifact artifact = node.getArtifact();
106         org.eclipse.aether.graph.Dependency dependency = node.getDependency();
107 
108         if (!verbose) {
109             sb.append(artifact);
110             if (dependency != null) {
111                 sb.append(":").append(dependency.getScope());
112             }
113             return sb.toString();
114         }
115 
116         List<String> details = new ArrayList<>();
117         org.eclipse.aether.graph.DependencyNode winner =
118                 (org.eclipse.aether.graph.DependencyNode) node.getData().get(ConflictResolver.NODE_DATA_WINNER);
119         String winnerVersion = winner != null ? winner.getArtifact().getBaseVersion() : null;
120         boolean included = (winnerVersion == null);
121 
122         String preManagedVersion = DependencyManagerUtils.getPremanagedVersion(node);
123         if (preManagedVersion != null) {
124             details.add("version managed from " + preManagedVersion);
125         }
126 
127         String preManagedScope = DependencyManagerUtils.getPremanagedScope(node);
128         if (preManagedScope != null) {
129             details.add("scope managed from " + preManagedScope);
130         }
131 
132         String originalScope = (String) node.getData().get(ConflictResolver.NODE_DATA_ORIGINAL_SCOPE);
133         if (originalScope != null && !originalScope.equals(dependency.getScope())) {
134             details.add("scope updated from " + originalScope);
135         }
136 
137         if (!included) {
138             if (Objects.equals(winnerVersion, artifact.getVersion())) {
139                 details.add("omitted for duplicate");
140             } else {
141                 details.add("omitted for conflict with " + winnerVersion);
142             }
143         }
144 
145         if (!included) {
146             sb.append('(');
147         }
148 
149         sb.append(artifact);
150         if (dependency != null) {
151             sb.append(":").append(dependency.getScope());
152         }
153 
154         if (!details.isEmpty()) {
155             sb.append(included ? " (" : " - ");
156             appendDetails(sb, details);
157             if (included) {
158                 sb.append(')');
159             }
160         }
161 
162         if (!included) {
163             sb.append(')');
164         }
165 
166         return sb.toString();
167     }
168 
169     private static void appendDetails(StringBuilder sb, List<String> details) {
170         boolean first = true;
171         for (String detail : details) {
172             if (first) {
173                 first = false;
174             } else {
175                 sb.append("; ");
176             }
177             sb.append(detail);
178         }
179     }
180 }