KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ca > commons > naming > DN


1 package com.ca.commons.naming;
2
3 import java.lang.String JavaDoc;
4 import java.util.Vector JavaDoc;
5 import java.util.Enumeration JavaDoc;
6
7 import javax.naming.*;
8
9 //import com.ca.commons.cbutil.CBIntText;
10

11 /**
12  * A Data class that encapsulated the idea of an
13  * ldap Distinguished Name of the form:
14  * ou=frog farmers,o=frogcorp,c=au
15  * - and provides a bunch of utility methods for modifying
16  * and reading these values, especially the various bits
17  * of each rdn in various ways. <p>
18  *
19  * implements javax.naming.Name.<p>
20  *
21  * Why don't we just use CompoundName or CompositeName?<br>
22  *
23  * &nbsp;&nbsp;&nbsp;- basically because we're not supporting multiple
24  * naming systems - we're *only* supporting ldap. So Name is implemented
25  * for support with existing jndi ftns, but also a lot of other stuff
26  * is needed for rdns, and multi-value rdns. This could be architected
27  * as, say, compound name with another helper class, but that seems
28  * clumsy.<p>
29  *
30  */

31
32
33 //
34
// Can be used stand alone with com.ca.commons.naming.RDN, com.ca.commons.naming.NameUtility, com.ca.commons.cbutil.CBParse
35
//
36
//
37
//
38

