KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ca > commons > jndi > SchemaOps


1 /**
2  *
3  *
4  * Author: Chris Betts
5  * Date: 28/11/2002 / 17:02:19
6  */

7 package com.ca.commons.jndi;
8
9 import javax.naming.directory.*;
10 import javax.naming.NamingException JavaDoc;
11 import javax.naming.NamingEnumeration JavaDoc;
12 import java.util.*;
13
14 import java.util.logging.*;
15
16
17 /**
18  * <p>The Schema Ops provides a number of convenience methods for accessing schema information.</p>
19  * <p/>
20  * <p>In addition, it allows the schema to be accessed even if a particular jndi provider
21  * does not provide a getSchemaOps() call, by attempting to access a subschema subentry call directly.
22  * (e.g. for a DSML directory).</p>
23  */

24 public class SchemaOps
25 {
26     protected DirContext ctx = null; // the root jndi context used for all directory queries.
27
Attributes rawSchemaAttributes = null; // what is read from the directory subschema subentry
28
private String JavaDoc schemaRoot = null; // the entry in the directory that holds the schema attribute set - usually cn=schema
29
HashMap oids = new HashMap(1000); // a fast lookup list of oids to descriptive names.
30

31     final static String JavaDoc subschemaAttributeName = "subschemaSubentry";
32
33     private final static Logger log = Logger.getLogger(SchemaOps.class.getName());
34
35     public static final String JavaDoc SCHEMA_FAKE_OBJECT_CLASS_NAME = "synthetic_JXplorer_schema_object";
36     private static final BasicAttribute schemaObjectClassAttribute = new BasicAttribute("objectClass");
37
38     private ArrayList fullObjectClassArray = null; // cache the complete list of object classes; it gets used a bit.
39

40     private ArrayList fullAttributeNameArray = null; // cache the complete list of attribute names; it gets used a bit.
41

42     static
43     {
44         schemaObjectClassAttribute.add("top");
45         schemaObjectClassAttribute.add(SCHEMA_FAKE_OBJECT_CLASS_NAME);
46     }
47
48     /**
49      * <p>Initialise the SchemaOps object, and read the
50      * full schema from the directory. Note that this is a very
51      * expensive operation, that can involve downloading 10-100k from
52      * the directory!</p>
53      */

54     public SchemaOps(DirContext context)
55             throws NamingException JavaDoc
56     {
57         ctx = context;
58         if (ctx == null)
59         {
60             setSchemaRoot("cn=schema"); // default - but doesn't do anything since ctx==null
61
loadOIDs(); // load static OIDs - for testing, and future off-line schema functionality
62

63             return;
64         }
65
66         log.finest("Reading Schema info from directory context");
67
68         setSchemaRoot(getSchemaRoot());
69         rawSchemaAttributes = getRawSchema();
70
71 //printRawSchema();
72
//System.out.println("\n\n\n\n");
73

74         loadOIDs();
75     }
76
77     /**
78      * <p>Initialise the SchemaOps object from an Attributes list.
79      * This constructor is intended for testing.
80      * </p>
81      */

82     // package visibility for testing
83

84     public SchemaOps(Attributes rawSchemaAtts)
85             // throws NamingException
86
{
87         ctx = null;
88         rawSchemaAttributes = rawSchemaAtts;
89
90         setSchemaRoot("cn=schema");
91         loadOIDs();
92         log.finest("SCHEMA ROOTX:" + getSchemaRoot());
93     }
94
95     /**
96      * This attempts to translate an OID into a descriptive name. If it cannot
97      * find a translation, it will return the oid unchanged.
98      *
99      * @param oid the 'dot form' OID, e.g. "2.5.6.2" or whatever
100      * @return the human readable string form of the OID (e.g. "country")
101      */

102     public String JavaDoc translateOID(String JavaDoc oid)
103     {
104         if (oids.containsKey(oid))
105             return (String JavaDoc) oids.get(oid);
106         else
107             return oid;
108     }
109
110     /**
111      * setup the global list of oids vs readable strings, by loading the schema and using the rfc defaults
112      */

113     protected void loadOIDs()
114     {
115         loadOIDsFromSchema();
116
117         loadStaticOIDs();
118     }
119
120     /**
121      * Iterates through the schema finding OIDs and their corresponding descriptions.
122      */

123     protected void loadOIDsFromSchema()
124     {
125         if (rawSchemaAttributes == null)
126             return;
127
128         try
129         {
130             NamingEnumeration JavaDoc rawSchemaAtts = rawSchemaAttributes.getAll();
131             while (rawSchemaAtts.hasMoreElements())
132             {
133                 Attribute rawSchemaAtt = (Attribute) rawSchemaAtts.nextElement();
134                 NamingEnumeration JavaDoc values = rawSchemaAtt.getAll();
135
136                 while (values.hasMoreElements())
137                 {
138                     String JavaDoc value = (String JavaDoc) values.nextElement();
139                     if (value.indexOf('(') == -1)
140                         log.finest("skipping non schema attribute: " + rawSchemaAtt.getID() + ":" + value);
141                     else
142                         oids.put(getOID(value), getFirstName(value));
143                 }
144             }
145         }
146         catch (NamingException JavaDoc e)
147         {
148             log.log(Level.WARNING, "Unable to read schema oids: ", e);
149         }
150     }
151
152
153     protected void loadStaticOIDs()
154     {
155         // a quick pick of common syntaxes for Active Directory support
156
// (and other servers that don't publish syntax descriptions)
157
// taken from rfc 2252
158

159         oids.put("1.3.6.1.4.1.1466.115.121.1.1", "ACI Item");
160         oids.put("1.3.6.1.4.1.1466.115.121.1.2", "Access Point");
161         oids.put("1.3.6.1.4.1.1466.115.121.1.3", "Attribute Type Description");
162         oids.put("1.3.6.1.4.1.1466.115.121.1.4", "Audio");
163         oids.put("1.3.6.1.4.1.1466.115.121.1.5", "Binary");
164         oids.put("1.3.6.1.4.1.1466.115.121.1.6", "Bit String");
165         oids.put("1.3.6.1.4.1.1466.115.121.1.7", "Boolean");
166         oids.put("1.3.6.1.4.1.1466.115.121.1.8", "Certificate");
167         oids.put("1.3.6.1.4.1.1466.115.121.1.9", "Certificate List");
168         oids.put("1.3.6.1.4.1.1466.115.121.1.10", "Certificate Pair");
169         oids.put("1.3.6.1.4.1.1466.115.121.1.11", "Country String");
170         oids.put("1.3.6.1.4.1.1466.115.121.1.12", "DN");
171         oids.put("1.3.6.1.4.1.1466.115.121.1.13", "Data Quality Syntax");
172         oids.put("1.3.6.1.4.1.1466.115.121.1.14", "Delivery Method");
173         oids.put("1.3.6.1.4.1.1466.115.121.1.15", "Directory String");
174         oids.put("1.3.6.1.4.1.1466.115.121.1.16", "DIT Content Rule Description");
175         oids.put("1.3.6.1.4.1.1466.115.121.1.17", "DIT Structure Rule Description");
176         oids.put("1.3.6.1.4.1.1466.115.121.1.18", "DL Submit Permission");
177         oids.put("1.3.6.1.4.1.1466.115.121.1.19", "DSA Quality Syntax");
178         oids.put("1.3.6.1.4.1.1466.115.121.1.20", "DSE Type");
179         oids.put("1.3.6.1.4.1.1466.115.121.1.21", "Enhanced Guide");
180         oids.put("1.3.6.1.4.1.1466.115.121.1.22", "Facsimile Telephone Number");
181         oids.put("1.3.6.1.4.1.1466.115.121.1.23", "Fax");
182         oids.put("1.3.6.1.4.1.1466.115.121.1.24", "Generalized Time");
183         oids.put("1.3.6.1.4.1.1466.115.121.1.25", "Guide");
184         oids.put("1.3.6.1.4.1.1466.115.121.1.26", "IA5 String");
185         oids.put("1.3.6.1.4.1.1466.115.121.1.27", "INTEGER");
186         oids.put("1.3.6.1.4.1.1466.115.121.1.28", "JPEG");
187         oids.put("1.3.6.1.4.1.1466.115.121.1.54", "LDAP Syntax Description");
188         oids.put("1.3.6.1.4.1.1466.115.121.1.56", "LDAP Schema Definition");
189         oids.put("1.3.6.1.4.1.1466.115.121.1.57", "LDAP Schema Description");
190         oids.put("1.3.6.1.4.1.1466.115.121.1.29", "Master And Shadow Access Points");
191         oids.put("1.3.6.1.4.1.1466.115.121.1.30", "Matching Rule Description");
192         oids.put("1.3.6.1.4.1.1466.115.121.1.31", "Matching Rule Use Description");
193         oids.put("1.3.6.1.4.1.1466.115.121.1.32", "Mail Preference");
194         oids.put("1.3.6.1.4.1.1466.115.121.1.33", "MHS OR Address");
195         oids.put("1.3.6.1.4.1.1466.115.121.1.55", "Modify Rights");
196         oids.put("1.3.6.1.4.1.1466.115.121.1.34", "Name And Optional UID");
197         oids.put("1.3.6.1.4.1.1466.115.121.1.35", "Name Form Description");
198         oids.put("1.3.6.1.4.1.1466.115.121.1.36", "Numeric String");
199         oids.put("1.3.6.1.4.1.1466.115.121.1.37", "Object Class Description");
200         oids.put("1.3.6.1.4.1.1466.115.121.1.40", "Octet String");
201         oids.put("1.3.6.1.4.1.1466.115.121.1.38", "OID");
202         oids.put("1.3.6.1.4.1.1466.115.121.1.39", "Other Mailbox");
203         oids.put("1.3.6.1.4.1.1466.115.121.1.41", "Postal Address");
204         oids.put("1.3.6.1.4.1.1466.115.121.1.42", "Protocol Information");
205         oids.put("1.3.6.1.4.1.1466.115.121.1.43", "Presentation Address");
206         oids.put("1.3.6.1.4.1.1466.115.121.1.44", "Printable String");
207         oids.put("1.3.6.1.4.1.1466.115.121.1.58", "Substring Assertion");
208         oids.put("1.3.6.1.4.1.1466.115.121.1.45", "Subtree Specification");
209         oids.put("1.3.6.1.4.1.1466.115.121.1.46", "Supplier Information");
210         oids.put("1.3.6.1.4.1.1466.115.121.1.47", "Supplier Or Consumer");
211         oids.put("1.3.6.1.4.1.1466.115.121.1.48", "Supplier And Consumer");
212         oids.put("1.3.6.1.4.1.1466.115.121.1.49", "Supported Algorithm");
213         oids.put("1.3.6.1.4.1.1466.115.121.1.50", "Telephone Number");
214         oids.put("1.3.6.1.4.1.1466.115.121.1.51", "Teletex Terminal Identifier");
215         oids.put("1.3.6.1.4.1.1466.115.121.1.52", "Telex Number");
216         oids.put("1.3.6.1.4.1.1466.115.121.1.53", "UTC Time");
217
218         oids.put("1.3.6.1.4.1.1466.115.121.1.1", "ACI Item");
219         oids.put("1.3.6.1.4.1.1466.115.121.1.2", "Access Point");
220         oids.put("1.3.6.1.4.1.1466.115.121.1.3", "Attribute Type Description");
221         oids.put("1.3.6.1.4.1.1466.115.121.1.4", "Audio");
222         oids.put("1.3.6.1.4.1.1466.115.121.1.5", "Binary");
223         oids.put("1.3.6.1.4.1.1466.115.121.1.6", "Bit String");
224         oids.put("1.3.6.1.4.1.1466.115.121.1.7", "Boolean");
225         oids.put("1.3.6.1.4.1.1466.115.121.1.8", "Certificate");
226         oids.put("1.3.6.1.4.1.1466.115.121.1.9", "Certificate List");
227         oids.put("1.3.6.1.4.1.1466.115.121.1.10", "Certificate Pair");
228         oids.put("1.3.6.1.4.1.1466.115.121.1.11", "Country String");
229         oids.put("1.3.6.1.4.1.1466.115.121.1.12", "DN");
230         oids.put("1.3.6.1.4.1.1466.115.121.1.13", "Data Quality Syntax");
231         oids.put("1.3.6.1.4.1.1466.115.121.1.14", "Delivery Method");
232         oids.put("1.3.6.1.4.1.1466.115.121.1.15", "Directory String");
233         oids.put("1.3.6.1.4.1.1466.115.121.1.16", "DIT Content Rule Description");
234         oids.put("1.3.6.1.4.1.1466.115.121.1.17", "DIT Structure Rule Description");
235         oids.put("1.3.6.1.4.1.1466.115.121.1.18", "DL Submit Permission");
236         oids.put("1.3.6.1.4.1.1466.115.121.1.19", "DSA Quality Syntax");
237         oids.put("1.3.6.1.4.1.1466.115.121.1.20", "DSE Type");
238         oids.put("1.3.6.1.4.1.1466.115.121.1.21", "Enhanced Guide");
239         oids.put("1.3.6.1.4.1.1466.115.121.1.22", "Facsimile Telephone Number");
240         oids.put("1.3.6.1.4.1.1466.115.121.1.23", "Fax");
241         oids.put("1.3.6.1.4.1.1466.115.121.1.24", "Generalized Time");
242         oids.put("1.3.6.1.4.1.1466.115.121.1.25", "Guide");
243         oids.put("1.3.6.1.4.1.1466.115.121.1.26", "IA5 String");
244         oids.put("1.3.6.1.4.1.1466.115.121.1.27", "INTEGER");
245         oids.put("1.3.6.1.4.1.1466.115.121.1.28", "JPEG");
246         oids.put("1.3.6.1.4.1.1466.115.121.1.54", "LDAP Syntax Description");
247         oids.put("1.3.6.1.4.1.1466.115.121.1.56", "LDAP Schema Definition");
248         oids.put("1.3.6.1.4.1.1466.115.121.1.57", "LDAP Schema Description");
249         oids.put("1.3.6.1.4.1.1466.115.121.1.29", "Master And Shadow Access Points");
250         oids.put("1.3.6.1.4.1.1466.115.121.1.30", "Matching Rule Description");
251         oids.put("1.3.6.1.4.1.1466.115.121.1.31", "Matching Rule Use Description");
252         oids.put("1.3.6.1.4.1.1466.115.121.1.32", "Mail Preference");
253         oids.put("1.3.6.1.4.1.1466.115.121.1.33", "MHS OR Address");
254         oids.put("1.3.6.1.4.1.1466.115.121.1.55", "Modify Rights");
255         oids.put("1.3.6.1.4.1.1466.115.121.1.34", "Name And Optional UID");
256         oids.put("1.3.6.1.4.1.1466.115.121.1.35", "Name Form Description");
257         oids.put("1.3.6.1.4.1.1466.115.121.1.36", "Numeric String");
258         oids.put("1.3.6.1.4.1.1466.115.121.1.37", "Object Class Description");
259         oids.put("1.3.6.1.4.1.1466.115.121.1.40", "Octet String");
260         oids.put("1.3.6.1.4.1.1466.115.121.1.38", "OID");
261         oids.put("1.3.6.1.4.1.1466.115.121.1.39", "Other Mailbox");
262         oids.put("1.3.6.1.4.1.1466.115.121.1.41", "Postal Address");
263         oids.put("1.3.6.1.4.1.1466.115.121.1.42", "Protocol Information");
264         oids.put("1.3.6.1.4.1.1466.115.121.1.43", "Presentation Address");
265         oids.put("1.3.6.1.4.1.1466.115.121.1.44", "Printable String");
266         oids.put("1.3.6.1.4.1.1466.115.121.1.58", "Substring Assertion");
267         oids.put("1.3.6.1.4.1.1466.115.121.1.45", "Subtree Specification");
268         oids.put("1.3.6.1.4.1.1466.115.121.1.46", "Supplier Information");
269         oids.put("1.3.6.1.4.1.1466.115.121.1.47", "Supplier Or Consumer");
270         oids.put("1.3.6.1.4.1.1466.115.121.1.48", "Supplier And Consumer");
271         oids.put("1.3.6.1.4.1.1466.115.121.1.49", "Supported Algorithm");
272         oids.put("1.3.6.1.4.1.1466.115.121.1.50", "Telephone Number");
273         oids.put("1.3.6.1.4.1.1466.115.121.1.51", "Teletex Terminal Identifier");
274         oids.put("1.3.6.1.4.1.1466.115.121.1.52", "Telex Number");
275         oids.put("1.3.6.1.4.1.1466.115.121.1.53", "UTC Time");
276     }
277
278     /**
279      * Utility method to print a syntax subtree...
280      *
281      * @param syntaxRoot the root of the syntax tree to print out, e.g. "", "objectClasses"
282      * @throws NamingException
283      */

284     protected void debugPrint(String JavaDoc syntaxRoot)
285             throws NamingException JavaDoc
286     {
287         System.out.println("---DEBUG PRINT---");
288         System.out.println("schema root: " + getSchemaRoot());
289         if (syntaxRoot.length() > 0 && syntaxRoot.startsWith("schema=") == false)
290             syntaxRoot = "schema=" + syntaxRoot;
291
292         tabbedDebugPrint(syntaxRoot, "");
293         System.out.println("-----------------");
294     }
295
296     protected void tabbedDebugPrint(String JavaDoc syntaxElement, String JavaDoc indent)
297             throws NamingException JavaDoc
298     {
299         System.out.println(indent + syntaxElement);
300         Attributes entry = getAttributes(syntaxElement);
301         System.out.println(indent + "--==< " + syntaxElement + ">==--");
302         if (entry == null)
303             System.out.println(indent + " ** NULL ENTRY **");
304         else
305         {
306             NamingEnumeration JavaDoc atts = entry.getAll();
307             while (atts.hasMoreElements())
308             {
309                 Attribute att = (Attribute) atts.nextElement();
310                 System.out.println(indent + "att " + att.getID());
311                 NamingEnumeration JavaDoc values = att.getAll();
312                 while (values.hasMoreElements())
313                     System.out.println(indent + " " + values.nextElement().toString());
314             }
315         }
316         System.out.println(indent + "-");
317
318         ArrayList list = listEntryNames(syntaxElement);
319         if (list == null)
320         {
321             return;
322         }
323         for (int i = 0; i < list.size(); i++)
324         {
325             String JavaDoc nextLevel = syntaxElement;
326             if (nextLevel.length() > 0)
327                 nextLevel = "," + nextLevel;
328             nextLevel = "schema=" + list.get(i) + nextLevel;
329             tabbedDebugPrint(nextLevel, "\t" + indent);
330         }
331     }
332
333     public void printRawSchema()
334     {
335         if (rawSchemaAttributes == null)
336         {
337             System.out.println("NO SCHEMA READ!");
338             return;
339         }
340         try
341         {
342             System.out.println("---RAW SCHEMA---");
343             Enumeration JavaDoc attEnum = rawSchemaAttributes.getAll();
344             while (attEnum.hasMoreElements())
345             {
346                 Attribute att = (Attribute) attEnum.nextElement();
347                 String JavaDoc ID = att.getID();
348                 Enumeration JavaDoc vals = att.getAll();
349                 while (vals.hasMoreElements())
350                     System.out.println(ID + " : " + vals.nextElement());
351             }
352         }
353         catch (NamingException JavaDoc e)
354         {
355             System.out.println("error printing raw schema:" + e);
356         }
357     }
358
359     /**
360      * <p>This returns the raw schema straight from the
361      * directory. Only use this if you <i>really</i>
362      * know what you're doing - see getAttributes() for
363      * an explanation.</p>
364      */

365
366     // ...Your schema caching code here...
367
public Attributes getRawSchema()
368             throws NamingException JavaDoc
369     {
370         String JavaDoc rawSchemaRoot = getSchemaRoot();
371         log.finest("reading raw schema from " + rawSchemaRoot);
372         // need to explicitly list operational attributes... (although eTrust Directory doesn't need this, others do)
373
Attributes rawSchema = null;
374
375         if (ctx != null)
376             rawSchema = ctx.getAttributes(rawSchemaRoot, new String JavaDoc[]{"attributeTypes", "objectClasses", "matchingRules", "ldapSyntaxes", "*"});
377
378         if (rawSchema == null)
379         {
380             log.warning("null schema read - returning empty schema list.");
381             rawSchema = new BasicAttributes(); // return empty atts rather than null, to cut down on 'check for null' code...
382
}
383         else
384         {
385             if (rawSchema.size() == 0) // may be a type of directory that requires explicit listing of schema objects...
386
{
387                 log.warning("Unable to read schema details from directory.");
388                 rawSchema = new BasicAttributes(); // give up - set to empty :-(
389
return rawSchema;
390             }
391
392             log.finest("some schema read...");
393
394
395             rawSchema.remove("objectClass"); // Paranoid. Yes.
396
rawSchema.remove("oc"); // But very thorough.
397
rawSchema.remove("objectclass");
398             String JavaDoc nameAttribute = rawSchemaRoot.substring(0, rawSchemaRoot.indexOf('='));
399             rawSchema.remove(nameAttribute);
400         }
401
402         return rawSchema;
403     }
404
405     private void setSchemaRoot(String JavaDoc schema)
406     {
407         schemaRoot = schema;
408     }
409
410     /**
411      * returns the root DN of the schema subentry as a string.
412      *
413      * @return the schema subentry (i.e. something like 'cn=schema')
414      */

415
416     public String JavaDoc getSchemaRoot()
417     {
418         if (schemaRoot != null)
419             return schemaRoot;
420
421         if (ctx != null)
422         {
423             try
424             {
425                 log.finest("start get schema root call");
426                 Attributes SSSE;
427                 SSSE = ctx.getAttributes("", new String JavaDoc[]{subschemaAttributeName});
428                 if (SSSE != null && SSSE.get(subschemaAttributeName) != null)
429                     schemaRoot = (String JavaDoc) SSSE.get(subschemaAttributeName).get();
430
431                 log.finest("schema root read as being: '" + String.valueOf(schemaRoot) + "'");
432             }
433             catch (NamingException JavaDoc e)
434             {
435                 // revert to using good old 'cn=schema' ...
436
}
437         }
438
439         if (schemaRoot == null)
440         {
441             log.finest("forcing value of schema root to 'cn=schema', since can't read subschema attribute name");
442             schemaRoot = "cn=schema"; // default: this is what it usually is anyway... :-)
443
}
444         return schemaRoot;
445     }
446
447     /**
448      * <p>HERE BE DRAGONS</p>
449      * <p/>
450      * <p>Similarly to jndi, we impose a structure on the raw schema entry. In the
451      * directory, there is a single schema entry, with a number of multi-valued attributes, e.g.
452      * one attribute for 'objectClasses', one for 'ldapSyntaxes'. Each attribute value has
453      * a wretched format, e.g. an objectClasses value might be:
454      * " ( 0.9.2342.19200300.100.4.4 NAME 'newPilotPerson' SUP ( person )
455      * STRUCTURAL MAY ( uid $ mail $ drink $ roomNumber $ userClass $ homePhone $ homePostalAddress
456      * $ secretary $ personalTitle $ preferredDeliveryMethod $ businessCategory $ janetMailbox
457      * $ otherMailbox $ mobile $ pager $ organizationalStatus $ mailPreferenceOption $ personalSignature ) ) "</p>
458      * <p/>
459      * <p>We break this up by adding an extra layer of virtual attributes, turning the above attribute
460      * value into an attributes object (with a 'MAY' attribute, a 'NAME' attribute etc. ...
461      * (If you need the real deal, use getRawSchema()... but be sure
462      * you know what you're doing :-). </p>
463      *
464      * @param entryName the name of an entry, e.g. schema=cn,schema=attributeTypes
465      */

466     public Attributes getAttributes(String JavaDoc entryName)
467             throws NamingException JavaDoc
468     {
469         entryName = mangleEntryName(entryName);
470
471         BasicAttributes schemaAttributes = new BasicAttributes(); // add fake object class to keep some DXattributes routines happy...
472
schemaAttributes.put(schemaObjectClassAttribute);
473
474         if (entryName == null || entryName.length() == 0) // return synthetic entry for syntax root
475
{
476             schemaAttributes.put(subschemaAttributeName, schemaRoot);
477         }
478         else if (entryName.indexOf(',') == -1 && entryName.indexOf('/') == -1) // return synthetic entry for syntax type headings
479
{
480             String JavaDoc schemaType = entryName.substring(entryName.indexOf('=') + 1);
481             schemaAttributes.put("schemaType", schemaType);
482         }
483         else
484         {
485             schemaAttributes = getAttributesFromSchemaName(entryName);
486         }
487         return schemaAttributes;
488     }
489
490     /**
491      * <p>This method does three things; firstly, it trims the schema root from
492      * the name if present (e.g. trims ",cn=schema" in most cases).
493      * Secondly, it trims any ';binary' from the end of the string.
494      * Finally, it translates the pseudo-schema names jndi imposes on top of the
495      * standard ldap/X500 syntax names from rfc 2252/2256:</p>
496      * <ul>
497      * <li>AttributeDefinition => attributeTypes
498      * <li>ClassDefinition => objectClasses
499      * <li>SyntaxDefinition => ldapSyntaxes
500      * </ul>
501      *
502      * @param entryName : schema=ClassDefinition,cn=schema or schema=cn,schema=attributeTypes
503      * @return
504      */

505     protected String JavaDoc mangleEntryName(String JavaDoc entryName)
506     {
507         if (entryName.indexOf("ClassDefinition") > -1)
508             entryName = entryName.replaceAll("(ClassDefinition)", "objectClasses");
509         if (entryName.indexOf("SyntaxDefinition") > -1)
510             entryName = entryName.replaceAll("(SyntaxDefinition)", "ldapSyntaxes");
511         if (entryName.indexOf("AttributeDefinition") > -1)
512             entryName = entryName.replaceAll("(AttributeDefinition)", "attributeTypes");
513
514         // if it is an ldap name, restructure it to the schema=..., schema=... used in JX.
515
if (entryName.indexOf('/') > 0)
516         {
517             // trim ;binary for prettiness...
518
// TODO: Is this such a good idea?; it may mess up some directories such as slapd...
519
int pos = entryName.indexOf(";binary");
520             if (pos > -1)
521                 entryName = entryName.substring(0, pos);
522
523
524             int slashpos = entryName.indexOf('/');
525             String JavaDoc topLevelName = entryName.substring(0, slashpos);
526             String JavaDoc specificName = entryName.substring(++slashpos);
527             return "schema=" + specificName + ",schema=" + topLevelName;
528         }
529
530         // otherwise it is already a JX style name, so we clean it up a bit to get it in a standard form
531

532         // trim the schema root off the end, since we're only interested in the next level (objectclasses etc.)
533
int pos = entryName.indexOf(schemaRoot);
534         if (pos > 0) // not '-1' due to need to trim preceeding comma
535
entryName = entryName.substring(0, pos - 1);
536
537         // a little naughtily, we often use 'cn=schema' as shorthand for the schema root... get rid of that instead if it is different from the schema root (usually it isn't)
538
pos = entryName.indexOf("cn=schema");
539         if (pos > 0) // not '-1' due to need to trim preceeding comma
540
entryName = entryName.substring(0, pos - 1);
541
542
543         return entryName;
544     }
545
546     /**
547      * returns the specific schema entry name - eg 'cn' in 'schema=cn,schema=attributeTypes'
548      *
549      * @param entryName the schema entry DN in JX format - 'schema=cn,schema=attributeTypes'
550      * @return the specific schema name - e.g. 'cn'
551      * @throws NamingException
552      */

553     protected String JavaDoc getSpecificName(String JavaDoc entryName)
554             throws NamingException JavaDoc
555     {
556         int equalpos = entryName.indexOf('=') + 1;
557         int commapos = entryName.indexOf(',');
558         if (equalpos <= 0 || commapos == -1 || equalpos > commapos)
559             throw new NamingException JavaDoc("error parsing schema dn '" + entryName + "' ");
560
561         return entryName.substring(equalpos, commapos);
562     }
563
564     /**
565      * returns the specific schema entry name - eg 'cn' in 'schema=cn,schema=attributeTypes'
566      *
567      * @param entryName the schema entry DN in JX format - 'schema=cn,schema=attributeTypes'
568      * @return the specific schema name - e.g. 'cn'
569      * @throws NamingException
570      */

571
572     protected String JavaDoc getTypeName(String JavaDoc entryName)
573             throws NamingException JavaDoc
574     {
575         if (entryName.endsWith(",cn=schema"))
576             entryName = entryName.substring(0, entryName.length() - 10);
577
578         int equalpos = entryName.lastIndexOf('=') + 1;
579         return entryName.substring(equalpos);
580     }
581
582
583     /**
584      * This looks up the raw 'Attribute' corresponding to the full entryName. It then takes the value
585      * of that attribute, and creates a new Attributes object by parsing that value.
586      *
587      * @param entryName
588      * @throws NamingException
589      */

590     // package visibility for testing
591
protected BasicAttributes getAttributesFromSchemaName(String JavaDoc entryName) throws NamingException JavaDoc
592     {
593         if (rawSchemaAttributes == null)
594             return null;
595
596         entryName = mangleEntryName(entryName);
597
598         // do all this the slow way for now...
599
String JavaDoc schemaTypeName = getTypeName(entryName);
600         String JavaDoc specificName = getSpecificName(entryName);
601
602         Attribute schemaGroup = rawSchemaAttributes.get(schemaTypeName);
603
604         if (schemaGroup == null)
605         {
606             // some wierdo directories manage to get their cases muddled. This is a last-gasp attempt
607
// to read them by using an all-lower case version of the name.
608
schemaGroup = rawSchemaAttributes.get(schemaTypeName.toLowerCase());
609             throw new NamingException JavaDoc("Unable to find schema entry for schema type '" + schemaTypeName + "'");
610         }
611
612         NamingEnumeration JavaDoc schemaValues = schemaGroup.getAll();
613         String JavaDoc schemaValue;
614         while (schemaValues.hasMore())
615         {
616             schemaValue = (String JavaDoc) schemaValues.next();
617             String JavaDoc[] names = getNames(schemaValue);
618             for (int i = 0; i < names.length; i++)
619             {
620                 // use case-insensitive match to cope with weirdo directories that muddle case
621
if (specificName.equalsIgnoreCase(names[i]))
622                 {
623                     return getAttributesFromSchemaValue(schemaValue);
624                 }
625             }
626         }
627         return null;
628     }
629
630     /**
631      * <p>This Parses the schema values from the syntaxValue. The parser is very simple,
632      * and this might cause trouble in future years. The assumptions are:</p>
633      * <ul>
634      * <li> syntax keywords are all in upper case (e.g. NAME, DESC etc.).
635      * <li> keywords are all followed by a list of values in mixed case.
636      * <li> all values are more than one character long.
637      * <li>
638      * </ul>
639      */

640     // package visibility for testing
641

642     /**
643      * A quicky parse routine to spin through a list ( 'fred' 0.2.3.4 'nigel' 'horse heads' ) fnord fnord fnord ( 'more fnords' )
644      * adding elements to the passed attribute until it hits the first ')'
645      *
646      * @param schemaAttribute attribute to add the bracketed strings to.
647      * @param st a string tokeniser to read values from.
648      */

649     private void addBracketedValues(Attribute schemaAttribute, StringTokenizer st)
650     {
651         while (st.hasMoreTokens())
652         {
653             String JavaDoc token = st.nextToken();
654             if (token.endsWith(")")) // that's all for this list...
655
{
656                 if (token.length() > 1)
657                     schemaAttribute.add(getQuotedTokens(token.substring(0, token.length() - 1), st));
658
659                 return;
660             }
661
662             schemaAttribute.add(getQuotedTokens(token, st));
663         }
664     }
665
666     /**
667      * If the token has an opening ' character, this will read either it or a sequence until
668      * it gets to the closing ' - otherwise it just returns the token.
669      *
670      * @param token
671      * @return
672      */

673     private String JavaDoc getQuotedTokens(String JavaDoc token, StringTokenizer st)
674     {
675         if (token.charAt(0) != '\'')
676             return token;
677
678         if (token.length() < 2) // ??? Wierd - this should never happen.
679
return token;
680
681         if (token.charAt(0) == '\'' && token.charAt(token.length() - 1) == '\'')
682             return token.substring(1, token.length() - 1);
683
684         // string of quoted text... this would be so much easier in perl. sigh.
685
StringBuffer JavaDoc returnText = new StringBuffer JavaDoc(token.substring(1));
686         while (st.hasMoreTokens())
687         {
688             token = st.nextToken();
689             if (token.endsWith("'"))
690                 return (returnText.append(" ").append(token.substring(0, token.length() - 1)).toString());
691             else
692                 returnText.append(" ").append(token);
693         }
694
695         return returnText.toString(); // someone forgot the closing quote I guess...
696
}
697
698
699     /**
700      * This parses an attribute schema string such as: "( 2.5.6.2 NAME 'country' SUP ( top ) STRUCTURAL MUST ( c ) MAY ( description $ searchGuide ) )"
701      * and breaks it up into a pseudo attribute with elements such as 'NAME'->'country' and 'MAY' -> 'description', 'searchGuide'
702      *
703      * @param syntaxValue the string of syntax details as per rfc 2252
704      * @return the pseudo attribute, suitable for display.
705      */

706     protected BasicAttributes getAttributesFromSchemaValue(String JavaDoc syntaxValue)
707     {
708         BasicAttributes schemaValues = new BasicAttributes(); // add fake object class to keep some DXattributes routines happy...
709
schemaValues.put(schemaObjectClassAttribute);
710
711         StringTokenizer st = new StringTokenizer(syntaxValue, " \t\n\r\f$");
712
713         // Special Handling for first OID case
714
if (st.hasMoreTokens())
715         {
716             String JavaDoc oid = st.nextToken();
717             if (oid.startsWith("(")) // can be a stray opening '('.
718
{
719                 if (oid.length() == 1)
720                     oid = st.nextToken();
721                 else
722                     oid = oid.substring(1); // handle case where there is no space between ( and Oid.
723
}
724             schemaValues.put(new BasicAttribute("OID", oid));
725         }
726
727         while (st.hasMoreTokens())
728         {
729             String JavaDoc attributeID = st.nextToken();
730             if (attributeID.endsWith(")") == false) // stray closing ')' is possible (see above)
731
{
732                 addAttribute(schemaValues, attributeID, st);
733             }
734             else
735             {
736                 if (attributeID.length() > 1)
737                     addAttribute(schemaValues, attributeID.substring(1), st);
738             }
739         }
740
741         try
742         {
743             Attribute syntaxOID;
744             if ((syntaxOID = schemaValues.get("SYNTAX")) != null)
745             {
746                 String JavaDoc syntaxOIDString = (String JavaDoc)syntaxOID.get();
747                 String JavaDoc syntaxDescription = translateOID(syntaxOIDString);
748                 schemaValues.put(new BasicAttribute("SYNTAX Description", syntaxDescription));
749             }
750         }
751         catch (Exception JavaDoc e)
752         {
753             log.log(Level.INFO, "unable to translate syntax oid ", e);
754         }
755         return schemaValues;
756     }
757
758     /**
759      * Parses the current attribute from the schema string. NB - this method will recurse if two
760      * attribute keyword tokens are discovered next to each other.
761      *
762      * @param schemaValues - the Attributes object to add the schema Attribut<b>e</b> objects to.
763      * @param attributeName - the name of the new Attribut<b>e</b> object to construct
764      * @param st the token list to read the attribute data from
765      */

766
767     private void addAttribute(Attributes schemaValues, String JavaDoc attributeName, StringTokenizer st)
768     {
769         BasicAttribute schemaAttribute = new BasicAttribute(attributeName);
770         schemaValues.put(schemaAttribute);
771
772         if (st.hasMoreTokens())
773         {
774             String JavaDoc token = st.nextToken();
775             if (token.startsWith("("))
776             {
777                 if (token.length() > 1)
778                 {
779                     if (token.endsWith(")") == true) // pathalogical case: "(VALUE)"
780
{
781                         token = token.substring(0, token.length() - 1);
782                         schemaAttribute.add(token.substring(1));
783                     }
784                     else
785                     {
786                         schemaAttribute.add(token.substring(1));
787                         addBracketedValues(schemaAttribute, st);
788                     }
789                 }
790                 else
791                     addBracketedValues(schemaAttribute, st);
792             }
793             else if (token.endsWith(")"))
794             { // do nothing - this should be the very end of the string tokenizer list, and this the left over bit at the end.
795
// (note this is *not* the match to the "{" case above...!)
796
}
797             else if (isSyntaxKeyword(token) == true)
798             {
799                 addAttribute(schemaValues, token, st);
800             }
801             else
802             {
803                 token = getQuotedTokens(token, st);
804                 schemaAttribute.add(token);
805             }
806         }
807     }
808
809
810     /**
811      * Read the next block of text in a single quoted block, e.g. DESC 'some stuff here',
812      * (assuming that the first quoted string has already been read before the string
813      * tokenizer has been passed in, e.g. this should get "stuff here'", and return
814      * "stuff here".
815      *
816      * @param st a string tokenizer with a series of tokens one of which ends in a single quote
817      * @return the concatenated string of all tokens up to and including the one suffixed with a single quote.
818      */

819     private static String JavaDoc readQuoteBlock(StringTokenizer st)
820     {
821         StringBuffer JavaDoc returnBuffer = new StringBuffer JavaDoc();
822
823         while (st.hasMoreTokens())
824         {
825             String JavaDoc token = st.nextToken();
826             returnBuffer.append(" ");
827             returnBuffer.append(token);
828             if (token.endsWith("'"))
829             {
830                 returnBuffer.deleteCharAt(returnBuffer.length() - 1); // remove final quote
831
return returnBuffer.toString();
832             }
833         }
834         log.finest("unexpected end of schema text in single quoted block");
835         return returnBuffer.toString();
836     }
837
838     /**
839      * This <i>should</i> probably have a list of keywords extracted from rfc 2252... but I can't be
840      * bothered, so I'll simply test that the token is entirely uppercase, alphabetic characters...
841      *
842      * @param token the token to test for being an rfc 2252 syntax keyword.
843      * @return
844      */

845     private static boolean isSyntaxKeyword(String JavaDoc token)
846     {
847         String JavaDoc[] reservedKeywords = {
848             "ABSTRACT",
849             "APPLIES",
850             "AUXILIARY",
851             "COLLECTIVE",
852             "DESC",
853             "EQUALITY",
854             "MAY",
855             "MUST",
856             "NAME",
857             "NO-USER-MODIFICATION",
858             "OBSOLETE",
859             "ORDERING",
860             "SINGLE-VALUE",
861             "STRUCTURAL",
862             "SUBSTR",
863             "SUP",
864             "SYNTAX",
865             "USAGE"};
866
867         int size = reservedKeywords.length;
868         for (int i = 0; i < size; i++)
869             if (reservedKeywords[i].equals(token))
870                 return true;
871
872         if (token.startsWith("X-"))
873             return true;
874
875         return false; // probably isn't - but they might change the standard I suppose. Oh well...
876
}
877 /* simply returning true for capitalised is too simplistic...
878         int i, len = token.length();
879         char c;
880         for (i=0; i<len; i++)
881         {
882             c = token.charAt(i);
883             if (c<'A' || c>'Z')
884                 return false;
885         }
886         return true;
887 */

888
889     /**
890      * <p>Returns the next level in our virtual view of the schema.
891      * The virtual view may either be in the jndi form (e.g. "objectClass/person")
892      * of a 'dn like form' (e.g. "schema=objectClass, schema=person"). If it is
893      * the latter, the 'fake attribute type' is ignored, e.g. "fnordle=objectClass,
894      * snork=person" would resolve the same as "schema=objectClass, schema=person").</p>
895      *
896      * @param entryName the full schema name to get the next level of: e.g. "schema=objectClass"
897      * @return the undecorated names of the next level (e.g. {'person', 'top', 'organisation'}
898      */

899     public ArrayList listEntryNames(String JavaDoc entryName)
900             throws NamingException JavaDoc
901     {
902         if (rawSchemaAttributes == null)
903             return new ArrayList();
904
905         entryName = mangleEntryName(entryName);
906
907         ArrayList schemaNames;
908
909         if (entryName == null || entryName.length() == 0 || entryName.equals("cn=schema") || entryName.equals(schemaRoot)) // The 'root node', i.e. the top of the schema tree - returns things like
910
{ // 'objectClasses', 'ldapSyntaxes', 'attributeTypes'
911
schemaNames = new ArrayList(10);
912             Enumeration JavaDoc schemaTopLevelNames = rawSchemaAttributes.getIDs();
913             while (schemaTopLevelNames.hasMoreElements())
914             {
915                 String JavaDoc name = (String JavaDoc) schemaTopLevelNames.nextElement();
916                 if (!schemaNames.contains(name)) //TE: don't add duplicates
917
schemaNames.add(name);
918             }
919         }
920         else if (entryName.indexOf(',') == -1 && entryName.indexOf('/') == -1) // the first layer - returns things like
921
{ // 'person', 'orgunit', 'newPilotPerson' etc...
922
schemaNames = new ArrayList(1000);
923             if (entryName.indexOf('=') > 0)
924                 entryName = entryName.substring(entryName.indexOf('=') + 1);
925             Attribute rawSyntaxAttribute = rawSchemaAttributes.get(entryName); // entryName might be 'attributeTypes'
926
if (rawSyntaxAttribute == null)
927                 throw new NamingException JavaDoc("unable to list syntaxes of type '" + entryName + "'");
928
929             Enumeration JavaDoc values = rawSyntaxAttribute.getAll();
930             String JavaDoc[] names;
931             while (values.hasMoreElements())
932             {
933                 names = getNames((String JavaDoc) values.nextElement());
934                 for (int i = 0; i < names.length; i++)
935                 {
936                     if (!schemaNames.contains(names[i])) //TE: don't add duplicates
937
schemaNames.add(names[i]);
938                 }
939             }
940         }
941         else // double element, e.g. objectClass/person -> never has children.
942
{
943             schemaNames = new ArrayList(0);
944         }
945
946         return schemaNames;
947     }
948
949
950     // note kind-a-sad attempt to make this method fastish.
951
private int name_pos, bracket_pos, quote_pos, last_pos, pos; // pointers for string parsing.
952

953     /**
954      * This strips the OID from a schema attribute description string. The OID
955      * is assumed to be the first element after an optional '(' character.
956      *
957      * @param ldapSchemaDescription
958      * @return the OID string ('1.2.3.4' etc.) - or '0' if ldapSchemaDescription is null or unknown
959      */

960     // package visibility for testing
961

962     public final String JavaDoc getOID(String JavaDoc ldapSchemaDescription)
963     {
964         if (ldapSchemaDescription == null)
965             return "0"; // error.
966

967         int start = 0;
968
969         if (ldapSchemaDescription.charAt(0) == '(')
970             start++;
971
972         while (ldapSchemaDescription.charAt(start) == ' ') // technically could be any whitespace, but practically it is only spaces... (I hope)
973
start++;
974
975         try
976         {
977             int endpos = ldapSchemaDescription.indexOf(' ', start);
978             if (endpos == -1)
979                 endpos = ldapSchemaDescription.indexOf(')', start);
980             if (endpos == -1)
981                 endpos = ldapSchemaDescription.length();
982
983             String JavaDoc ret = ldapSchemaDescription.substring(start, endpos);
984             return ret;
985         }
986         catch (Exception JavaDoc e)
987         {
988             log.log(Level.WARNING, "can't parse '" + ldapSchemaDescription + "'");
989             e.printStackTrace();
990             return "0";
991         }
992     }
993
994     /**
995      * parse strings that may be of the form either:
996      * ????????????????/ NAME 'myname' ???????????
997      * ????????????????/ NAME ('firstname', 'secondname', 'thirdname') ???????????
998      *
999      * @return the single name, or the first of the array of names, as a string.
1000     */

1001
1002    // IMP note - for speed, this is implemented separately from getNames()
1003

1004    public final String JavaDoc getFirstName(String JavaDoc ldapSchemaDescription)
1005    {
1006        name_pos = ldapSchemaDescription.indexOf("NAME");
1007        if (name_pos == -1)
1008            name_pos = ldapSchemaDescription.indexOf("DESC"); // for ldapSyntaxes entries
1009
if (name_pos == -1) // fall back - should never happen; try to return OID
1010
{
1011            if (ldapSchemaDescription.startsWith("{"))
1012                ldapSchemaDescription = ldapSchemaDescription.substring(1).trim();
1013            pos = ldapSchemaDescription.indexOf(' ');
1014            if (pos == -1)
1015            {
1016                log.log(Level.WARNING, "unable to get name from " + ldapSchemaDescription);
1017                return "syntax_error";
1018            }
1019            return ldapSchemaDescription.substring(0, pos).trim();
1020        }
1021        quote_pos = ldapSchemaDescription.indexOf('\'', name_pos);
1022        quote_pos++;
1023        last_pos = ldapSchemaDescription.indexOf('\'', quote_pos);
1024        if (quote_pos != 0 && last_pos != -1)
1025            return ldapSchemaDescription.substring(quote_pos, last_pos);
1026        else
1027        {
1028            log.log(Level.WARNING, "unable to parse " + ldapSchemaDescription);
1029            return "syntax_error";
1030        }
1031
1032    }
1033
1034    /**
1035     * parse strings that may be of the form either:
1036     * ????????????????/ NAME 'myname' ???????????
1037     * ????????????????/ NAME ('firstname', 'secondname', 'thirdname') ???????????
1038     *
1039     * @return the Name or array of names, as an array of strings 1 or more elements long.
1040     */

1041    // package visibility for testing
1042

1043    public final String JavaDoc[] getNames(String JavaDoc ldapSyntaxDescription)
1044    {
1045        try
1046        {
1047            name_pos = ldapSyntaxDescription.indexOf("NAME");
1048            if (name_pos == -1)
1049                name_pos = ldapSyntaxDescription.indexOf("DESC"); // for ldapSyntaxes entries
1050
if (name_pos == -1) // fall back - should never happen; try to return OID
1051
{
1052                if (ldapSyntaxDescription.startsWith("{"))
1053                    ldapSyntaxDescription = ldapSyntaxDescription.substring(1).trim();
1054                return new String JavaDoc[]{ldapSyntaxDescription.substring(0, ldapSyntaxDescription.indexOf(' ')).trim()};
1055            }
1056
1057            bracket_pos = ldapSyntaxDescription.indexOf('(', name_pos);
1058            quote_pos = ldapSyntaxDescription.indexOf('\'', name_pos);
1059
1060            if (bracket_pos != -1 && bracket_pos < quote_pos) // multiple names...
1061
{
1062                bracket_pos = ldapSyntaxDescription.indexOf(')', bracket_pos); // get end bracket pos
1063
ArrayList newList = new ArrayList(5);
1064                while (quote_pos < bracket_pos && quote_pos != -1) // iterate through grabbing 'quoted' substrings until we get to the end of the bracketed expression
1065
{
1066                    int start = ++quote_pos;
1067                    quote_pos = ldapSyntaxDescription.indexOf('\'', quote_pos);
1068
1069                    String JavaDoc temp = ldapSyntaxDescription.substring(start, quote_pos);
1070                    newList.add(temp);
1071
1072                    quote_pos++;
1073                    quote_pos = ldapSyntaxDescription.indexOf('\'', quote_pos);
1074                }
1075                return (String JavaDoc[]) newList.toArray(new String JavaDoc[]{});
1076            }
1077            else // return the single name
1078
{
1079                quote_pos++;
1080                int next_quote = ldapSyntaxDescription.indexOf('\'', quote_pos);
1081                String JavaDoc temp = ldapSyntaxDescription.substring(quote_pos, next_quote);
1082                return new String JavaDoc[]{temp};
1083            }
1084        }
1085        catch (StringIndexOutOfBoundsException JavaDoc e)
1086        {
1087            log.log(Level.WARNING, "unable to parse line: " + ldapSyntaxDescription, e);
1088            return new String JavaDoc[]{"syntax_error"};
1089        }
1090    }
1091
1092
1093    /**
1094     * Takes a DXAttributes set representing attribute schema defs,
1095     * and translates the oids into human friendly strings...
1096     */

1097/*
1098    protected Attributes addAttributeInfo(Attributes attdefs)
1099    {
1100        try
1101        {
1102            Attribute syntax = attdefs.get("SYNTAX"); // get syntax attribute
1103            String oid = syntax.get().toString(); // convert oid value to string
1104
1105            if (oid.indexOf('{') > -1)
1106                oid = oid.substring(0, oid.indexOf('{'));
1107
1108            String syntaxdesc = (String) oids.get(oid); // look up description for oid
1109
1110            attdefs.put("(SYNTAX-DESC)", syntaxdesc); // stick in synthetic attribute
1111            return attdefs;
1112        }
1113        catch (NamingException e) { return attdefs; }
1114        catch (NullPointerException e) { return attdefs; }
1115    }
1116*/

1117
1118    /**
1119     * This returns a sorted list of all known object classes, as read from the schema
1120     *
1121     * @return an array list of strings
1122     * @throws NamingException
1123     */

1124    public ArrayList objectClasses()
1125            throws NamingException JavaDoc
1126    {
1127        if (fullObjectClassArray == null)
1128        {
1129            ArrayList temp = listEntryNames("schema=objectClasses,cn=schema");
1130            if (temp == null)
1131                throw new NamingException JavaDoc("unable to read list of object classes from schema");
1132            String JavaDoc[] OCs = (String JavaDoc[]) temp.toArray(new String JavaDoc[temp.size()]);
1133            Arrays.sort(OCs, new Comparator()
1134            {
1135                public int compare(Object JavaDoc a, Object JavaDoc b)
1136                {
1137                    return ((String JavaDoc) a).compareToIgnoreCase((String JavaDoc) b);
1138                }
1139
1140                public boolean equals(Object JavaDoc a, Object JavaDoc b)
1141                {
1142                    return ((String JavaDoc) a).equalsIgnoreCase((String JavaDoc) b);
1143                }
1144            });
1145            int size = OCs.length;
1146            fullObjectClassArray = new ArrayList(size);
1147            for (int i = 0; i < size; i++)
1148                fullObjectClassArray.add(i, OCs[i]);
1149        }
1150
1151        return fullObjectClassArray;
1152    }
1153
1154
1155    /**
1156     * This returns a sorted list of all known attribute names, as read from the schema
1157     *
1158     * @return an array list of strings
1159     * @throws NamingException
1160     */

1161    public ArrayList attributeNames()
1162            throws NamingException JavaDoc
1163    {
1164        if (fullAttributeNameArray == null)
1165        {
1166            ArrayList temp = listEntryNames("schema=attributeTypes,cn=schema");
1167            if (temp == null)
1168                throw new NamingException JavaDoc("unable to read list of attribute types from schema");
1169            String JavaDoc[] ATs = (String JavaDoc[]) temp.toArray(new String JavaDoc[temp.size()]);
1170            Arrays.sort(ATs, new Comparator()
1171            {
1172                public int compare(Object JavaDoc a, Object JavaDoc b)
1173                {
1174                    return ((String JavaDoc) a).compareToIgnoreCase((String JavaDoc) b);
1175                }
1176
1177                public boolean equals(Object JavaDoc a, Object JavaDoc b)
1178                {
1179                    return ((String JavaDoc) a).equalsIgnoreCase((String JavaDoc) b);
1180                }
1181            });
1182            int size = ATs.length;
1183            fullAttributeNameArray = new ArrayList(size);
1184            for (int i = 0; i < size; i++)
1185                fullAttributeNameArray.add(i, ATs[i]);
1186        }
1187
1188        return fullAttributeNameArray;
1189    }
1190
1191    /**
1192     * Gets a list of the object classes most likely
1193     * to be used for the next Level of the DN...
1194     *
1195     * @param dn the dn of the parent to determine likely
1196     * child object classes for
1197     * @return list of recommended object classes...
1198     */

1199
1200    public ArrayList getRecommendedObjectClasses(String JavaDoc dn)
1201    {
1202        try
1203        {
1204            if ((dn != null) && ctx != null)
1205            {
1206                Attributes atts = ctx.getAttributes(dn);
1207
1208                if (atts == null)
1209                {
1210                    log.log(Level.WARNING, "error reading object classes for " + dn);
1211                }
1212                else
1213                {
1214                    Attribute objectClasses = atts.get("objectclass");
1215                    if (objectClasses == null) // hack. Shouldn't have to do this. Bad Server! Spank!
1216
objectClasses = atts.get("objectClass");
1217                    if (objectClasses == null) // still! - try 'oc'
1218
objectClasses = atts.get("oc");
1219
1220                    if (objectClasses == null) // aargh! Give up.
1221
{
1222                        log.log(Level.WARNING, "unable to recognize object classes for " + dn);
1223                    }
1224                    else
1225                    {
1226                        NamingEnumeration JavaDoc names = objectClasses.getAll();
1227                        if (names == null)
1228                            log.log(Level.WARNING, "object class has no attributes!");
1229
1230                        ArrayList returnArray = new ArrayList(10);
1231                        while (names.hasMore())
1232                            returnArray.add(names.next());
1233
1234                        return returnArray;
1235                    }
1236                }
1237            }
1238        }
1239        catch (NamingException JavaDoc e)
1240        {
1241            log.log(Level.WARNING, "error reading object classes for " + dn + "\n internal error III: ", e);
1242        }
1243        return null;
1244    }
1245
1246
1247    /**
1248     * Gets a list of all attribute definitions that have a 'binary' syntax
1249     * (currently defined as: SYNTAX = 1.3.6.1.4.1.1466.115.121.1.5, 1.3.6.1.4.1.1466.115.121.1.40,
1250     * 1.3.6.1.4.1.1466.115.121.1.28 or 1.3.6.1.4.1.1466.115.121.1.8)
1251     *
1252     * @return list of space separated attribute names as a string array - used to set ctx.addToEnvironment("java.naming.ldap.attributes.binary", -String- );
1253     */

1254// Performance note - this might be sped up a tad if required...
1255
public String JavaDoc getNewBinaryAttributes()
1256    {
1257        if (rawSchemaAttributes == null)
1258            return "";
1259
1260        try
1261        {
1262            StringBuffer JavaDoc binaryAttributeList = new StringBuffer JavaDoc(1000);
1263            Attribute rawSyntaxAttribute = getAttributeTypes();
1264            if (rawSyntaxAttribute == null)
1265                return "";
1266
1267            NamingEnumeration JavaDoc values = rawSyntaxAttribute.getAll();
1268            while (values.hasMore())
1269            {
1270                String JavaDoc attributeDescription = (String JavaDoc) values.next(); // something like "( 1.3.6.1.4.1.453.7.6.2.1 NAME 'mhsX400Domain' SYNTAX 1.3.6.1.4.1.1466.115.121.1.5 )"
1271
if ((attributeDescription.indexOf("1.3.6.1.4.1.1466.115.121.1.5 ") > 0) || // binary
1272
(attributeDescription.indexOf("1.3.6.1.4.1.1466.115.121.1.40") > 0) || // octet string
1273
(attributeDescription.indexOf("1.3.6.1.4.1.1466.115.121.1.28") > 0) || // jpeg
1274
(attributeDescription.indexOf("1.3.6.1.4.1.1466.115.121.1.8") > 0)) // certificate
1275
{
1276                    String JavaDoc[] names = getNames(attributeDescription);
1277                    for (int i = 0; i < names.length; i++)
1278                    {
1279                        binaryAttributeList.append(names[i]);
1280                        binaryAttributeList.append(' ');
1281                    }
1282                }
1283            }
1284            return binaryAttributeList.toString();
1285        }
1286        catch (NamingException JavaDoc e)
1287        {
1288            log.log(Level.WARNING, "unable to get binary attributes from schema", e);
1289            return "";
1290        }
1291
1292    }
1293
1294     /**
1295     * Convenience method to get the objectClasses Attribute, which represents the syntax of
1296     * attributeTypes.
1297     *
1298     * @return an Attribute with multiple values, each representing a particular attributeType
1299     * represented as complex string of values that must be parsed: e.g. the surname value might be:
1300     * 2.5.4.4 NAME ( 'sn' 'surname' ) SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
1301     */

1302
1303    public Attribute getAttributeTypes()
1304    {
1305        return rawSchemaAttributes.get("attributeTypes");
1306    }
1307
1308    /**
1309     * Finds the syntax of the corresponding attribute
1310     *
1311     * @param attID the undecorated attribute name - e.g. 'commonName'
1312     * @return returns the attribute syntax, or null if not found.
1313     */

1314    public String JavaDoc getAttributeSyntax(String JavaDoc attID)
1315    {
1316        if(attID == null)
1317            return null;
1318
1319        if (attID.indexOf(';') > 0)
1320            attID = attID.substring(0, attID.indexOf(';')); //TE: for example: userCertificate;binary.
1321

1322        return schemaLookup("schema=" + attID + ",schema=attributeTypes", "SYNTAX");
1323    }
1324
1325    /**
1326     * Looks up a particular value in a particular schema attribute
1327     *
1328     * @param entryName the entry to lookup; e.g. 'schema=person, schema=objectClass'
1329     * @param schemaAttribute the actual field to look up, e.g. "DESC"
1330     * @return the looked up value (or the first one found, if multiple)
1331     */

1332
1333    public String JavaDoc schemaLookup(String JavaDoc entryName, String JavaDoc schemaAttribute)
1334    {
1335        entryName = mangleEntryName(entryName);
1336
1337        try
1338        {
1339            Attributes schemaAtts = getAttributes(entryName);
1340            Attribute schemaAtt = schemaAtts.get(schemaAttribute);
1341            String JavaDoc att = (String JavaDoc) schemaAtt.get();
1342            return att;
1343
1344        }
1345        catch (NamingException JavaDoc e)
1346        {
1347            log.log(Level.WARNING, "unable to get value for " + entryName + " value: " + schemaAttribute, e);
1348        }
1349        catch (NullPointerException JavaDoc e2)
1350        {
1351            if ("DESC".equals(schemaAttribute) == false) // ignore frequent error encountered searching for option 'DESC' schema paramater
1352
log.log(Level.WARNING, "unable to read any schema entry for " + entryName + "and attribute: " + schemaAttribute, e2);
1353        }
1354        catch (NoSuchElementException e)
1355        {
1356            if ("DESC".equals(schemaAttribute) == false) // ignore frequent error encountered searching for option 'DESC' schema paramater
1357
log.log(Level.WARNING, "unable to read any schema entry for " + entryName + "and attribute: " + schemaAttribute, e);
1358        }
1359        return null;
1360    }
1361
1362    public String JavaDoc getNameOfObjectClassAttribute()
1363    {
1364        return schemaLookup("schema=objectClass,schema=attributeTypes", "NAME");
1365    }
1366
1367
1368    /**
1369     * Tries to determine if the attribute is a SINGLE-VALUE attribute.
1370     * The rawSchemaAttributes Attributes object represents each attribute
1371     * similar to
1372     * <br><br>
1373     * ( 1.3.6.1.4.1.3327.77.4.1.2 NAME 'uNSPSCTitle' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
1374     * <br><br>
1375     * This method gets each value (see above string) in the attribute, then each of their names in turn
1376     * until the name of the attribute we are seeking is found. Then checks if 'SINGLE-VALUE'
1377     * is present in that value.
1378     *
1379     * @param name the name of the attribute, for example uNSPCSTitle.
1380     * @return true if the attribute is a SINGLE-VALUE attribute, false otherwise.
1381     */

1382
1383    public boolean isAttributeSingleValued(String JavaDoc name)
1384    {/* TE */
1385        if (rawSchemaAttributes == null)
1386            return false;
1387
1388        try
1389        {
1390            Attribute attributeTypes = getAttributeTypes();
1391            NamingEnumeration JavaDoc en = attributeTypes.getAll();
1392
1393            while (en.hasMore())
1394            {
1395                String JavaDoc attr = (String JavaDoc) en.next();
1396                String JavaDoc[] attrName = getNames(attr);
1397
1398                for (int i = 0; i < attrName.length; i++)
1399                    if (attrName[i].equals(name) && (attr.indexOf("SINGLE-VALUE") >= 0))
1400                        return true;
1401            }
1402        }
1403        catch (NamingException JavaDoc e)
1404        {
1405            log.log(Level.WARNING, "Unable to determine if attribute '" + name + "' is SINGLE-VALUE." + e);
1406        }
1407        return false;
1408    }
1409}
1410
Popular Tags