    // ResourceFrame.java
    // $Id: ResourceFrame.html,v 1.2 1999/10/27 22:10:37 ylafon 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);
        }

    }