001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.directory.shared.ldap.util.tree; 021 022import java.util.ArrayList; 023import java.util.HashMap; 024import java.util.List; 025import java.util.Map; 026 027import org.apache.directory.shared.ldap.model.exception.LdapException; 028import org.apache.directory.shared.ldap.model.exception.LdapInvalidDnException; 029import org.apache.directory.shared.ldap.model.exception.LdapUnwillingToPerformException; 030import org.apache.directory.shared.ldap.model.message.ResultCodeEnum; 031import org.apache.directory.shared.ldap.model.name.Dn; 032import org.apache.directory.shared.ldap.model.name.Rdn; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036 037/** 038 * A class storing nodes in a tree designed to map DNs.<br/> 039 * Branch nodes in this tree refers to child nodes. Leaf nodes in the tree 040 * don't have any children. <br/> 041 * A node may contain a reference to an object whose suffix is the path through the 042 * nodes of the tree from the root. <br/> 043 * A node may also have no attached element.<br/> 044 * Each child node is referenced by a Rdn, and holds the full Dn corresponding to its position<br/> 045 * 046 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 047 * @param <N> The type of node we store 048 */ 049public class DnNode<N> implements Cloneable 050{ 051 /** The logger for this class */ 052 private static final Logger LOG = LoggerFactory.getLogger( DnNode.class ); 053 054 /** The stored element */ 055 private N nodeElement; 056 057 /** The node's key */ 058 private Rdn nodeRdn; 059 060 /** The node's Dn */ 061 private Dn nodeDn; 062 063 /** The node's depth in the tree */ 064 private int depth; 065 066 /** The parent, if any */ 067 private DnNode<N> parent; 068 069 /** Stores the list of all the descendant */ 070 private Map<Rdn, DnNode<N>> children; 071 072 //------------------------------------------------------------------------- 073 // Helper methods 074 //------------------------------------------------------------------------- 075 /** 076 * Check that the Dn is not null 077 */ 078 private void checkDn( Dn dn ) throws LdapException 079 { 080 if ( ( dn == null ) || dn.isEmpty() ) 081 { 082 String message = "Cannot process an empty Dn"; 083 LOG.error( message ); 084 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, message ); 085 } 086 } 087 088 /** 089 * Create a new DnNode, recursively creating all the intermediate nodes. 090 */ 091 private DnNode<N> createNode( Dn dn, N element, int nbRdns ) throws LdapException 092 { 093 checkDn( dn ); 094 095 DnNode<N> rootNode = null; 096 097 // No parent : add from the current position 098 for ( Rdn rdn : dn.getRdns() ) 099 { 100 if ( nbRdns == 0 ) 101 { 102 break; 103 } 104 105 if ( rootNode == null ) 106 { 107 // Create the new top node 108 DnNode<N> node = new DnNode<N>( element ); 109 node.nodeRdn = rdn; 110 node.nodeDn = dn; 111 node.depth = dn.size() + depth; 112 113 rootNode = node; 114 } 115 else 116 { 117 DnNode<N> node = new DnNode<N>(); 118 node.nodeRdn = rdn; 119 node.nodeDn = rootNode.nodeDn.getParent(); 120 node.depth = node.nodeDn.size() + depth; 121 rootNode.parent = node; 122 node.children.put( rootNode.nodeRdn, rootNode ); 123 rootNode = node; 124 } 125 126 nbRdns--; 127 } 128 129 return rootNode; 130 } 131 132 133 /** 134 * Store the given element into the node 135 */ 136 private synchronized void setElement( N element ) 137 { 138 this.nodeElement = element; 139 } 140 141 142 //------------------------------------------------------------------------- 143 // Constructors 144 //------------------------------------------------------------------------- 145 /** 146 * Creates a new instance of DnNode. 147 */ 148 public DnNode() 149 { 150 children = new HashMap<Rdn, DnNode<N>>(); 151 nodeDn = Dn.EMPTY_DN; 152 nodeRdn = Rdn.EMPTY_RDN; 153 } 154 155 156 /** 157 * Creates a new instance of DnNode. 158 * 159 * @param element the element to store 160 */ 161 public DnNode( N element ) 162 { 163 this.nodeElement = element; 164 children = new HashMap<Rdn, DnNode<N>>(); 165 } 166 167 168 /** 169 * Creates a new instance of DnNode. 170 * 171 * @param dn the node's Dn 172 * @param element the element to store 173 */ 174 public DnNode( Dn dn, N element ) 175 { 176 if ( ( dn == null ) || ( dn.isEmpty() ) ) 177 { 178 children = new HashMap<Rdn, DnNode<N>>(); 179 this.nodeDn = Dn.EMPTY_DN; 180 181 return; 182 } 183 184 try 185 { 186 DnNode<N> rootNode = createNode( dn, element, dn.size() ); 187 188 // Now copy back the created node into this 189 this.children = rootNode.children; 190 this.depth = rootNode.depth; 191 this.nodeDn = rootNode.nodeDn; 192 this.nodeElement = rootNode.nodeElement; 193 this.nodeRdn = rootNode.nodeRdn; 194 this.parent = null; 195 } 196 catch ( LdapException le ) 197 { 198 // Special cas e: the Dn is empty, this is not allowed 199 throw new IllegalArgumentException( le.getMessage() ); 200 } 201 } 202 203 204 /** 205 * Tells if the implementation is a leaf node. If it's a branch node 206 * then false is returned. 207 * 208 * @return <code>true</code> if the class is a leaf node, false otherwise. 209 */ 210 public synchronized boolean isLeaf() 211 { 212 return !hasChildren(); 213 } 214 215 216 /** 217 * Tells if the implementation is a leaf node. If it's a branch node 218 * then false is returned. 219 * 220 * @param dn The Dn we want to check 221 * @return <code>true</code> if this is a leaf node, false otherwise. 222 */ 223 public synchronized boolean isLeaf( Dn dn ) 224 { 225 DnNode<N> node = getNode( dn ); 226 227 if ( node == null ) 228 { 229 return false; 230 } 231 232 return node.children.size() == 0; 233 } 234 235 236 /** 237 * Returns the number of entries under this node. It includes 238 * the node itself, plus the number of all it children and descendants. 239 * 240 * @return The number of descendents 241 */ 242 public synchronized int size() 243 { 244 // The node itself 245 int size = 1; 246 247 // Iterate through the children if any 248 if ( children.size() !=0 ) 249 { 250 for ( DnNode<N> node : children.values() ) 251 { 252 size += node.size(); 253 } 254 } 255 256 return size; 257 } 258 259 260 /** 261 * @return Return the stored element, if any 262 */ 263 public synchronized N getElement() 264 { 265 return nodeElement; 266 } 267 268 269 /** 270 * @return Return the stored element, if any 271 * @param dn The Dn we want to get the element for 272 */ 273 public synchronized N getElement( Dn dn ) 274 { 275 DnNode<N> node = getNode( dn ); 276 277 if ( node == null ) 278 { 279 return null; 280 } 281 282 return node.nodeElement; 283 } 284 285 286 /** 287 * @return True if the Node stores an element. BranchNode may not hold any 288 * element. 289 */ 290 public synchronized boolean hasElement() 291 { 292 return nodeElement != null; 293 } 294 295 296 /** 297 * @return True if the Node stores an element. BranchNode may not hold any 298 * element. 299 * @param dn The Dn we want to get the element for 300 */ 301 public synchronized boolean hasElement( Dn dn ) 302 { 303 DnNode<N> node = getNode( dn ); 304 305 if ( node == null ) 306 { 307 return false; 308 } 309 310 return node.nodeElement != null; 311 } 312 313 314 /** 315 * recursively check if the node has a descendant having an element 316 */ 317 private synchronized boolean hasDescendantElement( DnNode<N> node ) 318 { 319 if ( node == null ) 320 { 321 return false; 322 } 323 324 if ( node.hasElement() ) 325 { 326 return true; 327 } 328 329 for ( DnNode<N> child : node.getChildren().values() ) 330 { 331 if ( hasDescendantElement( child ) ) 332 { 333 return true; 334 } 335 } 336 337 // Nothing found ... 338 return false; 339 } 340 341 342 /** 343 * @return True if one of the node below the current node has one element, 344 * False otherwise 345 * @param dn The Dn we want to get the element for 346 */ 347 public synchronized boolean hasDescendantElement( Dn dn ) 348 { 349 DnNode<N> node = getNode( dn ); 350 351 if ( node == null ) 352 { 353 return false; 354 } 355 356 // We must be at the right place in the tree 357 if ( node.getDn().size() != dn.size() ) 358 { 359 return false; 360 } 361 362 if ( node.hasChildren() ) 363 { 364 for ( DnNode<N> child : node.getChildren().values() ) 365 { 366 if ( hasDescendantElement( child ) ) 367 { 368 return true; 369 } 370 } 371 } 372 373 return false; 374 } 375 376 377 /** 378 * recursively get all the elements from nodes having an element 379 */ 380 private synchronized void getDescendantElements( DnNode<N> node, List<N> descendants ) 381 { 382 if ( node == null ) 383 { 384 return; 385 } 386 387 if ( node.hasElement() ) 388 { 389 descendants.add( node.getElement() ); 390 391 // Stop here 392 return; 393 } 394 395 for ( DnNode<N> child : node.getChildren().values() ) 396 { 397 getDescendantElements( child, descendants ); 398 } 399 } 400 401 402 /** 403 * @return True if one of the node below the current node has one element, 404 * False otherwise 405 * @param dn The Dn we want to get the element for 406 */ 407 public synchronized List<N> getDescendantElements( Dn dn ) 408 { 409 List<N> descendants = new ArrayList<N>(); 410 411 DnNode<N> node = getNode( dn ); 412 413 if ( node == null ) 414 { 415 return descendants; 416 } 417 418 // We must be at the right place in the tree 419 if ( node.getDn().size() != dn.size() ) 420 { 421 return descendants; 422 } 423 424 if ( node.hasChildren() ) 425 { 426 for ( DnNode<N> child : node.getChildren().values() ) 427 { 428 getDescendantElements( child, descendants ); 429 } 430 } 431 432 return descendants; 433 } 434 435 436 /** 437 * Tells if the current DnNode has some children or not 438 * 439 * @return <code>true</code> if the node has some children 440 */ 441 public synchronized boolean hasChildren() 442 { 443 return ( children != null ) && children.size() != 0; 444 } 445 446 447 /** 448 * Tells if a node has some children or not. 449 * 450 * @param dn the node's Dn 451 * @return <code>true</code> if the node has some children 452 * @throws LdapException if the Dn is null or empty 453 */ 454 public synchronized boolean hasChildren( Dn dn ) throws LdapException 455 { 456 checkDn( dn ); 457 458 DnNode<N> node = getNode( dn ); 459 460 return ( node != null ) && node.hasChildren(); 461 } 462 463 464 /** 465 * @return The list of DnNode 466 */ 467 public synchronized Map<Rdn, DnNode<N>> getChildren() 468 { 469 return children; 470 } 471 472 473 /** 474 * @return The parent DnNode, if any 475 */ 476 public synchronized DnNode<N> getParent() 477 { 478 return parent; 479 } 480 481 482 /** 483 * @return True if the current DnNode has a parent 484 */ 485 public synchronized boolean hasParent() 486 { 487 return parent != null; 488 } 489 490 491 /** 492 * Tells if there is a parent for a given Dn,. This parent should be a 493 * subset of the given dn.<br> 494 * For instance, if we have stored dc=acme, dc=org into the tree, 495 * the Dn: ou=example, dc=acme, dc=org will have a parent 496 * <br>For the Dn ou=apache, dc=org, there is no parent, so false will be returned. 497 * 498 * @param dn the normalized distinguished name to resolve to a parent 499 * @return true if there is a parent associated with the normalized dn 500 */ 501 public synchronized boolean hasParent( Dn dn ) 502 { 503 List<Rdn> rdns = dn.getRdns(); 504 505 DnNode<N> currentNode = this; 506 DnNode<N> parentNode = null; 507 508 // Iterate through all the Rdn until we find the associated element 509 for ( int i = rdns.size() - 1; i >= 0; i-- ) 510 { 511 Rdn rdn = rdns.get( i ); 512 513 if ( rdn.equals( currentNode.nodeRdn ) ) 514 { 515 parentNode = currentNode; 516 } 517 else if ( currentNode.hasChildren() ) 518 { 519 currentNode = currentNode.children.get( rdn ); 520 521 if ( currentNode == null ) 522 { 523 break; 524 } 525 526 parentNode = currentNode; 527 } 528 else 529 { 530 break; 531 } 532 } 533 534 return( parentNode != null ); 535 } 536 537 538 /** 539 * Add a new node in the tree. The added node won't have any element. 540 * 541 * @param dn The node's Dn 542 * @throws LdapException if the Dn is null or empty 543 */ 544 public synchronized void add( Dn dn ) throws LdapException 545 { 546 add( dn, null ); 547 } 548 549 550 /** 551 * Add a new node in the tree. We can't add a node if its Dn is empty. The 552 * added element is attached to the node, which is named by the Dn's Rdn.<br/> 553 * 554 * @param dn The node's Dn 555 * @param element The element to associate with this Node. Can be null. 556 * @throws LdapException if the Dn is null or empty 557 */ 558 public synchronized void add( Dn dn, N element ) throws LdapException 559 { 560 checkDn( dn ); 561 562 // We first have to find the Node which will be the parent 563 DnNode<N> parentNode = getNode( dn ); 564 565 if ( parentNode == null ) 566 { 567 // No parent : add a new node to the root 568 DnNode<N> childNode = createNode( dn, element, dn.size() ); 569 childNode.parent = this; 570 children.put( childNode.nodeRdn, childNode ); 571 } 572 else 573 { 574 // We have a parent. Add the new node to the found parent 575 int nbRdns = dn.size() - parentNode.depth; 576 577 if ( nbRdns == 0 ) 578 { 579 // That means the added Dn is already present. Check if it already has an element 580 if ( parentNode.hasElement() ) 581 { 582 String message = "Cannot add a node to a node already having an element"; 583 LOG.error( message ); 584 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, message ); 585 } 586 // We may try to add twice the same Dn, without any element 587 else if ( element == null ) 588 { 589 String message = "Cannot add a node with no element if it already exists"; 590 LOG.error( message ); 591 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, message ); 592 } 593 // All is fine : we are just injecting some data into an existing node 594 else 595 { 596 parentNode.setElement( element ); 597 } 598 } 599 else 600 { 601 DnNode<N> rootNode = createNode( dn, element, nbRdns ); 602 603 // done. now, add the newly created tree to the parent node 604 rootNode.parent = parentNode; 605 parentNode.children.put( rootNode.nodeRdn, rootNode ); 606 } 607 } 608 } 609 610 611 /** 612 * Removes a node from the tree. 613 * 614 * @param dn the node's Dn 615 * @throws LdapException if the Dn is null or empty 616 */ 617 public synchronized void remove( Dn dn ) throws LdapException 618 { 619 checkDn( dn ); 620 621 // Find the parent first : we won't be able to remove 622 // a node if it's not present in the tree ! 623 DnNode<N> parentNode = getNode( dn ); 624 625 if ( parentNode == null ) 626 { 627 return; 628 } 629 630 // Now, check that this parent has the same Dn than the one 631 // we gave and that there is no children 632 if ( ( dn.size() != parentNode.depth ) || parentNode.hasChildren() ) 633 { 634 return; 635 } 636 637 // Ok, no children, same Dn, let's remove what we can. 638 parentNode = parentNode.getParent(); 639 640 for ( Rdn rdn : dn.getRdns() ) 641 { 642 parentNode.children.remove( rdn ); 643 644 if ( parentNode.children.size() > 0 ) 645 { 646 // We have to stop here, because the parent's node is shared with other Node. 647 break; 648 } 649 650 parentNode = parentNode.getParent(); 651 } 652 } 653 654 655 /** 656 * Tells if the current DnBranchNode contains another node associated 657 * with an rdn. 658 * 659 * @param rdn The name we are looking for 660 * @return <code>true</code> if the tree instance contains this name 661 */ 662 public synchronized boolean contains( Rdn rdn ) 663 { 664 return children.containsKey( rdn ); 665 } 666 667 668 /** 669 * Get's a child using an rdn string. 670 * 671 * @param rdn the rdn to use as the node key 672 * @return the child node corresponding to the rdn. 673 */ 674 public synchronized DnNode<N> getChild( Rdn rdn ) 675 { 676 if ( children.containsKey( rdn ) ) 677 { 678 return children.get( rdn ); 679 } 680 681 return null; 682 } 683 684 685 /** 686 * @return The Node's Rdn 687 */ 688 public synchronized Rdn getRdn() 689 { 690 return nodeRdn; 691 } 692 693 694 /** 695 * Get the Node for a given Dn, if present in the tree.<br> 696 * For instance, if we have stored dc=acme, dc=org into the tree, 697 * the Dn: ou=example, dc=acme, dc=org will have a parent, and 698 * dc=acme, dc=org will be returned. 699 * <br>For the Dn ou=apache, dc=org, there is no parent, so null will be returned. 700 * 701 * @param dn the normalized distinguished name to resolve to a parent 702 * @return the Node associated with the normalized dn 703 */ 704 public synchronized DnNode<N> getNode( Dn dn ) 705 { 706 List<Rdn> rdns = dn.getRdns(); 707 708 DnNode<N> currentNode = this; 709 DnNode<N> parentNode = null; 710 711 // Iterate through all the Rdn until we find the associated partition 712 for ( int i = rdns.size() - 1; i >= 0; i-- ) 713 { 714 Rdn rdn = rdns.get( i ); 715 716 if ( currentNode.hasChildren() ) 717 { 718 currentNode = currentNode.children.get( rdn ); 719 720 if ( currentNode == null ) 721 { 722 break; 723 } 724 725 parentNode = currentNode; 726 } 727 else 728 { 729 break; 730 } 731 } 732 733 return parentNode; 734 } 735 736 737 /** 738 * Get the closest Node for a given Dn which has an element, if present in the tree.<br> 739 * For instance, if we have stored dc=acme, dc=org into the tree, 740 * the Dn: ou=example, dc=acme, dc=org will have a parent, and 741 * dc=acme, dc=org will be returned if it has an associated element. 742 * <br>For the Dn ou=apache, dc=org, there is no parent, so null will be returned. 743 * 744 * @param dn the normalized distinguished name to resolve to a parent 745 * @return the Node associated with the normalized dn 746 */ 747 public synchronized boolean hasParentElement( Dn dn ) 748 { 749 List<Rdn> rdns = dn.getRdns(); 750 751 DnNode<N> currentNode = this; 752 boolean hasElement = false; 753 754 // Iterate through all the Rdn until we find the associated partition 755 for ( int i = rdns.size() - 1; i >= 0; i-- ) 756 { 757 Rdn rdn = rdns.get( i ); 758 759 if ( currentNode.hasChildren() ) 760 { 761 currentNode = currentNode.children.get( rdn ); 762 763 if ( currentNode == null ) 764 { 765 break; 766 } 767 768 if ( currentNode.hasElement() ) 769 { 770 hasElement = true; 771 } 772 773 parent = currentNode; 774 } 775 else 776 { 777 break; 778 } 779 } 780 781 return hasElement; 782 } 783 784 785 /** 786 * Get the closest Node for a given Dn which has an element, if present in the tree.<br> 787 * For instance, if we have stored dc=acme, dc=org into the tree, 788 * the Dn: ou=example, dc=acme, dc=org will have a parent, and 789 * dc=acme, dc=org will be returned if it has an associated element. 790 * <br>For the Dn ou=apache, dc=org, there is no parent, so null will be returned. 791 * 792 * @param dn the normalized distinguished name to resolve to a parent 793 * @return the Node associated with the normalized dn 794 */ 795 public synchronized DnNode<N> getParentWithElement( Dn dn ) 796 { 797 List<Rdn> rdns = dn.getRdns(); 798 799 DnNode<N> currentNode = this; 800 DnNode<N> element = null; 801 802 // Iterate through all the Rdn until we find the associated partition 803 for ( int i = rdns.size() - 1; i >= 1; i-- ) 804 { 805 Rdn rdn = rdns.get( i ); 806 807 if ( currentNode.hasChildren() ) 808 { 809 currentNode = currentNode.children.get( rdn ); 810 811 if ( currentNode == null ) 812 { 813 break; 814 } 815 816 if ( currentNode.hasElement() ) 817 { 818 element = currentNode; 819 } 820 821 parent = currentNode; 822 } 823 else 824 { 825 break; 826 } 827 } 828 829 return element; 830 } 831 832 833 /** 834 * Get the closest Node for a given Dn which has an element, if present in the tree.<br> 835 * For instance, if we have stored dc=acme, dc=org into the tree, 836 * the Dn: ou=example, dc=acme, dc=org will have a parent, and 837 * dc=acme, dc=org will be returned if it has an associated element. 838 * <br>For the Dn ou=apache, dc=org, there is no parent, so null will be returned. 839 * 840 * @param dn the normalized distinguished name to resolve to a parent 841 * @return the Node associated with the normalized dn 842 */ 843 public synchronized DnNode<N> getParentWithElement() 844 { 845 DnNode<N> currentNode = parent; 846 847 while ( currentNode != null ) 848 { 849 if ( currentNode.nodeElement != null ) 850 { 851 return currentNode; 852 } 853 854 currentNode = currentNode.parent; 855 } 856 857 return null; 858 } 859 860 861 /** 862 * rename the DnNode's Dn 863 * 864 * @param newRdn the new Rdn of this node 865 * @throws LdapException 866 */ 867 public synchronized void rename( Rdn newRdn ) throws LdapException 868 { 869 Dn temp = nodeDn.getParent(); 870 temp = temp.add( newRdn ); 871 872 Rdn oldRdn = nodeRdn; 873 874 nodeRdn = temp.getRdn(); 875 nodeDn = temp; 876 877 if ( parent != null ) 878 { 879 parent.children.remove( oldRdn ); 880 parent.children.put( nodeRdn, this ); 881 } 882 883 updateAfterModDn( nodeDn ); 884 } 885 886 887 /** 888 * move the DnNode's Dn 889 * 890 * @param newParent the new parent Dn 891 * @throws LdapException 892 */ 893 public synchronized void move( Dn newParent ) throws LdapException 894 { 895 DnNode<N> tmp = null; 896 897 Dn tmpDn = null; 898 899 // check if the new parent Dn is child of the parent 900 if( newParent.isDescendantOf( parent.nodeDn ) ) 901 { 902 tmp = parent; 903 tmpDn = parent.nodeDn; 904 } 905 906 // if yes, then drill for the new parent node 907 if ( tmpDn != null ) 908 { 909 int parentNodeSize = tmpDn.size(); 910 int count = newParent.size() - parentNodeSize; 911 912 while( count-- > 0 ) 913 { 914 tmp = tmp.getChild( newParent.getRdn( parentNodeSize++ ) ); 915 } 916 } 917 918 // if not, we have to traverse all the way up to the 919 // root node and then find the new parent node 920 if ( tmp == null ) 921 { 922 tmp = this; 923 while( tmp.parent != null ) 924 { 925 tmp = tmp.parent; 926 } 927 928 tmp = tmp.getNode( newParent ); 929 } 930 931 nodeDn = newParent.add( nodeRdn ); 932 updateAfterModDn( nodeDn ); 933 934 if( parent != null ) 935 { 936 parent.children.remove( nodeRdn ); 937 } 938 939 parent = tmp; 940 parent.children.put( nodeRdn, this ); 941 } 942 943 944 /** 945 * update the children's Dn based on the new parent Dn created 946 * after a rename or move operation 947 * 948 * @param newParentDn 949 */ 950 private synchronized void updateAfterModDn( Dn newParentDn ) throws LdapInvalidDnException 951 { 952 if ( children != null ) 953 { 954 for( DnNode<N> child : children.values() ) 955 { 956 child.nodeDn = newParentDn.add( child.nodeRdn ); 957 child.updateAfterModDn( child.nodeDn ); 958 } 959 } 960 } 961 962 963 /** 964 * {@inheritDoc} 965 */ 966 public synchronized DnNode<N> clone() 967 { 968 DnNode<N> clonedDnNode = new DnNode<N>(); 969 970 clonedDnNode.nodeElement = nodeElement; 971 clonedDnNode.depth = depth; 972 clonedDnNode.parent = parent; 973 clonedDnNode.nodeRdn = nodeRdn; 974 clonedDnNode.nodeDn = nodeDn; 975 976 for ( DnNode<N> node : children.values() ) 977 { 978 clonedDnNode.children.put( node.getRdn(), node.clone() ); 979 } 980 981 return clonedDnNode; 982 } 983 984 985 private String toString( String tabs ) 986 { 987 if ( nodeRdn == null ) 988 { 989 return tabs; 990 } 991 992 StringBuilder sb = new StringBuilder(); 993 sb.append( tabs ); 994 995 boolean hasChildren = hasChildren(); 996 997 if ( isLeaf() ) 998 { 999 sb.append( "Leaf[" ).append( nodeDn ).append( "]: " ).append( "'" ).append( nodeElement ).append( "'" ); 1000 return sb.toString(); 1001 } 1002 1003 sb.append( "Branch[" ).append( nodeDn ).append( "]: " ); 1004 1005 if ( nodeElement != null ) 1006 { 1007 sb.append( "'" ).append( nodeElement ).append( "'" ); 1008 } 1009 1010 tabs += " "; 1011 1012 sb.append( '\n' ); 1013 1014 boolean isFirst = true; 1015 1016 if ( hasChildren ) 1017 { 1018 for ( Rdn rdn : children.keySet() ) 1019 { 1020 if ( isFirst ) 1021 { 1022 isFirst = false; 1023 } 1024 else 1025 { 1026 sb.append( "\n" ); 1027 } 1028 1029 DnNode<N> child = children.get( rdn ); 1030 1031 sb.append( child.toString( tabs ) ); 1032 } 1033 } 1034 1035 return sb.toString(); 1036 } 1037 1038 /** 1039 * {@inheritDoc} 1040 */ 1041 public String toString() 1042 { 1043 return toString( "" ); 1044 } 1045 1046 1047 /** 1048 * @return the dn 1049 */ 1050 public synchronized Dn getDn() 1051 { 1052 return nodeDn; 1053 } 1054}