// Resource.java
// $Id: Resource.java,v 1.1 1997/01/17 09:42:18 abaird Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html

package w3c.tools.store ;

import java.util.*;

/**
 * The resource class describes an object, accessible through the server.
 * Resource objects are required to have the following properties: 
 * <ul>
 * <li>They must be persistent (their life-time can span multiple httpd 
 * life-time).
 * <li>They must be editable, so that one can change some of their aspects
 * (such as any associated attribute).
 * <li>They must be self-described: each resource must now what kind
 * of attribute it <em>understands</em>.
 * <li>They must be able to update themselves: some of the meta-information
 * associated with a resource may require lot of CPU to compute. 
 * <li>They must implement some name-service policy.
 * </ul>
 * <p>These resource objects do not define how they are accessed. See the
 * sub-classes for specific accesses. It might be the case that HTTP-NG allows
 * <em>any</em> sub-class of resource to be accessible, although, right now
 * HTTP-1.x only allows you to access HTTPResource instances.
 */

public class Resource extends AttributeHolder {
    private static final boolean debugunload = false;

    /**
     * Attribute index - The index of the resource store entry attribute.
     */
    protected static int ATTR_STORE_ENTRY = -1;
    /**
     * Attribute index - The index for the identifier attribute.
     */
    protected static int ATTR_IDENTIFIER = -1 ;
    /**
     * Attribute index - The resource store associated with this resource.
     */
    protected static int ATTR_RESOURCE_STORE = -1 ;

    static {
	Attribute a   = null ;
	Class     cls = null ;
	// Get a pointer to our own class:
	try {
	    cls  = Class.forName("w3c.tools.store.Resource") ;
	} catch (Exception ex) {
	    ex.printStackTrace() ;
	    System.exit(1) ;
	}
	// The resource store entry for that resource:
	a = new ObjectAttribute("store-entry"
				, "java.lang.Object"
				, null
				, Attribute.DONTSAVE);
	ATTR_STORE_ENTRY = AttributeRegistry.registerAttribute(cls, a);
	// The identifier attribute:
	a = new StringAttribute("identifier"
				, null
				, Attribute.MANDATORY|Attribute.EDITABLE);
	ATTR_IDENTIFIER = AttributeRegistry.registerAttribute(cls, a);
	// The resource store:
	a = new ObjectAttribute("resource-store"
				, "w3c.tools.store.ResourceStore"
				, null
				, Attribute.DONTSAVE);
	ATTR_RESOURCE_STORE = AttributeRegistry.registerAttribute(cls, a);
    }

    /**
     * Get the store entry for that resource.
     * Only the resource store in charge of this resource knows about the
     * type of the resulting object. Buy declaring the class of that object
     * private, the resource store can assume some private access to it.
     * @return A java Object instance, or <strong>null</strong> if no 
     * store entry is attached to that resource.
     */

    public Object getStoreEntry() {
	return getValue(ATTR_STORE_ENTRY, null);
    }

    /**
     * Get this resource's help url.
     * @return An URL, encoded as a String, or <strong>null</strong> if not
     * available.
     */

    public String getHelpURL() {
	return null;
    }

    /**
     * Get the help URL for that resource's topic.
     * @param topic The topic you want help for.
     * @return A String encoded URL, or <strong>null</strong> if none
     * was found.
     */

    public String getHelpURL(String topics) {
	return null;
    }

    /**
     * Get this resource identifier.
     * @return The String value for the identifier.
     */

    public String getIdentifier() {
	return getString(ATTR_IDENTIFIER, null) ;
    }

    /**
     * Get the store associated with this resource.
     * @return The associated store or <strong>null</strong>. Not all resources
     *    have a store associated with them (eg the one that whose 
     *    creation is cheap, etc).
     */

    public ResourceStore getResourceStore() {
	return (ResourceStore) getValue(ATTR_RESOURCE_STORE, null) ;
    }

    /**
     * Mark this resource as having been modified.
     */

    public void markModified() {
	ResourceStore store = getResourceStore() ;
	if ( store != null ) {
	    store.markModified(this) ;
	}
    }

    /**
     * We overide setValue, to mark the resource as modified.
     * @param idx The index of the attribute to modify.
     * @param value The new attribute value.
     */

    public void setValue(int idx, Object value) {
	// Changing the identifier of a resource needs some special tricks:
	if ( idx == ATTR_IDENTIFIER ) {
	    ResourceStore store = getResourceStore();
	    if ( store != null ) {
		String oldid = getIdentifier();
		try {
		    super.setValue(idx, value);
		} catch (IllegalAttributeAccess ex) {
		    // We were not able to change the identifier, rethrow
		    throw ex;
		}
		// Change was successfull, update the resource store:
		store.renameResource(oldid, (String) value);
		markModified();
		return;
	    }
	}
	// Normal setValue, but markModified before leaving:
	super.setValue(idx, value) ;
	if ( ! attributes[idx].checkFlag(Attribute.DONTSAVE) ) 
	    markModified() ;
    }

    /**
     * Is that resource willing to be unloaded.
     * This method is a bit tricky to implement. The guideline is that you
     * should not dynamically change the returned value (since you can't 
     * control what happens between a call to that method and a call to
     * the <code>notifyUnload</code> method).
     * <p>Returning <strong>false</strong> should never be needed, except
     * for very strange resources.
     * @return A boolean <strong>true</strong> if the resource can be unloaded
     * <strong>false</strong> otherwise.
     */

    public boolean acceptUnload() {
	if ( debugunload ) {
	    try {
		System.out.println(getValue("url","<??>")+": acceptUnload");
	    } catch (IllegalAttributeAccess ex) {
	    }
	}
	return true;
    }

    /**
     * This resource is being unloaded.
     * The resource is being unloaded from memory, perform any additional
     * cleanup required.
     */

    public void notifyUnload() {
	if ( debugunload ) {
	    try {
		System.out.println(getValue("url","<??>")+": unloaded.");
	    } catch (IllegalAttributeAccess ex) {
	    }
	}
	values = null ;
    }

    /**
     * Lock this resource in its store.
     * Acquire a lock on this resource: if you do acquire the lock, than you 
     * are guaranteed that either the resource will be kept in memory, or
     * you will be notified through a call to
     * <code>notifyResourceLockBreak</code> that the resource is going down.
     * @param locker The object willing to acquire the lock.
     * @return A boolean <strong>true</strong> if locking succeeded, 
     *    <strong>false</strong> otherwise.
     */

    public boolean lock(ResourceLocker locker) {
	ResourceStore store = getResourceStore() ;
	if ( store == null )
	    return false ;
	else
	    return store.lockResource(locker, this) ;
    }

    /**
     * Unlock the given resource.
     * @param locker The object that wishes to relinquish the lock.
     */
    
    public void unlock(ResourceLocker locker) {
	ResourceStore store = getResourceStore() ;
	if ( store != null )
	    store.unlockResource(locker, this) ;
    }

    /**
     * The web admin wants us to update any out of date attribute.
     */

    public void updateAttributes() {
	return ;
    }

    /**
     * Delete this Resource instance, and remove it from its store.
     * This method will erase definitely this resource, for ever, by removing
     * it from its resource store (when doable).
     */

    public synchronized void delete() {
	ResourceStore store = getResourceStore();
	if ( store != null ) 
	    store.removeResource(getIdentifier());
    }

    /**
     * Create an empty resource instance.
     * Initialize the instance attributes description, and its values.
     */

    public Resource() {
	super() ;
    }
}
