Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
DependencyNode |
|
| 2.066666666666667;2,067 | ||||
DependencyNode$ItemAppender |
|
| 2.066666666666667;2,067 |
1 | package org.apache.maven.shared.dependency.tree; | |
2 | ||
3 | /* | |
4 | * Licensed to the Apache Software Foundation (ASF) under one | |
5 | * or more contributor license agreements. See the NOTICE file | |
6 | * distributed with this work for additional information | |
7 | * regarding copyright ownership. The ASF licenses this file | |
8 | * to you under the Apache License, Version 2.0 (the | |
9 | * "License"); you may not use this file except in compliance | |
10 | * with the License. You may obtain a copy of the License at | |
11 | * | |
12 | * http://www.apache.org/licenses/LICENSE-2.0 | |
13 | * | |
14 | * Unless required by applicable law or agreed to in writing, | |
15 | * software distributed under the License is distributed on an | |
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
17 | * KIND, either express or implied. See the License for the | |
18 | * specific language governing permissions and limitations | |
19 | * under the License. | |
20 | */ | |
21 | ||
22 | import java.io.StringWriter; | |
23 | import java.util.ArrayList; | |
24 | import java.util.Collections; | |
25 | import java.util.Iterator; | |
26 | import java.util.List; | |
27 | ||
28 | import org.apache.maven.artifact.Artifact; | |
29 | import org.apache.maven.artifact.versioning.ArtifactVersion; | |
30 | import org.apache.maven.artifact.versioning.VersionRange; | |
31 | import org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor; | |
32 | import org.apache.maven.shared.dependency.tree.traversal.SerializingDependencyNodeVisitor; | |
33 | ||
34 | /** | |
35 | * Represents an artifact node within a Maven project's dependency tree. | |
36 | * | |
37 | * @author Edwin Punzalan | |
38 | * @author <a href="mailto:markhobson@gmail.com">Mark Hobson</a> | |
39 | * @version $Id: DependencyNode.java 1100703 2011-05-08 08:27:33Z hboutemy $ | |
40 | */ | |
41 | public class DependencyNode | |
42 | { | |
43 | // constants -------------------------------------------------------------- | |
44 | ||
45 | /** | |
46 | * State that represents an included dependency node. | |
47 | * | |
48 | * @since 1.1 | |
49 | */ | |
50 | public static final int INCLUDED = 0; | |
51 | ||
52 | /** | |
53 | * State that represents a dependency node that has been omitted for duplicating another dependency node. | |
54 | * | |
55 | * @since 1.1 | |
56 | */ | |
57 | public static final int OMITTED_FOR_DUPLICATE = 1; | |
58 | ||
59 | /** | |
60 | * State that represents a dependency node that has been omitted for conflicting with another dependency node. | |
61 | * | |
62 | * @since 1.1 | |
63 | */ | |
64 | public static final int OMITTED_FOR_CONFLICT = 2; | |
65 | ||
66 | /** | |
67 | * State that represents a dependency node that has been omitted for introducing a cycle into the dependency tree. | |
68 | * | |
69 | * @since 1.1 | |
70 | */ | |
71 | public static final int OMITTED_FOR_CYCLE = 3; | |
72 | ||
73 | // classes ---------------------------------------------------------------- | |
74 | ||
75 | /** | |
76 | * Utility class to concatenate a number of parameters with separator tokens. | |
77 | */ | |
78 | private static class ItemAppender | |
79 | { | |
80 | private StringBuffer buffer; | |
81 | ||
82 | private String startToken; | |
83 | ||
84 | private String separatorToken; | |
85 | ||
86 | private String endToken; | |
87 | ||
88 | private boolean appended; | |
89 | ||
90 | public ItemAppender( StringBuffer buffer, String startToken, String separatorToken, String endToken ) | |
91 | 30 | { |
92 | 30 | this.buffer = buffer; |
93 | 30 | this.startToken = startToken; |
94 | 30 | this.separatorToken = separatorToken; |
95 | 30 | this.endToken = endToken; |
96 | ||
97 | 30 | appended = false; |
98 | 30 | } |
99 | ||
100 | public ItemAppender append( String item ) | |
101 | { | |
102 | 3 | appendToken(); |
103 | ||
104 | 3 | buffer.append( item ); |
105 | ||
106 | 3 | return this; |
107 | } | |
108 | ||
109 | public ItemAppender append( String item1, String item2 ) | |
110 | { | |
111 | 5 | appendToken(); |
112 | ||
113 | 5 | buffer.append( item1 ).append( item2 ); |
114 | ||
115 | 5 | return this; |
116 | } | |
117 | ||
118 | public void flush() | |
119 | { | |
120 | 30 | if ( appended ) |
121 | { | |
122 | 7 | buffer.append( endToken ); |
123 | ||
124 | 7 | appended = false; |
125 | } | |
126 | 30 | } |
127 | ||
128 | private void appendToken() | |
129 | { | |
130 | 8 | buffer.append( appended ? separatorToken : startToken ); |
131 | ||
132 | 8 | appended = true; |
133 | 8 | } |
134 | } | |
135 | ||
136 | // fields ----------------------------------------------------------------- | |
137 | ||
138 | /** | |
139 | * The artifact that is attached to this dependency node. | |
140 | */ | |
141 | private final Artifact artifact; | |
142 | ||
143 | /** | |
144 | * The list of child dependency nodes of this dependency node. | |
145 | */ | |
146 | private final List<DependencyNode> children; | |
147 | ||
148 | /** | |
149 | * The parent dependency node of this dependency node. | |
150 | */ | |
151 | private DependencyNode parent; | |
152 | ||
153 | /** | |
154 | * The state of this dependency node. This can be either <code>INCLUDED</code>, | |
155 | * <code>OMITTED_FOR_DUPLICATE</code>, <code>OMITTED_FOR_CONFLICT</code> or <code>OMITTED_FOR_CYCLE</code>. | |
156 | * | |
157 | * @see #INCLUDED | |
158 | * @see #OMITTED_FOR_DUPLICATE | |
159 | * @see #OMITTED_FOR_CONFLICT | |
160 | * @see #OMITTED_FOR_CYCLE | |
161 | */ | |
162 | private int state; | |
163 | ||
164 | /** | |
165 | * The artifact related to the state of this dependency node. For dependency nodes with a state of | |
166 | * <code>OMITTED_FOR_DUPLICATE</code> or <code>OMITTED_FOR_CONFLICT</code>, this represents the artifact that | |
167 | * was conflicted with. For dependency nodes of other states, this is always <code>null</code>. | |
168 | */ | |
169 | private Artifact relatedArtifact; | |
170 | ||
171 | /** | |
172 | * The scope of this node's artifact before it was updated due to conflicts, or <code>null</code> if the artifact | |
173 | * scope has not been updated. | |
174 | */ | |
175 | private String originalScope; | |
176 | ||
177 | /** | |
178 | * The scope that this node's artifact was attempted to be updated to due to conflicts, or <code>null</code> if | |
179 | * the artifact scope has not failed being updated. | |
180 | */ | |
181 | private String failedUpdateScope; | |
182 | ||
183 | /** | |
184 | * The version of this node's artifact before it was updated by dependency management, or <code>null</code> if the | |
185 | * artifact version has not been managed. | |
186 | */ | |
187 | private String premanagedVersion; | |
188 | ||
189 | /** | |
190 | * The scope of this node's artifact before it was updated by dependency management, or <code>null</code> if the | |
191 | * artifact scope has not been managed. | |
192 | */ | |
193 | private String premanagedScope; | |
194 | ||
195 | private VersionRange versionSelectedFromRange; | |
196 | ||
197 | private List<ArtifactVersion> availableVersions; | |
198 | ||
199 | // constructors ----------------------------------------------------------- | |
200 | ||
201 | /** | |
202 | * Creates a new dependency node for the specified artifact with an included state. | |
203 | * | |
204 | * @param artifact | |
205 | * the artifact attached to the new dependency node | |
206 | * @throws IllegalArgumentException | |
207 | * if the parameter constraints were violated | |
208 | * @since 1.1 | |
209 | */ | |
210 | public DependencyNode( Artifact artifact ) | |
211 | { | |
212 | 94 | this( artifact, INCLUDED ); |
213 | 94 | } |
214 | ||
215 | /** | |
216 | * Creates a new dependency node for the specified artifact with the specified state. | |
217 | * | |
218 | * @param artifact | |
219 | * the artifact attached to the new dependency node | |
220 | * @param state | |
221 | * the state of the new dependency node. This can be either <code>INCLUDED</code> or | |
222 | * <code>OMITTED_FOR_CYCLE</code>. | |
223 | * @throws IllegalArgumentException | |
224 | * if the parameter constraints were violated | |
225 | * @since 1.1 | |
226 | */ | |
227 | public DependencyNode( Artifact artifact, int state ) | |
228 | { | |
229 | 104 | this( artifact, state, null ); |
230 | 104 | } |
231 | ||
232 | /** | |
233 | * Creates a new dependency node for the specified artifact with the specified state and related artifact. | |
234 | * | |
235 | * @param artifact | |
236 | * the artifact attached to the new dependency node | |
237 | * @param state | |
238 | * the state of the new dependency node. This can be either <code>INCLUDED</code>, | |
239 | * <code>OMITTED_FOR_DUPLICATE</code>, <code>OMITTED_FOR_CONFLICT</code> or | |
240 | * <code>OMITTED_FOR_CYCLE</code>. | |
241 | * @param relatedArtifact | |
242 | * the artifact related to the state of this dependency node. For dependency nodes with a state of | |
243 | * <code>OMITTED_FOR_DUPLICATE</code> or <code>OMITTED_FOR_CONFLICT</code>, this represents the | |
244 | * artifact that was conflicted with. For dependency nodes of other states, this should always be | |
245 | * <code>null</code>. | |
246 | * @throws IllegalArgumentException | |
247 | * if the parameter constraints were violated | |
248 | * @since 1.1 | |
249 | */ | |
250 | public DependencyNode( Artifact artifact, int state, Artifact relatedArtifact ) | |
251 | 299 | { |
252 | 299 | if ( artifact == null ) |
253 | { | |
254 | 0 | throw new IllegalArgumentException( "artifact cannot be null" ); |
255 | } | |
256 | ||
257 | 299 | if ( state < INCLUDED || state > OMITTED_FOR_CYCLE ) |
258 | { | |
259 | 0 | throw new IllegalArgumentException( "Unknown state: " + state ); |
260 | } | |
261 | ||
262 | 299 | boolean requiresRelatedArtifact = ( state == OMITTED_FOR_DUPLICATE || state == OMITTED_FOR_CONFLICT ); |
263 | ||
264 | 299 | if ( requiresRelatedArtifact && relatedArtifact == null ) |
265 | { | |
266 | 0 | throw new IllegalArgumentException( "Related artifact is required for states " |
267 | + "OMITTED_FOR_DUPLICATE and OMITTED_FOR_CONFLICT" ); | |
268 | } | |
269 | ||
270 | 299 | if ( !requiresRelatedArtifact && relatedArtifact != null ) |
271 | { | |
272 | 0 | throw new IllegalArgumentException( "Related artifact is only required for states " |
273 | + "OMITTED_FOR_DUPLICATE and OMITTED_FOR_CONFLICT" ); | |
274 | } | |
275 | ||
276 | 299 | this.artifact = artifact; |
277 | 299 | this.state = state; |
278 | 299 | this.relatedArtifact = relatedArtifact; |
279 | ||
280 | 299 | children = new ArrayList<DependencyNode>(); |
281 | 299 | } |
282 | ||
283 | /** | |
284 | * Creates a new dependency node. | |
285 | * | |
286 | * @deprecated As of 1.1, replaced by {@link #DependencyNode(Artifact, int, Artifact)} | |
287 | */ | |
288 | DependencyNode() | |
289 | 0 | { |
290 | 0 | artifact = null; |
291 | 0 | children = new ArrayList<DependencyNode>(); |
292 | 0 | } |
293 | ||
294 | // public methods --------------------------------------------------------- | |
295 | ||
296 | /** | |
297 | * Applies the specified dependency node visitor to this dependency node and its children. | |
298 | * | |
299 | * @param visitor | |
300 | * the dependency node visitor to use | |
301 | * @return the visitor result of ending the visit to this node | |
302 | * @since 1.1 | |
303 | */ | |
304 | public boolean accept( DependencyNodeVisitor visitor ) | |
305 | { | |
306 | 25 | if ( visitor.visit( this ) ) |
307 | { | |
308 | 25 | for ( DependencyNode child : getChildren() ) |
309 | { | |
310 | 17 | if ( !child.accept( visitor ) ) |
311 | { | |
312 | 0 | break; |
313 | } | |
314 | } | |
315 | } | |
316 | ||
317 | 25 | return visitor.endVisit( this ); |
318 | } | |
319 | ||
320 | /** | |
321 | * Adds the specified dependency node to this dependency node's children. | |
322 | * | |
323 | * @param child | |
324 | * the child dependency node to add | |
325 | * @since 1.1 | |
326 | */ | |
327 | public void addChild( DependencyNode child ) | |
328 | { | |
329 | 187 | children.add( child ); |
330 | 187 | child.parent = this; |
331 | 187 | } |
332 | ||
333 | /** | |
334 | * Removes the specified dependency node from this dependency node's children. | |
335 | * | |
336 | * @param child | |
337 | * the child dependency node to remove | |
338 | * @since 1.1 | |
339 | */ | |
340 | public void removeChild( DependencyNode child ) | |
341 | { | |
342 | 0 | children.remove( child ); |
343 | 0 | child.parent = null; |
344 | 0 | } |
345 | ||
346 | /** | |
347 | * Gets the parent dependency node of this dependency node. | |
348 | * | |
349 | * @return the parent dependency node | |
350 | */ | |
351 | public DependencyNode getParent() | |
352 | { | |
353 | 374 | return parent; |
354 | } | |
355 | ||
356 | /** | |
357 | * Gets the artifact attached to this dependency node. | |
358 | * | |
359 | * @return the artifact | |
360 | */ | |
361 | public Artifact getArtifact() | |
362 | { | |
363 | 1011 | return artifact; |
364 | } | |
365 | ||
366 | /** | |
367 | * Gets the depth of this dependency node within its hierarchy. | |
368 | * | |
369 | * @return the depth | |
370 | * @deprecated As of 1.1, depth is computed by node hierarchy. With the introduction of node | |
371 | * visitors and filters this method can give misleading results. For example, consider | |
372 | * serializing a tree with a filter using a visitor: this method would return the | |
373 | * unfiltered depth of a node, whereas the correct depth would be calculated by the | |
374 | * visitor. | |
375 | */ | |
376 | public int getDepth() | |
377 | { | |
378 | 8 | int depth = 0; |
379 | ||
380 | 8 | DependencyNode node = getParent(); |
381 | ||
382 | 19 | while ( node != null ) |
383 | { | |
384 | 11 | depth++; |
385 | ||
386 | 11 | node = node.getParent(); |
387 | } | |
388 | ||
389 | 8 | return depth; |
390 | } | |
391 | ||
392 | /** | |
393 | * Gets the list of child dependency nodes of this dependency node. | |
394 | * | |
395 | * @return the list of child dependency nodes | |
396 | */ | |
397 | public List<DependencyNode> getChildren() | |
398 | { | |
399 | 373 | return Collections.unmodifiableList( children ); |
400 | } | |
401 | ||
402 | public boolean hasChildren() | |
403 | { | |
404 | 0 | return children.size() > 0; |
405 | } | |
406 | ||
407 | /** | |
408 | * Gets the state of this dependency node. | |
409 | * | |
410 | * @return the state: either <code>INCLUDED</code>, <code>OMITTED_FOR_DUPLICATE</code>, | |
411 | * <code>OMITTED_FOR_CONFLICT</code> or <code>OMITTED_FOR_CYCLE</code>. | |
412 | * @since 1.1 | |
413 | */ | |
414 | public int getState() | |
415 | { | |
416 | 425 | return state; |
417 | } | |
418 | ||
419 | /** | |
420 | * Gets the artifact related to the state of this dependency node. For dependency nodes with a state of | |
421 | * <code>OMITTED_FOR_CONFLICT</code>, this represents the artifact that was conflicted with. For dependency nodes | |
422 | * of other states, this is always <code>null</code>. | |
423 | * | |
424 | * @return the related artifact | |
425 | * @since 1.1 | |
426 | */ | |
427 | public Artifact getRelatedArtifact() | |
428 | { | |
429 | 320 | return relatedArtifact; |
430 | } | |
431 | ||
432 | /** | |
433 | * Gets the scope of this node's artifact before it was updated due to conflicts. | |
434 | * | |
435 | * @return the original scope, or <code>null</code> if the artifact scope has not been updated | |
436 | * @since 1.1 | |
437 | */ | |
438 | public String getOriginalScope() | |
439 | { | |
440 | 350 | return originalScope; |
441 | } | |
442 | ||
443 | /** | |
444 | * Sets the scope of this node's artifact before it was updated due to conflicts. | |
445 | * | |
446 | * @param originalScope | |
447 | * the original scope, or <code>null</code> if the artifact scope has not been updated | |
448 | * @since 1.1 | |
449 | */ | |
450 | public void setOriginalScope( String originalScope ) | |
451 | { | |
452 | 15 | this.originalScope = originalScope; |
453 | 15 | } |
454 | ||
455 | /** | |
456 | * Gets the scope that this node's artifact was attempted to be updated to due to conflicts. | |
457 | * | |
458 | * @return the failed update scope, or <code>null</code> if the artifact scope has not failed being updated | |
459 | * @since 1.1 | |
460 | */ | |
461 | public String getFailedUpdateScope() | |
462 | { | |
463 | 348 | return failedUpdateScope; |
464 | } | |
465 | ||
466 | /** | |
467 | * Sets the scope that this node's artifact was attempted to be updated to due to conflicts. | |
468 | * | |
469 | * @param failedUpdateScope | |
470 | * the failed update scope, or <code>null</code> if the artifact scope has not failed being updated | |
471 | * @since 1.1 | |
472 | */ | |
473 | public void setFailedUpdateScope( String failedUpdateScope ) | |
474 | { | |
475 | 13 | this.failedUpdateScope = failedUpdateScope; |
476 | 13 | } |
477 | ||
478 | /** | |
479 | * Gets the version of this node's artifact before it was updated by dependency management. | |
480 | * | |
481 | * @return the premanaged version, or <code>null</code> if the artifact version has not been managed | |
482 | * @since 1.1 | |
483 | */ | |
484 | public String getPremanagedVersion() | |
485 | { | |
486 | 349 | return premanagedVersion; |
487 | } | |
488 | ||
489 | /** | |
490 | * Sets the version of this node's artifact before it was updated by dependency management. | |
491 | * | |
492 | * @param premanagedVersion | |
493 | * the premanaged version, or <code>null</code> if the artifact version has not been managed | |
494 | * @since 1.1 | |
495 | */ | |
496 | public void setPremanagedVersion( String premanagedVersion ) | |
497 | { | |
498 | 14 | this.premanagedVersion = premanagedVersion; |
499 | 14 | } |
500 | ||
501 | /** | |
502 | * Gets the scope of this node's artifact before it was updated by dependency management. | |
503 | * | |
504 | * @return the premanaged scope, or <code>null</code> if the artifact scope has not been managed | |
505 | * @since 1.1 | |
506 | */ | |
507 | public String getPremanagedScope() | |
508 | { | |
509 | 349 | return premanagedScope; |
510 | } | |
511 | ||
512 | /** | |
513 | * Sets the scope of this node's artifact before it was updated by dependency management. | |
514 | * | |
515 | * @param premanagedScope | |
516 | * the premanaged scope, or <code>null</code> if the artifact scope has not been managed | |
517 | * @since 1.1 | |
518 | */ | |
519 | public void setPremanagedScope( String premanagedScope ) | |
520 | { | |
521 | 14 | this.premanagedScope = premanagedScope; |
522 | 14 | } |
523 | ||
524 | /** | |
525 | * If the version was selected from a range this method will return the range. | |
526 | * | |
527 | * @return the version range before a version was selected, or <code>null</code> if the artifact had a explicit | |
528 | * version. | |
529 | * @since 1.2 | |
530 | */ | |
531 | public VersionRange getVersionSelectedFromRange() | |
532 | { | |
533 | 336 | return versionSelectedFromRange; |
534 | } | |
535 | ||
536 | public void setVersionSelectedFromRange( VersionRange versionSelectedFromRange ) | |
537 | { | |
538 | 0 | this.versionSelectedFromRange = versionSelectedFromRange; |
539 | 0 | } |
540 | ||
541 | /** | |
542 | * If the version was selected from a range this method will return the available versions when making the decision. | |
543 | * | |
544 | * @return {@link List} < {@link String} > the versions available when a version was selected from a range, or | |
545 | * <code>null</code> if the artifact had a explicit version. | |
546 | * @since 1.2 | |
547 | */ | |
548 | public List<ArtifactVersion> getAvailableVersions() | |
549 | { | |
550 | 306 | return availableVersions; |
551 | } | |
552 | ||
553 | public void setAvailableVersions( List<ArtifactVersion> availableVersions ) | |
554 | { | |
555 | 0 | this.availableVersions = availableVersions; |
556 | 0 | } |
557 | ||
558 | /** | |
559 | * Changes the state of this dependency node to be omitted for conflict or duplication, depending on the specified | |
560 | * related artifact. | |
561 | * | |
562 | * <p> | |
563 | * If the related artifact has a version equal to this dependency node's artifact, then this dependency node's state | |
564 | * is changed to <code>OMITTED_FOR_DUPLICATE</code>, otherwise it is changed to <code>OMITTED_FOR_CONFLICT</code>. | |
565 | * Omitting this dependency node also removes all of its children. | |
566 | * </p> | |
567 | * | |
568 | * @param relatedArtifact | |
569 | * the artifact that this dependency node conflicted with | |
570 | * @throws IllegalStateException | |
571 | * if this dependency node's state is not <code>INCLUDED</code> | |
572 | * @throws IllegalArgumentException | |
573 | * if the related artifact was <code>null</code> or had a different dependency conflict id to this | |
574 | * dependency node's artifact | |
575 | * @see #OMITTED_FOR_DUPLICATE | |
576 | * @see #OMITTED_FOR_CONFLICT | |
577 | * @since 1.1 | |
578 | */ | |
579 | public void omitForConflict( Artifact relatedArtifact ) | |
580 | { | |
581 | 9 | if ( getState() != INCLUDED ) |
582 | { | |
583 | 0 | throw new IllegalStateException( "Only INCLUDED dependency nodes can be omitted for conflict" ); |
584 | } | |
585 | ||
586 | 9 | if ( relatedArtifact == null ) |
587 | { | |
588 | 0 | throw new IllegalArgumentException( "Related artifact cannot be null" ); |
589 | } | |
590 | ||
591 | 9 | if ( !relatedArtifact.getDependencyConflictId().equals( getArtifact().getDependencyConflictId() ) ) |
592 | { | |
593 | 0 | throw new IllegalArgumentException( "Related artifact has a different dependency conflict id" ); |
594 | } | |
595 | ||
596 | 9 | this.relatedArtifact = relatedArtifact; |
597 | ||
598 | 9 | boolean duplicate = false; |
599 | 9 | if ( getArtifact().getVersion() != null ) |
600 | { | |
601 | 9 | duplicate = getArtifact().getVersion().equals( relatedArtifact.getVersion() ); |
602 | } | |
603 | 0 | else if ( getArtifact().getVersionRange() != null ) |
604 | { | |
605 | 0 | duplicate = getArtifact().getVersionRange().equals( relatedArtifact.getVersionRange() ); |
606 | } | |
607 | else | |
608 | { | |
609 | 0 | throw new RuntimeException( "Artifact version and version range is null: " + getArtifact() ); |
610 | } | |
611 | ||
612 | 9 | state = duplicate ? OMITTED_FOR_DUPLICATE : OMITTED_FOR_CONFLICT; |
613 | ||
614 | 9 | removeAllChildren(); |
615 | 9 | } |
616 | ||
617 | /** | |
618 | * Changes the state of this dependency node to be omitted for a cycle in the dependency tree. | |
619 | * | |
620 | * <p> | |
621 | * Omitting this node sets its state to <code>OMITTED_FOR_CYCLE</code> and removes all of its children. | |
622 | * </p> | |
623 | * | |
624 | * @throws IllegalStateException | |
625 | * if this dependency node's state is not <code>INCLUDED</code> | |
626 | * @see #OMITTED_FOR_CYCLE | |
627 | * @since 1.1 | |
628 | */ | |
629 | public void omitForCycle() | |
630 | { | |
631 | 2 | if ( getState() != INCLUDED ) |
632 | { | |
633 | 0 | throw new IllegalStateException( "Only INCLUDED dependency nodes can be omitted for cycle" ); |
634 | } | |
635 | ||
636 | 2 | state = OMITTED_FOR_CYCLE; |
637 | ||
638 | 2 | removeAllChildren(); |
639 | 2 | } |
640 | ||
641 | /** | |
642 | * Gets an iterator that returns this dependency node and it's children in preorder traversal. | |
643 | * | |
644 | * @return the preorder traversal iterator | |
645 | * @see #preorderIterator() | |
646 | */ | |
647 | public Iterator<DependencyNode> iterator() | |
648 | { | |
649 | 1 | return preorderIterator(); |
650 | } | |
651 | ||
652 | /** | |
653 | * Gets an iterator that returns this dependency node and it's children in preorder traversal. | |
654 | * | |
655 | * @return the preorder traversal iterator | |
656 | * @see DependencyTreePreorderIterator | |
657 | */ | |
658 | public Iterator<DependencyNode> preorderIterator() | |
659 | { | |
660 | 1 | return new DependencyTreePreorderIterator( this ); |
661 | } | |
662 | ||
663 | /** | |
664 | * Gets an iterator that returns this dependency node and it's children in postorder traversal. | |
665 | * | |
666 | * @return the postorder traversal iterator | |
667 | * @see DependencyTreeInverseIterator | |
668 | */ | |
669 | public Iterator<DependencyNode> inverseIterator() | |
670 | { | |
671 | 1 | return new DependencyTreeInverseIterator( this ); |
672 | } | |
673 | ||
674 | /** | |
675 | * Returns a string representation of this dependency node. | |
676 | * | |
677 | * @return the string representation | |
678 | * @see #toString() | |
679 | * @since 1.1 | |
680 | */ | |
681 | public String toNodeString() | |
682 | { | |
683 | 30 | StringBuffer buffer = new StringBuffer(); |
684 | ||
685 | 30 | boolean included = ( getState() == INCLUDED ); |
686 | ||
687 | 30 | if ( !included ) |
688 | { | |
689 | 4 | buffer.append( '(' ); |
690 | } | |
691 | ||
692 | 30 | buffer.append( artifact ); |
693 | ||
694 | 30 | ItemAppender appender = new ItemAppender( buffer, included ? " (" : " - ", "; ", included ? ")" : "" ); |
695 | ||
696 | 30 | if ( getPremanagedVersion() != null ) |
697 | { | |
698 | 1 | appender.append( "version managed from ", getPremanagedVersion() ); |
699 | } | |
700 | ||
701 | 30 | if ( getPremanagedScope() != null ) |
702 | { | |
703 | 1 | appender.append( "scope managed from ", getPremanagedScope() ); |
704 | } | |
705 | ||
706 | 30 | if ( getOriginalScope() != null ) |
707 | { | |
708 | 2 | appender.append( "scope updated from ", getOriginalScope() ); |
709 | } | |
710 | ||
711 | 30 | if ( getFailedUpdateScope() != null ) |
712 | { | |
713 | 0 | appender.append( "scope not updated to ", getFailedUpdateScope() ); |
714 | } | |
715 | ||
716 | 30 | if ( getVersionSelectedFromRange() != null ) |
717 | { | |
718 | 0 | appender.append( "version selected from range ", getVersionSelectedFromRange().toString() ); |
719 | 0 | appender.append( "available versions ", getAvailableVersions().toString() ); |
720 | } | |
721 | ||
722 | 30 | switch ( state ) |
723 | { | |
724 | case INCLUDED: | |
725 | 26 | break; |
726 | ||
727 | case OMITTED_FOR_DUPLICATE: | |
728 | 2 | appender.append( "omitted for duplicate" ); |
729 | 2 | break; |
730 | ||
731 | case OMITTED_FOR_CONFLICT: | |
732 | 1 | appender.append( "omitted for conflict with ", relatedArtifact.getVersion() ); |
733 | 1 | break; |
734 | ||
735 | case OMITTED_FOR_CYCLE: | |
736 | 1 | appender.append( "omitted for cycle" ); |
737 | break; | |
738 | } | |
739 | ||
740 | 30 | appender.flush(); |
741 | ||
742 | 30 | if ( !included ) |
743 | { | |
744 | 4 | buffer.append( ')' ); |
745 | } | |
746 | ||
747 | 30 | return buffer.toString(); |
748 | } | |
749 | ||
750 | /** | |
751 | * Returns a string representation of this dependency node and its children, indented to the specified depth. | |
752 | * | |
753 | * <p> | |
754 | * As of 1.1, this method ignores the indentation depth and simply delegates to <code>toString()</code>. | |
755 | * </p> | |
756 | * | |
757 | * @param indentDepth | |
758 | * the indentation depth | |
759 | * @return the string representation | |
760 | * @deprecated As of 1.1, replaced by {@link #toString()} | |
761 | */ | |
762 | public String toString( int indentDepth ) | |
763 | { | |
764 | 0 | return toString(); |
765 | } | |
766 | ||
767 | // Object methods --------------------------------------------------------- | |
768 | ||
769 | /** | |
770 | * {@inheritDoc} | |
771 | */ | |
772 | public int hashCode() | |
773 | { | |
774 | // TODO: probably better using commons-lang HashCodeBuilder | |
775 | ||
776 | 0 | int hashCode = 1; |
777 | ||
778 | 0 | hashCode = hashCode * 31 + getArtifact().hashCode(); |
779 | // DefaultArtifact.hashCode does not consider scope | |
780 | 0 | hashCode = hashCode * 31 + nullHashCode( getArtifact().getScope() ); |
781 | ||
782 | // TODO: use parent's artifact to prevent recursion - how can we improve this? | |
783 | 0 | hashCode = hashCode * 31 + nullHashCode( nullGetArtifact( getParent() ) ); |
784 | ||
785 | 0 | hashCode = hashCode * 31 + getChildren().hashCode(); |
786 | 0 | hashCode = hashCode * 31 + getState(); |
787 | 0 | hashCode = hashCode * 31 + nullHashCode( getRelatedArtifact() ); |
788 | 0 | hashCode = hashCode * 31 + nullHashCode( getPremanagedVersion() ); |
789 | 0 | hashCode = hashCode * 31 + nullHashCode( getPremanagedScope() ); |
790 | 0 | hashCode = hashCode * 31 + nullHashCode( getOriginalScope() ); |
791 | 0 | hashCode = hashCode * 31 + nullHashCode( getFailedUpdateScope() ); |
792 | 0 | hashCode = hashCode * 31 + nullHashCode( getVersionSelectedFromRange() ); |
793 | 0 | hashCode = hashCode * 31 + nullHashCode( getAvailableVersions() ); |
794 | ||
795 | 0 | return hashCode; |
796 | } | |
797 | ||
798 | @Override | |
799 | public boolean equals( Object object ) | |
800 | { | |
801 | // TODO: probably better using commons-lang EqualsBuilder | |
802 | ||
803 | boolean equal; | |
804 | ||
805 | 153 | if ( object instanceof DependencyNode ) |
806 | { | |
807 | 153 | DependencyNode node = (DependencyNode) object; |
808 | ||
809 | 153 | equal = getArtifact().equals( node.getArtifact() ); |
810 | // DefaultArtifact.hashCode does not consider scope | |
811 | 153 | equal &= nullEquals( getArtifact().getScope(), node.getArtifact().getScope() ); |
812 | ||
813 | // TODO: use parent's artifact to prevent recursion - how can we improve this? | |
814 | 153 | equal &= nullEquals( nullGetArtifact( getParent() ), nullGetArtifact( node.getParent() ) ); |
815 | ||
816 | 153 | equal &= getChildren().equals( node.getChildren() ); |
817 | 153 | equal &= getState() == node.getState(); |
818 | 153 | equal &= nullEquals( getRelatedArtifact(), node.getRelatedArtifact() ); |
819 | 153 | equal &= nullEquals( getPremanagedVersion(), node.getPremanagedVersion() ); |
820 | 153 | equal &= nullEquals( getPremanagedScope(), node.getPremanagedScope() ); |
821 | 153 | equal &= nullEquals( getOriginalScope(), node.getOriginalScope() ); |
822 | 153 | equal &= nullEquals( getFailedUpdateScope(), node.getFailedUpdateScope() ); |
823 | 153 | equal &= nullEquals( getVersionSelectedFromRange(), node.getVersionSelectedFromRange() ); |
824 | 153 | equal &= nullEquals( getAvailableVersions(), node.getAvailableVersions() ); |
825 | 153 | } |
826 | else | |
827 | { | |
828 | 0 | equal = false; |
829 | } | |
830 | ||
831 | 153 | return equal; |
832 | } | |
833 | ||
834 | /** | |
835 | * Returns a string representation of this dependency node and its children. | |
836 | * | |
837 | * @return the string representation | |
838 | * @see #toNodeString() | |
839 | * @see java.lang.Object#toString() | |
840 | */ | |
841 | public String toString() | |
842 | { | |
843 | 1 | StringWriter writer = new StringWriter(); |
844 | 1 | accept( new SerializingDependencyNodeVisitor( writer ) ); |
845 | 1 | return writer.toString(); |
846 | } | |
847 | ||
848 | // private methods -------------------------------------------------------- | |
849 | ||
850 | /** | |
851 | * Removes all of this dependency node's children. | |
852 | */ | |
853 | private void removeAllChildren() | |
854 | { | |
855 | 11 | for ( DependencyNode child : children ) |
856 | { | |
857 | 8 | child.parent = null; |
858 | } | |
859 | ||
860 | 11 | children.clear(); |
861 | 11 | } |
862 | ||
863 | /** | |
864 | * Computes a hash-code for the specified object. | |
865 | * | |
866 | * @param a | |
867 | * the object to compute a hash-code for, possibly <code>null</code> | |
868 | * @return the computed hash-code | |
869 | */ | |
870 | private int nullHashCode( Object a ) | |
871 | { | |
872 | 0 | return ( a == null ) ? 0 : a.hashCode(); |
873 | } | |
874 | ||
875 | /** | |
876 | * Gets whether the specified objects are equal. | |
877 | * | |
878 | * @param a | |
879 | * the first object to compare, possibly <code>null</code> | |
880 | * @param b | |
881 | * the second object to compare, possibly <code>null</code> | |
882 | * @return <code>true</code> if the specified objects are equal | |
883 | */ | |
884 | private boolean nullEquals( Object a, Object b ) | |
885 | { | |
886 | 1377 | return ( a == null ? b == null : a.equals( b ) ); |
887 | } | |
888 | ||
889 | /** | |
890 | * Gets the artifact for the specified node. | |
891 | * | |
892 | * @param node | |
893 | * the dependency node, possibly <code>null</code> | |
894 | * @return the node's artifact, or <code>null</code> if the specified node was <code>null</code> | |
895 | */ | |
896 | private static Artifact nullGetArtifact( DependencyNode node ) | |
897 | { | |
898 | 306 | return ( node != null ) ? node.getArtifact() : null; |
899 | } | |
900 | } |