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