KickJava   Java API By Example, From Geeks To Geeks.

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


1
2 package com.ca.commons.naming;
3
4 import com.ca.commons.cbutil.*;
5 import com.ca.commons.jndi.SchemaOps;
6
7 import javax.naming.*;
8 import javax.naming.directory.*;
9
10 import java.util.*;
11 import java.util.logging.Logger JavaDoc;
12 import java.util.logging.Level JavaDoc;
13
14 /**
15  * This class is a container for a collection of
16  * Attribute objects. It is schema aware, and will
17  * search schema to find a complete set of attributes
18  * for its component objectClass(s).<p>
19  *
20  * The class is built around a hashtable of id/attribute
21  * pairs.
22  */

23
24 // nb this class must work with both oid's (2.3.1.2.3.1.2.41.23.1.pi.2.e.34.phi)
25
// and ldap names ('c'). Ideally it should get the long form as well
26
// (i.e. 'country') but the server doesn't always oblige...
27
// I try to document the difference by referring to ids as either oids or ldap ids.
28
// if the distinction isn't obvious, it's probably an ldap id (i.e. a string name)
29

30 // XXX a lot of effort is gone to to trim out ';binary' from the names of
31
// XXX ldap attributes. THis is a grubby hack, and handling should be improved
32
// XXX somehow.
33

