View Javadoc
1   /*
2    * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  package com.sun.jndi.toolkit.dir;
26  
27  import javax.naming.*;
28  import javax.naming.directory.*;
29  import javax.naming.spi.*;
30  import java.util.*;
31  
32  /**
33   * A sample service provider that implements a hierarchical directory in memory.
34   * Every operation begins by doing a lookup on the name passed to it and then
35   * calls a corresponding "do<OperationName>" on the result of the lookup. The
36   * "do<OperationName>" does the work without any further resolution (it assumes
37   * that it is the target context).
38   */
39  
40  public class HierMemDirCtx implements DirContext {
41  
42      static private final boolean debug = false;
43      private static final NameParser defaultParser = new HierarchicalNameParser();
44  
45      protected Hashtable<String, Object> myEnv;
46      protected Hashtable<Name, Object> bindings;
47      protected Attributes attrs;
48      protected boolean ignoreCase = false;
49      protected NamingException readOnlyEx = null;
50      protected NameParser myParser = defaultParser;
51  
52      private boolean alwaysUseFactory;
53  
54      public void close() throws NamingException {
55          myEnv = null;
56          bindings = null;
57          attrs = null;
58      }
59  
60      public String getNameInNamespace() throws NamingException {
61          throw new OperationNotSupportedException(
62              "Cannot determine full name");
63      }
64  
65      public HierMemDirCtx() {
66          this(null, false, false);
67      }
68  
69      public HierMemDirCtx(boolean ignoreCase) {
70          this(null, ignoreCase, false);
71      }
72  
73      public HierMemDirCtx(Hashtable<String, Object> environment, boolean ignoreCase) {
74          this(environment, ignoreCase, false);
75      }
76  
77      protected HierMemDirCtx(Hashtable<String, Object> environment,
78          boolean ignoreCase, boolean useFac) {
79          myEnv = environment;
80          this.ignoreCase = ignoreCase;
81          init();
82          this.alwaysUseFactory = useFac;
83      }
84  
85      private void init() {
86          attrs = new BasicAttributes(ignoreCase);
87          bindings = new Hashtable<>(11, 0.75f);
88      }
89  
90      public Object lookup(String name) throws NamingException {
91          return lookup(myParser.parse(name));
92      }
93  
94      public Object lookup(Name name) throws NamingException {
95          return doLookup(name, alwaysUseFactory);
96      }
97  
98      public Object doLookup(Name name, boolean useFactory)
99          throws NamingException {
100 
101         Object target = null;
102         name = canonizeName(name);
103 
104         switch(name.size()) {
105         case 0:
106             // name is empty, caller wants this object
107             target = this;
108             break;
109 
110         case 1:
111             // name is atomic, caller wants one of this object's bindings
112             target = bindings.get(name);
113             break;
114 
115         default:
116             // name is compound, delegate to child context
117             HierMemDirCtx ctx = (HierMemDirCtx)bindings.get(name.getPrefix(1));
118             if(ctx == null) {
119                 target = null;
120             } else {
121                 target = ctx.doLookup(name.getSuffix(1), false);
122             }
123             break;
124         }
125 
126         if(target == null) {
127             throw new NameNotFoundException(name.toString());
128         }
129 
130         if (useFactory) {
131             try {
132                 return DirectoryManager.getObjectInstance(target,
133                     name, this, myEnv,
134                     (target instanceof HierMemDirCtx) ?
135                     ((HierMemDirCtx)target).attrs : null);
136             } catch (NamingException e) {
137                 throw e;
138             } catch (Exception e) {
139                 NamingException e2 = new NamingException(
140                     "Problem calling getObjectInstance");
141                 e2.setRootCause(e);
142                 throw e2;
143             }
144         } else {
145             return target;
146         }
147     }
148 
149     public void bind(String name, Object obj) throws NamingException {
150         bind(myParser.parse(name), obj);
151     }
152 
153     public void bind(Name name, Object obj) throws NamingException {
154         doBind(name, obj, null, alwaysUseFactory);
155     }
156 
157     public void bind(String name, Object obj, Attributes attrs)
158             throws NamingException {
159         bind(myParser.parse(name), obj, attrs);
160     }
161 
162     public void bind(Name name, Object obj, Attributes attrs)
163             throws NamingException {
164         doBind(name, obj, attrs, alwaysUseFactory);
165     }
166 
167     protected void doBind(Name name, Object obj, Attributes attrs,
168         boolean useFactory) throws NamingException {
169         if (name.isEmpty()) {
170             throw new InvalidNameException("Cannot bind empty name");
171         }
172 
173         if (useFactory) {
174             DirStateFactory.Result res = DirectoryManager.getStateToBind(
175                 obj, name, this, myEnv, attrs);
176             obj = res.getObject();
177             attrs = res.getAttributes();
178         }
179 
180         HierMemDirCtx ctx= (HierMemDirCtx) doLookup(getInternalName(name), false);
181         ctx.doBindAux(getLeafName(name), obj);
182 
183         if (attrs != null && attrs.size() > 0) {
184             modifyAttributes(name, ADD_ATTRIBUTE, attrs);
185         }
186     }
187 
188     protected void doBindAux(Name name, Object obj) throws NamingException {
189         if (readOnlyEx != null) {
190             throw (NamingException) readOnlyEx.fillInStackTrace();
191         }
192 
193         if (bindings.get(name) != null) {
194             throw new NameAlreadyBoundException(name.toString());
195         }
196         if(obj instanceof HierMemDirCtx) {
197             bindings.put(name, obj);
198         } else {
199             throw new SchemaViolationException(
200                 "This context only supports binding objects of it's own kind");
201         }
202     }
203 
204     public void rebind(String name, Object obj) throws NamingException {
205         rebind(myParser.parse(name), obj);
206     }
207 
208     public void rebind(Name name, Object obj) throws NamingException {
209         doRebind(name, obj, null, alwaysUseFactory);
210     }
211 
212     public void rebind(String name, Object obj, Attributes attrs)
213             throws NamingException {
214         rebind(myParser.parse(name), obj, attrs);
215     }
216 
217     public void rebind(Name name, Object obj, Attributes attrs)
218             throws NamingException {
219         doRebind(name, obj, attrs, alwaysUseFactory);
220     }
221 
222     protected void doRebind(Name name, Object obj, Attributes attrs,
223         boolean useFactory) throws NamingException {
224         if (name.isEmpty()) {
225             throw new InvalidNameException("Cannot rebind empty name");
226         }
227 
228         if (useFactory) {
229             DirStateFactory.Result res = DirectoryManager.getStateToBind(
230                 obj, name, this, myEnv, attrs);
231             obj = res.getObject();
232             attrs = res.getAttributes();
233         }
234 
235         HierMemDirCtx ctx= (HierMemDirCtx) doLookup(getInternalName(name), false);
236         ctx.doRebindAux(getLeafName(name), obj);
237 
238         //
239         // attrs == null -> use attrs from obj
240         // attrs != null -> use attrs
241         //
242         // %%% Strictly speaking, when attrs is non-null, we should
243         // take the explicit step of removing obj's attrs.
244         // We don't do that currently.
245 
246         if (attrs != null && attrs.size() > 0) {
247             modifyAttributes(name, ADD_ATTRIBUTE, attrs);
248         }
249     }
250 
251     protected void doRebindAux(Name name, Object obj) throws NamingException {
252         if (readOnlyEx != null) {
253             throw (NamingException) readOnlyEx.fillInStackTrace();
254         }
255         if(obj instanceof HierMemDirCtx) {
256             bindings.put(name, obj);
257 
258         } else {
259             throw new SchemaViolationException(
260                 "This context only supports binding objects of it's own kind");
261         }
262     }
263 
264     public void unbind(String name) throws NamingException {
265         unbind(myParser.parse(name));
266     }
267 
268     public void unbind(Name name) throws NamingException {
269         if (name.isEmpty()) {
270             throw new InvalidNameException("Cannot unbind empty name");
271         } else {
272             HierMemDirCtx ctx=
273                 (HierMemDirCtx) doLookup(getInternalName(name), false);
274             ctx.doUnbind(getLeafName(name));
275         }
276     }
277 
278     protected void doUnbind(Name name) throws NamingException {
279         if (readOnlyEx != null) {
280             throw (NamingException) readOnlyEx.fillInStackTrace();
281         }
282 
283         bindings.remove(name);  // attrs will also be removed along with ctx
284     }
285 
286     public void rename(String oldname, String newname)
287             throws NamingException {
288          rename(myParser.parse(oldname), myParser.parse(newname));
289     }
290 
291     public void rename(Name oldname, Name newname)
292             throws NamingException {
293 
294         if(newname.isEmpty() || oldname.isEmpty()) {
295             throw new InvalidNameException("Cannot rename empty name");
296         }
297 
298         if (!getInternalName(newname).equals(getInternalName(oldname))) {
299             throw new InvalidNameException("Cannot rename across contexts");
300         }
301 
302         HierMemDirCtx ctx =
303             (HierMemDirCtx) doLookup(getInternalName(newname), false);
304         ctx.doRename(getLeafName(oldname), getLeafName(newname));
305     }
306 
307     protected void doRename(Name oldname, Name newname) throws NamingException {
308         if (readOnlyEx != null) {
309             throw (NamingException) readOnlyEx.fillInStackTrace();
310         }
311 
312         oldname = canonizeName(oldname);
313         newname = canonizeName(newname);
314 
315         // Check if new name exists
316         if (bindings.get(newname) != null) {
317             throw new NameAlreadyBoundException(newname.toString());
318         }
319 
320         // Check if old name is bound
321         Object oldBinding = bindings.remove(oldname);
322         if (oldBinding == null) {
323             throw new NameNotFoundException(oldname.toString());
324         }
325 
326         bindings.put(newname, oldBinding);
327     }
328 
329     public NamingEnumeration<NameClassPair> list(String name) throws NamingException {
330         return list(myParser.parse(name));
331     }
332 
333     public NamingEnumeration<NameClassPair> list(Name name) throws NamingException {
334         HierMemDirCtx ctx = (HierMemDirCtx) doLookup(name, false);
335         return ctx.doList();
336     }
337 
338     protected NamingEnumeration<NameClassPair> doList () throws NamingException {
339         return new FlatNames(bindings.keys());
340     }
341 
342 
343     public NamingEnumeration<Binding> listBindings(String name) throws NamingException {
344         return listBindings(myParser.parse(name));
345     }
346 
347     public NamingEnumeration<Binding> listBindings(Name name) throws NamingException {
348         HierMemDirCtx ctx = (HierMemDirCtx)doLookup(name, false);
349         return ctx.doListBindings(alwaysUseFactory);
350     }
351 
352     protected NamingEnumeration<Binding> doListBindings(boolean useFactory)
353         throws NamingException {
354         return new FlatBindings(bindings, myEnv, useFactory);
355     }
356 
357     public void destroySubcontext(String name) throws NamingException {
358         destroySubcontext(myParser.parse(name));
359     }
360 
361     public void destroySubcontext(Name name) throws NamingException {
362         HierMemDirCtx ctx =
363             (HierMemDirCtx) doLookup(getInternalName(name), false);
364         ctx.doDestroySubcontext(getLeafName(name));
365     }
366 
367     protected void doDestroySubcontext(Name name) throws NamingException {
368 
369         if (readOnlyEx != null) {
370             throw (NamingException) readOnlyEx.fillInStackTrace();
371         }
372         name = canonizeName(name);
373         bindings.remove(name);
374     }
375 
376     public Context createSubcontext(String name) throws NamingException {
377         return createSubcontext(myParser.parse(name));
378     }
379 
380     public Context createSubcontext(Name name) throws NamingException {
381         return createSubcontext(name, null);
382     }
383 
384     public DirContext createSubcontext(String name, Attributes attrs)
385             throws NamingException {
386         return createSubcontext(myParser.parse(name), attrs);
387     }
388 
389     public DirContext createSubcontext(Name name, Attributes attrs)
390             throws NamingException {
391         HierMemDirCtx ctx =
392             (HierMemDirCtx) doLookup(getInternalName(name), false);
393         return ctx.doCreateSubcontext(getLeafName(name), attrs);
394     }
395 
396     protected DirContext doCreateSubcontext(Name name, Attributes attrs)
397         throws NamingException {
398         if (readOnlyEx != null) {
399             throw (NamingException) readOnlyEx.fillInStackTrace();
400         }
401 
402         name = canonizeName(name);
403 
404         if (bindings.get(name) != null) {
405             throw new NameAlreadyBoundException(name.toString());
406         }
407         HierMemDirCtx newCtx = createNewCtx();
408         bindings.put(name, newCtx);
409         if(attrs != null) {
410             newCtx.modifyAttributes("", ADD_ATTRIBUTE, attrs);
411         }
412         return newCtx;
413     }
414 
415 
416     public Object lookupLink(String name) throws NamingException {
417         // This context does not treat links specially
418         return lookupLink(myParser.parse(name));
419     }
420 
421     public Object lookupLink(Name name) throws NamingException {
422         // Flat namespace; no federation; just call string version
423         return lookup(name);
424     }
425 
426     public NameParser getNameParser(String name) throws NamingException {
427         return myParser;
428     }
429 
430     public NameParser getNameParser(Name name) throws NamingException {
431         return myParser;
432     }
433 
434     public String composeName(String name, String prefix)
435             throws NamingException {
436         Name result = composeName(new CompositeName(name),
437                                   new CompositeName(prefix));
438         return result.toString();
439     }
440 
441     public Name composeName(Name name, Name prefix)
442             throws NamingException {
443         name = canonizeName(name);
444         prefix = canonizeName(prefix);
445         Name result = (Name)(prefix.clone());
446         result.addAll(name);
447         return result;
448     }
449 
450     @SuppressWarnings("unchecked") // clone()
451     public Object addToEnvironment(String propName, Object propVal)
452             throws NamingException {
453         myEnv = (myEnv == null)
454                 ? new Hashtable<String, Object>(11, 0.75f)
455                 : (Hashtable<String, Object>)myEnv.clone();
456 
457         return myEnv.put(propName, propVal);
458     }
459 
460     @SuppressWarnings("unchecked") // clone()
461     public Object removeFromEnvironment(String propName)
462             throws NamingException {
463         if (myEnv == null)
464             return null;
465 
466         myEnv = (Hashtable<String, Object>)myEnv.clone();
467         return myEnv.remove(propName);
468     }
469 
470     @SuppressWarnings("unchecked") // clone()
471     public Hashtable<String, Object> getEnvironment() throws NamingException {
472         if (myEnv == null) {
473             return new Hashtable<>(5, 0.75f);
474         } else {
475             return (Hashtable<String, Object>)myEnv.clone();
476         }
477     }
478 
479     public Attributes getAttributes(String name)
480        throws NamingException {
481        return getAttributes(myParser.parse(name));
482     }
483 
484     public Attributes getAttributes(Name name)
485         throws NamingException {
486         HierMemDirCtx ctx = (HierMemDirCtx) doLookup(name, false);
487         return ctx.doGetAttributes();
488     }
489 
490     protected Attributes doGetAttributes() throws NamingException {
491         return (Attributes)attrs.clone();
492     }
493 
494     public Attributes getAttributes(String name, String[] attrIds)
495         throws NamingException {
496         return getAttributes(myParser.parse(name), attrIds);
497     }
498 
499     public Attributes getAttributes(Name name, String[] attrIds)
500         throws NamingException {
501         HierMemDirCtx ctx = (HierMemDirCtx) doLookup(name, false);
502         return ctx.doGetAttributes(attrIds);
503     }
504 
505     protected Attributes doGetAttributes(String[] attrIds)
506         throws NamingException {
507 
508         if (attrIds == null) {
509             return doGetAttributes();
510         }
511         Attributes attrs = new BasicAttributes(ignoreCase);
512         Attribute attr = null;
513             for(int i=0; i<attrIds.length; i++) {
514                 attr = this.attrs.get(attrIds[i]);
515                 if (attr != null) {
516                     attrs.put(attr);
517                 }
518             }
519         return attrs;
520     }
521 
522     public void modifyAttributes(String name, int mod_op, Attributes attrs)
523         throws NamingException   {
524         modifyAttributes(myParser.parse(name), mod_op, attrs);
525     }
526 
527     public void modifyAttributes(Name name, int mod_op, Attributes attrs)
528         throws NamingException {
529 
530         if (attrs == null || attrs.size() == 0) {
531             throw new IllegalArgumentException(
532                 "Cannot modify without an attribute");
533         }
534 
535         // turn it into a modification Enumeration and pass it on
536         NamingEnumeration<? extends Attribute> attrEnum = attrs.getAll();
537         ModificationItem[] mods = new ModificationItem[attrs.size()];
538         for (int i = 0; i < mods.length && attrEnum.hasMoreElements(); i++) {
539             mods[i] = new ModificationItem(mod_op, attrEnum.next());
540         }
541 
542         modifyAttributes(name, mods);
543     }
544 
545     public void modifyAttributes(String name, ModificationItem[] mods)
546         throws NamingException   {
547         modifyAttributes(myParser.parse(name), mods);
548     }
549 
550     public void modifyAttributes(Name name, ModificationItem[] mods)
551         throws NamingException {
552         HierMemDirCtx ctx = (HierMemDirCtx) doLookup(name, false);
553         ctx.doModifyAttributes(mods);
554     }
555 
556     protected void doModifyAttributes(ModificationItem[] mods)
557         throws NamingException {
558 
559         if (readOnlyEx != null) {
560             throw (NamingException) readOnlyEx.fillInStackTrace();
561         }
562 
563         applyMods(mods, attrs);
564     }
565 
566     protected static Attributes applyMods(ModificationItem[] mods,
567         Attributes orig) throws NamingException {
568 
569         ModificationItem mod;
570         Attribute existingAttr, modAttr;
571         NamingEnumeration<?> modVals;
572 
573         for (int i = 0; i < mods.length; i++) {
574             mod = mods[i];
575             modAttr = mod.getAttribute();
576 
577             switch(mod.getModificationOp()) {
578             case ADD_ATTRIBUTE:
579                 if (debug) {
580                     System.out.println("HierMemDSCtx: adding " +
581                                        mod.getAttribute().toString());
582                 }
583                 existingAttr = orig.get(modAttr.getID());
584                 if (existingAttr == null) {
585                     orig.put((Attribute)modAttr.clone());
586                 } else {
587                     // Add new attribute values to existing attribute
588                     modVals = modAttr.getAll();
589                     while (modVals.hasMore()) {
590                         existingAttr.add(modVals.next());
591                     }
592                 }
593                 break;
594             case REPLACE_ATTRIBUTE:
595                 if (modAttr.size() == 0) {
596                     orig.remove(modAttr.getID());
597                 } else {
598                     orig.put((Attribute)modAttr.clone());
599                 }
600                 break;
601             case REMOVE_ATTRIBUTE:
602                 existingAttr = orig.get(modAttr.getID());
603                 if (existingAttr != null) {
604                     if (modAttr.size() == 0) {
605                         orig.remove(modAttr.getID());
606                     } else {
607                         // Remove attribute values from existing attribute
608                         modVals = modAttr.getAll();
609                         while (modVals.hasMore()) {
610                             existingAttr.remove(modVals.next());
611                         }
612                         if (existingAttr.size() == 0) {
613                             orig.remove(modAttr.getID());
614                         }
615                     }
616                 }
617                 break;
618             default:
619                 throw new AttributeModificationException("Unknown mod_op");
620             }
621         }
622 
623         return orig;
624     }
625 
626     public NamingEnumeration<SearchResult> search(String name,
627                                                   Attributes matchingAttributes)
628         throws NamingException {
629         return search(name, matchingAttributes, null);
630     }
631 
632     public NamingEnumeration<SearchResult> search(Name name,
633                                                   Attributes matchingAttributes)
634         throws NamingException {
635             return search(name, matchingAttributes, null);
636     }
637 
638      public NamingEnumeration<SearchResult> search(String name,
639                                                    Attributes matchingAttributes,
640                                                    String[] attributesToReturn)
641         throws NamingException {
642         return search(myParser.parse(name), matchingAttributes,
643             attributesToReturn);
644     }
645 
646      public NamingEnumeration<SearchResult> search(Name name,
647                                                    Attributes matchingAttributes,
648                                                    String[] attributesToReturn)
649          throws NamingException {
650 
651         HierMemDirCtx target = (HierMemDirCtx) doLookup(name, false);
652 
653         SearchControls cons = new SearchControls();
654         cons.setReturningAttributes(attributesToReturn);
655 
656         return new LazySearchEnumerationImpl(
657             target.doListBindings(false),
658             new ContainmentFilter(matchingAttributes),
659             cons, this, myEnv,
660             false); // alwaysUseFactory ignored because objReturnFlag == false
661     }
662 
663     public NamingEnumeration<SearchResult> search(Name name,
664                                                   String filter,
665                                                   SearchControls cons)
666         throws NamingException {
667         DirContext target = (DirContext) doLookup(name, false);
668 
669         SearchFilter stringfilter = new SearchFilter(filter);
670         return new LazySearchEnumerationImpl(
671             new HierContextEnumerator(target,
672                 (cons != null) ? cons.getSearchScope() :
673                 SearchControls.ONELEVEL_SCOPE),
674             stringfilter,
675             cons, this, myEnv, alwaysUseFactory);
676     }
677 
678      public NamingEnumeration<SearchResult> search(Name name,
679                                                    String filterExpr,
680                                                    Object[] filterArgs,
681                                                    SearchControls cons)
682             throws NamingException {
683 
684         String strfilter = SearchFilter.format(filterExpr, filterArgs);
685         return search(name, strfilter, cons);
686     }
687 
688     public NamingEnumeration<SearchResult> search(String name,
689                                                   String filter,
690                                                   SearchControls cons)
691         throws NamingException {
692         return search(myParser.parse(name), filter, cons);
693     }
694 
695     public NamingEnumeration<SearchResult> search(String name,
696                                                   String filterExpr,
697                                                   Object[] filterArgs,
698                                                   SearchControls cons)
699             throws NamingException {
700         return search(myParser.parse(name), filterExpr, filterArgs, cons);
701     }
702 
703     // This function is called whenever a new object needs to be created.
704     // this is used so that if anyone subclasses us, they can override this
705     // and return object of their own kind.
706     protected HierMemDirCtx createNewCtx() throws NamingException {
707         return new HierMemDirCtx(myEnv, ignoreCase);
708     }
709 
710     // If the supplied name is a composite name, return the name that
711     // is its first component.
712     protected Name canonizeName(Name name) throws NamingException {
713         Name canonicalName = name;
714 
715         if(!(name instanceof HierarchicalName)) {
716             // If name is not of the correct type, make copy
717             canonicalName = new HierarchicalName();
718             int n = name.size();
719             for(int i = 0; i < n; i++) {
720                 canonicalName.add(i, name.get(i));
721             }
722         }
723 
724         return canonicalName;
725     }
726 
727      protected Name getInternalName(Name name) throws NamingException {
728          return (name.getPrefix(name.size() - 1));
729      }
730 
731      protected Name getLeafName(Name name) throws NamingException {
732          return (name.getSuffix(name.size() - 1));
733      }
734 
735 
736      public DirContext getSchema(String name) throws NamingException {
737         throw new OperationNotSupportedException();
738     }
739 
740      public DirContext getSchema(Name name) throws NamingException {
741         throw new OperationNotSupportedException();
742     }
743 
744      public DirContext getSchemaClassDefinition(String name)
745         throws NamingException {
746         throw new OperationNotSupportedException();
747     }
748 
749     public DirContext getSchemaClassDefinition(Name name)
750             throws NamingException {
751         throw new OperationNotSupportedException();
752     }
753 
754     // Set context in readonly mode; throw e when update operation attempted.
755     public void setReadOnly(NamingException e) {
756         readOnlyEx = e;
757     }
758 
759     // Set context to support case-insensitive names
760     public void setIgnoreCase(boolean ignoreCase) {
761         this.ignoreCase = ignoreCase;
762     }
763 
764     public void setNameParser(NameParser parser) {
765         myParser = parser;
766     }
767 
768     /*
769      * Common base class for FlatNames and FlatBindings.
770      */
771     private abstract class BaseFlatNames<T> implements NamingEnumeration<T> {
772         Enumeration<Name> names;
773 
774         BaseFlatNames (Enumeration<Name> names) {
775             this.names = names;
776         }
777 
778         public final boolean hasMoreElements() {
779             try {
780                 return hasMore();
781             } catch (NamingException e) {
782                 return false;
783             }
784         }
785 
786         public final boolean hasMore() throws NamingException {
787             return names.hasMoreElements();
788         }
789 
790         public final T nextElement() {
791             try {
792                 return next();
793             } catch (NamingException e) {
794                 throw new NoSuchElementException(e.toString());
795             }
796         }
797 
798         public abstract T next() throws NamingException;
799 
800         public final void close() {
801             names = null;
802         }
803     }
804 
805     // Class for enumerating name/class pairs
806     private final class FlatNames extends BaseFlatNames<NameClassPair> {
807         FlatNames (Enumeration<Name> names) {
808             super(names);
809         }
810 
811         @Override
812         public NameClassPair next() throws NamingException {
813             Name name = names.nextElement();
814             String className = bindings.get(name).getClass().getName();
815             return new NameClassPair(name.toString(), className);
816         }
817     }
818 
819     // Class for enumerating bindings
820     private final class FlatBindings extends BaseFlatNames<Binding> {
821         private Hashtable<Name, Object> bds;
822         private Hashtable<String, Object> env;
823         private boolean useFactory;
824 
825         FlatBindings(Hashtable<Name, Object> bindings,
826                      Hashtable<String, Object> env,
827                      boolean useFactory) {
828             super(bindings.keys());
829             this.env = env;
830             this.bds = bindings;
831             this.useFactory = useFactory;
832         }
833 
834         @Override
835         public Binding next() throws NamingException {
836             Name name = names.nextElement();
837 
838             HierMemDirCtx obj = (HierMemDirCtx)bds.get(name);
839 
840             Object answer = obj;
841             if (useFactory) {
842                 Attributes attrs = obj.getAttributes(""); // only method available
843                 try {
844                     answer = DirectoryManager.getObjectInstance(obj,
845                         name, HierMemDirCtx.this, env, attrs);
846                 } catch (NamingException e) {
847                     throw e;
848                 } catch (Exception e) {
849                     NamingException e2 = new NamingException(
850                         "Problem calling getObjectInstance");
851                     e2.setRootCause(e);
852                     throw e2;
853                 }
854             }
855 
856             return new Binding(name.toString(), answer);
857         }
858     }
859 
860     public class HierContextEnumerator extends ContextEnumerator {
861         public HierContextEnumerator(Context context, int scope)
862             throws NamingException {
863                 super(context, scope);
864         }
865 
866         protected HierContextEnumerator(Context context, int scope,
867             String contextName, boolean returnSelf) throws NamingException {
868             super(context, scope, contextName, returnSelf);
869         }
870 
871         protected NamingEnumeration<Binding> getImmediateChildren(Context ctx)
872             throws NamingException {
873                 return ((HierMemDirCtx)ctx).doListBindings(false);
874         }
875 
876         protected ContextEnumerator newEnumerator(Context ctx, int scope,
877             String contextName, boolean returnSelf) throws NamingException {
878                 return new HierContextEnumerator(ctx, scope, contextName,
879                     returnSelf);
880         }
881     }
882 }
883 
884     // CompundNames's HashCode() method isn't good enough for many string.
885     // The only prupose of this subclass is to have a more discerning
886     // hash function. We'll make up for the performance hit by caching
887     // the hash value.
888 
889 final class HierarchicalName extends CompoundName {
890     private int hashValue = -1;
891 
892     // Creates an empty name
893     HierarchicalName() {
894         super(new Enumeration<String>() {
895                   public boolean hasMoreElements() {return false;}
896                   public String nextElement() {throw new NoSuchElementException();}
897               },
898               HierarchicalNameParser.mySyntax);
899     }
900 
901     HierarchicalName(Enumeration<String> comps, Properties syntax) {
902         super(comps, syntax);
903     }
904 
905     HierarchicalName(String n, Properties syntax) throws InvalidNameException {
906         super(n, syntax);
907     }
908 
909     // just like String.hashCode, only it pays no attention to length
910     public int hashCode() {
911         if (hashValue == -1) {
912 
913             String name = toString().toUpperCase(Locale.ENGLISH);
914             int len = name.length();
915             int off = 0;
916             char val[] = new char[len];
917 
918             name.getChars(0, len, val, 0);
919 
920             for (int i = len; i > 0; i--) {
921                 hashValue = (hashValue * 37) + val[off++];
922             }
923         }
924 
925         return hashValue;
926     }
927 
928     public Name getPrefix(int posn) {
929         Enumeration<String> comps = super.getPrefix(posn).getAll();
930         return (new HierarchicalName(comps, mySyntax));
931     }
932 
933     public Name getSuffix(int posn) {
934         Enumeration<String> comps = super.getSuffix(posn).getAll();
935         return (new HierarchicalName(comps, mySyntax));
936     }
937 
938     public Object clone() {
939         return (new HierarchicalName(getAll(), mySyntax));
940     }
941 
942     private static final long serialVersionUID = -6717336834584573168L;
943 }
944 
945 // This is the default name parser (used if setNameParser is not called)
946 final class HierarchicalNameParser implements NameParser {
947     static final Properties mySyntax = new Properties();
948     static {
949         mySyntax.put("jndi.syntax.direction", "left_to_right");
950         mySyntax.put("jndi.syntax.separator", "/");
951         mySyntax.put("jndi.syntax.ignorecase", "true");
952         mySyntax.put("jndi.syntax.escape", "\\");
953         mySyntax.put("jndi.syntax.beginquote", "\"");
954         //mySyntax.put("jndi.syntax.separator.ava", "+");
955         //mySyntax.put("jndi.syntax.separator.typeval", "=");
956         mySyntax.put("jndi.syntax.trimblanks", "false");
957     };
958 
959     public Name parse(String name) throws NamingException {
960         return new HierarchicalName(name, mySyntax);
961     }
962 }