    // GZIPFilter.java
    // $Id: GZIPFilter.html,v 1.1 1998/03/30 08:30:27 benoit Exp $
    // (c) COPYRIGHT MIT and INRIA, 1996.
    // Please first read the full copyright statement in file COPYRIGHT.html

    package org.w3c.jigsaw.filters;

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

    import org.w3c.tools.resources.*;
    import org.w3c.www.mime.*;
    import org.w3c.jigsaw.http.*;
    import org.w3c.jigsaw.resources.*;

    class GZIPDataMover extends Thread {
        InputStream in = null;
        OutputStream out = null;

        public void run() {
    	try {
    	    byte buf[] = new byte[1024];
    	    int  got   = -1;
    	    while ((got = in.read(buf)) > 0)
    		out.write(buf, 0, got);
    	} catch (IOException ex) {
    	    ex.printStackTrace();
    	} finally {
    	    try { in.close(); } catch (Exception ex) {};
    	    try { out.close(); } catch (Exception ex) {} ;
    	}
        }

        GZIPDataMover(InputStream in, OutputStream out) {
    	this.in  = in;
    	this.out = out;
    	setName("GZIPDataMover");
    	start();
        }
    }

    /**
     * This filter will compress the content of replies using GZIP.
     * Compression is done <em>on the fly</em>. This assumes that you're really
     * on a slow link, where you have lots of CPU, but not much bandwidth.
     * <p>A nifty usage for that filter, is to plug it on top of a
     * <code>org.w3c.jigsaw.proxy.ProxyDirectory</code>, in which case it
     * will compress the data when it flies out of the proxy.
     */

    public class GZIPFilter extends ResourceFilter {
        /**
         * Attribute index - List of MIME type that we can compress
         */
        protected static int ATTR_MIME_TYPES = -1;

        static {
    	Class     c = null;
    	Attribute a = null;
    	try {
    	    c = Class.forName("org.w3c.jigsaw.filters.GZIPFilter");
    	} catch (Exception ex) {
    	    ex.printStackTrace();
    	    System.exit(1);
    	}
    	// Register the MIME types attribute:
    	a = new StringArrayAttribute("mime-types"
    				     , null
    				     , Attribute.EDITABLE);
    	ATTR_MIME_TYPES = AttributeRegistry.registerAttribute(c, a);
        }

        /**
         * The set of MIME types we are allowed to compress.
         */
        protected MimeType types[] = null;

        /**
         * Catch the setting of mime types to compress.
         * @param idx The attribute being set.
         * @param val The new attribute value.
         */

        public void setValue(int idx, Object value) {
    	super.setValue(idx, value);
    	if ( idx == ATTR_MIME_TYPES ) {
    	    synchronized (this) {
    		types = null;
    	    }
    	}
        }

        /**
         * Get the set of MIME types to match:
         * @return An array of MimeType instances.
         */

        public synchronized MimeType[] getMimeTypes() {
    	if ( types == null ) {
    	    String strtypes[] = (String[]) getValue(ATTR_MIME_TYPES, null);
    	    if ( strtypes == null )
    		return null;
    	    types = new MimeType[strtypes.length];
    	    for (int i = 0 ; i < types.length ; i++) {
    		try {
    		    types[i] = new MimeType(strtypes[i]);
    		} catch (Exception ex) {
    		    types[i] = null;
    		}
    	    }
    	}
    	return types;
        }

        public ReplyInterface outgoingFilter(RequestInterface req,
    					 ReplyInterface rep)
    	throws ProtocolException
        {
          Request request = (Request) req;
          Reply   reply   = (Reply) rep;
    	// Anything to compress ?
    	if ( ! reply.hasStream() )
    	    return null;
    	// Match possible mime types:
    	MimeType t[]     = getMimeTypes();
    	boolean  matched = false;
    	if ( t != null ) {
    	    for (int i = 0 ; i < t.length ; i++) {
    		if ( t[i] == null )
    		    continue;
    		if ( t[i].match(reply.getContentType()) > 0 ) {
    		    matched = true;
    		    break;
    		}
    	    }
    	}
    	if ( ! matched )
    	    return null;
    	// Compress:
    	try {
    	    PipedOutputStream pout = new PipedOutputStream();
    	    PipedInputStream  pin  = new PipedInputStream(pout);
    	    new GZIPDataMover(reply.openStream()
    			      , new GZIPOutputStream(pout));
    	    reply.addContentEncoding("gzip");
    	    reply.setContentLength(-1);
    	    reply.setStream(pin);
    	} catch (Exception ex) {
    	    ex.printStackTrace();
    	}
    	return null;
        }

    }