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 "{@code do<OperationName>}" on the result of the lookup. The
  36  * "{@code 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     // CompoundNames's HashCode() method isn't good enough for many strings.
 885     // The only purpose 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 }