/**
 *  Copyright (C) 2005 Orbeon, Inc.
 *
 *  This program is free software; you can redistribute it and/or modify it under the terms of the
 *  GNU Lesser General Public License as published by the Free Software Foundation; either version
 *  2.1 of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *  See the GNU Lesser General Public License for more details.
 *
 *  The full text of the license is available at http://www.gnu.org/copyleft/lesser.html
 */
package org.orbeon.oxf.xml.saxrewrite;

import org.orbeon.oxf.xml.XMLReceiver;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;

/**
 * Base state. Simply forwards data to the destination content handler and returns itself. That is unless the (element)
 * depth becomes negative after an end element event. In this case the previous state is returned. This means btw that
 * we are really only considering state changes on start and end element events.
 */
public abstract class State {
    /**
     * At the moment are state transitions only happen on start element and end element events.
     * Therefore we track element depth and by default when the depth becomes negative we switch
     * to the previous state.
     */
    private int depth = 0;

    /**
     * The destination of the rewrite transformation.
     */
    protected final XMLReceiver xmlReceiver;

    /**
     * What you think.
     */
    protected final State previousState;

    /**
     *
     * @param previousState previous state
     * @param xmlReceiver   destination for the rewrite transformation

     * @see #previousState
     * @see #xmlReceiver
     */
    public State(final State previousState, final XMLReceiver xmlReceiver) {
        this.previousState = previousState;
        this.xmlReceiver = xmlReceiver;
    }

    /**
     * Just forwards the event to the content handler.
     *
     * @see #endElement( String, String, String )
     */
    protected void endElementStart(final String ns, final String lnam, final String qnam)
            throws SAXException {
        xmlReceiver.endElement(ns, lnam, qnam);
    }

    /**
     * @return What you think
     */
    protected int getDepth() {
        return depth;
    }

    /**
     * @see #startElement(String, String, String, Attributes)
     */
    protected abstract State startElementStart
            (final String ns, final String lnam, final String qnam, final Attributes atts)
            throws SAXException;

    public State characters(final char[] ch, final int strt, final int len)
            throws SAXException {
        xmlReceiver.characters(ch, strt, len);
        return this;
    }

    public State endDocument() throws SAXException {
        xmlReceiver.endDocument();
        return this;
    }

    /**
     * Template method. Calls endElementStart and then decrements depth. If the depth == 0 then the previous state is
     * returned. Otherwise this state is returned.
     */
    public final State endElement(final String ns, final String lnam, final String qnam)
            throws SAXException {
        endElementStart(ns, lnam, qnam);
        depth--;
        return depth == 0 ? previousState : this;
    }

    public State endPrefixMapping(final String pfx) throws SAXException {
        xmlReceiver.endPrefixMapping(pfx);
        return this;
    }

    public State ignorableWhitespace(final char[] ch, final int strt, final int len)
            throws SAXException {
        xmlReceiver.ignorableWhitespace(ch, strt, len);
        return this;
    }

    public State processingInstruction(final String trgt, final String dat)
            throws SAXException {
        xmlReceiver.processingInstruction(trgt, dat);
        return this;
    }

    public State setDocumentLocator(final Locator lctr) {
        xmlReceiver.setDocumentLocator(lctr);
        return this;
    }

    public State skippedEntity(final String nam) throws SAXException {
        xmlReceiver.skippedEntity(nam);
        return this;
    }

    public State startDocument() throws SAXException {
        xmlReceiver.startDocument();
        return this;
    }

    /**
     * Template method.  Calls startElementStart.  If startElementStart returns this increments
     * depth.  Lastly, returns the value returned by startElementStart.
     */
    public final State startElement
            (final String ns, final String lnam, final String qnam, final Attributes atts)
            throws SAXException {
        final State ret = startElementStart(ns, lnam, qnam, atts);
        if (ret == this) depth++;
        return ret;
    }

    public State startPrefixMapping(final String pfx, final String uri) throws SAXException {
        xmlReceiver.startPrefixMapping(pfx, uri);
        return this;
    }

    public State startDTD(String name, String publicId, String systemId) throws SAXException {
        xmlReceiver.startDTD(name, publicId, systemId);
        return this;
    }

    public State endDTD() throws SAXException {
        xmlReceiver.endDTD();
        return this;
    }

    public State startEntity(String name) throws SAXException {
        xmlReceiver.startEntity(name);
        return this;
    }

    public State endEntity(String name) throws SAXException {
        xmlReceiver.endEntity(name);
        return this;
    }

    public State startCDATA() throws SAXException {
        xmlReceiver.startCDATA();
        return this;
    }

    public State endCDATA() throws SAXException {
        xmlReceiver.endCDATA();
        return this;
    }

    public State comment(char[] ch, int start, int length) throws SAXException {
        xmlReceiver.comment(ch, start, length);
        return this;
    }
}
