1 package org.apache.maven.shared.dependency.tree;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.IdentityHashMap;
26 import java.util.Iterator;
27 import java.util.Map;
28 import java.util.Stack;
29
30 import org.apache.maven.artifact.Artifact;
31 import org.apache.maven.artifact.resolver.ResolutionListener;
32 import org.apache.maven.artifact.resolver.ResolutionListenerForDepMgmt;
33 import org.apache.maven.artifact.versioning.VersionRange;
34 import org.apache.maven.shared.dependency.tree.traversal.CollectingDependencyNodeVisitor;
35 import org.codehaus.plexus.logging.Logger;
36
37 /**
38 * An artifact resolution listener that constructs a dependency tree.
39 *
40 * @author Edwin Punzalan
41 * @author <a href="mailto:markhobson@gmail.com">Mark Hobson</a>
42 * @version $Id: DependencyTreeResolutionListener.java 661727 2008-05-30 14:21:49Z bentmann $
43 */
44 public class DependencyTreeResolutionListener implements ResolutionListener, ResolutionListenerForDepMgmt
45 {
46
47
48 /**
49 * The log to write debug messages to.
50 */
51 private final Logger logger;
52
53 /**
54 * The parent dependency nodes of the current dependency node.
55 */
56 private final Stack parentNodes;
57
58 /**
59 * A map of dependency nodes by their attached artifact.
60 */
61 private final Map nodesByArtifact;
62
63 /**
64 * The root dependency node of the computed dependency tree.
65 */
66 private DependencyNode rootNode;
67
68 /**
69 * The dependency node currently being processed by this listener.
70 */
71 private DependencyNode currentNode;
72
73 /**
74 * Map < String replacementId, String premanaged version >
75 */
76 private Map managedVersions = new HashMap();
77
78 /**
79 * Map < String replacementId, String premanaged scope >
80 */
81 private Map managedScopes = new HashMap();
82
83
84
85 /**
86 * Creates a new dependency tree resolution listener that writes to the specified log.
87 *
88 * @param logger
89 * the log to write debug messages to
90 */
91 public DependencyTreeResolutionListener( Logger logger )
92 {
93 this.logger = logger;
94
95 parentNodes = new Stack();
96 nodesByArtifact = new IdentityHashMap();
97 rootNode = null;
98 currentNode = null;
99 }
100
101
102
103 /**
104 * {@inheritDoc}
105 */
106 public void testArtifact( Artifact artifact )
107 {
108 log( "testArtifact: artifact=" + artifact );
109 }
110
111 /**
112 * {@inheritDoc}
113 */
114 public void startProcessChildren( Artifact artifact )
115 {
116 log( "startProcessChildren: artifact=" + artifact );
117
118 if ( !currentNode.getArtifact().equals( artifact ) )
119 {
120 throw new IllegalStateException( "Artifact was expected to be " + currentNode.getArtifact() + " but was "
121 + artifact );
122 }
123
124 parentNodes.push( currentNode );
125 }
126
127 /**
128 * {@inheritDoc}
129 */
130 public void endProcessChildren( Artifact artifact )
131 {
132 DependencyNode node = (DependencyNode) parentNodes.pop();
133
134 log( "endProcessChildren: artifact=" + artifact );
135
136 if ( node == null )
137 {
138 throw new IllegalStateException( "Parent dependency node was null" );
139 }
140
141 if ( !node.getArtifact().equals( artifact ) )
142 {
143 throw new IllegalStateException( "Parent dependency node artifact was expected to be " + node.getArtifact()
144 + " but was " + artifact );
145 }
146 }
147
148 /**
149 * {@inheritDoc}
150 */
151 public void includeArtifact( Artifact artifact )
152 {
153 log( "includeArtifact: artifact=" + artifact );
154
155 DependencyNode existingNode = getNode( artifact );
156
157
158
159
160
161 if ( existingNode == null && isCurrentNodeIncluded() )
162 {
163 DependencyNode node = addNode( artifact );
164
165
166
167
168
169 flushDependencyManagement( node );
170 }
171 }
172
173 /**
174 * {@inheritDoc}
175 */
176 public void omitForNearer( Artifact omitted, Artifact kept )
177 {
178 log( "omitForNearer: omitted=" + omitted + " kept=" + kept );
179
180 if ( !omitted.getDependencyConflictId().equals( kept.getDependencyConflictId() ) )
181 {
182 throw new IllegalArgumentException( "Omitted artifact dependency conflict id "
183 + omitted.getDependencyConflictId() + " differs from kept artifact dependency conflict id "
184 + kept.getDependencyConflictId() );
185 }
186
187 if ( isCurrentNodeIncluded() )
188 {
189 DependencyNode omittedNode = getNode( omitted );
190
191 if ( omittedNode != null )
192 {
193 removeNode( omitted );
194 }
195 else
196 {
197 omittedNode = createNode( omitted );
198
199 currentNode = omittedNode;
200 }
201
202 omittedNode.omitForConflict( kept );
203
204
205
206
207
208 flushDependencyManagement( omittedNode );
209
210 DependencyNode keptNode = getNode( kept );
211
212 if ( keptNode == null )
213 {
214 addNode( kept );
215 }
216 }
217 }
218
219 /**
220 * {@inheritDoc}
221 */
222 public void updateScope( Artifact artifact, String scope )
223 {
224 log( "updateScope: artifact=" + artifact + ", scope=" + scope );
225
226 DependencyNode node = getNode( artifact );
227
228 if ( node == null )
229 {
230
231 node = addNode( artifact );
232 }
233
234 node.setOriginalScope( artifact.getScope() );
235 }
236
237 /**
238 * {@inheritDoc}
239 */
240 public void manageArtifact( Artifact artifact, Artifact replacement )
241 {
242
243
244 log( "manageArtifact: artifact=" + artifact + ", replacement=" + replacement );
245
246 if ( replacement.getVersion() != null )
247 {
248 manageArtifactVersion( artifact, replacement );
249 }
250
251 if ( replacement.getScope() != null )
252 {
253 manageArtifactScope( artifact, replacement );
254 }
255 }
256
257 /**
258 * {@inheritDoc}
259 */
260 public void omitForCycle( Artifact artifact )
261 {
262 log( "omitForCycle: artifact=" + artifact );
263
264 if ( isCurrentNodeIncluded() )
265 {
266 DependencyNode node = createNode( artifact );
267
268 node.omitForCycle();
269 }
270 }
271
272 /**
273 * {@inheritDoc}
274 */
275 public void updateScopeCurrentPom( Artifact artifact, String scopeIgnored )
276 {
277 log( "updateScopeCurrentPom: artifact=" + artifact + ", scopeIgnored=" + scopeIgnored );
278
279 DependencyNode node = getNode( artifact );
280
281 if ( node == null )
282 {
283
284 node = addNode( artifact );
285
286 }
287
288 node.setFailedUpdateScope( scopeIgnored );
289 }
290
291 /**
292 * {@inheritDoc}
293 */
294 public void selectVersionFromRange( Artifact artifact )
295 {
296 log( "selectVersionFromRange: artifact=" + artifact );
297
298 DependencyNode node = getNode( artifact );
299
300
301
302
303 if ( node == null && isCurrentNodeIncluded() )
304 {
305 node = addNode( artifact );
306 }
307
308 node.setVersionSelectedFromRange( artifact.getVersionRange() );
309 node.setAvailableVersions( artifact.getAvailableVersions() );
310 }
311
312 /**
313 * {@inheritDoc}
314 */
315 public void restrictRange( Artifact artifact, Artifact replacement, VersionRange versionRange )
316 {
317 log( "restrictRange: artifact=" + artifact + ", replacement=" + replacement + ", versionRange=" + versionRange );
318
319
320 }
321
322
323
324 /**
325 * {@inheritDoc}
326 */
327 public void manageArtifactVersion( Artifact artifact, Artifact replacement )
328 {
329 log( "manageArtifactVersion: artifact=" + artifact + ", replacement=" + replacement );
330
331
332
333
334
335 if ( isCurrentNodeIncluded() && !replacement.getVersion().equals( artifact.getVersion() ) )
336 {
337
338
339
340
341 managedVersions.put( replacement.getId(), artifact.getVersion() );
342 }
343 }
344
345 /**
346 * {@inheritDoc}
347 */
348 public void manageArtifactScope( Artifact artifact, Artifact replacement )
349 {
350 log( "manageArtifactScope: artifact=" + artifact + ", replacement=" + replacement );
351
352
353
354
355
356 if ( isCurrentNodeIncluded() && !replacement.getScope().equals( artifact.getScope() ) )
357 {
358
359
360
361
362 managedScopes.put( replacement.getId(), artifact.getScope() );
363 }
364 }
365
366
367
368 /**
369 * Gets a list of all dependency nodes in the computed dependency tree.
370 *
371 * @return a list of dependency nodes
372 * @deprecated As of 1.1, use a {@link CollectingDependencyNodeVisitor} on the root dependency node
373 */
374 public Collection getNodes()
375 {
376 return Collections.unmodifiableCollection( nodesByArtifact.values() );
377 }
378
379 /**
380 * Gets the root dependency node of the computed dependency tree.
381 *
382 * @return the root node
383 */
384 public DependencyNode getRootNode()
385 {
386 return rootNode;
387 }
388
389
390
391 /**
392 * Writes the specified message to the log at debug level with indentation for the current node's depth.
393 *
394 * @param message
395 * the message to write to the log
396 */
397 private void log( String message )
398 {
399 int depth = parentNodes.size();
400
401 StringBuffer buffer = new StringBuffer();
402
403 for ( int i = 0; i < depth; i++ )
404 {
405 buffer.append( " " );
406 }
407
408 buffer.append( message );
409
410 logger.debug( buffer.toString() );
411 }
412
413 /**
414 * Creates a new dependency node for the specified artifact and appends it to the current parent dependency node.
415 *
416 * @param artifact
417 * the attached artifact for the new dependency node
418 * @return the new dependency node
419 */
420 private DependencyNode createNode( Artifact artifact )
421 {
422 DependencyNode node = new DependencyNode( artifact );
423
424 if ( !parentNodes.isEmpty() )
425 {
426 DependencyNode parent = (DependencyNode) parentNodes.peek();
427
428 parent.addChild( node );
429 }
430
431 return node;
432 }
433
434 /**
435 * Creates a new dependency node for the specified artifact, appends it to the current parent dependency node and
436 * puts it into the dependency node cache.
437 *
438 * @param artifact
439 * the attached artifact for the new dependency node
440 * @return the new dependency node
441 */
442
443 DependencyNode addNode( Artifact artifact )
444 {
445 DependencyNode node = createNode( artifact );
446
447 DependencyNode previousNode = (DependencyNode) nodesByArtifact.put( node.getArtifact(), node );
448
449 if ( previousNode != null )
450 {
451 throw new IllegalStateException( "Duplicate node registered for artifact: " + node.getArtifact() );
452 }
453
454 if ( rootNode == null )
455 {
456 rootNode = node;
457 }
458
459 currentNode = node;
460
461 return node;
462 }
463
464 /**
465 * Gets the dependency node for the specified artifact from the dependency node cache.
466 *
467 * @param artifact
468 * the artifact to find the dependency node for
469 * @return the dependency node, or <code>null</code> if the specified artifact has no corresponding dependency
470 * node
471 */
472 private DependencyNode getNode( Artifact artifact )
473 {
474 return (DependencyNode) nodesByArtifact.get( artifact );
475 }
476
477 /**
478 * Removes the dependency node for the specified artifact from the dependency node cache.
479 *
480 * @param artifact
481 * the artifact to remove the dependency node for
482 */
483 private void removeNode( Artifact artifact )
484 {
485 DependencyNode node = (DependencyNode) nodesByArtifact.remove( artifact );
486
487 if ( !artifact.equals( node.getArtifact() ) )
488 {
489 throw new IllegalStateException( "Removed dependency node artifact was expected to be " + artifact
490 + " but was " + node.getArtifact() );
491 }
492 }
493
494 /**
495 * Gets whether the all the ancestors of the dependency node currently being processed by this listener have an
496 * included state.
497 *
498 * @return <code>true</code> if all the ancestors of the current dependency node have a state of
499 * <code>INCLUDED</code>
500 */
501 private boolean isCurrentNodeIncluded()
502 {
503 boolean included = true;
504
505 for ( Iterator iterator = parentNodes.iterator(); included && iterator.hasNext(); )
506 {
507 DependencyNode node = (DependencyNode) iterator.next();
508
509 if ( node.getState() != DependencyNode.INCLUDED )
510 {
511 included = false;
512 }
513 }
514
515 return included;
516 }
517
518 /**
519 * Updates the specified node with any dependency management information cached in prior <code>manageArtifact</code>
520 * calls.
521 *
522 * @param node
523 * the node to update
524 */
525 private void flushDependencyManagement( DependencyNode node )
526 {
527 Artifact artifact = node.getArtifact();
528 String premanagedVersion = (String) managedVersions.get( artifact.getId() );
529 String premanagedScope = (String) managedScopes.get( artifact.getId() );
530
531 if ( premanagedVersion != null || premanagedScope != null )
532 {
533 if ( premanagedVersion != null )
534 {
535 node.setPremanagedVersion( premanagedVersion );
536 }
537
538 if ( premanagedScope != null )
539 {
540 node.setPremanagedScope( premanagedScope );
541 }
542
543 premanagedVersion = null;
544 premanagedScope = null;
545 }
546 }
547 }