1 /*
   2  * Copyright (c) 1999, 2002, 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 myEnv;
  46     protected Hashtable 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 environment, boolean ignoreCase) {
  74         this(environment, ignoreCase, false);
  75     }
  76 
  77     protected HierMemDirCtx(Hashtable environment, boolean ignoreCase,
  78         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 list(String name) throws NamingException {
 330         return list(myParser.parse(name));
 331     }
 332 
 333     public NamingEnumeration list(Name name) throws NamingException {
 334         HierMemDirCtx ctx = (HierMemDirCtx) doLookup(name, false);
 335         return ctx.doList();
 336     }
 337 
 338     protected NamingEnumeration doList () throws NamingException {
 339         return new FlatNames(bindings.keys());
 340     }
 341 
 342 
 343     public NamingEnumeration listBindings(String name) throws NamingException {
 344         return listBindings(myParser.parse(name));
 345     }
 346 
 347     public NamingEnumeration listBindings(Name name) throws NamingException {
 348         HierMemDirCtx ctx = (HierMemDirCtx)doLookup(name, false);
 349         return ctx.doListBindings(alwaysUseFactory);
 350     }
 351 
 352     protected NamingEnumeration 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     public Object addToEnvironment(String propName, Object propVal)
 451             throws NamingException {
 452         myEnv = (myEnv == null) ?
 453             new Hashtable(11, 0.75f) : (Hashtable)myEnv.clone();
 454 
 455         return myEnv.put(propName, propVal);
 456     }
 457 
 458     public Object removeFromEnvironment(String propName)
 459             throws NamingException {
 460         if (myEnv == null)
 461             return null;
 462 
 463         myEnv = (Hashtable)myEnv.clone();
 464         return myEnv.remove(propName);
 465     }
 466 
 467     public Hashtable getEnvironment() throws NamingException {
 468         if (myEnv == null) {
 469             return new Hashtable(5, 0.75f);
 470         } else {
 471             return (Hashtable)myEnv.clone();
 472         }
 473     }
 474 
 475     public Attributes getAttributes(String name)
 476        throws NamingException {
 477        return getAttributes(myParser.parse(name));
 478     }
 479 
 480     public Attributes getAttributes(Name name)
 481         throws NamingException {
 482         HierMemDirCtx ctx = (HierMemDirCtx) doLookup(name, false);
 483         return ctx.doGetAttributes();
 484     }
 485 
 486     protected Attributes doGetAttributes() throws NamingException {
 487         return (Attributes)attrs.clone();
 488     }
 489 
 490     public Attributes getAttributes(String name, String[] attrIds)
 491         throws NamingException {
 492         return getAttributes(myParser.parse(name), attrIds);
 493     }
 494 
 495     public Attributes getAttributes(Name name, String[] attrIds)
 496         throws NamingException {
 497         HierMemDirCtx ctx = (HierMemDirCtx) doLookup(name, false);
 498         return ctx.doGetAttributes(attrIds);
 499     }
 500 
 501     protected Attributes doGetAttributes(String[] attrIds)
 502         throws NamingException {
 503 
 504         if (attrIds == null) {
 505             return doGetAttributes();
 506         }
 507         Attributes attrs = new BasicAttributes(ignoreCase);
 508         Attribute attr = null;
 509             for(int i=0; i<attrIds.length; i++) {
 510                 attr = this.attrs.get(attrIds[i]);
 511                 if (attr != null) {
 512                     attrs.put(attr);
 513                 }
 514             }
 515         return attrs;
 516     }
 517 
 518     public void modifyAttributes(String name, int mod_op, Attributes attrs)
 519         throws NamingException   {
 520         modifyAttributes(myParser.parse(name), mod_op, attrs);
 521     }
 522 
 523     public void modifyAttributes(Name name, int mod_op, Attributes attrs)
 524         throws NamingException {
 525 
 526         if (attrs == null || attrs.size() == 0) {
 527             throw new IllegalArgumentException(
 528                 "Cannot modify without an attribute");
 529         }
 530 
 531         // turn it into a modification Enumeration and pass it on
 532         NamingEnumeration attrEnum = attrs.getAll();
 533         ModificationItem[] mods = new ModificationItem[attrs.size()];
 534         for (int i = 0; i < mods.length && attrEnum.hasMoreElements(); i++) {
 535             mods[i] = new ModificationItem(mod_op, (Attribute)attrEnum.next());
 536         }
 537 
 538         modifyAttributes(name, mods);
 539     }
 540 
 541     public void modifyAttributes(String name, ModificationItem[] mods)
 542         throws NamingException   {
 543         modifyAttributes(myParser.parse(name), mods);
 544     }
 545 
 546     public void modifyAttributes(Name name, ModificationItem[] mods)
 547         throws NamingException {
 548         HierMemDirCtx ctx = (HierMemDirCtx) doLookup(name, false);
 549         ctx.doModifyAttributes(mods);
 550     }
 551 
 552     protected void doModifyAttributes(ModificationItem[] mods)
 553         throws NamingException {
 554 
 555         if (readOnlyEx != null) {
 556             throw (NamingException) readOnlyEx.fillInStackTrace();
 557         }
 558 
 559         applyMods(mods, attrs);
 560     }
 561 
 562     protected static Attributes applyMods(ModificationItem[] mods,
 563         Attributes orig) throws NamingException {
 564 
 565         ModificationItem mod;
 566         Attribute existingAttr, modAttr;
 567         NamingEnumeration modVals;
 568 
 569         for (int i = 0; i < mods.length; i++) {
 570             mod = mods[i];
 571             modAttr = mod.getAttribute();
 572 
 573             switch(mod.getModificationOp()) {
 574             case ADD_ATTRIBUTE:
 575                 if (debug) {
 576                     System.out.println("HierMemDSCtx: adding " +
 577                                        mod.getAttribute().toString());
 578                 }
 579                 existingAttr = orig.get(modAttr.getID());
 580                 if (existingAttr == null) {
 581                     orig.put((Attribute)modAttr.clone());
 582                 } else {
 583                     // Add new attribute values to existing attribute
 584                     modVals = modAttr.getAll();
 585                     while (modVals.hasMore()) {
 586                         existingAttr.add(modVals.next());
 587                     }
 588                 }
 589                 break;
 590             case REPLACE_ATTRIBUTE:
 591                 if (modAttr.size() == 0) {
 592                     orig.remove(modAttr.getID());
 593                 } else {
 594                     orig.put((Attribute)modAttr.clone());
 595                 }
 596                 break;
 597             case REMOVE_ATTRIBUTE:
 598                 existingAttr = orig.get(modAttr.getID());
 599                 if (existingAttr != null) {
 600                     if (modAttr.size() == 0) {
 601                         orig.remove(modAttr.getID());
 602                     } else {
 603                         // Remove attribute values from existing attribute
 604                         modVals = modAttr.getAll();
 605                         while (modVals.hasMore()) {
 606                             existingAttr.remove(modVals.next());
 607                         }
 608                         if (existingAttr.size() == 0) {
 609                             orig.remove(modAttr.getID());
 610                         }
 611                     }
 612                 }
 613                 break;
 614             default:
 615                 throw new AttributeModificationException("Unknown mod_op");
 616             }
 617         }
 618 
 619         return orig;
 620     }
 621 
 622     public NamingEnumeration search(String name,
 623                                     Attributes matchingAttributes)
 624         throws NamingException {
 625         return search(name, matchingAttributes, null);
 626     }
 627 
 628     public NamingEnumeration search(Name name,
 629                                     Attributes matchingAttributes)
 630         throws NamingException {
 631             return search(name, matchingAttributes, null);
 632     }
 633 
 634      public NamingEnumeration search(String name,
 635                                     Attributes matchingAttributes,
 636                                     String[] attributesToReturn)
 637         throws NamingException {
 638         return search(myParser.parse(name), matchingAttributes,
 639             attributesToReturn);
 640     }
 641 
 642      public NamingEnumeration search(Name name,
 643                                     Attributes matchingAttributes,
 644                                     String[] attributesToReturn)
 645          throws NamingException {
 646 
 647         HierMemDirCtx target = (HierMemDirCtx) doLookup(name, false);
 648 
 649         SearchControls cons = new SearchControls();
 650         cons.setReturningAttributes(attributesToReturn);
 651 
 652         return new LazySearchEnumerationImpl(
 653             target.doListBindings(false),
 654             new ContainmentFilter(matchingAttributes),
 655             cons, this, myEnv,
 656             false); // alwaysUseFactory ignored because objReturnFlag == false
 657     }
 658 
 659     public NamingEnumeration search(Name name,
 660                                     String filter,
 661                                     SearchControls cons)
 662         throws NamingException {
 663         DirContext target = (DirContext) doLookup(name, false);
 664 
 665         SearchFilter stringfilter = new SearchFilter(filter);
 666         return new LazySearchEnumerationImpl(
 667             new HierContextEnumerator(target,
 668                 (cons != null) ? cons.getSearchScope() :
 669                 SearchControls.ONELEVEL_SCOPE),
 670             stringfilter,
 671             cons, this, myEnv, alwaysUseFactory);
 672     }
 673 
 674      public NamingEnumeration search(Name name,
 675                                     String filterExpr,
 676                                     Object[] filterArgs,
 677                                     SearchControls cons)
 678             throws NamingException {
 679 
 680         String strfilter = SearchFilter.format(filterExpr, filterArgs);
 681         return search(name, strfilter, cons);
 682     }
 683 
 684     public NamingEnumeration search(String name,
 685                                     String filter,
 686                                     SearchControls cons)
 687         throws NamingException {
 688         return search(myParser.parse(name), filter, cons);
 689     }
 690 
 691     public NamingEnumeration search(String name,
 692                                     String filterExpr,
 693                                     Object[] filterArgs,
 694                                     SearchControls cons)
 695             throws NamingException {
 696         return search(myParser.parse(name), filterExpr, filterArgs, cons);
 697     }
 698 
 699     // This function is called whenever a new object needs to be created.
 700     // this is used so that if anyone subclasses us, they can override this
 701     // and return object of their own kind.
 702     protected HierMemDirCtx createNewCtx() throws NamingException {
 703         return new HierMemDirCtx(myEnv, ignoreCase);
 704     }
 705 
 706     // If the supplied name is a composite name, return the name that
 707     // is its first component.
 708     protected Name canonizeName(Name name) throws NamingException {
 709         Name canonicalName = name;
 710 
 711         if(!(name instanceof HierarchicalName)) {
 712             // If name is not of the correct type, make copy
 713             canonicalName = new HierarchicalName();
 714             int n = name.size();
 715             for(int i = 0; i < n; i++) {
 716                 canonicalName.add(i, name.get(i));
 717             }
 718         }
 719 
 720         return canonicalName;
 721     }
 722 
 723      protected Name getInternalName(Name name) throws NamingException {
 724          return (name.getPrefix(name.size() - 1));
 725      }
 726 
 727      protected Name getLeafName(Name name) throws NamingException {
 728          return (name.getSuffix(name.size() - 1));
 729      }
 730 
 731 
 732      public DirContext getSchema(String name) throws NamingException {
 733         throw new OperationNotSupportedException();
 734     }
 735 
 736      public DirContext getSchema(Name name) throws NamingException {
 737         throw new OperationNotSupportedException();
 738     }
 739 
 740      public DirContext getSchemaClassDefinition(String name)
 741         throws NamingException {
 742         throw new OperationNotSupportedException();
 743     }
 744 
 745     public DirContext getSchemaClassDefinition(Name name)
 746             throws NamingException {
 747         throw new OperationNotSupportedException();
 748     }
 749 
 750     // Set context in readonly mode; throw e when update operation attempted.
 751     public void setReadOnly(NamingException e) {
 752         readOnlyEx = e;
 753     }
 754 
 755     // Set context to support case-insensitive names
 756     public void setIgnoreCase(boolean ignoreCase) {
 757         this.ignoreCase = ignoreCase;
 758     }
 759 
 760     public void setNameParser(NameParser parser) {
 761         myParser = parser;
 762     }
 763 
 764     // Class for enumerating name/class pairs
 765     private class FlatNames implements NamingEnumeration {
 766         Enumeration names;
 767 
 768         FlatNames (Enumeration names) {
 769             this.names = names;
 770         }
 771 
 772         public boolean hasMoreElements() {
 773             try {
 774                 return hasMore();
 775             } catch (NamingException e) {
 776                 return false;
 777             }
 778         }
 779 
 780         public boolean hasMore() throws NamingException {
 781             return names.hasMoreElements();
 782         }
 783 
 784         public Object nextElement() {
 785             try {
 786                 return next();
 787             } catch (NamingException e) {
 788                 throw new NoSuchElementException(e.toString());
 789             }
 790         }
 791 
 792         public Object next() throws NamingException {
 793             Name name = (Name)names.nextElement();
 794             String className = bindings.get(name).getClass().getName();
 795             return new NameClassPair(name.toString(), className);
 796         }
 797 
 798         public void close() {
 799             names = null;
 800         }
 801     }
 802 
 803    // Class for enumerating bindings
 804     private final class FlatBindings extends FlatNames {
 805         private Hashtable bds;
 806         private Hashtable env;
 807         private boolean useFactory;
 808 
 809         FlatBindings(Hashtable bindings, Hashtable env, boolean useFactory) {
 810             super(bindings.keys());
 811             this.env = env;
 812             this.bds = bindings;
 813             this.useFactory = useFactory;
 814         }
 815 
 816         public Object next() throws NamingException {
 817             Name name = (Name)names.nextElement();
 818 
 819             HierMemDirCtx obj = (HierMemDirCtx)bds.get(name);
 820 
 821             Object answer = obj;
 822             if (useFactory) {
 823                 Attributes attrs = obj.getAttributes(""); // only method available
 824                 try {
 825                     answer = DirectoryManager.getObjectInstance(obj,
 826                         name, HierMemDirCtx.this, env, attrs);
 827                 } catch (NamingException e) {
 828                     throw e;
 829                 } catch (Exception e) {
 830                     NamingException e2 = new NamingException(
 831                         "Problem calling getObjectInstance");
 832                     e2.setRootCause(e);
 833                     throw e2;
 834                 }
 835             }
 836 
 837             return new Binding(name.toString(), answer);
 838         }
 839     }
 840 
 841     public class HierContextEnumerator extends ContextEnumerator {
 842         public HierContextEnumerator(Context context, int scope)
 843             throws NamingException {
 844                 super(context, scope);
 845         }
 846 
 847         protected HierContextEnumerator(Context context, int scope,
 848             String contextName, boolean returnSelf) throws NamingException {
 849             super(context, scope, contextName, returnSelf);
 850         }
 851 
 852         protected NamingEnumeration getImmediateChildren(Context ctx)
 853             throws NamingException {
 854                 return ((HierMemDirCtx)ctx).doListBindings(false);
 855         }
 856 
 857         protected ContextEnumerator newEnumerator(Context ctx, int scope,
 858             String contextName, boolean returnSelf) throws NamingException {
 859                 return new HierContextEnumerator(ctx, scope, contextName,
 860                     returnSelf);
 861         }
 862     }
 863 }
 864 
 865     // CompundNames's HashCode() method isn't good enough for many string.
 866     // The only prupose of this subclass is to have a more discerning
 867     // hash function. We'll make up for the performance hit by caching
 868     // the hash value.
 869 
 870 final class HierarchicalName extends CompoundName {
 871     private int hashValue = -1;
 872 
 873     // Creates an empty name
 874     HierarchicalName() {
 875         super(new Enumeration() {
 876             public boolean hasMoreElements() {return false;}
 877             public Object nextElement() {throw new NoSuchElementException();}
 878         },
 879             HierarchicalNameParser.mySyntax);
 880     }
 881 
 882     HierarchicalName(Enumeration comps, Properties syntax) {
 883         super(comps, syntax);
 884     }
 885 
 886     HierarchicalName(String n, Properties syntax) throws InvalidNameException {
 887         super(n, syntax);
 888     }
 889 
 890     // just like String.hashCode, only it pays no attention to length
 891     public int hashCode() {
 892         if (hashValue == -1) {
 893 
 894             String name = toString().toUpperCase();
 895             int len = name.length();
 896             int off = 0;
 897             char val[] = new char[len];
 898 
 899             name.getChars(0, len, val, 0);
 900 
 901             for (int i = len; i > 0; i--) {
 902                 hashValue = (hashValue * 37) + val[off++];
 903             }
 904         }
 905 
 906         return hashValue;
 907     }
 908 
 909     public Name getPrefix(int posn) {
 910         Enumeration comps = super.getPrefix(posn).getAll();
 911         return (new HierarchicalName(comps, mySyntax));
 912     }
 913 
 914     public Name getSuffix(int posn) {
 915         Enumeration comps = super.getSuffix(posn).getAll();
 916         return (new HierarchicalName(comps, mySyntax));
 917     }
 918 
 919     public Object clone() {
 920         return (new HierarchicalName(getAll(), mySyntax));
 921     }
 922 
 923     private static final long serialVersionUID = -6717336834584573168L;
 924 }
 925 
 926 // This is the default name parser (used if setNameParser is not called)
 927 final class HierarchicalNameParser implements NameParser {
 928     static final Properties mySyntax = new Properties();
 929     static {
 930         mySyntax.put("jndi.syntax.direction", "left_to_right");
 931         mySyntax.put("jndi.syntax.separator", "/");
 932         mySyntax.put("jndi.syntax.ignorecase", "true");
 933         mySyntax.put("jndi.syntax.escape", "\\");
 934         mySyntax.put("jndi.syntax.beginquote", "\"");
 935         //mySyntax.put("jndi.syntax.separator.ava", "+");
 936         //mySyntax.put("jndi.syntax.separator.typeval", "=");
 937         mySyntax.put("jndi.syntax.trimblanks", "false");
 938     };
 939 
 940     public Name parse(String name) throws NamingException {
 941         return new HierarchicalName(name, mySyntax);
 942     }
 943 }