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}