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