/**
 * This is a tag handler that will validate that the JSP compiler
 * is invoking the methods in the proper order
 *
 * The validity checking seems most easily described using a simple check on
 * what was the previously called method.
 *
 * This tag takes several optional attributes:
 *   att1, att2 do nothing
 *   includeBody - whether the body is to be included or not ("true" is default)
 *   skipPage - whether to skip the rest of the page ("false" is default)
 */

package tags;

import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.util.*;

public class Count extends TagSupport {

    public static String CREATED = "Created";
    public static String SET_PC = "Set_pc";
    public static String SET_PARENT = "Set_parent";
    public static String SET_ATT_1 = "Set_att_1";
    public static String SET_ATT_2 = "Set_att_2";
    public static String SET_INCLUDE_BODY = "Set_include_body";
    public static String SET_SKIP_PAGE = "Set_skip_page";
    public static String DO_START_TAG = "Do_start_tag";
    public static String DO_END_TAG = "Do_end_tag";
    public static String RELEASE = "Release";

    public static String DO_START_TAG_COUNTER = "Do_start_tag_counter";
    public static String DO_END_TAG_COUNTER = "Do_end_tag_counter";

    // setpageContext
    public void setPageContext(PageContext pc) {
	if ((state == DO_START_TAG)) {
	    throw new Error("Cannot setPageContext when in: "+state);
	}

	state = SET_PC;
	if (pc == null) {
	    throw new Error("Cannot set a null PageContext");
	}
	super.setPageContext(pc);
    }

    // setParent
    public void setParent(Tag parent) {
	if ((state == DO_START_TAG) ||
	    (state == CREATED) ||
	    (state == RELEASE)) {
	    throw new Error("Cannot setParent when in: "+state);
	}

	state = SET_PARENT;
	super.setParent(parent);
    }

    // getParent
    public Tag getParent() {
	return super.getParent();
    }

    //  att1
    public void setAtt1(String s) {
	if ((state == SET_PARENT) ||
	    (aSetter(state)) ||
	    (state == DO_END_TAG)) {
	} else {
	    throw new Error("Cannot setAtt1 when in: "+state);
	}

	state = SET_ATT_1;
	att1 = s;
    }
    
    public String getAtt1() {
	return att1;
    }

    //  att2
    public void setAtt2(String s) {
	if ((state == SET_PARENT) ||
	    (aSetter(state)) ||
	    (state == DO_END_TAG)) {
	} else {
	    throw new Error("Cannot setAtt1 when in: "+state);
	}

	state = SET_ATT_1;
	att1 = s;
    }

    public String getAtt2() {
	return att2;
    }

    //  includeBody
    public void setIncludeBody(String s) {
	if ((state == SET_PARENT) ||
	    (aSetter(state)) ||
	    (state == DO_END_TAG)) {
	} else {
	    throw new Error("Cannot setIncludeBody when in: "+state);
	}

	state = SET_INCLUDE_BODY;
	includeBody = s;
    }

    public String getIncludeBody() {
	return includeBody;
    }

    //  skipPage
    public void setSkipPage(String s) {
	if ((state == SET_PARENT) ||
	    (aSetter(state)) ||
	    (state == DO_END_TAG)) {
	} else {
	    throw new Error("Cannot setSkipPage when in: "+state);
	}

	state = SET_SKIP_PAGE;
	skipPage = s;
    }

    public String getSkipPage() {
	return skipPage;
    }

    // ================== Actions ====================

    // doStartTag
    public int doStartTag() {
	if ((aSetter(state)) ||
	    (state == SET_PARENT) ||
	    (state == DO_END_TAG)) {
	} else {
	    throw new Error("Cannot doStartTag when in: "+state);
	}

	state = DO_START_TAG;
	addCount(pageContext, DO_START_TAG_COUNTER);
	if (includeBody.equals("true")) {
	    return EVAL_BODY_INCLUDE;
	} else {
	    return SKIP_BODY;
	}
    }

    // doEndTag
    public int doEndTag() {
	if ((state != DO_START_TAG)) {
	    throw new Error("Cannot doEndTag when in: "+state);
	}

	state = DO_END_TAG;
	addCount(pageContext, DO_END_TAG_COUNTER);
	if (skipPage.equals("true")) {
	    return SKIP_PAGE;
	} else {
	    return EVAL_PAGE;
	}
    }


    // release
    public void release() {
	if ((state == DO_START_TAG)) {
	    throw new Error("Cannot release when in: "+state);
	}

	state = RELEASE;
	super.release();
    }

    // ========== Public methods for counting ============

    public static void addCount(PageContext pc, String s) {
	synchronized (pc) {
	    Integer c = (Integer) pc.getAttribute(s);
	    if (c == null) {
		pc.setAttribute(s, new Integer(1),
				PageContext.PAGE_SCOPE);
	    } else {
		int i = c.intValue();
		pc.setAttribute(s, new Integer(i+1),
				PageContext.PAGE_SCOPE);
	    }
	}
    }

    public static int getCount(PageContext pc, String s) {
	synchronized (pc) {
	    Integer c = (Integer) pc.getAttribute(s);
	    if (c == null) {
		return 0;
	    } else {
		return c.intValue();
	    }
	}
    }


    //  Auxiliary methods
    protected boolean aSetter(String state) {
	if ((state == SET_INCLUDE_BODY) ||
	    (state == SET_SKIP_PAGE) ||
	    (state == SET_ATT_1) ||
	    (state == SET_ATT_2)) {
	    return true;
	} else {
	    return false;
	}
    }

    // private data

    private String state = CREATED;
    private String att1 = null;
    private String att2 = null;
    private String includeBody = "true";
    private String skipPage = "false";
}
