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

package w3c.jigsaw.pics ;

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

import w3c.tools.store.*;
import w3c.www.http.*;
import w3c.www.mime.*;
import w3c.jigsaw.http.* ;
import w3c.jigsaw.resources.* ;
import w3c.jigsaw.forms.* ;

/**
 * An HTTP interface to query a Label bureau.
 * This conforms to the
 * <a href="http://www.w3.org/hypertext/WWW/PICS/labels.html">PICS 
 * protocol specification</a>.
 * <p>This entity defines the following parameter:</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>Name of the LabelBureau database</th>
 * <th align=left><em>none</em></th> 
 * <th align=left>java.lang.String</th>
 * </tr>
 * </table>
 * <p>This entity also inherits the parameters from the PostableResource.</p>
 * @see w3c.jigsaw.core.PostableResource
 */

public class LabelBureauResource extends PostableResource {
    public static HttpMimeType APPLICATION_PICSLABEL = null;

    static {
	try {
	    MimeType type = new MimeType("application/pics-label");
	    APPLICATION_PICSLABEL = HttpFactory.makeMimeType(type);
	} catch (MimeTypeFormatException ex) {
	    ex.printStackTrace();
	    System.exit(1);
	}
    }

    /**
     * Attribute index - Our label bureau identifier.
     */
    protected static int ATTR_BUREAU_IDENTIFIER = -1 ;

    static {
	Attribute   a = null ;
	Class     cls = null ;
	
	try {
	    cls = Class.forName("w3c.jigsaw.pics.LabelBureauResource");
	} catch (Exception ex) {
	    ex.printStackTrace() ;
	    System.exit(1) ;
	}
	// The bureau identifier (a file 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) ;
    }

    /**
     * Check that we have loaded our bureau, or load it.
     */

    protected final void acquireBureau() {
	if ( bureau != null )
	    return ;
	File repository = getBureauIdentifier() ;
	if ( repository != null )
	    bureau = LabelBureauFactory.getLabelBureau(getBureauIdentifier()) ;
	return ;
    }

    /**
     * Get the integer code for the String based PICS format.
     * @param request The request to be handled.
     * @param format The string representation of the format.
     * @return An LabelBureauInterface defined format code.
     * @see w3c.jigsaw.pics.LabelBureauInterface
     */

    protected int parseFormat (Request request, String format)
	throws HTTPException
    {
	if ( format.equals ("minimal") ) {
	    return LabelBureauInterface.FMT_MINIMAL ;
	} else if ( format.equals ("short") ) {
	    return LabelBureauInterface.FMT_SHORT ;
	} else if ( format.equals ("full") ) {
	    return LabelBureauInterface.FMT_FULL ;
	} else if ( format.equals ("signed") ) {
	    return LabelBureauInterface.FMT_SIGNED ;
	}
	Reply error = request.makeReply(HTTP.BAD_REQUEST) ;
	error.setContent ("Unknown label format: "+format);
	throw new HTTPException (error) ;
    }

    /**
     * Parse the URLs given in the URLDecoder, as the <strong>u</strong> field.
     * This method just unquote any quoted URLs, before sending them back.
     * @param data The URLDecoder to get data from.
     * @return An array of String, properly parsed.
     */

    protected String[] parseURLs (String us[]) {
	String urls[] = new String[us.length] ;
	for (int i = 0 ; i < us.length ; i++) {
	    if ( us[i].charAt(0) == '"' ) {
		int ulen = us[i].length() ;
		// unquote only if last char is a quote, otherwise, leave as is
		// which will trigger an exception when trying to build the URL
		// object out of it.
		if (us[i].charAt(ulen-1) == '"')
		    urls[i] = us[i].substring(1, ulen-1) ; 
	    } else {
		urls[i] = us[i] ;
	    }
	}
	return urls ;
    }

    /**
     * Build a PICS reply out of the given content.
     * @param request The request to reply to.
     * @param sb The StringBuffer containing the content to send back.
     */