34 public class DXAttributes implements Attributes
35 {
36
37     static Hashtable attOIDs = new Hashtable(100); // a global hashset of all attribute oid-s and corresponding ldap descr names known to the program...
38

39     static int ID = 0;
40
41     int id; // unique debug id.
42

43     Hashtable atts; // a hashtable of attribute id/ attribute object values, keyed by lowercase id.
44

45     HashSet must; // a hashset of attribute ldap ids that *must* be entered
46
// - set in checkSchema()
47

48     boolean ignoreCase = true; // whether attribute IDs are case sensitive
49

50     boolean schemaChecked = false; // indicates a schema search has been made,
51
// and a full list of objectclass(s) attribute obtained
52
Attribute allObjectClasses; // a list of allObjectClasses, including parents.
53

54     //String baseObjectClass = null; // the deepest ObjectClass.
55

56     String JavaDoc objectClassName = null; // the current name of 'objectclass' or 'objectClass'
57

58     Vector orderedSOCs = new Vector(); // structural object classes, in deepest-first order
59

60
61     static SchemaOps schema; // the schema context
62

63     static Hashtable knownParents = new Hashtable(30); // hash of DXAttributes containing the known object class parents for a particular object class subset (e.g. 'inetorgperson' -> 'orgperson','person','top')
64
static Hashtable knownSubSets = new Hashtable(30); // hash of known object class subsets
65
static Hashtable objectClassDepths = new Hashtable(30); // hash of known object class 'depths' in object class inheritance tree
66

67     private final static Logger JavaDoc log = Logger.getLogger(DXAttributes.class.getName());
68
69
70     static
71     {
72         objectClassDepths.put("top", new Integer JavaDoc(0));
73
74         // pre set 'synthetic' schema attributes
75
objectClassDepths.put("schema", new Integer JavaDoc(1));
76         objectClassDepths.put("AttributeDefinition", new Integer JavaDoc(2));
77         objectClassDepths.put("ClassDefinition", new Integer JavaDoc(2));
78         objectClassDepths.put("SyntaxDefinition", new Integer JavaDoc(2));
79
80     }
81
82     // common code run by all the more basic constructors.
83
void basicInit()
84     {
85         id = ID++;
86
87         atts = new Hashtable();
88         must = new HashSet();
89
90 // schema = null;
91
}
92
93     /**
94      * Initialises an empty set of attributes with no schema
95      */

96     public DXAttributes()
97     {
98         basicInit();
99     }
100
101     /**
102      * Initialises a set of attributes with a single attribute
103      */

104
105     public DXAttributes(Attribute a)
106     {
107         basicInit();
108         put(a);
109     }
110
111     /**
112      * Copies an existing Attributes object into a DXAttributes
113      * object.
114      */

115
116     public DXAttributes(Attributes a)
117     {
118
119         if (a==null)
120         {
121             atts = new Hashtable();
122             must = new HashSet();
123         }
124         else
125         {
126             atts = new Hashtable(a.size()+10);
127             must = new HashSet(a.size()); // overkill, but what the heck...
128

129             Enumeration e = a.getAll();
130             while (e.hasMoreElements())
131             {
132                 DXAttribute newAtt = new DXAttribute((Attribute)e.nextElement());
133                 put (newAtt);
134             }
135         }
136     }
137
138     /**
139      * Initialises a set of DXattributes using
140      * a Hashtable of existing id/attribute pairs...
141      * @param newAtts hashtable of id/attribute pairs to
142      * initialise the new DXAttributes object
143      */

144
145     public DXAttributes(Hashtable newAtts)
146     {
147         atts = (Hashtable) newAtts.clone();
148     }
149
150
151     /**
152      * Initialises a set of DXattributes using
153      * a NamingEnumeration of existing attribute(s).
154      * @param newAtts namingenumeration of attributes to
155      * initialise the new DXAttributes object
156      */

157
158     public DXAttributes(NamingEnumeration newAtts)
159     {
160         atts = new Hashtable();
161         while (newAtts.hasMoreElements())
162         {
163             Attribute current = (Attribute) newAtts.nextElement();
164             atts.put(current.getID().toLowerCase(), current);
165         }
166     }
167
168
169     /**
170      * <p>This sets the standard schema to use while this connection is open.
171      * (It may be possible in future releases to set schema on a per-Attribute
172      * basis - it is not clear yet whether this would be useful.)</p>
173      *
174      * <p>Note: currently this also sets the default schema for the DXAttribute
175      * Class.</p>
176      */

177     public static void setDefaultSchema(SchemaOps defaultSchema)
178     {
179         schema = defaultSchema;
180
181         DXAttribute.setDefaultSchema(defaultSchema);
182     }
183
184
185     public int getID() { return id; }
186
187     /**
188      * creates a new DXAttributes, copying each Attribute object.
189      * @return a new DXAttributes object
190      */

191
192     public Object JavaDoc clone()
193     {
194         return new DXAttributes(atts);
195     }
196
197     /**
198      * gets an Attribute specified by its ID
199      * @param attrID the ID of the attribute to retrieve
200      * @return the specified attribute (actually a DXAttribute)
201      * - null if it can't be found.
202      */

203
204     public Attribute get(java.lang.String JavaDoc attrID)
205     {
206         Attribute ret = (Attribute) atts.get(attrID.toLowerCase());
207         if (ret==null)
208         {
209             ret = (Attribute) atts.get(attrID.toLowerCase() + ";binary");
210         }
211
212         return ret;
213     }
214
215     /**
216      * returns all the Attribute(s) in this DXAttributes object.
217      * - the NamingEnumeration is (evily) pre-sorted alphabetically...
218      * @return enumeration of all stored Attribute objects
219      */

220     public NamingEnumeration getAll()
221     {
222         return (new DXNamingEnumeration (atts.elements())).sort();
223     }
224
225     /**
226      * For convenience and display, DXAttributes objects have a complete
227      * list of all attribute(s) that the objectclass(s) represented might
228      * possibly contain. However sometimes we need an <b>Attributes</b>
229      * object that has no null-valued attributes (i.e. when adding it to
230      * the directory).
231      * @return an Attributes object containing no null valued attribute(s).
232      */

233     public Attributes getAsNonNullAttributes()
234     {
235         return new DXAttributes(getAllNonNull());
236     }
237
238     /**
239      * returns all the Attribute(s) that have non-null values
240      * in this DXAttributes object.
241      * - the NamingEnumeration is (evily) pre-sorted alphabetically...<p>
242      *
243      * Warning: whether an attribute is added is undefined if the
244      * attribute is multi-valued and contains one or more null values in
245      * addition to other non-null values.
246      *
247      * @return enumeration of all stored Attribute objects with non-null values
248      */

249
250     public NamingEnumeration getAllNonNull()
251     {
252         DXNamingEnumeration returnEnumeration = new DXNamingEnumeration ();
253         Enumeration allatts = getAll();
254
255         while (allatts.hasMoreElements())
256         {
257             Attribute fnord = (Attribute) allatts.nextElement();
258             if (fnord != null) // should never happen...
259
{
260                 try
261                 {
262                     if (fnord.get() != null) // if there is at least one non-null value...
263
returnEnumeration.add(fnord); // add it to the list
264
}
265                 catch (NoSuchElementException e) // 'expected' exception (love that jndi)
266
{
267
268                 }
269                 catch (NamingException e2)
270                 {
271                     log.log(Level.WARNING, "whoops: Naming Exception reading " + fnord.getID(), e2);
272                 }
273             }
274         }
275
276         returnEnumeration.sort();
277
278         return returnEnumeration;
279     }
280
281     /**
282      * returns all the mandatory 'MUST' have Attribute(s) in this DXAttributes object.
283      * - the NamingEnumeration is (evily) pre-sorted alphabetically...
284      * @return enumeration of all stored Attribute objects
285      */

286     public NamingEnumeration getMandatory()
287     {
288         DXNamingEnumeration returnEnumeration = new DXNamingEnumeration ();
289
290         if (must==null) return returnEnumeration; // return empty enumeration if not initialised...
291

292         Iterator musts = must.iterator();
293         while (musts.hasNext())
294         {
295             String JavaDoc s = (String JavaDoc)musts.next();
296             returnEnumeration.add(get(s));
297         }
298         returnEnumeration.sort();
299
300         return returnEnumeration;
301     }
302
303
304
305    /**
306     * Returns the list of mandatory attribute IDs (HashSet:must).
307     * @return the list of mandatory attribute IDs (e.g. objectClass, cn, sn).
308     */

309
310     public HashSet getMandatoryIDs()
311     {
312         return must;
313     }
314
315
316
317     /**
318      * returns all the optional 'MAY' Attribute(s) in this DXAttributes object.
319      * - the NamingEnumeration is (evily) pre-sorted alphabetically...
320      * @return enumeration of all stored Attribute objects
321      */

322     public NamingEnumeration getOptional()
323     {
324         DXNamingEnumeration returnEnumeration = new DXNamingEnumeration ();
325         Enumeration allIDs = atts.keys();
326         while (allIDs.hasMoreElements())
327         {
328             String JavaDoc id = (String JavaDoc) allIDs.nextElement();
329             if (must.contains(id)==false) // if it's *not* mandatory
330
returnEnumeration.add(get(id)); // add it to the optional list
331
}
332         returnEnumeration.sort();
333
334         return returnEnumeration;
335     }
336
337
338
339     /**
340      * returns all the attribute IDs held in this DXAttributes object.
341      * @return all attribute IDs
342      */

343
344     public NamingEnumeration getIDs()
345     {
346         // cannot simply return hash keys, as they are standardised to lower case.
347
DXNamingEnumeration ret = new DXNamingEnumeration();
348         NamingEnumeration allAtts = getAll();
349         while (allAtts.hasMoreElements())
350             ret.add(((Attribute)allAtts.nextElement()).getID());
351
352         return ret;
353
354         //return (NamingEnumeration)(new DXNamingEnumeration (atts.keys()));
355
}
356
357     /**
358      * returns whether attribute IDs are stored in a case
359      * sensitive manner; i.e. whether 'person' is different
360      * from 'Person'. The default is <i>false</i>, implying
361      * case sensitivity.
362      * @return whether case is ignored for attribute IDs
363      */

364
365     public boolean isCaseIgnored()
366     {
367         return ignoreCase;
368     }
369
370     /**
371      * adds an attribute to the DXAttributes collection,
372      * using the attribute.getID() method to find a key.
373      * NB: doco seems unclear on whether this adds to,
374      * or replaces, any existing attribute with the same
375      * ID. At the moment this <b>replaces</b> the values...
376      * @param attr the attribute to add
377      * @return the previous attribute (if any) with this key.
378      */

379
380     public Attribute put(Attribute attr)
381     {
382         if (attr == null) return null; // sanity check - can't add a null attribute...
383

384         Attribute old = get(attr.getID().toLowerCase());
385         schemaChecked = false;
386
387         if (old!=null)
388         {
389             atts.remove(old.getID().toLowerCase()); // code for *replacing* existing attribute values
390
}
391
392         String JavaDoc ID = attr.getID().toLowerCase();
393         if (attr instanceof DXAttribute)
394             atts.put(ID, attr);
395         else
396             atts.put(ID, new DXAttribute(attr));
397
398         return old;
399     }
400
401     /**
402      * creates an attribute with the specified values and adds it
403      * to the DXAttributes collection,
404      * using the attrID string as a key.
405      * NB: doco seems unclear on whether this adds to, or replaces,
406      * an existing attribute with the same ID. This implementation
407      * <b>adds</b> the new object value...
408      *
409      * @param attrID the String ID of the newly added attribute
410      * @param val the value to associate with the ID for the newly
411      * created attribute
412      * @return the previous attribute (if any) with this key.
413      */

414
415     public Attribute put(java.lang.String JavaDoc attrID, java.lang.Object JavaDoc val)
416     {
417         schemaChecked = false;
418         return put(new DXAttribute(attrID.toLowerCase(), val));
419     }
420
421     /**
422      * Adds an Enumeration of Attribute(s) to a DXAttribute
423      *
424      * @param attributeList the list of attributes to add.
425      */

426
427     public void put(Enumeration attributeList)
428     {
429         while (attributeList.hasMoreElements())
430         {
431             Attribute a = (Attribute)attributeList.nextElement();
432             if (a instanceof DXAttribute)
433                 put(a);
434             else
435                 put(new DXAttribute(a));
436         }
437     }
438
439     /**
440      * removes the attribute containing this key (if any).
441      * @param attrID the ID of the attribute to remove
442      * @return the removed attribute (if any)
443      */

444
445     public Attribute remove(java.lang.String JavaDoc attrID)
446     {
447         schemaChecked = false;
448         return (Attribute) atts.remove(attrID.toLowerCase());
449     }
450
451     /**
452      * returns the number of Attribute objects held.
453      *
454      * @return number of attribute objects
455      */

456     public int size()
457     {
458         return atts.size();
459     }
460
461     /**
462      * searches the schema for all the possible attributes that the
463      * objectClasses present *could* have, and adds new
464      * attributes that are not already present to the list
465      * of current Attribute objects as empty attributes.
466      */

467 /*
468     public void checkSchema()
469     {
470
471         if ((schema == null)|| (schemaChecked==true)) return; // nothing to do
472
473         try
474         {
475                 // update the hashtable with complete list
476                 // of attributes as read from schema
477             expandAllAttributes(getAllObjectClasses());
478
479             //print();
480         }
481         catch (NamingException e)
482         {
483             log.warning("Naming Exception in Schema Read of DXAttribute\n " + e);
484         }
485         schemaChecked = true;
486
487     }
488 */

489 /*
490     public String getBaseObjectClass()
491     {
492         if (baseObjectClass == null)
493         {
494             setAllObjectClasses();
495             if (baseObjectClass == null) // Still? Must be an error somewhere...
496                 return "top";
497         }
498
499         return baseObjectClass;
500     }
501 */

502     public void setAllObjectClasses()
503     {
504         allObjectClasses = getAllObjectClasses();
505     }
506
507     /**
508      * Return a list of object classes as a vector from deepest (at pos 0) to 'top' (at pos (size()-1) ).
509      */

510     public Vector getOrderedOCs()
511     {
512         Vector ret = null;
513         try
514         {
515             Attribute oc = getAllObjectClasses();
516             if (oc == null)
517                 return null;
518
519             ret = new Vector(oc.size());
520             NamingEnumeration vals = oc.getAll();
521             while (vals.hasMore())
522             {
523                 ret.add(vals.next());
524             }
525             return ret;
526         }
527         catch (NamingException e)
528         {
529             log.log(Level.WARNING, "Yet another rare naming exception - DXAttributes:getOrderedOCs ", e);
530             return new Vector(0);
531         }
532     }
533
534     public Attribute getAllObjectClasses()
535     {
536         Attribute att = get("objectclass");
537
538         if (att != null) //TE: check that the attribute is set.
539
{
540             if (att instanceof DXAttribute)
541                 return getAllObjectClasses((DXAttribute)att);
542             else
543                 return getAllObjectClasses(new DXAttribute(att));
544         }
545         return null; //TE: return null if att is null.
546
}
547
548     /**
549      * Some directories don't include all of an entry's object classes,
550      * but only the lowest level ones. This looks up all the parents
551      * the low level object classes are inherited from and, forms a
552      * complete ordered list of all object classes for this Attributes object.
553      *
554      * @return an Attribute containing the names of all the object classes...
555      */

556
557     // XXX objecClass should no longer be case sensitive. When happy this works,
558
// XXX delete code below.
559

560     public static DXAttribute getAllObjectClasses(DXAttribute oc)
561     {
562         if (oc==null) return null; // no object classes (may be virtual entry such as DSA prefix)
563

564         if (knownSubSets.containsKey(oc))
565             return(DXAttribute) knownSubSets.get(oc);
566
567         try
568         {
569             DXAttribute orig = new DXAttribute(oc);
570
571             Enumeration vals = oc.getAll();
572             while (vals.hasMoreElements())
573             {
574                 Attribute parents = getParentObjectClasses((String JavaDoc)vals.nextElement());
575                 if (parents != null)
576                 {
577                     Enumeration parentVals = parents.getAll();
578                     while (parentVals.hasMoreElements())
579                     {
580                         String JavaDoc parent = (String JavaDoc) parentVals.nextElement();
581                         if (oc.contains(parent) == false)
582                         {
583                             oc.add(parent);
584                         }
585                     }
586                 }
587             }
588
589             DXAttribute fullOC = sortOCByDepth(oc);
590             knownSubSets.put(orig, fullOC);
591             return fullOC;
592         }
593         catch (NamingException e)
594         {
595             log.log(Level.WARNING, "NamingException in getAllObjectClasses ", e);
596             return oc;
597         }
598     }
599
600     /**
601      * Takes a list of *all* object class values, and returns them
602      * sorted by depth. This requires the objectClassDepths hashtable
603      * to be set (which is done by getParentObjectClasses() ).
604      */

605
606     protected static DXAttribute sortOCByDepth(Attribute oc)
607     {
608         DXAttribute ret = new DXAttribute("objectClass");
609         ret.setOrdered(true);
610
611         try
612         {
613             Enumeration vals = oc.getAll();
614             while (vals.hasMoreElements())
615             {
616                 String JavaDoc val = (String JavaDoc) vals.nextElement();
617                 Integer JavaDoc myInt = (Integer JavaDoc)objectClassDepths.get(val);
618
619                 if (myInt == null) // shouldn't happen (if schema is correct), but in case we missed one...
620
{
621                     getParentObjectClasses(val); // try again to set the objectClassDepths hash for this value. (probably won't work).
622
myInt = (Integer JavaDoc)objectClassDepths.get(val); // and try to reget the value.
623
if (myInt == null) // if still null, give up and set to zero.
624
myInt = new Integer JavaDoc(0);
625                 }
626                 int depth = myInt.intValue();
627                 int i;
628                 for (i=ret.size()-1; i>=0; i--)
629                 {
630                     int existing = ( (Integer JavaDoc)objectClassDepths.get(ret.get(i)) ).intValue();
631                     if ( existing >= depth)
632                     {
633                         ret.add(i+1, val);
634                         break;
635                     }
636                 }
637                 if (i == -1)
638                     ret.add(0, val);
639             }
640             return ret;
641         }
642         catch (NamingException e)
643         {
644             log.log(Level.WARNING, "Naming Exception in DXAttributes sortOCByDepth()", e);
645             return new DXAttribute(oc);
646         }
647     }
648
649
650
651     /**
652      * recursively builds up a complete ordered list of all the parents of a particular
653      * object class (including the child object class) from schema.
654      *
655      * @param childOC the child Object Class to search for parents of
656      * @return an attribute containing the child class and all parents
657      */

658
659     public static DXAttribute getParentObjectClasses(String JavaDoc childOC)
660         throws NamingException
661     {
662         if (schema == null) // in the absence of a schema, everything is at level '1', just below 'top'
663
{
664             objectClassDepths.put(childOC, new Integer JavaDoc(1));
665             return null;
666         }
667
668         if ("schema attributedefinition classdefinition syntaxdefinition matchingrule".indexOf(childOC.toLowerCase()) != -1) return null; // don't bother looking up synthetic object classes.
669

670         if (knownParents.containsKey(childOC))
671         {
672             return (DXAttribute) knownParents.get(childOC);
673         }
674
675         DXAttribute parents = new DXAttribute("objectClass"); // this is the attribute returned
676

677         String JavaDoc schemaParent = null;
678         try
679         {
680             //schemaParents = schema.getAttributes("ClassDefinition/" + childOC, new String[] {"SUP"});
681
Attributes schemaDef = schema.getAttributes("ClassDefinition/" + childOC);
682             if (schemaDef!=null)
683             {
684                 Attribute sup = schemaDef.get("SUP");
685                 if (sup!=null)
686                     schemaParent = (String JavaDoc)sup.get();
687             }
688         }
689         catch (NamingException e) // easily throws a name-not-found exception
690
{
691             log.warning("Possible Schema Error: class definition for " + childOC + " could not be found");
692             objectClassDepths.put(childOC, new Integer JavaDoc(1)); // BAD SCHEMA! NO BISCUIT! Set to one 'cause we don't know true depth (and top is always zero).
693

694             return null; // do nothing
695
}
696
697         // TODO: this is silly; why don't we just reuse the DXAttribute object returned?
698
// XXX - no time to fix now; maybe later...
699

700         if (schemaParent != null) // may not: e.g. 'top' has no parent
701
{
702             DXAttribute oc = getParentObjectClasses(schemaParent); // recurse -> this should also set the depth in objectClassDepths
703

704             if (oc != null)
705             {
706                 Enumeration vals = oc.getAll();
707                 while (vals.hasMoreElements()) // load up the return attribute
708
{
709                     parents.add(vals.nextElement()); // (o.k. to add the same value twice...)
710
}
711             }
712
713             int depth = ((Integer JavaDoc)objectClassDepths.get(schemaParent)).intValue();
714             if (objectClassDepths.containsKey(childOC) == false)
715             {
716                 objectClassDepths.put(childOC, new Integer JavaDoc(depth+1));
717             }
718             else
719             {
720                 int oldDepth = ((Integer JavaDoc)objectClassDepths.get(childOC)).intValue();
721                 if (oldDepth <= depth)
722                     objectClassDepths.put(childOC, new Integer JavaDoc(depth+1));
723             }
724         }
725         else // no schemaParents
726
{
727             objectClassDepths.put(childOC, new Integer JavaDoc(1)); // BAD SCHEMA! NO BISCUIT! Set to one 'cause we don't know true depth (and top is always zero).
728
//schemaParents = null; // so store a blank place holder
729
}
730
731         parents.add(childOC); // *** Note Cunning Recursive Magic - this is where base stuff actually gets added to parents... ***
732

733         knownParents.put(childOC, parents);
734
735         return parents;
736     }
737
738     /**
739      * Get structural object classes, in deepest oc first order.
740      * @return vector of structural OCs (may be null if can't be resolved)
741      */

742 /*
743     public Vector getOrderedStructOCs()
744     {
745         try
746         {
747             if (orderedSOCs.size() != 0) return orderedSOCs;
748
749             if (baseObjectClass == null) setAllObjectClasses();
750
751             setOrderedSOCs(baseObjectClass);
752
753             return orderedSOCs;
754         }
755         catch (Exception e)
756         {
757             return orderedSOCs;
758         }
759     }
760 */

761     /**
762      * Sets the vector of structural object classes
763      * in this attribute set.
764      * @param oc the object class to find (and add) the structural parents of.
765      */

766
767     void setOrderedSOCs(String JavaDoc oc)
768         throws NamingException
769     {
770         orderedSOCs.add(oc);
771
772         if (oc.equalsIgnoreCase("top")) return; // recursive search finished.
773

774
775         String JavaDoc parent = schema.schemaLookup("ClassDefinition/" + oc, "SUP");
776         String JavaDoc struct = schema.schemaLookup("ClassDefinition/" + parent, "STRUCTURAL");
777         // try to figure out if that was a structural object class...
778
if ("true".equalsIgnoreCase(struct) )
779         {
780             setOrderedSOCs(parent); // recurse to next set of parents.
781
return; // finished.
782
}
783
784 /*
785         Attributes parents = null;
786         parents = schema.getAttributes("ClassDefinition/" + oc, new String[] {"SUP"}); // returns one attribute
787         NamingEnumeration parentList = parents.get("SUP").getAll(); // get that attribute's values.
788         while (parentList.hasMore())
789         {
790             String id = parentList.next().toString();
791             Attributes struct = schema.getAttributes("ClassDefinition/" + id, new String[] {"STRUCTURAL"});
792             // try to figure out if that was a structural object class...
793             if (struct != null && struct.get("STRUCTURAL") != null && ("true".equalsIgnoreCase(struct.get("STRUCTURAL").get().toString())) )
794             {
795                 setOrderedSOCs(id); // recurse to next set of parents.
796                 return; // finished.
797             }
798         }
799 */

800     }
801
802
803     /**
804      * Adds an OID / ldapName combination to the hashtable
805      * of all oid / ldapNames.<p>
806      *
807      * If the oid is actually an ldap String (different servers
808      * return different things...) it doesn't bother registering
809      * the string...
810      *
811      * @param oid the numeric oid to register
812      * @param ldapName the name of the corresponding ldap descr
813      * @return true if new oid was registered, false if it was already known
814      */

815     public boolean registerOID(String JavaDoc oid, String JavaDoc ldapName)
816     {
817         // Don't register non-oids...
818
char test = oid.charAt(0);
819         if (test < '0' || test > '9') // not an oid
820
{
821             return false;
822         }
823
824         // XXX ;binary hack for ASN1 attributes
825
if (oid.endsWith(";binary")) oid = oid.substring(0,oid.indexOf(";binary"));
826         if (ldapName.endsWith(";binary")) ldapName = ldapName.substring(0,ldapName.indexOf(";binary"));
827
828         if (attOIDs.contains(oid)==true) return false;
829         attOIDs.put(oid, ldapName); // add it to the list...
830
return true;
831     }
832
833     /**
834      * This method trims all empty attributes (attributes without values) from
835      * the DXAttributes object.
836      */

837
838     public void removeEmptyAttributes()
839     {
840         Enumeration atts = getAll();
841         while (atts.hasMoreElements())
842         {
843             Attribute att = (Attribute)atts.nextElement();
844             if (att.size() == 0)
845                 remove(att.getID());
846         }
847     }
848
849
850
851
852     /**
853      * <p>Sets the internal list of all attribute IDs needed for
854      * a given set of object classes, as well as noting which
855      * are mandatory and which optional.</p>
856      *
857      * <p>As an added wrinkle, this must be able to cope with attribute
858      * ID's expressed either as ldap strings, or as numeric OIDs. It
859      * does this by automatically detecting OIDs, and translating them
860      * to strings using schema lookups.</p>
861      *
862      * <p>This method uses the schema to create empty valued attributes for
863      * attributes which don't currently exist, but which are allowed.</p>
864      *
865      */

866
867     public void expandAllAttributes()
868     {
869         if (schema == null) return;
870
871         Attribute oc = null;
872         oc = getAllObjectClasses();
873
874         try
875         {
876         // Quick Hack to eliminate 'fake attributes' used for top level of syntaxes...
877
//XXX Might want to redo if efficiency is ever a concern :-)
878

879             if (oc.contains(SchemaOps.SCHEMA_FAKE_OBJECT_CLASS_NAME) )
880                 return; // ignore the synthetic 'schema' object classes...
881

882             NamingEnumeration ocs = oc.getAll();
883
884             // cycle through all object classes, finding attributes for each
885
while (ocs.hasMore())
886             {
887                 String JavaDoc objectClass = (String JavaDoc)ocs.next();
888                 Attributes ocAttrs = schema.getAttributes("ClassDefinition/" + objectClass);
889                 Attribute mustAtt = ocAttrs.get("MUST"); // get mandatory attribute IDs
890
Attribute mayAtt = ocAttrs.get("MAY"); // get optional attribute IDs
891

892                 if (mustAtt != null)
893                 {
894                     NamingEnumeration musts = mustAtt.getAll();
895                     while (musts.hasMore())
896                     {
897                         String JavaDoc attOID = (String JavaDoc) musts.next();
898
899                         //XXX ;binary hack
900
if (attOID.indexOf(";binary")>0) attOID = attOID.substring(0,attOID.indexOf(";binary"));
901
902                         String JavaDoc ldapName = getldapName(attOID);
903
904                         registerOID(attOID, ldapName);
905                         if (get(ldapName)==null) // if we don't already have this attribute...
906
{
907                             put(new DXAttribute(getldapName(attOID))); // ... add it to the list
908
//CB empty atts now. put(new DXAttribute(getldapName(attOID), null)); // ... add it to the list
909
}
910
911                         if (must.contains(ldapName.toLowerCase())==false) // and if it isn't already mandatory
912
{
913                             must.add(ldapName.toLowerCase()); // ... add it to the mandatory list as well
914
}
915                     }
916                 }
917
918                 if (mayAtt != null)
919                 {
920                     NamingEnumeration mays = mayAtt.getAll();
921                     while (mays.hasMore())
922                     {
923                         String JavaDoc attOID = (String JavaDoc) mays.next();
924                         //XXX isNonString hack
925
if (attOID.indexOf(";binary")>0) attOID = attOID.substring(0,attOID.indexOf(";binary"));
926
927                         String JavaDoc ldapName = getldapName(attOID);
928                         registerOID(attOID, ldapName);
929
930                         if (get(ldapName)==null) // if we don't already have this one...
931
{
932                             put(new DXAttribute(getldapName(attOID))); // ... add it to the list
933
//put(new DXAttribute(getldapName(attOID), null)); // ... add it to the list
934
}
935                     }
936                 }
937             }
938         }
939         catch (NamingException e)
940         {
941             log.log(Level.WARNING, "unable to read attribute list for object classes: ", e);
942             try
943             {
944                 CBUtility.printEnumeration(oc.getAll());
945             }
946             catch (NamingException e2)
947             {
948                 log.warning("...(further error: can't print object class list)...");
949             }
950             return;
951         }
952         catch (NullPointerException JavaDoc e)
953         {
954             log.log(Level.WARNING, "ERROR: unable to read list of object classes from schema - some functionality will not be available", e);
955         }
956     }
957
958     /**
959      * This method does it's darndnest to return a string ldap name.<p>
960      * First, it checks whether the string is <i>already</i> an ldap
961      * name; if it is, it returns it unchanged.<p>
962      *
963      * Secondly, it tries to find the ldap text name for an oid
964      * (i.e. converts 2.5.4.0 to 'objectClass').<p>
965      *
966      * Finally, if it <b>can't</b> find the name it returns the oid instead...
967      * (This shouldn't really happen, but means that the system may still work,
968      * although the raw OIDs aren't very user friendly)<p>
969      */

970
971     public String JavaDoc getldapName(String JavaDoc attOID)
972     {
973         return (schema==null?attOID:schema.translateOID(attOID));
974 /*
975         try
976         {
977         // Don't register non-oids...
978         char test = attOID.charAt(0);
979         if (test < '0' || test > '9') // not an oid
980         {
981             return attOID;
982         }
983
984
985              if (attOIDs.containsKey(attOID)) // check if we already have one
986                  return (String) attOIDs.get(attOID);
987
988              // specify search constraints to search subtree
989              SearchControls constraints = new SearchControls();
990              constraints.setSearchScope(SearchControls.ONELEVEL_SCOPE);
991              constraints.setCountLimit(1);
992              constraints.setTimeLimit(0);
993
994              String searchfilter = "(NUMERICOID="+attOID + ")";
995              NamingEnumeration results = schema.search("AttributeDefinition", searchfilter, constraints);
996
997              if (results.hasMoreElements())
998              {
999                  SearchResult result = (SearchResult) results.next();
1000                 Attributes a = result.getAttributes(); // may throw null pointer exception, caught below?
1001                 Attribute ldapName = (Attribute)a.get("NAME"); // may throw null pointer exception, caught below?
1002                 String name = (String)ldapName.get(); // may throw null pointer exception, caught below?
1003                 //XXX ;binary hack
1004                 if (name.indexOf(";binary")>0) name = name.substring(0,name.indexOf(";binary"));
1005
1006                 return name;
1007             }
1008             else
1009                 {CBUtility.log("can't read schema for: " + attOID + "... no results"); return attOID; }
1010
1011        }
1012        catch (Exception e)
1013        {
1014             {CBUtility.log("can't read schema for: " + attOID + "\n " +e); return attOID; }
1015        }
1016*/

1017    }
1018
1019    public String JavaDoc toString()
1020    {
1021        StringBuffer JavaDoc text = new StringBuffer JavaDoc("size (" + size() + ")\n");
1022
1023        NamingEnumeration allatts = this.getAll();
1024        while (allatts.hasMoreElements())
1025        {
1026            Attribute fnord = (Attribute) allatts.nextElement();
1027            if (fnord == null)
1028                log.warning("bizarre null attribute in element list");
1029            else
1030            {
1031                if (must != null && must.contains(fnord.getID()))
1032                    text.append("must ");
1033
1034                if (fnord instanceof DXAttribute)
1035                    text.append(" dx ").append(((DXAttribute)fnord).toDebugString()).append(" ");
1036                else
1037                {
1038                    String JavaDoc ID = fnord.getID();
1039                    text.append("\n " + ID + " (not DXAttribute)" );
1040                    try
1041                    {
1042                        if (fnord.size() == 0)
1043                            text.append(" " + " (empty) ");
1044                        else
1045                        {
1046                            Enumeration vals = fnord.getAll();
1047
1048                            while (vals.hasMoreElements())
1049                            {
1050                                Object JavaDoc val = vals.nextElement();
1051                                String JavaDoc fnordel = (val==null)?"*null*":val.toString();
1052                                text.append(" '" + fnordel + "'");
1053                            }
1054                        }
1055                    }
1056                    catch (NamingException e)
1057                    {
1058                        log.log(Level.WARNING, "whoops: Naming Exception reading " + ID, e);
1059                    }
1060                }
1061            }
1062        }
1063        return text.toString();
1064    }
1065
1066    public void print() { print(null); }
1067
1068    public void print(String JavaDoc msg)
1069    {
1070        if (msg!=null) System.out.println(msg);
1071        printAttributes(this);
1072    }
1073
1074    public static void printAttributes(Attributes a)
1075    {
1076        if (a==null) System.out.println("null attributes set");
1077        NamingEnumeration allatts = a.getAll();
1078
1079        printAttributeList(allatts);
1080    }
1081
1082    public static void printAttributeList(NamingEnumeration en)
1083    {
1084        while (en.hasMoreElements())
1085        {
1086            Attribute fnord = (Attribute) en.nextElement();
1087            if (fnord == null)
1088                log.warning("bizarre null attribute in element list");
1089            else
1090            {
1091                String JavaDoc ID = fnord.getID();
1092                System.out.println(" " + ID);
1093                try
1094                {
1095                    Enumeration vals = fnord.getAll();
1096
1097                    while (vals.hasMoreElements())
1098                    {
1099                        Object JavaDoc val = vals.nextElement();
1100                        String JavaDoc fnordel = (val==null)?"*null*":val.toString();
1101                        System.out.println(" " + fnordel);
1102                    }
1103                }
1104                catch (NamingException e)
1105                {
1106                    log.log(Level.WARNING, "whoops: Naming Exception reading " + ID, e);
1107                }
1108            }
1109        }
1110    }
1111
1112
1113
1114    /**
1115     * <p>Returns the set of attributes that are in the newSet, but
1116     * not in the old set, excluding the rdnclass).</p>
1117     *
1118     * <p>It also returns partial attributes that contain values that
1119     * are in the newSet but not in the oldSet, when either a)
1120     * either attribute has a size larger than one, or b) the attribute has
1121     * a distinguished value.</p>
1122     *
1123     * <p>The logic for case a) is that we can use adds and deletes on
1124     * individual attributes to simulate a 'replace' operation, but we
1125     * need to avoid doing this for single valued attributes (for which
1126     * we *always* use replace). So if Attribute A has values {1,2,3},
1127     * and is changed to A' {1,3,5,6}, this method returns an 'add' {5,6}.</p>
1128     *
1129     * <p>The logic for case b) is that we cannot use 'replace' on an
1130     * attribute with a naming value in it, so we *must* use adds and
1131     * deletes - and we'll have to take our chances that it is not
1132     * single valued. (Possibly later on we can check this from schema).
1133     * This method cannot change the distinguished value, but produces an
1134     * 'add' for any other changed. So if, for entry cn=fred, Attribute cn
1135     * has values {fred,erik}
1136     * and cn' has values {fred, nigel], this method produces an 'add' {nigel}.</p>
1137     *
1138     *
1139     * @param newRDN the new RDN of the entry that is being created
1140     * (usually this is the same as the RDN of the original entry)
1141     * May be null if it is not to be checked.
1142     * @param oldSet the set of already existing attributes
1143     * @param newSet the set of new attributes to test
1144     * @return attributes that must be added to the object.
1145     */

1146
1147    public static DXAttributes getAdditionSet(RDN newRDN, Attributes oldSet, Attributes newSet)
1148        throws NamingException
1149    {
1150        DXAttributes additionSet = new DXAttributes();
1151        NamingEnumeration listOfNewAttributes = newSet.getAll();
1152
1153        while (listOfNewAttributes.hasMore())
1154        {
1155            Attribute newAtt = (Attribute)listOfNewAttributes.next();
1156
1157            String JavaDoc attributeName = newAtt.getID();
1158
1159            boolean isNamingAttribute = newRDN.contains(attributeName);
1160
1161            Attribute oldAtt = oldSet.get(attributeName);
1162
1163            if (! emptyAtt(newAtt)) // don't add empty atts!
1164
{
1165                /*
1166                 * Check for simple "whole attribute" adds
1167                 * if the old Att is null, or only had null values add it.
1168                 * (It was probably created by the browser,
1169                 * and doesn't exist in the database)
1170                 */

1171
1172                if ((isNamingAttribute == false) && (oldAtt == null || emptyAtt(oldAtt)))
1173                {
1174                    additionSet.put(newAtt);
1175                }
1176
1177                /*
1178                 * Check for attribute values that have been changed in attributes
1179                 * that are larger than 1, or that are naming attributes
1180                 */

1181// TODO: - clean this up for DSML etc. ...
1182

1183                else if (isNamingAttribute || (oldAtt.size() > 1 || newAtt.size() > 1 ))
1184                {
1185                    DXNamingEnumeration valuesToAdd = getMissingValues(oldAtt.getAll(), newAtt.getAll());
1186
1187                    // check for distinguished value, and ignore it...
1188
if (isNamingAttribute)
1189                        removeAnyDistinguishedValues(newRDN, attributeName, valuesToAdd);
1190
1191                    if (valuesToAdd.size()>0)
1192                        additionSet.put(new DXAttribute(attributeName, valuesToAdd));
1193                }
1194            }
1195        }
1196        return additionSet;
1197    }
1198
1199
1200
1201    /**
1202     * <p>Returns all single valued attributes whose values have changed -
1203     * that is, exist in both the new set and the old set, but have different values.
1204     * Note that this function ignores the naming attribute.</p>
1205     *
1206     * <p>We need this function to cope with mandatory single valued attributes
1207     * (otherwise we could just use add and delete).</p>
1208     *
1209     * <p>All other attribute combinations are handled by attribute value adds and
1210     * deletes. (This is slightly more efficient, and is required to modify
1211     * non-distinguished values of the naming attribute anyway).</p>
1212     *
1213     * @param newRDN the RDN of the newer entry.
1214     * (usually this is the same as the RDN of the original entry)
1215     * May be null if it is not to be checked.
1216     * @param oldSet the set of already existing attributes
1217     * @param newSet the set of new attributes to test
1218
1219     * @return attributes that require updating
1220     */

1221
1222    // LOGIC NOTE - only replace attributes that are single valued (old & new) and NOT naming values.
1223

1224    public static DXAttributes getReplacementSet(RDN newRDN, Attributes oldSet, Attributes newSet)
1225        throws NamingException
1226    {
1227        DXAttributes replacementSet = new DXAttributes();
1228
1229        NamingEnumeration listOfNewAttributes = newSet.getAll();
1230
1231        while (listOfNewAttributes.hasMore()) // find changed attributes
1232
{
1233            Attribute newAtt = (Attribute)listOfNewAttributes.next();
1234
1235            if (newAtt != null && newAtt.size() == 1) // only consider a single valued new attribute
1236
{
1237                String JavaDoc attributeName = newAtt.getID();
1238
1239                if (newRDN.contains(attributeName) == false) // skip any naming attributes
1240
{
1241                    Attribute oldAtt = oldSet.get(attributeName);
1242
1243                    if (oldAtt != null && oldAtt.size() == 1) // only look at changed single valued attributes.
1244
{
1245                        // if a single valued attribute has changed, make it a 'replace' op.
1246
if (attributesEqual(newAtt, oldAtt)==false)
1247                            replacementSet.put(newAtt);
1248                    }
1249                }
1250            }
1251        }
1252        return replacementSet;
1253    }
1254
1255
1256
1257    /**
1258     * <p>Returns the set of attributes that are in the oldSet, but
1259     * not in the new set, and thus must be deleted. </p>
1260     *
1261     * <p>It also returns the set of attribute *values* that are in
1262     * the old set, but not in the new set. E.g. if attribute A
1263     * has values {1,2,3,4}, and the new attribute A' has {1,4,6,7},
1264     * this returns {2,3} for deletion.</p>
1265     *
1266     * <p>This method will ignore naming values, but will correctly
1267     * handle other values of the naming attribute.</p>
1268     *
1269     * @param newRDN the RDN of the newer entry.
1270     * (usually this is the same as the RDN of the original entry).
1271     * May be null if it is not to be checked.
1272     * @param oldSet the set of already existing attributes
1273     * @param newSet the set of new attributes to test
1274     */

1275
1276    public static DXAttributes getDeletionSet(RDN newRDN, Attributes oldSet, Attributes newSet)
1277        throws NamingException
1278    {
1279        DXAttributes deletionSet = new DXAttributes();
1280
1281        NamingEnumeration listOfOldAttributes = oldSet.getAll();
1282
1283        while (listOfOldAttributes.hasMore())
1284        {
1285            Attribute oldAtt = (Attribute)listOfOldAttributes.next();
1286
1287            if (! emptyAtt(oldAtt)) // don't delete empty atts!
1288
{
1289                String JavaDoc attributeName = oldAtt.getID();
1290
1291                boolean isNamingAttribute = newRDN.contains(attributeName);
1292
1293                Attribute newAtt = newSet.get(attributeName);
1294
1295                if (newAtt == null)
1296                    newAtt = new DXAttribute(attributeName);
1297
1298                /*
1299                 * Check for simple "whole attribute" deletes
1300                 */

1301
1302                if (emptyAtt(newAtt) && !isNamingAttribute)
1303                {
1304                    deletionSet.put(newAtt);
1305                }
1306
1307                /*
1308                 * Check for attribute values that have been dropped, in attributes
1309                 * that are larger than 1
1310                 */

1311
1312                else if (isNamingAttribute || oldAtt.size() > 1 || newAtt.size() > 1 )
1313                {
1314                    DXNamingEnumeration valuesToDelete = getMissingValues(newAtt.getAll(), oldAtt.getAll());
1315                    // check for distinguished value, and ignore it...
1316
if (isNamingAttribute)
1317                        removeAnyDistinguishedValues(newRDN, attributeName, valuesToDelete);
1318
1319                    if (valuesToDelete.size()>0)
1320                        deletionSet.put(new DXAttribute(attributeName, valuesToDelete));
1321                }
1322            }
1323        }
1324        return deletionSet;
1325    }
1326
1327
1328
1329    /**
1330     * Checks two 'Attribute' objects for equality.
1331     * XXX - should this be moved to DXAttribute?
1332     */

1333
1334    private static boolean attributesEqual(Attribute a, Attribute b)
1335        throws NamingException
1336    {
1337        // sanity checks...
1338
if (a == null && b == null) return true;
1339        if (a == null || b == null) return false;
1340        if (a.size() == 0 && b.size() == 0) return true;
1341        if (a.size() != b.size()) return false;
1342        if (a.get() == null && b.get() == null) return true;
1343        if (a.get() == null || b.get() == null) return false;
1344        if (a.getID().equalsIgnoreCase(b.getID())==false) return false;
1345
1346
1347        try
1348        {
1349            Object JavaDoc[] A = CBArray.enumerationToArray(a.getAll());
1350            Object JavaDoc[] B = CBArray.enumerationToArray(b.getAll());
1351            return CBArray.isUnorderedEqual(A,B);
1352        }
1353        catch (NamingException e)
1354        {
1355            log.log(Level.WARNING, "Naming Exception testing attributes " + a.getID() + " & " + b.getID() + " in DXAttributes:attributesEqual()", e);
1356        }
1357        return false; // only here if error occurred.
1358
}
1359
1360    /**
1361     * Checks whether two 'Attributes' objects are equivalent (including naming attributes, if any).
1362     */

1363
1364    public static boolean attributesEqual(Attributes a, Attributes b)
1365    {
1366        if (a == null && b == null) return true;
1367        if (a == null || b == null) return false;
1368        return a.equals(b);
1369    }
1370
1371    public boolean equals(Object JavaDoc o)
1372    {
1373        if (o == null) return false;
1374
1375        try
1376        {
1377            if (o instanceof Attributes)
1378                return this.equals((Attributes) o);
1379        }
1380        catch (NamingException e)
1381        {
1382            return false; // suppress exception :-(...
1383
}
1384
1385        return false;
1386    }
1387
1388    public boolean equals(Attributes atts) throws NamingException
1389    {
1390        // some quick and simple equality checks
1391
if (atts == null) return false;
1392        if (size() == 0 && atts.size() == 0) return true;
1393        if (size() != atts.size()) return false;
1394
1395        // at this stage, we have equality candidates - two equally sized attributes...
1396

1397        NamingEnumeration testAtts = getAll();
1398        while (testAtts.hasMore()) // find changed attributes
1399
{
1400            Attribute testAtt = (Attribute)testAtts.next();
1401            String JavaDoc ID = testAtt.getID();
1402
1403            Attribute bAtt = atts.get(ID);
1404
1405            if ( emptyAtt(bAtt) ^ emptyAtt(testAtt) ) return false;
1406
1407            if (attributesEqual(testAtt, bAtt) == false) return false;
1408        }
1409
1410        // if we're here, the attributes must be equal!
1411

1412        return true;
1413    }
1414
1415    /**
1416     * Checks the naming enumeration and removes any distinguished values that
1417     * occur in the RDN.
1418     * @param newRDN the rdn to check for values in
1419     * @param attributeName the name of the attribute (potentially) in the RDN
1420     * @param values the list of values to potentially remove the distinguished value from
1421     */

1422
1423    private static void removeAnyDistinguishedValues(RDN newRDN, String JavaDoc attributeName, DXNamingEnumeration values)
1424    {
1425        String JavaDoc distinguishedValue = newRDN.getRawVal(attributeName);
1426        values.remove(distinguishedValue); // remove dist. val. (if it is there)
1427
}
1428
1429
1430    /**
1431     * Returns all the values in B that are missing in A. (i.e. return B minus (B union A)).
1432     * @param A the list of values to exclude.
1433     * @param B the list of values to include.
1434     * @return all elements of B not found in A.
1435     */

1436
1437    static private DXNamingEnumeration getMissingValues(NamingEnumeration A, NamingEnumeration B)
1438        throws NamingException
1439    {
1440        DXNamingEnumeration ret = new DXNamingEnumeration(B);
1441
1442        if (A == null) return ret;
1443
1444        while (A.hasMore())
1445        {
1446            ret.remove(A.next());
1447        }
1448        return ret;
1449    }
1450
1451// public DirContext getSchema() { return schema; }
1452

1453    public String JavaDoc[] toIDStringArray()
1454    {
1455        DXNamingEnumeration ret = new DXNamingEnumeration(getIDs());
1456        return ret.toStringArray();
1457    }
1458
1459    /**
1460     * A quick test - do we have any numeric OIDs
1461     *
1462     */

1463    public boolean hasOIDs()
1464    {
1465        return (attOIDs.size()>0);
1466    }
1467
1468    /**
1469     * Utility ftn: checks that an attribute is not null and has at least
1470     * one non-null value.
1471     */

1472    public static boolean emptyAtt(Attribute att)
1473    {
1474         return DXAttribute.isEmpty(att);
1475    }
1476
1477
1478
1479}
Popular Tags