/* A child of javax.swing.text.html.StyleSheet, patched to treat
   html anchors (<a ...> </a>) differently if they start with isolde
   protocols (e.g., "action:" rather than "http:").

   kvlinden, 10Feb00
*/

package TV;

import javax.swing.text.html.*;
import java.util.*;
import java.awt.*;
import java.io.*;
import java.net.*;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.border.*;
import javax.swing.text.*;

public class PatchedStyleSheet extends StyleSheet {

    // create a modified ViewAttributeSet class - kvlinden
    public AttributeSet getViewAttributes(View v) {
	return new ViewAttributeSet(v);
    }

    // an overridden class which treats isolde protocols differently - kvlinden
    static class ViewAttributeSet implements AttributeSet {

	ViewAttributeSet(View v) {
	    host = v;

	    // PENDING(prinz) fix this up to be a more realistic
	    // implementation.
	    Document doc = v.getDocument();
	    if (doc instanceof HTMLDocument) {
		StyleSheet styles = ((HTMLDocument) doc).getStyleSheet();
		Element elem = v.getElement();
		AttributeSet a = elem.getAttributes();
		muxList.removeAllElements();

		AttributeSet htmlAttr = styles.translateHTMLToCSS(a);

		if (htmlAttr.getAttributeCount() != 0) {
		    muxList.addElement(htmlAttr);
		}
		if (elem.isLeaf()) {
		    Enumeration keys = a.getAttributeNames();
		    while (keys.hasMoreElements()) {
			Object key = keys.nextElement();
			if (key instanceof HTML.Tag) {
			    if ((HTML.Tag)key  == HTML.Tag.A) {
				Object o = a.getAttribute((HTML.Tag)key);
				/**
				   In the case of an A tag, the css rules
				   apply only for tags that have their
				   href attribute defined and not for
				   anchors that only have their name attributes
				   defined, i.e anchors that function as
				   destinations.  Hence we do not add the
				   attributes for that latter kind of 
				   anchors.  When CSS2 support is added,
				   it will be possible to specifiy this
				   kind of conditional behaviour in the 
				   stylesheet.
				**/
				if (o != null && o instanceof AttributeSet) {
				    AttributeSet attr = (AttributeSet)o;
				    if (attr.getAttribute(HTML.Attribute.HREF) == null)
					continue;
				    else {  // extra cases for isolde protocols - kvlinden
					String hatt = attr.getAttribute(HTML.Attribute.HREF).toString();
					if ((hatt.startsWith("action:")) ||
					    (hatt.startsWith("text:")))
					    continue;
				    }
				}
			    }
			    AttributeSet cssRule = styles.getRule((HTML.Tag) key, elem);
			    if (cssRule != null) {
				muxList.addElement(cssRule);
			    }
			}
		    }
		} else {
		    HTML.Tag t = (HTML.Tag) a.getAttribute(StyleConstants.NameAttribute);
		    if (t == HTML.Tag.IMPLIED) {
			t = HTML.Tag.P;
		    }
		    AttributeSet cssRule = styles.getRule(t, elem);
		    if (cssRule != null) {
			muxList.addElement(cssRule);
		    }
		}
	    }
	    attrs = new AttributeSet[muxList.size()];
	    muxList.copyInto(attrs);
	}
	//  --- AttributeSet methods ----------------------------

	/**
	 * Gets the number of attributes that are defined.
	 *
	 * @return the number of attributes
	 * @see AttributeSet#getAttributeCount
	 */
        public int getAttributeCount() {
	    int n = 0;
	    for (int i = 0; i < attrs.length; i++) {
		n += attrs[i].getAttributeCount();
	    }
	    return n;
	}

	/**
	 * Checks whether a given attribute is defined.
	 *
	 * @param key the attribute key
	 * @return true if the attribute is defined
	 * @see AttributeSet#isDefined
	 */
        public boolean isDefined(Object key) {
	    int n = 0;
	    for (int i = 0; i < attrs.length; i++) {
		if (attrs[i].isDefined(key)) {
		    return true;
		}
	    }
	    return false;
	}

	/**
	 * Checks whether two attribute sets are equal.
	 *
	 * @param attr the attribute set to check against
	 * @return true if the same
	 * @see AttributeSet#isEqual
	 */
        public boolean isEqual(AttributeSet attr) {
	    return ((getAttributeCount() == attr.getAttributeCount()) &&
		    containsAttributes(attr));
	}

	/**
	 * Copies a set of attributes.
	 *
	 * @return the copy
	 * @see AttributeSet#copyAttributes
	 */
        public AttributeSet copyAttributes() {
	    MutableAttributeSet a = new SimpleAttributeSet();
	    int n = 0;
	    for (int i = 0; i < attrs.length; i++) {
		a.addAttributes(attrs[i]);
	    }
	    return a;
	}

	/**
	 * Gets the value of an attribute.
	 *
	 * @param key the attribute name
	 * @return the attribute value
	 * @see AttributeSet#getAttribute
	 */
        public Object getAttribute(Object key) {
	    int n = attrs.length;
	    for (int i = 0; i < n; i++) {
		Object o = attrs[i].getAttribute(key);
		if (o != null) {
		    return o;
		}
	    }
	    // didn't find it... try parent if it's a css attribute
	    // that is inherited.
	    if (key instanceof CSS.Attribute) {
		CSS.Attribute css = (CSS.Attribute) key;
		if (css.isInherited()) {
		    AttributeSet parent = getResolveParent();
		    if (parent != null)
			return parent.getAttribute(key);
		}
	    }
	    return null;
	}

	/**
	 * Gets the names of all attributes.
	 *
	 * @return the attribute names
	 * @see AttributeSet#getAttributeNames
	 */
        public Enumeration getAttributeNames() {
	    // PENDING(prinz) implement an enumeration
	    // that merges the children.
	    return null;
	}

	/**
	 * Checks whether a given attribute name/value is defined.
	 *
	 * @param name the attribute name
	 * @param value the attribute value
	 * @return true if the name/value is defined
	 * @see AttributeSet#containsAttribute
	 */
        public boolean containsAttribute(Object name, Object value) {
	    return value.equals(getAttribute(name));
	}

	/**
	 * Checks whether the attribute set contains all of
	 * the given attributes.
	 *
	 * @param attrs the attributes to check
	 * @return true if the element contains all the attributes
	 * @see AttributeSet#containsAttributes
	 */
        public boolean containsAttributes(AttributeSet attrs) {
	    boolean result = true;

	    Enumeration names = attrs.getAttributeNames();
	    while (result && names.hasMoreElements()) {
		Object name = names.nextElement();
		result = attrs.getAttribute(name).equals(getAttribute(name));
	    }

	    return result;
	}

	/**
	 * If not overriden, the resolving parent defaults to
	 * the parent element.
	 *
	 * @return the attributes from the parent
	 * @see AttributeSet#getResolveParent
	 */
        public AttributeSet getResolveParent() {
	    View parent = host.getParent();
	    String name = (parent != null) ? parent.getElement().getName() : "*none*";
	    return (parent != null) ? parent.getAttributes() : null;
	}

	static Vector muxList = new Vector();
	AttributeSet[] attrs;
	View host;
    }


}

