// PICSFilter.java
// $Id: PICSFilter.java,v 1.12 2000/08/17 12:54:55 ylafon Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html

package org.w3c.jigsaw.pics ;

import java.io.File;

import java.net.URL;

import java.util.Enumeration;

import org.w3c.tools.resources.Attribute;
import org.w3c.tools.resources.AttributeRegistry;
import org.w3c.tools.resources.FileAttribute;
import org.w3c.tools.resources.FramedResource;
import org.w3c.tools.resources.ReplyInterface;
import org.w3c.tools.resources.RequestInterface;
import org.w3c.tools.resources.Resource;
import org.w3c.tools.resources.ResourceFilter;

import org.w3c.www.http.HTTP;
import org.w3c.www.http.HttpBag;
import org.w3c.www.http.HttpMessage;
import org.w3c.www.http.HttpRequestMessage;

import org.w3c.jigsaw.http.HTTPException;
import org.w3c.jigsaw.http.Reply;
import org.w3c.jigsaw.http.Request;

import org.w3c.jigsaw.html.HtmlGenerator ;

/** 
 * This package implements a PICS filter. The PICS filters allows server
 * administrator to rate the documents they deliver. The references for this
 * protocol is <a href="http://www.w3.org/hypertext/WWW/PICS/">here</a>.</p>
 * <p>The PICS filter defines the following attributes:</p>
 * <table border>
 * <caption>The list of parameters</caption>
 * <tr> 
 * <th align=left>Parameter name</th> 
 * <th align=left>Semantics</th>
 * <th align=left>Default value</th> 
 * <th align=left>Type</th>
 * </tr>
 * <tr> 
 * <th align=left>bureau</th> 
 * <th align=left>The label bureau to query for this entity labels.</th>
 * <th align=left><em>none</em></th> 
 * <th align=left>java.lang.String</th>
 * </tr>
 * </table>
 */

public class PICSFilter extends ResourceFilter {

    /**
     * Attribute index - The identifier of our bureau.
     */
    protected static int ATTR_BUREAU_IDENTIFIER = -1 ;

    static {
	Attribute a   = null ;
	Class     cls = null ;
	try {
	    cls = Class.forName("org.w3c.jigsaw.pics.PICSFilter");
	} catch (Exception ex) {
	    ex.printStackTrace() ;
	    System.exit(1) ;
	}
	// The bureau identifier attribute
	a = new FileAttribute("bureau"
			      , null
			      , Attribute.EDITABLE|Attribute.MANDATORY) ;
	ATTR_BUREAU_IDENTIFIER = AttributeRegistry.registerAttribute(cls, a);
    }

    /**
     * Our loaded lable bureau.
     */
    protected LabelBureauInterface bureau      = null ;

    /**
     * Get our label bureau identifier.
     */

    public File getBureauIdentifier() {
	return (File) getValue(ATTR_BUREAU_IDENTIFIER, null) ;
    }

    /**
     * Make sure our label bureau is loaded.
     */

    protected final void acquireBureau() {
	if ( bureau != null )
	    return ;
	File file = getBureauIdentifier();
	if ( file == null )
	    // Not initialize yet:
	    return ;
	bureau = LabelBureauFactory.getLabelBureau(file);
    }

    /**
     * Check the query to examine if it requires some PICS handling. 
     * If this is the case, it returns a <em>Bag</em> object
     * corresponding to the part of the <em>Accept-Protocol</em> header that
     * relates with PICS.
     * @param request The request to be checked.
     * @return A Bag object if PICS handling required, <string>null</strong>
     *         otherwise.
     * @exception HTTPException if processing the request failed.
     */

    protected HttpBag isPICSQuery (Request request) 
	throws HTTPException
    {
	// If the request doesn't ask for labels, return right now.
	HttpBag requested = request.getProtocolRequest();
	if ( requested == null )
	    return null ;
	if ( ! requested.hasBag (PICS.PICS_PROTOCOL_ID) )
	    return null ;
	// Now, the request has some PICS stuff in it, let look inside this:
	HttpBag pics = null ;
	try {
	    pics = requested.getBag(PICS.PICS_PROTOCOL_ID) ;
	} catch (ClassCastException e) {
	    return null ;
	}
	return pics ;
    }

    /**
     * The outgoingFilter method.
     * This method is the one that gets called by Jigsaw core. By default it
     * will call the simpler <code>outgoingFilter</code> method that takes
     * only the request and the reply as parameters.
     * @param request The request that has been processed.
     * @param reply The original reply as emitted by the resource.
     * @param filters The whole filter that applies to the resource.
     * @param i The current index of filters. The <em>i</em> filter is ourself,
     * filters with lower indexes have already been applied, and filters with
     * greater indexes are still to be applied.
     * @return A Reply instance, if that filter know how to complete the
     * request processing, or <strong>null</strong> if reminaing filters
     * are to be called by Jigsaw engine.
     * @exception HTTPException If processing should be interrupted,
     * because an abnormal situation occured.
     */

    public ReplyInterface outgoingFilter (RequestInterface req, 
					  ReplyInterface rep) 
	throws HTTPException
    {
	Request request = (Request) req;
	Reply reply = (Reply) rep;

	HttpBag pics = isPICSQuery (request) ;
	if ( pics == null )
	    return reply ;
	// Get the requested services:
	HttpBag params    = pics.getBag("params") ;
	HttpBag services  = params.getBag("services") ;
	URL url       = request.getURL();
	int format    = LabelBureauInterface.FMT_MINIMAL ;
	// Get any format parameter:
	if ( params.hasItem ("minimal") ) {
	    format = LabelBureauInterface.FMT_MINIMAL ;
	} else if ( params.hasItem ("short") ) {
	    format = LabelBureauInterface.FMT_SHORT ;
	} else if ( params.hasItem ("full") ) {
	    format = LabelBureauInterface.FMT_FULL ;
	} else if ( params.hasItem ("signed") ) {
	    format = LabelBureauInterface.FMT_SIGNED ;
	} else {
	    Reply error = request.makeReply(HTTP.BAD_REQUEST) ;
	    error.setContent ("Invalid label format: "+format) ;
	    throw new HTTPException (error) ;
	}
	// Get labels for each service, building out the ret hashtable
	StringBuffer sb = new StringBuffer(128) ;
	Enumeration  e  = services.keys() ;
	sb.append ("("+PICS.PICS_PROTOCOL_ID) ;
    sloop:
	while ( e.hasMoreElements() ) {
	    String                n = (String) e.nextElement() ;
	    LabelServiceInterface s = bureau.getLabelService (n) ;
	    if ( s == null ) {
		sb.append (" error "
			   + "(service-unavailable \"unknown service\")") ;
		continue sloop ;
	    } 
	    s.dump(sb, format) ;
	    LabelInterface l = s.getSpecificLabel (url) ;
	    if ( (l == null) && ((l = s.getGenericLabel (url)) == null) ) {
		sb.append (" error (not-labeled \"" + url +"\")") ;
	    }  else {
		sb.append (" labels ") ;
		l.dump (sb, format) ;
	    }
	}
	sb.append (")") ;
	// Add additional reply headers:
	reply.setProtocol(PICS.PICS_EXTENSION);
	reply.setValue("PICS-label", sb.toString());
	return reply ;
    }

    public void initialize (Object values[]) {
	super.initialize(values);
	acquireBureau() ;
    }
}
