// DirectoryResource.java
// $Id: DirectoryResource.java,v 1.49 1998/12/15 16:22:59 bmahe Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html

package org.w3c.jigsaw.resources ;

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

import org.w3c.tools.resources.*;
import org.w3c.tools.resources.indexer.*;
import org.w3c.jigsaw.frames.NegotiatedFrame;

public class DirectoryResource 
             extends org.w3c.tools.resources.DirectoryResource 
{

    /**
     * Attribute index - Should this directory support content negotiation.
     */
    protected static int ATTR_NEGOTIABLE = -1 ;

    static {
	Attribute a   = null ;
	Class     cls = null ;
	// Get a pointer to our class.
	try {
	    cls = Class.forName("org.w3c.jigsaw.resources.DirectoryResource") ;
	} catch (Exception ex) {
	    ex.printStackTrace() ;
	    System.exit(1) ;
	}
	// The negotiate flag
	a = new BooleanAttribute("negotiable"
				 , Boolean.FALSE
				 , Attribute.EDITABLE) ;
	ATTR_NEGOTIABLE = AttributeRegistry.registerAttribute(cls, a) ;
    }

    /**
     * Get the negotiable flag for this directory.
     * When turned to <strong>true</strong>, this flag indicates to the
     * directory resource that it should automatically build negotiated 
     * resources ont op of all existing resources.
     * <p>You should know, at least, that turning this flag on has some
     * not so small cost in terms of the size of the index files, and some
     * not so small costs in CPU time when detecting not found documents. 
     * Otherwise, in all other situations its cost is probably negligible.
     * @return A boolean, <strong>true</strong> if the directory is extensible
     * <strong>false</strong> otherwise.
     */
    public boolean getNegotiableFlag() {
	return getBoolean(ATTR_NEGOTIABLE, false) ;
    }

    private String getUnextendedName(String name) {
	int strlen = name.length() ;
	for (int i = 0 ; i < strlen ; i++) {
	    // FIXME: Should use the system props to get the right sep
	    if ( name.charAt(i) == '.' ) {
		if ( i == 0 )
		    return null ;
		return name.substring(0, i) ;
	    }
	}
	return null ;
    }

    /**
     * Update a negotiable resource.
     * Given the name of a resource that exists, create or update the 
     * attributes of a resource that allows to negotiate its content.
     * <p>I hate this part here: it has nothing to do within the directory
     * resource itself, and the indexer shouldn't know that much about
     * directory resource, so I am stuck.
     * @param name The name of the newly created resource.
     */
    public synchronized void updateNegotiableResource(String name) {
	// Does the maintainer really wants us to perform this ugly hack ?
	if ( ! getNegotiableFlag() )
	    return ;
	// Check for the corresponding negotiable resource:
	String noext = getUnextendedName(name) ;
	if ( noext == null ) {
	    return ;
	} else {
	    ResourceReference rr = lookup(noext);
	    ResourceReference rr_neg_frame = null;

	    if (rr != null) {
		try {
		    FramedResource r = (FramedResource)rr.lock();
		    Class nClass = 
			Class.forName("org.w3c.jigsaw.frames.NegotiatedFrame");
		    rr_neg_frame = r.getFrameReference(nClass);
		    if (rr_neg_frame == null) 
			return;
		} catch (ClassNotFoundException cex) {
		    return;
		} catch (InvalidResourceException ex) {
		    return;
		} finally {
		    rr.unlock();
		}
	    }

	    if (rr_neg_frame == null) {
		// we can't add a NegotiatedFrame to an existing resource.
		if (rr == null) {
		    // create the resource.
		    FramedResource resource = new FramedResource();
		    Hashtable defs = new Hashtable(10) ;
		    defs.put("identifier", noext);
		    ResourceContext context = 
			updateDefaultChildAttributes(defs);
		    resource.initialize(defs);
		    addResource(resource, defs);
		    // add a NegotiatedFrame.
		    String variants[] = new String[1] ;
		    variants[0] = name ;
		    NegotiatedFrame negotiated = new NegotiatedFrame();
		    Hashtable f_defs = new Hashtable(10) ;
		    f_defs.put("variants", variants) ;
		    resource.registerFrame(negotiated, f_defs);
		} 
	    } else {
		try {
		    NegotiatedFrame negotiated = 
			(NegotiatedFrame) rr_neg_frame.lock();
		    String variants[]  = negotiated.getVariantNames() ;
		    boolean exists = false;
		    for ( int i=0 ; i < variants.length ; i++ ) {
			if (variants[i].equals(name))
			    exists = true;
		    }
		    if (!exists) {
			String nvariants[] = new String[variants.length+1] ;
			System.arraycopy(variants, 0, nvariants,
					 0, variants.length);
			nvariants[variants.length] = name ;
			negotiated.setVariants(nvariants) ;
		    }
		} catch (InvalidResourceException ex) {
		    //FIXME
		} finally {
		    rr_neg_frame.unlock();
		}
	    }
	}
    }

    protected Resource index(String name, 
			     Hashtable defs, 
			     RequestInterface req) 
    {
	Resource resource = super.index(name, defs, req);
	if ((resource != null) && ( getNegotiableFlag() )) 
	    updateNegotiableResource(name) ;
	return resource;
    }

    /**
     * Lookup the next component of this lookup state in here.
     * @param ls The current lookup state.
     * @param lr The lookup result under construction.
     * @exception ProtocolException If an error occurs.
     * @return A boolean, <strong>true</strong> if lookup has completed, 
     * <strong>false</strong> if it should be continued by the caller.
     */
    public boolean lookup(LookupState ls, LookupResult lr) 
	throws ProtocolException
    {
	if (getNegotiableFlag() && getExtensibleFlag()) {
	    //create all negotiated resources
	    File directory = getDirectory() ;
	    if ( directory != null ) {
		synchronized(this) {
		    long dirstamp  = directory.lastModified() ;
		    if ( dirstamp > getDirStamp() ) {
			String lst[] = directory.list() ;
			if ( lst != null ) {
			    for (int i = 0 ; i < lst.length ; i++) {
				if (lst[i].equals(".") || 
				    lst[i].equals(".."))
				    continue ;
				if (super.lookup(lst[i]) == null) {
				    String indexed = 
					getIndexedName(lst[i]);
				    if (indexed.equals(lst[i]))
					createDefaultResource(lst[i]) ;
				    else if (super.lookup(indexed) == null)
					createDefaultResource(lst[i]) ;
				}
			    }
			}
			setLong(ATTR_DIRSTAMP, dirstamp) ;
		    }
		}
	    }
	}
	return super.lookup(ls, lr);
    }

}