    protected Reply makePICSReply (Request request, StringBuffer buffer) {
	String content = buffer.toString() ;
	Reply  reply   = request.makeReply(HTTP.OK) ;
	reply.setHeaderValue(reply.H_CONTENT_TYPE, APPLICATION_PICSLABEL);
	reply.setContent (content) ;
	return reply ;
    }

    /**
     * Get the generic labels for a set of service and a set of urls.
     */

    protected Reply getGenericLabels (Request request
				      , int format
				      , String urls[]
				      , String services[]
				      , URLDecoder data) 
	throws HTTPException
    {
	StringBuffer sb = new StringBuffer (128) ;

	sb.append ("("+PICSFilter.PICS_PROTOCOL_ID) ;
	// Go through each service:
    sloop:
	for (int is = 0; is < services.length ; is++) {
	    LabelServiceInterface s = bureau.getLabelService (services[is]) ;
	    if ( s == null ) {
		sb.append (" error " + "(no-ratings \"unknown service\")") ;
		continue sloop ;
	    }
	    s.dump (sb, format) ;
	    sb.append (" labels ") ;
	    try {
	    uloop:
		for (int iu = 0 ; iu < urls.length ; iu++) {
		    LabelInterface l = s.getGenericLabel (new URL(urls[iu])) ;
		    if ( l == null ) {
			sb.append (" error "+"(not-labeled \""+urls[iu]+"\")");
			continue uloop ;
		    }
		    if ( iu != 0 )
			sb.append (" ") ;
		    l.dump (sb, format) ;
		}
	    } catch (MalformedURLException e) {
		Reply error = request.makeReply(HTTP.BAD_REQUEST) ;
		error.setContent ("You are requesting an invalid URL,") ;
		throw new HTTPException (error) ;
	    }
	}
	sb.append (")") ;
	return makePICSReply (request, sb) ;
    }

    /**
     * Get the normal (generic or specific) labels.
     */

    protected Reply getNormalLabels (Request request
				     , int format
				     , String urls[]
				     , String services[]
				     , URLDecoder data) 
	throws HTTPException
    {
	StringBuffer sb = new StringBuffer (128) ;

	sb.append ("("+PICSFilter.PICS_PROTOCOL_ID) ;
	// Go through each service:
    sloop:
	for (int is = 0; is < services.length ; is++) {
	    LabelServiceInterface s = bureau.getLabelService (services[is]) ;
	    if ( s == null ) {
		sb.append (" error " + "(no-ratings \"unknown service\")") ;
		continue sloop ;
	    }
	    s.dump (sb, format) ;
	    sb.append (" labels ") ;
	    try {
	    uloop:
		for (int iu = 0 ; iu < urls.length ; iu++) {
		    URL            u = new URL (urls[iu]) ;
		    LabelInterface l = s.getSpecificLabel (u) ;
		    if ((l == null) && ((l = s.getGenericLabel(u)) == null)) {
			sb.append (" error "+"(not-labeled \""+urls[iu]+"\")");
			continue uloop ;
		    }
		    if (iu != 0)
			sb.append (" ") ;
		    l.dump (sb, format) ;
		}
	    } catch (MalformedURLException e) {
		Reply error = request.makeReply(HTTP.BAD_REQUEST) ;
		error.setContent ("You are requesting an invalid URL.") ;
		throw new HTTPException (error) ;
	    }
	}
	sb.append (")") ;
	return makePICSReply (request, sb) ;
    }

    /**
     * Get tree labels.
     */

