// FormResourceEditor.java
// $Id: FormResourceEditor.java,v 1.15 1997/03/07 12:45:32 abaird Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html

package w3c.jigsaw.formedit;

import java.util.*;
import java.net.*;

import w3c.tools.store.*;
import w3c.www.http.*;
import w3c.jigsaw.http.*;
import w3c.jigsaw.resources.*;
import w3c.jigsaw.forms.*;

 
public class FormResourceEditor extends HTTPResource
    implements ResourceLocker 
{
    private static final String nocache[] = { "no-cache" };

    Hashtable loaded   = null ;
    Hashtable bytarget = null ;

    int       count  = 1 ;

    /**
     * Clone the generic form based resource editor.
     */

    public Object clone()
	throws CloneNotSupportedException
    {
	FormResourceEditor cl = (FormResourceEditor) super.clone();
	cl.loaded   = (Hashtable) loaded.clone();
	cl.bytarget = (Hashtable) bytarget.clone();
	return cl;
    }

    /**
     * Generate a new uniq identifier for the editor's resource.
     */

    protected String getNextURLPart() {
	return "editor@"+(count++);
    }

    /**
     * Resource locker implementation - This resource is going down.
     * @param resource The resource going down.
     */

    public synchronized void notifyResourceLockBreak(Resource r) {
	if ( ! (r instanceof HTTPResource) ) 
	    throw new RuntimeException ("*** Implementation bug") ;
	HTTPResource resource = (HTTPResource) r ;
	String       urlpath  = resource.getURLPath();
	HTTPResource editor   = (HTTPResource) bytarget.get(urlpath);
	if ( editor != null ) {
	    loaded.remove(editor.getIdentifier()) ;
	    bytarget.remove(urlpath) ;
	}
    }

    /**
     * Lookup the target we want to edit in the server space.
     * @param state A lookup state to pursue to find the target.
     * @return An instance of Resource, or <strong>null</strong>.
     */
    
    public HTTPResource lookupTarget(HTTPResource resource, LookupState state)
        throws HTTPException             
    {                        
        HTTPResource target = resource ;
	LookupResult result = new LookupResult(resource);
	// Mark the lookup state as being internal:
	state.markInternal() ;
	// Lookup the target to be edited:
	resource.lookup(state, result);
        return result.getTarget();
    }    

    /**
     * lookup a resource in the editor space.
     * If we have started an editor for the resource, relocate to it, otherwise
     * create a new ediutor, install it, and return it.
     * @param state The current lookup state.
     * @@return A resource instance, or <strong>null</strong>
     * @exception HTTPException If something wen't wrong, or if a relocation
     *    is needed.
     */

    public boolean lookup(LookupState ls, LookupResult lr) 
	throws HTTPException
    {
	if ( super.lookup(ls, lr) )
	    return true;
	// Do we have an editor ready for this resource ?
	String       comp     = ls.peekNextComponent() ;
	HTTPResource resource = (HTTPResource) loaded.get(comp) ;
	Request      request  = (ls.hasRequest() ? ls.getRequest() : null);
	if ( resource != null ) {
	    ls.getNextComponent() ;
	    lr.setTarget(resource);
	    return resource.lookup(ls, lr);
	}
	// Lookup the target resource, from the server's root, and edit it:
	HTTPResource target = lookupTarget(getServer().getEditRoot(), ls) ;
	if ( target == null ) {
	    lr.setTarget(null);
	    return true;
	}
	// Check for access by target:
	resource = (HTTPResource) bytarget.get(target.getURLPath()) ;
	if ( resource != null ) {
	    Reply  reloc = ls.getRequest().makeReply(HTTP.MOVED_TEMPORARILY);
	    URL    loc   = null;
	    try {
		loc = ((request != null)
		       ? resource.getURL(request)
		       : new URL(getServer().getURL(), resource.getURLPath()));
	    } catch (Exception ex) {
		getServer().errlog(this, "unable to build url for "
				   + resource.getURLPath());
		throw new HTTPException("Internal server error");
	    }
	    reloc.setPragma(nocache);
	    reloc.setNoCache(null);
	    reloc.setContent("relocate !");
	    reloc.setLocation(loc);
	    lr.setReply(reloc);
	    return true;
	}
	// Instantiate an editor for this target:
	Hashtable defs = new Hashtable(11);
	String    part = getNextURLPart() ;
	defs.put("identifier", part) ;
	defs.put("parent", this) ;
	defs.put("url", getURLPath() +"/" + part) ;
	defs.put("target", target) ;
	defs.put("context", getContext());
	HTTPResource editor = FormEditorFactory.getEditor(target);
	editor.initialize(defs) ;
	target.lock(this) ;
	// Install it in our loaded editor set, and relocate to it:
	loaded.put(part, editor) ;
	bytarget.put(target.getURLPath(), editor) ;
	Reply reloc = ls.getRequest().makeReply(HTTP.MOVED_TEMPORARILY);
	URL   loc   = null;
	try {
	    loc = ((request != null)
		   ? editor.getURL(request)
		   : new URL(getServer().getURL(), editor.getURLPath()));
	} catch (Exception ex) {
	    getServer().errlog(this, "unable to build url for "
                               + editor.getURLPath());
	    throw new HTTPException("Internal server error");
	}
	reloc.setPragma(nocache);
	reloc.setNoCache(null);
	reloc.setLocation(editor.getURL(request));
	reloc.setContent("relocate !");
	lr.setReply(reloc);
	return true;
    }

    /**
     * If a get is performed directly on this resource, edit the root.
     * @param request The request to handle.
     */

    public Reply get(Request request)
	throws HTTPException
    {
	HTTPResource target = getServer().getEditRoot() ;
	// Check by target:
	HTTPResource resource = (HTTPResource) bytarget.get("_root_") ;
	if ( resource != null ) {
	    Reply reloc = request.makeReply(HTTP.MOVED_TEMPORARILY);
	    reloc.setPragma(nocache);
	    reloc.setNoCache(null);
	    reloc.setContent("relocate !");
	    reloc.setLocation(resource.getURL(request)) ;
	    throw new HTTPException(reloc) ;
	}
	// Instantiate an editor for this target:
	Hashtable defs = new Hashtable(5);
	String    part = getNextURLPart() ;
	defs.put("parent", this) ;
	defs.put("url", getURLPath() +"/" + part) ;
	defs.put("target", target) ;
	defs.put("context", getContext());
	HTTPResource editor = FormEditorFactory.getEditor(target);
	editor.initialize(defs) ;
	// Install it in our loaded editor set, and relocate to it:
	loaded.put(part, editor) ;
	bytarget.put("_root_", editor) ;
	Reply reloc = request.makeReply(HTTP.MOVED_TEMPORARILY);
	reloc.setPragma(nocache);
	reloc.setNoCache(null);
	reloc.setContent("relocated !");
	reloc.setLocation(editor.getURL(request)) ;
	throw new HTTPException(reloc) ;
    }

    /**
     * initialize this resource editor.
     * @param values The default attribute values.
     */

    public void initialize(Object values[]) {
	super.initialize(values) ;
	// Some unfortunate hacking:
	this.loaded   = new Hashtable() ;
	this.bytarget = new Hashtable() ;
    }

}
