//----------------------------BEGIN LICENSE----------------------------
/* 
 * Willow : the Open Source WorkFlow Project
 * Distributable under GNU LGPL license by gun.org
 *
 * Copyright (C) 2004-2010  huihoo.org 
 * Copyright (C) 2004-2010  ZosaTapo <dertyang@hotmail.com> 
 * 
 * ====================================================================
 * Project Homepage : http://www.huihoo.org/willow
 * Source Forge     : http://sourceforge.net/projects/huihoo
 * Mailing list     : willow@lists.sourceforge.net 
 */
//----------------------------END  LICENSE-----------------------------

package org.huihoo.willow.naming.resources;

import java.net.URL;
import java.net.URLConnection;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileNotFoundException;
import java.security.Permission;
import java.util.Date;
import java.util.Enumeration;
import java.util.Vector;
import javax.naming.NamingException;
import javax.naming.NamingEnumeration;
import javax.naming.NameClassPair;
import javax.naming.directory.DirContext;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import org.huihoo.willow.naming.JndiPermission;
import org.huihoo.willow.naming.resources.Resource;
import org.huihoo.willow.naming.resources.ResourceAttributes;

/**
 * Connection to a JNDI directory context.
 * <p/>
 * Note: All the object attribute names are the WebDAV names, not the HTTP 
 * names, so this class overrides some methods from URLConnection to do the
 * queries using the right names. Content handler is also not used; the 
 * content is directly returned.
 * 
 */
public class DirContextURLConnection extends URLConnection
{

	// ----------------------------------------------------------- Constructors

	public DirContextURLConnection(DirContext context, URL url)
	{
		super(url);
		if (context == null)
		{
			throw new IllegalArgumentException("Directory context can't be null");
		}
		if (System.getSecurityManager() != null)
		{
			this.permission = new JndiPermission(url.toString());
		}
		this.context = context;
	}

	// ----------------------------------------------------- Instance Variables

	/**
	 * Directory context.
	 */
	protected DirContext context;

	/**
	 * Associated resource.
	 */
	protected Resource resource;

	/**
	 * Associated DirContext.
	 */
	protected DirContext collection;

	/**
	 * Other unknown object.
	 */
	protected Object object;

	/**
	 * Attributes.
	 */
	protected Attributes attributes;

	/**
	 * Date.
	 */
	protected long date;

	/**
	 * Permission
	 */
	protected Permission permission;

	// ------------------------------------------------------------- Properties

	/**
	 * Connect to the DirContext, and retrive the bound object, as well as
	 * its attributes. If no object is bound with the name specified in the
	 * URL, then an IOException is thrown.
	 * 
	 * @throws IOException Object not found
	 */
	public void connect() throws IOException
	{

		if (!connected)
		{

			try
			{
				date = System.currentTimeMillis();
				String path = getURL().getFile();
				if (context instanceof ProxyDirContext)
				{
					ProxyDirContext proxyDirContext = (ProxyDirContext) context;
					String engineName = proxyDirContext.getEngineName();
					String contextName = proxyDirContext.getContextName();
					
					
					if (engineName != null)
					{
						if (!path.startsWith("/" + engineName + "/"))
						{
							return;
						}
						path = path.substring(engineName.length() + 1);
						System.err.println("path xxxx==> "+path);
					}
					if (contextName != null)
					{
						if (!path.startsWith("/"+contextName + "/"))
						{
							return;
						}
		
						path = path.substring(contextName.length()+1);
					}
				}
				
				object = context.lookup(path);
				
				attributes = context.getAttributes(path);
				if (object instanceof Resource)
				{
					resource = (Resource) object;
				}
				if (object instanceof DirContext)
				{
					collection = (DirContext) object;
				}
			}
			catch (NamingException e)
			{
				// Object not found
			}

			connected = true;

		}

	}

	/**
	 * Return the content length value.
	 */
	public int getContentLength()
	{
		return getHeaderFieldInt(ResourceAttributes.CONTENT_LENGTH, -1);
	}

	/**
	 * Return the content type value.
	 */
	public String getContentType()
	{
		return getHeaderField(ResourceAttributes.CONTENT_TYPE);
	}

	/**
	 * Return the last modified date.
	 */
	public long getDate()
	{
		return date;
	}

	/**
	 * Return the last modified date.
	 */
	public long getLastModified()
	{

		if (!connected)
		{
			// Try to connect (silently)
			try
			{
				connect();
			}
			catch (IOException e)
			{
			}
		}

		if (attributes == null)
		{
			return 0;
		}

		Attribute lastModified = attributes.get(ResourceAttributes.LAST_MODIFIED);
		if (lastModified != null)
		{
			try
			{
				Date lmDate = (Date) lastModified.get();
				return lmDate.getTime();
			}
			catch (Exception e)
			{
			}
		}

		return 0;
	}

	/**
	 * Returns the name of the specified header field.
	 */
	public String getHeaderField(String name)
	{

		if (!connected)
		{
			// Try to connect (silently)
			try
			{
				connect();
			}
			catch (IOException e)
			{
			}
		}

		if (attributes == null)
		{
			return (null);
		}

		Attribute attribute = attributes.get(name);
		try
		{
			return attribute.get().toString();
		}
		catch (Exception e)
		{
			// Shouldn't happen, unless the attribute has no value
		}

		return (null);

	}

	/**
	 * Get object content.
	 */
	public Object getContent() throws IOException
	{

		if (!connected)
		{
			connect();
		}

		if (resource != null)
		{
			return getInputStream();
		}
		if (collection != null)
		{
			return collection;
		}
		if (object != null)
		{
			return object;
		}

		throw new FileNotFoundException();

	}

	/**
	 * Get object content.
	 */
	public Object getContent(Class[] classes) throws IOException
	{

		Object object = getContent();

		for (int i = 0; i < classes.length; i++)
		{
			if (classes[i].isInstance(object))
			{
				return object;
			}
		}

		return null;

	}

	/**
	 * Get input stream.
	 */
	public InputStream getInputStream() throws IOException
	{

		if (!connected)
		{
			connect();
		}

		if (resource == null)
		{
			throw new FileNotFoundException();
		}
		else
		{
			// Reopen resource
			try
			{
				resource = (Resource) context.lookup(getURL().getFile());
			}
			catch (NamingException e)
			{
			}
		}

		return (resource.streamContent());

	}

	/**
	 * Get the Permission for this URL
	 */
	public Permission getPermission()
	{

		return permission;
	}

	// --------------------------------------------------------- Public Methods

	/**
	 * List children of this collection. The names given are relative to this
	 * URI's path. The full uri of the children is then : path + "/" + name.
	 */
	public Enumeration list() throws IOException
	{

		if (!connected)
		{
			connect();
		}

		if ((resource == null) && (collection == null))
		{
			throw new FileNotFoundException();
		}

		Vector result = new Vector();

		if (collection != null)
		{
			try
			{
				NamingEnumeration enum = context.list(getURL().getFile());
				while (enum.hasMoreElements())
				{
					NameClassPair ncp = (NameClassPair) enum.nextElement();
					result.addElement(ncp.getName());
				}
			}
			catch (NamingException e)
			{
				// Unexpected exception
				throw new FileNotFoundException();
			}
		}

		return result.elements();

	}

}
