/**
   * Creates an instance of an object for the specified object and environment.
   *
   * <p>If an object factory builder has been installed, it is used to create a factory for creating
   * the object. Otherwise, the following rules are used to create the object:
   *
   * <ol>
   *   <li>If <code>refInfo</code> is a <code>Reference</code> or <code>Referenceable</code>
   *       containing a factory class name, use the named factory to create the object. Return
   *       <code>refInfo</code> if the factory cannot be created. Under JDK 1.1, if the factory
   *       class must be loaded from a location specified in the reference, a
   *       <tt>SecurityManager</tt> must have been installed or the factory creation will fail. If
   *       an exception is encountered while creating the factory, it is passed up to the caller.
   *   <li>If <tt>refInfo</tt> is a <tt>Reference</tt> or <tt>Referenceable</tt> with no factory
   *       class name, and the address or addresses are <tt>StringRefAddr</tt>s with address type
   *       "URL", try the URL context factory corresponding to each URL's scheme id to create the
   *       object (see <tt>getURLContext()</tt>). If that fails, continue to the next step.
   *   <li>Use the object factories specified in the <tt>Context.OBJECT_FACTORIES</tt> property of
   *       the environment, and of the provider resource file associated with <tt>nameCtx</tt>, in
   *       that order. The value of this property is a colon-separated list of factory class names
   *       that are tried in order, and the first one that succeeds in creating an object is the one
   *       used. If none of the factories can be loaded, return <code>refInfo</code>. If an
   *       exception is encountered while creating the object, the exception is passed up to the
   *       caller.
   * </ol>
   *
   * <p>Service providers that implement the <tt>DirContext</tt> interface should use
   * <tt>DirectoryManager.getObjectInstance()</tt>, not this method. Service providers that
   * implement only the <tt>Context</tt> interface should use this method.
   *
   * <p>Note that an object factory (an object that implements the ObjectFactory interface) must be
   * public and must have a public constructor that accepts no arguments.
   *
   * <p>The <code>name</code> and <code>nameCtx</code> parameters may optionally be used to specify
   * the name of the object being created. <code>name</code> is the name of the object, relative to
   * context <code>nameCtx</code>. This information could be useful to the object factory or to the
   * object implementation. If there are several possible contexts from which the object could be
   * named -- as will often be the case -- it is up to the caller to select one. A good rule of
   * thumb is to select the "deepest" context available. If <code>nameCtx</code> is null, <code>name
   * </code> is relative to the default initial context. If no name is being specified, the <code>
   * name</code> parameter should be null.
   *
   * @param refInfo The possibly null object for which to create an object.
   * @param name The name of this object relative to <code>nameCtx</code>. Specifying a name is
   *     optional; if it is omitted, <code>name</code> should be null.
   * @param nameCtx The context relative to which the <code>name</code> parameter is specified. If
   *     null, <code>name</code> is relative to the default initial context.
   * @param environment The possibly null environment to be used in the creation of the object
   *     factory and the object.
   * @return An object created using <code>refInfo</code>; or <code>refInfo</code> if an object
   *     cannot be created using the algorithm described above.
   * @exception NamingException if a naming exception was encountered while attempting to get a URL
   *     context, or if one of the factories accessed throws a NamingException.
   * @exception Exception if one of the factories accessed throws an exception, or if an error was
   *     encountered while loading and instantiating the factory and object classes. A factory
   *     should only throw an exception if it does not want other factories to be used in an attempt
   *     to create an object. See ObjectFactory.getObjectInstance().
   * @see #getURLContext
   * @see ObjectFactory
   * @see ObjectFactory#getObjectInstance
   */
  public static Object getObjectInstance(
      Object refInfo, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {

    ObjectFactory factory;

    // Use builder if installed
    ObjectFactoryBuilder builder = getObjectFactoryBuilder();
    if (builder != null) {
      // builder must return non-null factory
      factory = builder.createObjectFactory(refInfo, environment);
      return factory.getObjectInstance(refInfo, name, nameCtx, environment);
    }

    // Use reference if possible
    Reference ref = null;
    if (refInfo instanceof Reference) {
      ref = (Reference) refInfo;
    } else if (refInfo instanceof Referenceable) {
      ref = ((Referenceable) (refInfo)).getReference();
    }

    Object answer;

    if (ref != null) {
      String f = ref.getFactoryClassName();
      if (f != null) {
        // if reference identifies a factory, use exclusively

        factory = getObjectFactoryFromReference(ref, f);
        if (factory != null) {
          return factory.getObjectInstance(ref, name, nameCtx, environment);
        }
        // No factory found, so return original refInfo.
        // Will reach this point if factory class is not in
        // class path and reference does not contain a URL for it
        return refInfo;

      } else {
        // if reference has no factory, check for addresses
        // containing URLs

        answer = processURLAddrs(ref, name, nameCtx, environment);
        if (answer != null) {
          return answer;
        }
      }
    }

    // try using any specified factories
    answer = createObjectFromFactories(refInfo, name, nameCtx, environment);
    return (answer != null) ? answer : refInfo;
  }
  /**
   * Creates an object for the given URL scheme id using the supplied urlInfo.
   *
   * <p>If urlInfo is null, the result is a context for resolving URLs with the scheme id 'scheme'.
   * If urlInfo is a URL, the result is a context named by the URL. Names passed to this context is
   * assumed to be relative to this context (i.e. not a URL). For example, if urlInfo is
   * "ldap://ldap.wiz.com/o=Wiz,c=us", the resulting context will be that pointed to by "o=Wiz,c=us"
   * on the server 'ldap.wiz.com'. Subsequent names that can be passed to this context will be LDAP
   * names relative to this context (e.g. cn="Barbs Jensen"). If urlInfo is an array of URLs, the
   * URLs are assumed to be equivalent in terms of the context to which they refer. The resulting
   * context is like that of the single URL case. If urlInfo is of any other type, that is handled
   * by the context factory for the URL scheme.
   *
   * @param scheme the URL scheme id for the context
   * @param urlInfo information used to create the context
   * @param name name of this object relative to <code>nameCtx</code>
   * @param nameCtx Context whose provider resource file will be searched for package prefix values
   *     (or null if none)
   * @param environment Environment properties for creating the context
   * @see javax.naming.InitialContext
   */
  private static Object getURLObject(
      String scheme, Object urlInfo, Name name, Context nameCtx, Hashtable<?, ?> environment)
      throws NamingException {

    // e.g. "ftpURLContextFactory"
    ObjectFactory factory =
        (ObjectFactory)
            ResourceManager.getFactory(
                Context.URL_PKG_PREFIXES,
                environment,
                nameCtx,
                "." + scheme + "." + scheme + "URLContextFactory",
                defaultPkgPrefix);

    if (factory == null) return null;

    // Found object factory
    try {
      return factory.getObjectInstance(urlInfo, name, nameCtx, environment);
    } catch (NamingException e) {
      throw e;
    } catch (Exception e) {
      NamingException ne = new NamingException();
      ne.setRootCause(e);
      throw ne;
    }
  }
  /**
   * Creates an object using the factories specified in the <tt>Context.OBJECT_FACTORIES</tt>
   * property of the environment or of the provider resource file associated with <tt>nameCtx</tt>.
   *
   * @return factory created; null if cannot create
   */
  private static Object createObjectFromFactories(
      Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {

    FactoryEnumeration factories =
        ResourceManager.getFactories(Context.OBJECT_FACTORIES, environment, nameCtx);

    if (factories == null) return null;

    // Try each factory until one succeeds
    ObjectFactory factory;
    Object answer = null;
    while (answer == null && factories.hasMore()) {
      factory = (ObjectFactory) factories.next();
      answer = factory.getObjectInstance(obj, name, nameCtx, environment);
    }
    return answer;
  }