// ResourceFrame.java
// $Id: ResourceFrame.java,v 1.10 1998/06/03 09:29:59 bmahe Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html

package org.w3c.tools.resources ;

import java.util.* ;
import java.io.* ;

import org.w3c.tools.resources.event.*;

/**
 * The resource frame class. A ResourceFrame can be attached to a
 * resource.
 */
public class ResourceFrame extends FramedResource 
    implements AttributeChangedListener
{

    /**
     * Our FrameEventListener.
     */
    protected transient FrameEventListener frameListener = null;

    /**
     * Our target resource.
     */
    protected FramedResource resource = null;

    static {
	Attribute a   = null ;
	Class     cls = null ;
	// Get a pointer to our own class:
	try {
	    cls  = Class.forName("org.w3c.tools.resources.ResourceFrame") ;
	} catch (Exception ex) {
	    ex.printStackTrace() ;
	    System.exit(1) ;
	}
    }

    /**
     * Get the file part of the URL this resource is attached to.
     * @return An URL object specifying the location in the information 
     *    space of this resource.
     */
    public String getURLPath() {
	return getString(ATTR_URL, getResource().getURLPath()) ;
    }

    /**
     * Get the space entry for that resource. This Object is use to
     * retrieve the resource in the resource space.
     * A ResourceFrame has no SpaceEntry.
     * @return always null.
     */
    protected SpaceEntry getSpaceEntry() {
	return null;
    }

    private ResourceReference self = null;
    /**
     * Get The FrameReference of this frame, or <strong>null</strong>
     * if this frame is not registered.
     * @return A ResourceReference instance.
     */
    public ResourceReference getFrameReference() {
	if ((self == null) &&  (resource != null)) {
	    self = resource.getFrameReference(this);
	}
	return self;
    }

    public ResourceReference getResourceReference() {
	return getFrameReference();
    }
  
    /**
     * If our target resource has some children, we could have
     * some attribute to give to them.
     * @param attrs A Hashtable.
     */
    protected void updateDefaultChildAttributes(Hashtable attrs) {
	//nothing here
    }

    /**
     * Check if this kind of request can be perform by this resource.
     * @param request A RequestInterface instance
     * @return a boolean.
     */
    public boolean checkRequest(RequestInterface request) {
	return true;
    }

    /**
     * FIXME doc
     */ 
    public ReplyInterface perform(RequestInterface request) 
	throws ProtocolException, NotAProtocolException
	{
	    return super.perform(request);
	}

    /**
     * FIXME doc
     */ 
    public boolean lookup(LookupState ls, LookupResult lr) 
	throws ProtocolException
	{
	    //FIXME does a frame can have frames other than filters?
	    //exclude filters?
	    ResourceFrame frames[] = getFrames();
	    if (frames != null) {
		for (int i = 0 ; i < frames.length ; i++) {
		    if (frames[i] == null)
			continue;
		    if (frames[i].lookup(ls,lr))
			return true;
		}
	    }
	    //
	    // FIXME unuseful.
	    //
	    if ( ls.hasMoreComponents() ) {
		// We are not a container resource, and we don't have children:
		lr.setTarget(null);
		return false;
	    } else {
		// We are done !
		//    org.w3c.util.Trace.showTrace("lookup done : "+
		//		    this+", "+resource.getResourceReference());
		lr.setTarget(resource.getResourceReference());
		return true;
	    }
	}

    public void processEvent(ResourceEvent evt) {
	if (evt instanceof FrameEvent) {
	    fireFrameEvent((FrameEvent)evt);
	} else if (evt instanceof AttributeChangedEvent) {
	    fireAttributeChangeEvent((AttributeChangedEvent)evt);
	}
    }

    /**
     * Add a frame event listener.
     * @param l The new frame event listener.
     */

    public void addFrameEventListener(FrameEventListener l) {
	frameListener = ResourceEventMulticaster.add(frameListener, l);
    }

    /**
     * Remove a frame event listener.
     * @param l The listener to remove.
     */
    
    public void removeFrameEventListener (FrameEventListener l) {
	frameListener = ResourceEventMulticaster.remove(frameListener, l);
    }

    /**
     * Post a frameEvent.
     * @param the frame event type.
     */
    protected void postFrameEvent(int type) {
	if (frameListener != null) {
	    FrameEvent evt = new FrameEvent(this, type);
	    postEvent(evt);
	}
    }

    /**
     * Fire a frameEvent.
     * @param the frame event type.
     */
    protected void fireFrameEvent(FrameEvent evt) {
	if (frameListener != null) {
	    int type = evt.getID();
	    switch (type) {
	    case Events.FRAME_ADDED :
		frameListener.frameAdded(evt);
		break;
	    case Events.FRAME_MODIFIED :
		frameListener.frameModified(evt);
		break;
	    case Events.FRAME_REMOVED :
		frameListener.frameRemoved(evt);
		break;
	    }
	}
    }

    /**
     * Listen its resource.
     */
    public void attributeChanged(AttributeChangedEvent evt) {
	displayEvent( this, evt );
	setValue(ATTR_LAST_MODIFIED, new Long(System.currentTimeMillis()));
    }

    /**
     * This handles the <code>FRAME_MODIFIED</code> kind of events.
     * @param evt The event describing the change.
     */
  
    public void frameModified(FrameEvent evt) {
	displayEvent( this, evt );
	markModified();
	postFrameEvent(evt.getID());
    }

    /**
     * We overide setValue, to fire event.
     * @param idx The index of the attribute to modify.
     * @param value The new attribute value.
     */
    public synchronized void setValue(int idx, Object value) {
	super.setValue(idx,value);
	if (idx != ATTR_LAST_MODIFIED)
	    postFrameEvent(Events.FRAME_MODIFIED); 
    }

    /**
     * Get the target resource.
     * @return a resource instance.
     */
    public FramedResource getResource() {
	return resource;
    }

    /**
     * Register a target resource. Called after initialize,
     * set the context. getServer() can be call only after
     * this method call.
     * @parame resource The resource to register.
     */ 
    public void registerResource(FramedResource resource) {
	this.resource = resource;
	postFrameEvent(Events.FRAME_ADDED);
	setValue(ATTR_CONTEXT, resource.getContext());
    }

    /**
     * Register a target resource.
     * @parame resource The resource to register.
     */ 
    public void unregisterResource(Resource resource) {
	//FIXME (can we have more than one resource? )
	this.resource = null;
	postFrameEvent(Events.FRAME_REMOVED);
    }

}
