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}