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> implements Cloneable
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    // Helper methods
076    //-------------------------------------------------------------------------
077    /**
078     * Check that the Dn is not null
079     */
080    private void checkDn( Dn dn ) throws LdapException
081    {
082        if ( ( dn == null ) || dn.isEmpty() )
083        {
084            String message = "Cannot process an empty Dn";
085            LOG.error( message );
086            throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, message );
087        }
088    }
089
090
091    /**
092     * Create a new DnNode, recursively creating all the intermediate nodes.
093     */
094    private DnNode<N> createNode( Dn dn, N element, int nbRdns ) throws LdapException
095    {
096        checkDn( dn );
097
098        DnNode<N> rootNode = null;
099
100        // No parent : add from the current position
101        for ( Rdn rdn : dn.getRdns() )
102        {
103            if ( nbRdns == 0 )
104            {
105                break;
106            }
107
108            if ( rootNode == null )
109            {
110                // Create the new top node
111                DnNode<N> node = new DnNode<N>( element );
112                node.nodeRdn = rdn;
113                node.nodeDn = dn;
114                node.depth = dn.size() + depth;
115
116                rootNode = node;
117            }
118            else
119            {
120                DnNode<N> node = new DnNode<N>();
121                node.nodeRdn = rdn;
122                node.nodeDn = rootNode.nodeDn.getParent();
123                node.depth = node.nodeDn.size() + depth;
124                rootNode.parent = node;
125                node.children.put( rootNode.nodeRdn, rootNode );
126                rootNode = node;
127            }
128
129            nbRdns--;
130        }
131
132        return rootNode;
133    }
134
135
136    /**
137     * Store the given element into the node
138     */
139    private synchronized void setElement( N element )
140    {
141        this.nodeElement = element;
142    }
143
144
145    //-------------------------------------------------------------------------
146    // Constructors
147    //-------------------------------------------------------------------------
148    /**
149     * Creates a new instance of DnNode.
150     */
151    public DnNode()
152    {
153        children = new HashMap<Rdn, DnNode<N>>();
154        nodeDn = Dn.EMPTY_DN;
155        nodeRdn = Rdn.EMPTY_RDN;
156    }
157
158
159    /**
160     * Creates a new instance of DnNode.
161     *
162     * @param element the element to store
163     */
164    public DnNode( N element )
165    {
166        this.nodeElement = element;
167        children = new HashMap<Rdn, DnNode<N>>();
168    }
169
170
171    /**
172     * Creates a new instance of DnNode.
173     *
174     * @param dn the node's Dn
175     * @param element the element to store
176     */
177    public DnNode( Dn dn, N element )
178    {
179        if ( ( dn == null ) || ( dn.isEmpty() ) )
180        {
181            children = new HashMap<Rdn, DnNode<N>>();
182            this.nodeDn = Dn.EMPTY_DN;
183
184            return;
185        }
186
187        try
188        {
189            DnNode<N> rootNode = createNode( dn, element, dn.size() );
190
191            // Now copy back the created node into this
192            this.children = rootNode.children;
193            this.depth = rootNode.depth;
194            this.nodeDn = rootNode.nodeDn;
195            this.nodeElement = rootNode.nodeElement;
196            this.nodeRdn = rootNode.nodeRdn;
197            this.parent = null;
198        }
199        catch ( LdapException le )
200        {
201            // Special cas e: the Dn is empty, this is not allowed
202            throw new IllegalArgumentException( le.getMessage() );
203        }
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<N>();
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     * @param dn the normalized distinguished name to resolve to a parent
852     * @return the Node associated with the normalized dn
853     */
854    public synchronized DnNode<N> getParentWithElement()
855    {
856        DnNode<N> currentNode = parent;
857
858        while ( currentNode != null )
859        {
860            if ( currentNode.nodeElement != null )
861            {
862                return currentNode;
863            }
864
865            currentNode = currentNode.parent;
866        }
867
868        return null;
869    }
870
871
872    /**
873     * rename the DnNode's Dn
874     * 
875     * @param newRdn the new Rdn of this node
876     * @throws LdapException
877     */
878    public synchronized void rename( Rdn newRdn ) throws LdapException
879    {
880        Dn temp = nodeDn.getParent();
881        temp = temp.add( newRdn );
882
883        Rdn oldRdn = nodeRdn;
884
885        nodeRdn = temp.getRdn();
886        nodeDn = temp;
887
888        if ( parent != null )
889        {
890            parent.children.remove( oldRdn );
891            parent.children.put( nodeRdn, this );
892        }
893
894        updateAfterModDn( nodeDn );
895    }
896
897
898    /**
899     * move the DnNode's Dn
900     *
901     * @param newParent the new parent Dn
902     * @throws LdapException
903     */
904    public synchronized void move( Dn newParent ) throws LdapException
905    {
906        DnNode<N> tmp = null;
907
908        Dn tmpDn = null;
909
910        // check if the new parent Dn is child of the parent
911        if ( newParent.isDescendantOf( parent.nodeDn ) )
912        {
913            tmp = parent;
914            tmpDn = parent.nodeDn;
915        }
916
917        // if yes, then drill for the new parent node
918        if ( tmpDn != null )
919        {
920            int parentNodeSize = tmpDn.size();
921            int count = newParent.size() - parentNodeSize;
922
923            while ( count-- > 0 )
924            {
925                tmp = tmp.getChild( newParent.getRdn( parentNodeSize++ ) );
926            }
927        }
928
929        // if not, we have to traverse all the way up to the 
930        // root node and then find the new parent node
931        if ( tmp == null )
932        {
933            tmp = this;
934            while ( tmp.parent != null )
935            {
936                tmp = tmp.parent;
937            }
938
939            tmp = tmp.getNode( newParent );
940        }
941
942        nodeDn = newParent.add( nodeRdn );
943        updateAfterModDn( nodeDn );
944
945        if ( parent != null )
946        {
947            parent.children.remove( nodeRdn );
948        }
949
950        parent = tmp;
951        parent.children.put( nodeRdn, this );
952    }
953
954
955    /**
956     * update the children's Dn based on the new parent Dn created
957     * after a rename or move operation
958     * 
959     * @param newParentDn
960     */
961    private synchronized void updateAfterModDn( Dn newParentDn ) throws LdapInvalidDnException
962    {
963        if ( children != null )
964        {
965            for ( DnNode<N> child : children.values() )
966            {
967                child.nodeDn = newParentDn.add( child.nodeRdn );
968                child.updateAfterModDn( child.nodeDn );
969            }
970        }
971    }
972
973
974    /**
975     * {@inheritDoc}
976     */
977    public synchronized DnNode<N> clone()
978    {
979        DnNode<N> clonedDnNode = new DnNode<N>();
980
981        clonedDnNode.nodeElement = nodeElement;
982        clonedDnNode.depth = depth;
983        clonedDnNode.parent = parent;
984        clonedDnNode.nodeRdn = nodeRdn;
985        clonedDnNode.nodeDn = nodeDn;
986
987        for ( DnNode<N> node : children.values() )
988        {
989            clonedDnNode.children.put( node.getRdn(), node.clone() );
990        }
991
992        return clonedDnNode;
993    }
994
995
996    private String toString( String tabs )
997    {
998        if ( nodeRdn == null )
999        {
1000            return tabs;
1001        }
1002
1003        StringBuilder sb = new StringBuilder();
1004        sb.append( tabs );
1005
1006        boolean hasChildren = hasChildren();
1007
1008        if ( isLeaf() )
1009        {
1010            sb.append( "Leaf[" ).append( nodeDn ).append( "]: " ).append( "'" ).append( nodeElement ).append( "'" );
1011            return sb.toString();
1012        }
1013
1014        sb.append( "Branch[" ).append( nodeDn ).append( "]: " );
1015
1016        if ( nodeElement != null )
1017        {
1018            sb.append( "'" ).append( nodeElement ).append( "'" );
1019        }
1020
1021        tabs += "    ";
1022
1023        sb.append( '\n' );
1024
1025        boolean isFirst = true;
1026
1027        if ( hasChildren )
1028        {
1029            for ( Rdn rdn : children.keySet() )
1030            {
1031                if ( isFirst )
1032                {
1033                    isFirst = false;
1034                }
1035                else
1036                {
1037                    sb.append( "\n" );
1038                }
1039
1040                DnNode<N> child = children.get( rdn );
1041
1042                sb.append( child.toString( tabs ) );
1043            }
1044        }
1045
1046        return sb.toString();
1047    }
1048
1049
1050    /**
1051     * {@inheritDoc}
1052     */
1053    public String toString()
1054    {
1055        return toString( "" );
1056    }
1057
1058
1059    /**
1060     * @return the dn
1061     */
1062    public synchronized Dn getDn()
1063    {
1064        return nodeDn;
1065    }
1066}