    protected Reply getTreeLabels (Request request
				   , int format
				   , String urls[]
				   , String services[]
				   , URLDecoder data) 
	throws HTTPException
    {
	StringBuffer sb = new StringBuffer (128) ;
	sb.append ("("+PICSFilter.PICS_PROTOCOL_ID) ;
	// Go through each service:
    sloop:
	for (int is = 0; is < services.length ; is++) {
	    LabelServiceInterface s = bureau.getLabelService (services[is]) ;
	    if ( s == null ) {
		sb.append (" error " + "(no-ratings \"unknown service\")") ;
		continue sloop ;
	    }
	    s.dump (sb, format) ;
	    sb.append (" labels ") ;
	    try {
	      uloop:
		for (int iu = 0 ; iu < urls.length ; iu++) {
		    boolean spaced = false ;
		    LabelInterface l[] = s.getTreeLabels(new URL(urls[iu]));
		    if ( l == null ) {
			sb.append (" error "+"(not-labeled \""+urls[iu]+"\")");
			continue uloop ;
		    }
		    sb.append ((iu == 0) ? "(" : " (") ;
		    for (int il = 0 ; il < l.length ; il++) {
			if ( il != 0 )
			    sb.append (" ") ;
			l[il].dump (sb, format) ;
		    }
		    sb.append (")") ;
		}
	    } catch (MalformedURLException e) {
		Reply error = request.makeReply(HTTP.BAD_REQUEST) ;
		error.setContent ("You are requesting an invalid URL.") ;
		throw new HTTPException (error) ;
	    }
	}
	sb.append (")") ;
	return makePICSReply (request, sb) ;
    }

    /**
     * Get generic tree labels.
     */

    protected Reply getGenericTreeLabels (Request request
					  , int format
					  , String urls[]
					  , String services[]
					  , URLDecoder data) 
	throws HTTPException
    {
	StringBuffer sb = new StringBuffer (128) ;
	sb.append ("("+PICSFilter.PICS_PROTOCOL_ID) ;
	// Go through each service:
    sloop:
	for (int is = 0; is < services.length ; is++) {
	    LabelServiceInterface s = bureau.getLabelService (services[is]) ;
	    if ( s == null ) {
		sb.append (" error " + "(no-ratings \"unknown service\")") ;
		continue sloop ;
	    }
	    s.dump (sb, format) ;
	    sb.append (" labels ") ;
	    try {
	      uloop:
		for (int iu = 0 ; iu < urls.length ; iu++) {
		    URL u = new URL (urls[iu]) ;
		    LabelInterface l[] = s.getGenericTreeLabels(u) ;
		    if ( l == null ) {
			sb.append (" error "+"(not-labeled \""+urls[iu]+"\")");
			continue uloop ;
		    }
		    sb.append ((iu == 0) ? "(" : " (") ;
		    for (int il = 0 ; il < l.length ; il++) {
			if ( il != 0 )
			    sb.append (" ") ;
			l[il].dump (sb, format) ;
		    }
		    sb.append (")") ;
		}
	    } catch (MalformedURLException e) {
		Reply error = request.makeReply(HTTP.BAD_REQUEST) ;
		error.setContent ("You are requesting an invalid URL.") ;
		throw new HTTPException (error) ;
	    }

	}
	sb.append (")") ;
	return makePICSReply (request, sb) ;
    }


    public Reply handle (Request request, URLDecoder data) 
	throws HTTPException 
    {
	// Get opt (only generic/normal)
	String opt        = data.getValue("opt") ;
	String services[] = parseURLs (data.getMultipleValues ("s")) ;
	String urls[]     = parseURLs (data.getMultipleValues ("u")) ;
	int    iformat   = parseFormat(request,data.getValue("format"));
	// Perform request
	if ( opt.equals ("generic") ) {
	    return getGenericLabels (request, iformat, urls, services, data) ;
	} else if ( opt.equals ("normal") ) {
	    return getNormalLabels (request, iformat, urls, services, data) ;
	} else if ( opt.equals ("tree") ) {
	    return getTreeLabels (request, iformat, urls, services, data) ;
	} else  if ( opt.equals ("generic+tree") ) {
	    return getGenericTreeLabels(request,iformat, urls, services, data);
	} else {
	    Reply error = request.makeReply(HTTP.BAD_REQUEST);
	    error.setContent ("Invalid label bureau query, bad option: "+opt);
	    throw new HTTPException (error) ;
	}
    }

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

}
