/* **************************************************************************

  $Archive: /njcl_v2/src/com/novell/service/file/nw/naming/ReferenceFactoryImpl.java $
  $Revision: 11 $
  $Modtime: 8/23/99 5:54p $
 
  Copyright (c) 1998 Novell, Inc.  All Rights Reserved.

  THIS WORK IS  SUBJECT  TO  U.S.  AND  INTERNATIONAL  COPYRIGHT  LAWS  AND
  TREATIES.   NO  PART  OF  THIS  WORK MAY BE  USED,  PRACTICED,  PERFORMED
  COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED,  ABRIDGED, CONDENSED,
  EXPANDED,  COLLECTED,  COMPILED,  LINKED,  RECAST, TRANSFORMED OR ADAPTED
  WITHOUT THE PRIOR WRITTEN CONSENT OF NOVELL, INC. ANY USE OR EXPLOITATION
  OF THIS WORK WITHOUT AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO
  CRIMINAL AND CIVIL LIABILITY.

****************************************************************************/

package com.novell.service.file.nw.naming;

import java.util.Hashtable;

import javax.naming.CompositeName;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.StringRefAddr;
import javax.naming.Context;

import com.novell.utility.naming.Environment;

import com.novell.utility.naming.spi.ReferenceFactory;
import com.novell.utility.naming.URLParser;

/** @internal
 * This class contains factory methods that generate references to contexts
 * inside the file system naming system.
 */
public class ReferenceFactoryImpl implements ReferenceFactory
{
   public static final String refAddrName =
         "com.novell.service.file.ServerPathRefAddr";

   /**
    * Creates a JNDI Reference for a given set of properties.
    *
    * <p>The properties are expected in this format:
    * <ul>
    * <li>FSEnvironment.SERVER_NAME = server name
    * <li>(optional) if FSEnvironment.SERVER_NAME is not present,
    *     then the property "java.naming.provider.url" will be used to
    *     supply a default value.
    * <li>FSEnvironment.PATH = full JNDI path of file system
    *     object
    * </ul>
    *
    * @param      props          Properties defining the reference
    *                            to return.
    * @return                    A fully constructed JNDI Reference
    *                            which contains all information to
    *                            allow recreation of the object through
    *                            com.novell.service.file.nw.naming.ContextFactoryImpl.
    */
   public Reference createReference (Hashtable props)
      throws NamingException
   {
      if (props == null)
         return (null);

      String serverName = null;
      String path = null;

      // look for host and path in "java.naming.provider.url"
      URLParser urlp = new 
         URLParser((String)props.get(Context.PROVIDER_URL));
      serverName = urlp.getHost();
      path = urlp.getPath();

      if (path == null)
         path = "";

      if (serverName == null)
         return (null);

      return (createReference (serverName, path, props));
   }

   /**
    * Creates a JNDI Reference given a server name and valid JNDI path.
    *
    * @param      serverName     NetWare server to connect to
    *                            to allow access to the file system in
    *                            which the object logically resides.
    * @param      path           Path to look up from the initial
    *                            context to get to the object.
    * @param      env            Environment that the context should be
    *                            constructed in.
    * @return                    A fully constructed JNDI Reference
    *                            which contains all information to
    *                            allow recreation of the object through
    *                            com.novell.service.file.nw.naming.ContextFactoryImpl.
    */
   public Reference createReference (String serverName, String path,
         Hashtable env)
      throws NamingException
   {
      Object obj = null;

      try
      {
         // we have to get the object, so that we can fill out all
         //   of the Reference members like className, etc.
         obj = ContextFactoryImpl.getObjectInstance (serverName, path, env);
      }
      catch (NamingException ne)
      {
         throw ne;
      }
      catch (Exception e)
      {
         NamingException ne = new NamingException ();

         ne.setRootCause (e);
         throw ne;
      }

      return (createReference (obj, serverName, path));
   }

   /**
    * Creates a JNDI Reference given an existing file system context.
    *
    * @param      ctx            Context inside the file system
    *                            naming system, for which to create
    *                            the reference.
    * @return                    A fully constructed JNDI Reference
    *                            which contains all information to
    *                            allow recreation of 'ctx' through
    *                            com.novell.service.file.nw.naming.ContextFactoryImpl.
    */
   public Reference createReference (FileSystemInitialDirContext ctx)
      throws NamingException
   {
      // this is simple, just a server name and an empty path
      return (createReference (ctx, ctx.getServerName(), ""));
   }

   /**
    * Creates a JNDI Reference given an existing file system context.
    *
    * @param      ctx            Context inside the file system
    *                            naming system, for which to create
    *                            the reference.
    * @return                    A fully constructed JNDI Reference
    *                            which contains all information to
    *                            allow recreation of 'ctx' through
    *                            com.novell.service.file.nw.naming.ContextFactoryImpl.
    */
   public Reference createReference (DirEntryDirContext ctx)
      throws NamingException
   {
      Name name;
      DirEntryDirContext iter = ctx;
      DirEntryDirContext temp;
      String comp;

      // use the file system naming convention to construct the file name
      name = ctx.getNameParser ("").parse ("");

      // while there is a parent directory, only a VolumeDirContext will
      //   not have a parent directory
      while (iter != null)
      {
         comp = iter.getName ();
         temp = iter.getDirectory ();

         // if there is a parent directory, prepend the component name
         if (temp != null)
            name.add (0, comp);
         // if there is no parent directory, that means we're appending
         //   the volume name, so we append the component name
         //   plus the name space specifier -- this forces the reference
         //   path to be stored (and later evaluated) in the name space in
         //   which it was originally resolved
         else
            name.add (0, comp + "+" + iter.getNameSpace ());

         iter = temp;
      }

      return (createReference (ctx, ctx.getServerName (),
            name.toString ()));
   }

   /**
    * Used by public methods to construct the actual JNDI reference
    * that will be passed to the outside world.
    *
    * @param      obj            Context inside the file system
    *                            naming system, for which to create
    *                            the reference.
    * @param      serverName     NetWare server to connect to
    *                            to allow access to the file system in
    *                            which 'obj' logically resides.
    * @param      path           Path to look up from the initial
    *                            context to get to 'obj'.
    * @return                    A fully constructed JNDI Reference
    *                            which contains all information to
    *                            allow recreation of 'obj' through
    *                            com.novell.service.file.nw.naming.ContextFactoryImpl.
    */
   private Reference createReference (Object obj, String serverName,
         String path)
      throws NamingException
   {
      StringRefAddr refAddr;
      String factoryName = null;
      Name serverPathCombo = new CompositeName ();

      // use CompositeName to put the two strings together -- actually,
      //   the use of CompositeName to accomplish this task is
      //   irrelevant -- it is just a convenient String concatenator,
      //   that you can get the components out of again
      serverPathCombo.add (serverName);
      serverPathCombo.add (path);

      // make one StringRefAddr with the server name, path combo
      refAddr = new StringRefAddr (ReferenceFactoryImpl.refAddrName,
            serverPathCombo.toString ());

      // pull the class name out of ContextFactoryImpl
      factoryName = new ContextFactoryImpl ().getClass ().getName ();

      if (obj == null || factoryName == null)
         return (null);

      return (new Reference (obj.getClass ().getName (), refAddr,
            factoryName, ""));
   }
}
