
import java.util.*;

/**
 * this class represents xml tags. they should 
 * have a name and a state, and a hash of parms
 * if applicable. (currently a string...)
 *
 * waplet classes are not currently in a package, because 
 * of strange browser/jar behavior with packages.
 */
public class Tag{

    /** stores the text tag passed in */
    private String _tagtext;

    /** tag state for an undefined tag */
    public static final int UNDEFINED = 0;

    /** tag state for an open tag */
    public static final int TAG_OPEN = 1;

    /** tag state for a close tag (starts with /) */
    public static final int TAG_CLOSE = 2;
    
    /** tag state for a self-contained tag (ends with /) */
    public static final int TAG_SELFCONTAINED = 3;

    /** the tag name, or primary identifier */
    private String _name;

    /** a hash of parameters for this tag */
    private Hashtable _parms;

    /** an internal field representing the tag state */
    private int _state; 

    /** constructor creates an empty tag with undefined state. */
    public Tag(){
	_name = "";
	_parms = null;
	_state = UNDEFINED;
    }

    /** constructor parses a text representation. */
    public Tag(String text){
	this();
	_tagtext = text;

	if(!(text.charAt(0) == '<' && text.charAt(text.length()-1) == '>')){
	    _name = text;
	}
	else{
	    // can't lowercase it here: if a parameter is a url, it
	    // needs to maintain case.
	    // text = text.substring(1, text.length() - 1).toLowerCase();

	    // clear out opening and closing brackets
	    text = text.substring(1, text.length() - 1);

	    // check the state
	    if(text.charAt(0) == '/'){
		text = text.substring(1).trim();
		_state = TAG_CLOSE;
	    }
	    else if(text.charAt(text.length()-1) == '/'){
		text = text.substring(0, text.length() - 1).trim();
		_state = TAG_SELFCONTAINED;
	    }
	    else{
		text = text.trim();
		_state = TAG_OPEN;
	    }

	    // check for parameters, if any: parse them.
	    int idx = 0;
	    if((idx = text.indexOf(' ')) > -1){
		parseParms( text.substring(idx + 1).trim());
		// now that you have the parms, lower case the id...
		text = text.substring(0, idx).trim().toLowerCase();
	    }
	    _name = text;
	}
    }

    /** 
     * the string is chopped into name value pairs, and pairs 
     * are stored in the parameter hash. watch out for quoted values:
     * don't put the quotes in the hash. there shouldn't be
     * any space between the key and the value, each pair should
     * look like key=value.
     */
    public void parseParms( String s){
	
	_parms = new Hashtable();
	StringBuffer sb = null;
	String key = null;
	String val = null;
	int idx = 0;
	int eidx = 0;
	
	while((idx = s.indexOf('=')) > -1){
	    key = s.substring(0, idx).trim();
	    s = s.substring(idx + 1).trim();
	    // is s quoted? if so, look for the end quote...
	    if(s.charAt(0) == '"')
		val = s.substring(1, (eidx = s.indexOf('"', 1)));	    
	    else {
		eidx = s.indexOf( ' ');
		if( eidx == -1) eidx = s.length() - 1;
		val = s.substring(0, eidx);

	    }
	    s = s.substring(eidx + 1).trim();
	    _parms.put(key, val);
	}
    }

    /** accessor method for tag id */
    public String getName(){ return _name; }

    /** set the id of a tag */
    public void setName(String name){ this._name = name; }

    /** accessor method for parameter hash */
    public Hashtable getParms(){ return _parms; }

    /** set the parameter hash directly */
    public void setParms(Hashtable parms){ this._parms = parms; }

    /** accessor method for a single parameter value, by key (name). */
    public String getParm(String key){
	Object obj = null;
	if(null == _parms) return null;
	return (null == (obj = _parms.get(key))) ? null : (String)obj;
    }

    /** 
     * set a single parameter name/value pair. this method will 
     * overwrite existing values.
     */
    public void setParm(String key, String val){
	if(null == _parms) _parms = new Hashtable();
	_parms.put(key, val);
    }

    /** accessor method for the tag state. */
    public int getState(){ return _state; }

    /** set the tag state directly. */
    public void setState(int state){ this._state = state; }

    /** 
     * return a string representation of the tag. this includes the
     * name and all name-value parameter pairs. (this is a descriptor
     * method, it won't return the actual tag that was passed in). 
     */
    public String toString(){
	StringBuffer sb = 
	    new StringBuffer("Tag name=``" + _name + "'' state = " + _state);
	if(null != _parms){
	    String key = null;
	    for(Enumeration e = _parms.keys(); e.hasMoreElements(); ){
		key = ((String)(e.nextElement()));
		sb.append("\n\t" + key + "=``" + 
                          ((String)(_parms.get(key))) + "''");
	    }
	}
	return sb.toString();
    }
}