39 public class DN implements Name
40 {
41     // these variables are all there is! Basically the magic is in the vector of
42
// rdns - all the code below is simply utility stuff for parsing and manipulating
43
// those rdns.
44

45     private Vector JavaDoc RDNs; // a list of segment RDNs, as strings, e.g. 'ou=frog farmers'
46
// element 0 is the 'root' RDN (i.e. 'c=au')
47
// element (RDNs.size()-1) is the lowest RDN (i.e. 'cn=fred').
48

49     boolean binary = false; // whether the dn contains isNonString data, and should
50
// be base64 encoded before being written out...
51

52     // XXX Candidate for refactoring!
53
// Rather than throwing errors (which they probably should) some methods
54
// cache error information and expect the caller to check the error status
55
// what can I say. I was young. - CB
56
String JavaDoc errorString = "";
57
58     // the cached root exception.
59
NamingException rootException = null;
60 // boolean empty = false; // whether this is the blank DN "".
61

62     /**
63      * Default constructor creates a DN with no value set.
64      */

65
66     public static String JavaDoc BLANKBASEDN = "World";
67
68     public DN()
69     {
70         RDNs = new Vector JavaDoc();
71     }
72
73     /**
74      * Copy constructor creates a new DN with an item by item
75      * <i>copy</i> of the parameter DN.
76      *
77      * @param copyMe the DN to be copied
78      */

79
80     public DN(DN copyMe)
81     {
82         try
83         {
84             RDNs = new Vector JavaDoc();
85
86             if (copyMe != null)
87             {
88                 for (int i=0; i<copyMe.size(); i++)
89                 {
90                     add(new String JavaDoc(copyMe.get(i)));
91                 }
92             }
93         }
94         catch (InvalidNameException e) // 'impossible' error - if copyMe is DN, how can this fail?
95
{
96             setError("error cloningDN " + copyMe.toString(), e);
97             clear();
98         }
99     }
100
101     /**
102      * Main Constructor takes an ldap Distinguished Name string
103      * (e.g. 'ou=wombat botherers,o=nutters inc,c=au') and
104      * breaks it up into a vector of RDNs
105      *
106      * @param ldapDN the ldap distinguished name to be parsed.
107      */

108
109     public DN(String JavaDoc ldapDN)
110     {
111         try
112         {
113             RDNs = new Vector JavaDoc();
114
115             if ("".equals(ldapDN) || BLANKBASEDN.equals(ldapDN))
116             {
117                 return;
118             }
119
120             int start = 0;
121             int end = NameUtility.next(ldapDN, 0, ',');
122
123             // get the RDNs in the form xxx=xxx,xxx=xxx,xxx=xxx
124
while (end!=-1)
125             {
126                 String JavaDoc rdn = ldapDN.substring(start,end);
127
128                 add(0,rdn);
129                 start = end+1;
130                 end = NameUtility.next(ldapDN, start, ',');
131             }
132
133             // ... and the last bit...
134
add(0,ldapDN.substring(start).trim());
135
136         }
137         catch (InvalidNameException e)
138         {
139             setError("unable to make DN from " + ldapDN ,e);
140             clear();
141         }
142
143     }
144
145
146
147
148     /**
149      * This Constructor takes an existing jndi Name,
150      * And initialises itself by taking that Name's rdn elements
151      * an element at a time, and converting them to RDN objects.
152      *
153      * @param name the ldap distinguished name to be parsed.
154      */

155
156
157     public DN(Name name)
158     {
159         try
160         {
161             RDNs = new Vector JavaDoc();
162
163             if (name.isEmpty()) return;
164
165             for (int i=0; i<name.size(); i++)
166             {
167                 add(i,name.get(i));
168             }
169         }
170         catch (InvalidNameException e)
171         {
172             setError("unable to create DN from name: " + name.toString(), e);
173             clear();
174         }
175     }
176
177 /*
178     public DN(byte[] name)
179     {
180         setError("Binary Distinguished Names not yet implemented ");
181     }
182 */

183
184     /**
185      * Spits back the DN as an escaped ldap DN string
186      *
187      * @return ldap DN in normal form (e.g. 'ou=linux fanatics,o=penguin fanciers pty. ltd.,c=us')
188      */

189
190     public String JavaDoc toString()
191     {
192         String JavaDoc ldapDN = "";
193         for (int i=0; i<RDNs.size(); i++)
194             ldapDN = get(i) + (i!=0?",":"") + ldapDN;
195         if (ldapDN.endsWith(","))
196         {
197             if (ldapDN.charAt(ldapDN.length()-2) != '\\')
198             {
199                 ldapDN = ldapDN.substring(0,ldapDN.length()-1);
200             }
201         }
202         return ldapDN;
203     }
204
205
206
207     /**
208      * Spits back the DN as a tree-level formatted string (mainly for debugging)
209      *
210      * @return ldap DN in level form (e.g. <pre>\nou=linux fanatics \no=penguin fanciers pty. ltd.\n c=us\n</pre>)
211      */

212
213     public String JavaDoc toFormattedString()
214     {
215         String JavaDoc ldapDN = "";
216         for (int i=0; i<RDNs.size(); i++)
217             ldapDN += get(i) + "\n";
218         return ldapDN;
219     }
220
221     /**
222      * a synonym for 'toString()' this returns the full ldap DN.
223      * @deprecated - use toString().
224      * @return the full ldap Distinguished Name
225      */

226
227     public String JavaDoc getDN() { return toString(); }
228     /**
229      * gets the ldap 'class' (e.g. 'c' or 'cn') for a particular
230      * RDN. If there are multiple attributes (for a multi-valued
231      * rdn) only the first is returned (XXX).
232      *
233      * @param i the index of the RDN class to return.
234      * @return the ldap class name for the specified RDN
235      */

236
237     public String JavaDoc getRDNAttribute(int i)
238     {
239         if (isEmpty()) return "";
240         if (i >= size()) return "";
241         if (i < 0) return "";
242
243         return getRDN(i).getAtt();
244     }
245
246     /**
247      * gets the ldap value (e.g. 'au' or 'Silverstone, Alicia') for a particular
248      * RDN. If there are multiple values, only the first is
249      * returned (XXX).
250      *
251      * @param i the index of the RDN value to return.
252      * @return the actual value for the specified RDN.
253      */

254
255     public String JavaDoc getRDNValue(int i)
256     {
257         if (isEmpty()) return "";
258         if (i >= size()) return "";
259         if (i < 0) return "";
260
261         return getRDN(i).getRawVal();
262     }
263
264     /**
265      * dumps the dn in a structured form, demonstrating parsing.
266      */

267
268     public void debugPrint()
269     {
270         System.out.print("\n");
271         for (int i=0; i<size(); i++)
272         {
273             System.out.print("element [" + i + "] = " + get(i).toString() + "\n");
274             getRDN(i).dump();
275
276         }
277     }
278
279     /**
280      * gets the full RDN (e.g. 'c=au' or 'cn=Englebert Humperdink') for a particular
281      * indexed RDN.
282      *
283      * @param rdn the ldap RDN string name for the specified index
284      * @param i the index of the RDN to set.
285      */

286
287     public void setRDN(RDN rdn, int i)
288     {
289         if (i<size() && i>= 0)
290             RDNs.setElementAt(rdn, i);
291     }
292
293
294     /**
295      * gets the full RDN (e.g. 'c=au' or 'cn=Englebert Humperdink') for a particular
296      * indexed RDN.
297      *
298      * @param i the index of the RDN to return.
299      * @return the ldap RDN string name for the specified index
300      */

301
302     public RDN getRDN(int i)
303     {
304         if (i==0 && isEmpty()) return new RDN(); // return empty RDN for empty DN
305

306         if (i<0) return new RDN();
307         if (i >= size()) new RDN();
308
309         return (RDN) RDNs.elementAt(i);
310     }
311
312     /**
313      * Returns the root RDN as a string (e.g. 'c=au')
314      *
315      * @return the root RDN string.
316      */

317
318     public RDN getRootRDN()
319     {
320         if (isEmpty())
321             return new RDN("");
322         else
323             return getRDN(0);
324     }
325
326     /**
327      * Gets the value of the lowest LDAP RDN.
328      * That is, the furthest-from-the-root class value of the DN.
329      * For example, 'ou=frog fanciers' in 'ou=frog fanciers,o=nutters,c=uk'
330      *
331      * @return the lowest level ldap value for the DN
332      */

333
334     public RDN getLowestRDN()
335     {
336         return getRDN(size()-1);
337     }
338
339
340     /**
341      * Adds an RDN to an existing DN at the highest level
342      * - mainly used internally
343      * while parsing a DN.
344      *
345      * @param rdn the RDN string to apend to the DN
346      */

347
348     public void addParentRDN(String JavaDoc rdn)
349     {
350         try
351         {
352             add(0,rdn);
353         }
354         catch (InvalidNameException e)
355         {
356             setError("Error adding RDN in DN.addParentRDN()", e);
357         }
358
359     }
360
361     /**
362      * Adds a new 'deepest level' RDN to a DN
363      *
364      * @param rdn the RDN to append to the end of the DN
365      */

366
367     public void addChildRDN(String JavaDoc rdn)
368         throws InvalidNameException
369     {
370             add(rdn);
371     }
372
373     /**
374      * Adds a new 'deepest level' RDN to a DN
375      *
376      * @param rdn the RDN to append to the end of the DN
377      */

378     public void addChildRDN(RDN rdn)
379         throws InvalidNameException
380     {
381             add(rdn);
382     }
383
384
385     /**
386      * sets (or more often <i>re</i>sets) the lowest (furthest-from-root)
387      * value of the DN
388      *
389      * @param value the (raw, unescaped) new lowest RDN value to overwrite the existing lowest RDN value with
390      */

391
392     // XXX code should be turned into wrapper for add(0,...) when that handles multi-val rdns properly
393
public void setLowestRDNRawValue(String JavaDoc value)
394     {
395         try
396         {
397             RDN rdn = getRDN(size()-1);
398             rdn.setRawVal(value);
399         }
400         catch (InvalidNameException e)
401         {
402             setError("Error setting DN.setLowestRDNRawValue: to " + value, e);
403         }
404
405     }
406
407     /**
408      * Exchanges the value of an rdn att=val element, and returns
409      */

410     protected String JavaDoc exchangeRDNelementValue(String JavaDoc rdn, String JavaDoc value)
411     {
412         return rdn.substring(0,NameUtility.next(rdn,0,'=')) + "=" + value;
413
414     }
415
416
417    /**
418     * Check whether this DN is equal to another DN...
419     *
420     * @param testDN the DN to compare against this DN
421     */

422
423     public boolean equals(DN testDN)
424     {
425        //XXX return (toString().equals(testDN.toString()));
426
if (testDN == null) return false;
427
428         if (testDN.size()!= size()) return false;
429
430         for (int i=0; i<size(); i++)
431         {
432             if (getRDN(i).equals(testDN.getRDN(i)) == false)
433                 return false;
434         }
435         return true;
436     }
437
438     /**
439      * implement the object.equals(object) method for genericity and unit testing.
440      * Note that this is slower than DN.equals(DN), since it requires instanceof checks.
441      * @param o a DN or Name object to test against
442      */

443     public boolean equals(Object JavaDoc o)
444     {
445         if (o == null)
446             return false;
447         if (o instanceof DN)
448             return equals((DN)o);
449         else if (o instanceof Name)
450             return (compareTo((Name)o) == 0);
451         else
452             return false; // cannot be equal in any sense if not a name
453
}
454     /**
455      * Checks whether the testDN is a subset of the current DN,
456      * starting from the root. Currently case insensitive.
457      *
458      * @param testDN the subset DN to test against
459      */

460
461     public boolean startsWith(DN testDN)
462     {
463         return startsWith((Name)testDN);
464     }
465
466     /**
467      * Test if the DNs are identical except for the
468      * lowest RDN.
469      * In other words, test if they are leaves on the same branch.
470      *
471      * @param testDN the putatitive sibling DN
472      */

473
474     public boolean sharesParent(DN testDN)
475     {
476         if (testDN.size()!= size()) return false;
477
478         for (int i=0; i<size()-1; i++)
479         {
480             if ((testDN.getRDN(i).equals(getRDN(i)))==false)
481                 return false;
482         }
483         return true;
484     }
485
486     /**
487      * Return the full DN of this DN's immediate parent.
488      * In other words, return this DN after removing the lowest RDN.
489      *
490      * @return the parent of this DN, or an empty DN if this is the top level DN.
491      */

492
493     public DN parentDN()
494     {
495         // XXX what to do if this is already an empty DN? The same?
496

497         if (size()<=1) return new DN(); // return empty DN for top level DNs
498

499            DN newDN = new DN(this);
500             newDN.RDNs.removeElementAt(size()-1);
501             return newDN;
502     }
503
504     /**
505      * reverse the order of elements in a DN...
506      */

507
508     public void reverse()
509     {
510         Vector JavaDoc rev = new Vector JavaDoc();
511         for (int i=RDNs.size()-1; i>=0; i--)
512             rev.add(RDNs.elementAt(i));
513         RDNs = rev;
514     }
515
516     /**
517      * Empties the DN of all RDNs.
518      */

519     public void clear()
520     {
521         RDNs.clear();
522         errorString = null;
523     }
524
525     /**
526      * Overload this method for app specific error handling.
527      */

528     public void setError(String JavaDoc msg, NamingException e)
529     {
530         errorString = msg;
531         rootException = e;
532         System.out.println(e);
533     }
534
535     /**
536      * Whether there was an error using this DN (i.e. when creating it).
537      */

538     public boolean error()
539     {
540         return (errorString == null);
541     }
542
543     /**
544      * Gets the error message (if any) associated with this DN.
545      */

546     public String JavaDoc getError()
547     {
548         return errorString;
549     }
550
551     /**
552      * Gets the root exception (if any) associated with this DN
553      */

554
555     public NamingException getNamingException()
556     {
557         return rootException;
558     }
559
560     /**
561      * Prepare a dn for jndi transmission
562      */

563 /*
564     public void escape()
565     {
566          for (int i=0; i<size(); i++)
567          {
568              getRDN(i).escape();
569          }
570     }
571 */

572     /**
573      * Unescape a dn that has been *normally* escaped using ldap v3 (i.e. by the
574      * preceeding ftn.).
575      */

576 /*
577     public void unescape()
578         throws InvalidNameException
579     {
580          for (int i=0; i<size(); i++)
581          {
582              getRDN(i).unescape();
583          }
584     }
585 */

586     /** (Obsolete)
587      * Unescape a dn that has been returned by jndi, that may contain either
588      * ldap v2 escaping, or the multiple-slash wierdness bug.
589      */

590 /*
591     public void unescapeJndiReturn()
592         throws InvalidNameException // shouldn't happen...
593     {
594          for (int i=0; i<size(); i++)
595          {
596              getRDN(i).unescapeJndiReturn();
597          }
598     }
599 */

600
601     /**
602      * Add an RDN to the end of the DN.
603      */

604     public Name add(RDN rdn)
605     {
606         //RDNs.insertElementAt(rdn,size());
607
add(size(), rdn);
608         return this;
609     }
610
611     /**
612      * The core method for adding RDN objects to the name.
613      * Called by all add methods.
614      * @param posn the position in the DN to add the RDN at (0 = root)
615      * @param rdn the RDN to add (may be multi-valued).
616      */

617
618     public Name add(int posn, RDN rdn)
619     {
620         RDNs.insertElementAt(rdn,posn);
621         return this;
622     }
623
624
625
626     // NN N A MM MM EEEEEE
627
// NNN N A A M MMM M EE
628
// N NN N A A M M M EEEE (Interface Def.)
629
// N NNN AAAAAAA M M M EE
630
// N NN AA AA M M M EEEEEE
631

632     /*
633      * Adds a single component at a specified position within this name.
634      */

635
636     // These two ftns should be used by all code to add rdns... the RDN array
637
// should not be accessed directly.
638

639     public Name add(int posn, String JavaDoc rdn)
640         throws InvalidNameException
641     {
642         RDN r = new RDN(rdn); // may throw invalidName Exception
643
add(posn, r);
644         return this;
645     }
646
647     /*
648      * Adds a single component to the end of this name.
649      */

650
651     public Name add(String JavaDoc rdn)
652         throws InvalidNameException
653     {
654         RDN r = new RDN(rdn); // may throw invalidName Exception
655
add(size(), r);
656         return this;
657     }
658
659
660     /*
661      * Adds the components of a name -- in order -- at a specified position within this name.
662      */

663
664     public Name addAll(int posn, Name n)
665         throws InvalidNameException
666     {
667          Enumeration JavaDoc e = n.getAll();
668          while (e.hasMoreElements())
669              add(posn++, e.nextElement().toString());
670          return this;
671     }
672
673
674     /*
675      * Adds the components of a name -- in order -- to the end of this name.
676      */

677
678     public Name addAll(Name suffix)
679         throws InvalidNameException
680     {
681          Enumeration JavaDoc e = suffix.getAll();
682          while (e.hasMoreElements())
683              add(e.nextElement().toString());
684          return this;
685     }
686
687
688     /*
689      * Generates a new copy of this name.
690      */

691     public Object JavaDoc clone()
692     {
693         return new DN(this);
694     }
695
696
697     /*
698      * Compares this name with another name for order.
699      * ... for the time being, ordering is alphabetical by rdns ordered
700      * right to left. Damn but the ldap rdn ordering system is screwed.
701      */

702
703     public int compareTo(Object JavaDoc obj)
704     {
705         int val = 0;
706         int pos = 1;
707         if (obj instanceof Name)
708         {
709             Name compareMe = (Name)obj;
710             int size = size();
711             int compSize = compareMe.size();
712             while (val == 0)
713             {
714                 String JavaDoc RDN = get(size-pos);
715                 String JavaDoc compRDN = compareMe.get(compSize-pos);
716                 int rdnOrder = RDN.compareTo(compRDN);
717
718                 if (rdnOrder != 0)
719                     return rdnOrder; // return alphabetic order of rdn.
720

721                 pos++;
722                 if (pos>size || pos>compSize)
723                 {
724                     if (size==compSize)
725                         return 0; // names are equal
726
if (pos>size)
727                         return -1; // shorter dns first
728
else
729                         return 1;
730                 }
731             }
732         }
733         else
734             throw new ClassCastException JavaDoc("non Name object in DN.compareTo - object was " + obj.getClass());
735
736         return 0; // never reached.
737
}
738
739
740     /*
741      * Determines whether this name ends with a specified suffix.
742      */

743
744     public boolean endsWith(Name n)
745     {
746           return false;
747     }
748
749
750     /*
751      * Retrieves a component of this name. Returns zero length string for
752      * an empty DN's first element - otherwise throws a ArrayIndexException.
753      * (is this correct behaviour?).
754      */

755
756     public String JavaDoc get(int posn)
757     {
758         if (posn==0 && isEmpty()) return ""; // return empty string for empty DN
759

760         return RDNs.elementAt(posn).toString();
761     }
762
763     /*
764      * Retrieves the components of this name as an enumeration of strings.
765      */

766
767     public java.util.Enumeration JavaDoc getAll()
768     {
769         DXNamingEnumeration ret = new DXNamingEnumeration();
770         for (int i=0; i<size(); i++)
771             ret.add(get(i));
772         return ret;
773     }
774
775     /*
776      * Creates a name whose components consist of a prefix of the components of this name.
777      */

778
779     public Name getPrefix(int posn)
780     {
781         DN returnMe = new DN();
782         try
783         {
784             for (int i=0; i<posn; i++)
785                 returnMe.add(get(i));
786             return returnMe;
787         }
788         catch (InvalidNameException e)
789         {
790             System.err.println("unexpected error in DN:\n " + e);
791             return new DN();
792         }
793     }
794
795     /*
796      * Creates a name whose components consist of a suffix of the components in this name.
797      */

798
799     public Name getSuffix(int posn)
800     {
801         DN returnMe = new DN();
802         for (int i=posn; i<size(); i++)
803         {
804             returnMe.add(new RDN(getRDN(i)));
805         }
806         return returnMe;
807     }
808
809    /**
810     * returns true if this is an 'empty', or root DN (i.e. \"\")
811     * @return empty status
812     */

813
814     public boolean isEmpty()
815     {
816         return (size()==0);
817     }
818
819
820     /*
821      * Removes a component from this name.
822      */

823
824     public Object JavaDoc remove(int posn)
825     {
826           return RDNs.remove(posn);
827     }
828
829    /*
830     * Returns the number of components in this name,
831     * and hence the level (the number
832     * of nodes from root) of the DN.
833     */

834
835     public int size()
836     {
837         return RDNs.size();
838     }
839
840    /*
841     * Returns the number of components in this name,
842     * and hence the level (the number
843     * of nodes from root) of the DN.
844     * (synonym of 'size()'. Why java didn't standardise on just one...)
845     */

846 /*
847     public int length()
848     {
849         return RDNs.size();
850     }
851 */

852     /*
853      * Determines whether this name starts with a specified prefix.
854      */

855
856     public boolean startsWith(Name n)
857     {
858         int pos = 0;
859         Enumeration JavaDoc e = n.getAll();
860         while (e.hasMoreElements())
861             if (e.nextElement().toString().equalsIgnoreCase(get(pos++).toString())==false)
862                 return false;
863
864         return true; // falls through - all tested components must be equal!
865
}
866
867
868
869 }
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
Popular Tags