/**
   * Destroys the named context and removes it from the namespace. Any attributes associated with
   * the name are also removed. Intermediate contexts are not destroyed.
   *
   * <p>This method is idempotent. It succeeds even if the terminal atomic name is not bound in the
   * target context, but throws NameNotFoundException if any of the intermediate contexts do not
   * exist.
   *
   * <p>In a federated naming system, a context from one naming system may be bound to a name in
   * another. One can subsequently look up and perform operations on the foreign context using a
   * composite name. However, an attempt destroy the context using this composite name will fail
   * with NotContextException, because the foreign context is not a "subcontext" of the context in
   * which it is bound. Instead, use unbind() to remove the binding of the foreign context.
   * Destroying the foreign context requires that the destroySubcontext() be performed on a context
   * from the foreign context's "native" naming system.
   *
   * @param name the name of the context to be destroyed; may not be empty
   * @exception NameNotFoundException if an intermediate context does not exist
   * @exception NotContextException if the name is bound but does not name a context, or does not
   *     name a context of the appropriate type
   */
  @Override
  public void destroySubcontext(Name name) throws NamingException {

    if (!checkWritable()) {
      return;
    }

    while ((!name.isEmpty()) && (name.get(0).length() == 0)) name = name.getSuffix(1);
    if (name.isEmpty()) throw new NamingException(sm.getString("namingContext.invalidName"));

    NamingEntry entry = bindings.get(name.get(0));

    if (entry == null) {
      throw new NameNotFoundException(
          sm.getString("namingContext.nameNotBound", name, name.get(0)));
    }

    if (name.size() > 1) {
      if (entry.type == NamingEntry.CONTEXT) {
        ((Context) entry.value).destroySubcontext(name.getSuffix(1));
      } else {
        throw new NamingException(sm.getString("namingContext.contextExpected"));
      }
    } else {
      if (entry.type == NamingEntry.CONTEXT) {
        ((Context) entry.value).close();
        bindings.remove(name.get(0));
      } else {
        throw new NotContextException(sm.getString("namingContext.contextExpected"));
      }
    }
  }
  /**
   * Retrieves the named object.
   *
   * @param name the name of the object to look up
   * @param resolveLinks If true, the links will be resolved
   * @return the object bound to name
   * @exception NamingException if a naming exception is encountered
   */
  protected Object lookup(Name name, boolean resolveLinks) throws NamingException {

    // Removing empty parts
    while ((!name.isEmpty()) && (name.get(0).length() == 0)) name = name.getSuffix(1);
    if (name.isEmpty()) {
      // If name is empty, a newly allocated naming context is returned
      return new NamingContext(env, this.name, bindings);
    }

    NamingEntry entry = bindings.get(name.get(0));

    if (entry == null) {
      throw new NameNotFoundException(
          sm.getString("namingContext.nameNotBound", name, name.get(0)));
    }

    if (name.size() > 1) {
      // If the size of the name is greater that 1, then we go through a
      // number of subcontexts.
      if (entry.type != NamingEntry.CONTEXT) {
        throw new NamingException(sm.getString("namingContext.contextExpected"));
      }
      return ((Context) entry.value).lookup(name.getSuffix(1));
    } else {
      if ((resolveLinks) && (entry.type == NamingEntry.LINK_REF)) {
        String link = ((LinkRef) entry.value).getLinkName();
        if (link.startsWith(".")) {
          // Link relative to this context
          return lookup(link.substring(1));
        } else {
          return (new InitialContext(env)).lookup(link);
        }
      } else if (entry.type == NamingEntry.REFERENCE) {
        try {
          // TODO ÐèѧϰNamingManager
          Object obj = NamingManager.getObjectInstance(entry.value, name, this, env);
          if (entry.value instanceof ResourceRef) {
            boolean singleton =
                Boolean.parseBoolean(
                    (String) ((ResourceRef) entry.value).get("singleton").getContent());
            if (singleton) {
              entry.type = NamingEntry.ENTRY;
              entry.value = obj;
            }
          }
          return obj;
        } catch (NamingException e) {
          throw e;
        } catch (Exception e) {
          log.warn(sm.getString("namingContext.failResolvingReference"), e);
          throw new NamingException(e.getMessage());
        }
      } else {
        return entry.value;
      }
    }
  }
  /**
   * Binds a name to an object. All intermediate contexts and the target context (that named by all
   * but terminal atomic component of the name) must already exist.
   *
   * @param name the name to bind; may not be empty
   * @param obj the object to bind; possibly null
   * @param rebind if true, then perform a rebind (ie, overwrite)
   * @exception NameAlreadyBoundException if name is already bound
   * @exception javax.naming.directory.InvalidAttributesException if object did not supply all
   *     mandatory attributes
   * @exception NamingException if a naming exception is encountered
   */
  protected void bind(Name name, Object obj, boolean rebind) throws NamingException {

    if (!checkWritable()) {
      return;
    }

    while ((!name.isEmpty()) && (name.get(0).length() == 0)) name = name.getSuffix(1);
    if (name.isEmpty()) throw new NamingException(sm.getString("namingContext.invalidName"));

    NamingEntry entry = bindings.get(name.get(0));

    if (name.size() > 1) {
      if (entry == null) {
        throw new NameNotFoundException(
            sm.getString("namingContext.nameNotBound", name, name.get(0)));
      }
      if (entry.type == NamingEntry.CONTEXT) {
        if (rebind) {
          ((Context) entry.value).rebind(name.getSuffix(1), obj);
        } else {
          ((Context) entry.value).bind(name.getSuffix(1), obj);
        }
      } else {
        throw new NamingException(sm.getString("namingContext.contextExpected"));
      }
    } else {
      if ((!rebind) && (entry != null)) {
        throw new NameAlreadyBoundException(
            sm.getString("namingContext.alreadyBound", name.get(0)));
      } else {
        // Getting the type of the object and wrapping it within a new
        // NamingEntry
        Object toBind = NamingManager.getStateToBind(obj, name, this, env);
        if (toBind instanceof Context) {
          entry = new NamingEntry(name.get(0), toBind, NamingEntry.CONTEXT);
        } else if (toBind instanceof LinkRef) {
          entry = new NamingEntry(name.get(0), toBind, NamingEntry.LINK_REF);
        } else if (toBind instanceof Reference) {
          entry = new NamingEntry(name.get(0), toBind, NamingEntry.REFERENCE);
        } else if (toBind instanceof Referenceable) {
          toBind = ((Referenceable) toBind).getReference();
          entry = new NamingEntry(name.get(0), toBind, NamingEntry.REFERENCE);
        } else {
          entry = new NamingEntry(name.get(0), toBind, NamingEntry.ENTRY);
        }
        bindings.put(name.get(0), entry);
      }
    }
  }
 /** Throws a naming exception is Context is not writable. */
 protected boolean checkWritable() throws NamingException {
   if (isWritable()) {
     return true;
   } else {
     if (exceptionOnFailedWrite) {
       throw new javax.naming.OperationNotSupportedException(
           sm.getString("namingContext.readOnly"));
     }
   }
   return false;
 }
  /**
   * Enumerates the names bound in the named context, along with the objects bound to them. The
   * contents of any subcontexts are not included.
   *
   * <p>If a binding is added to or removed from this context, its effect on an enumeration
   * previously returned is undefined.
   *
   * @param name the name of the context to list
   * @return an enumeration of the bindings in this context. Each element of the enumeration is of
   *     type Binding.
   * @exception NamingException if a naming exception is encountered
   */
  @Override
  public NamingEnumeration<Binding> listBindings(Name name) throws NamingException {
    // Removing empty parts
    while ((!name.isEmpty()) && (name.get(0).length() == 0)) name = name.getSuffix(1);
    if (name.isEmpty()) {
      return new NamingContextBindingsEnumeration(bindings.values().iterator(), this);
    }

    NamingEntry entry = bindings.get(name.get(0));

    if (entry == null) {
      throw new NameNotFoundException(
          sm.getString("namingContext.nameNotBound", name, name.get(0)));
    }

    if (entry.type != NamingEntry.CONTEXT) {
      throw new NamingException(sm.getString("namingContext.contextExpected"));
    }
    return ((Context) entry.value).listBindings(name.getSuffix(1));
  }
  /**
   * Retrieves the parser associated with the named context. In a federation of namespaces,
   * different naming systems will parse names differently. This method allows an application to get
   * a parser for parsing names into their atomic components using the naming convention of a
   * particular naming system. Within any single naming system, NameParser objects returned by this
   * method must be equal (using the equals() test).
   *
   * @param name the name of the context from which to get the parser
   * @return a name parser that can parse compound names into their atomic components
   * @exception NamingException if a naming exception is encountered
   */
  @Override
  public NameParser getNameParser(Name name) throws NamingException {

    while ((!name.isEmpty()) && (name.get(0).length() == 0)) name = name.getSuffix(1);
    if (name.isEmpty()) return nameParser;

    if (name.size() > 1) {
      Object obj = bindings.get(name.get(0));
      if (obj instanceof Context) {
        return ((Context) obj).getNameParser(name.getSuffix(1));
      } else {
        throw new NotContextException(sm.getString("namingContext.contextExpected"));
      }
    }

    return nameParser;
  }
 /**
  * Retrieves the full name of this context within its own namespace.
  *
  * <p>Many naming services have a notion of a "full name" for objects in their respective
  * namespaces. For example, an LDAP entry has a distinguished name, and a DNS record has a fully
  * qualified name. This method allows the client application to retrieve this name. The string
  * returned by this method is not a JNDI composite name and should not be passed directly to
  * context methods. In naming systems for which the notion of full name does not make sense,
  * OperationNotSupportedException is thrown.
  *
  * @return this context's name in its own namespace; never null
  * @exception OperationNotSupportedException if the naming system does not have the notion of a
  *     full name
  * @exception NamingException if a naming exception is encountered
  */
 @Override
 public String getNameInNamespace() throws NamingException {
   throw new OperationNotSupportedException(sm.getString("namingContext.noAbsoluteName"));
   // FIXME ?
 }