Coverage Report - org.apache.maven.shared.dependency.tree.DependencyNode
 
Classes in this File Line Coverage Branch Coverage Complexity
DependencyNode
72 %
114/158
72 %
59/81
2,067
DependencyNode$ItemAppender
100 %
20/20
100 %
4/4
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} &lt; {@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  
 }