//*****************************************************************************
/*
** FILE:   xs_XslElement.java
**
** (c) 1998 Steve Withall.
**
** HISTORY:
**    15Jun98  stevew  Created.
*/
package xs;

import xg.xg_Element;
import xg.xg_Node;
import xg.xg_VerificationException;

import eh.eh_Debug;

import com.sun.java.swing.JProgressBar;

import java.io.IOException;
import java.io.Writer;

import java.util.Hashtable;
import java.util.Vector;

//*****************************************************************************
/** An XML element to represent the root 'xsl' element of an XSL stylesheet.
 */
public class xs_XslElement extends xg_Element
{
    /** Vector of the rules defined in this stylesheet. */
    protected xs_StylesheetDefn  TheStylesheetDefn = new xs_StylesheetDefn();

    // The root rule.
    xs_RuleElement               RootRuleElement   = null;

    /** The element type name normally used in XML for this sort of element. */
    public final static String   RegisteredName    = "xsl";

    //*****************************************************************************
    /** Construct an xsl stylesheet element with no type and no name.
     */
    public xs_XslElement()
    {
    }

    //*****************************************************************************
    /** Construct an xsl stylesheet element with a name.
     *
     *  @param  InputNodeName  The name of the element
     */
    public xs_XslElement(String InputNodeName)
    {
        super(InputNodeName);
    }

    //*****************************************************************************
    /** <p>Verify that this node is correct (ie. internally correct and/or consistent
     *  with other nodes - such as its parent). The node is OK iff it has a at least
     *  one 'rule' child.</p>
     *
     *  @exception  xg_VerificationException  Description of verification problem
     */
    public void verify() throws xg_VerificationException
    {
        eh_Debug.add(8, "xs_XslElement.verify: Verify '" + toString() + "'");

        // Get this stylesheet's rules, and make sure there is at least one of them.
        Vector  RuleVector = getChildrenOfClass("xs.xs_RuleElement");
//        Vector  RuleVector = getChildrenOfType(xs_RuleElement.RegisteredName);
        if (RuleVector.size() < 1)
            throw new xg_VerificationException("xsl stylesheet contains no rules",
                                               getStartOffset(),
                                               getEndOffset() );
        TheStylesheetDefn.setRules(RuleVector);
        checkRootRule(RuleVector);  // Check there is a root rule - but no more than one
                                    // (and also number the rules)

        // Get this stylesheet's style rules.
        Vector  StyleRuleVector = getChildrenOfClass("xs.xs_StyleRuleElement");
//        Vector  StyleRuleVector = getChildrenOfType(xs_StyleRuleElement.RegisteredName);
        TheStylesheetDefn.setStyleRules(StyleRuleVector);
        numberStyleRules(StyleRuleVector);

        // Get this stylesheet's defined styles.
//        Vector      DefinedStyleVector  = getChildrenOfClass("xs.xs_DefinedStyleElement");
        Vector      DefinedStyleVector  = getChildrenOfType("define-style");  //TEMP hardcoding
//        Vector  DefinedStyleVector = getChildrenOfType(xs_DefinedStyleElement.RegisteredName);
        xg_Element  CurrentDefinedStyle = null;
        for (int DefinedStyleIndex = 0;
             DefinedStyleIndex < DefinedStyleVector.size();
             DefinedStyleIndex++)
        {
            CurrentDefinedStyle = (xg_Element)DefinedStyleVector.elementAt(DefinedStyleIndex);
            TheStylesheetDefn.addDefinedStyle(CurrentDefinedStyle);
        }
    }

    //*****************************************************************************
    /** Check this XSL stylesheet contains a root rule, and no more than one root
     *  rule. Along the way, set the RootRuleElement attribute accordingly.
     *
     *  @param      InputRuleVector    The rules
     *  @exception  xs_StyleException  Error applying style
     */
    private void checkRootRule(Vector  InputRuleVector) throws xg_VerificationException
    {
        eh_Debug.add(6, "xs_XslElement.checkRootRule:");
        RootRuleElement = null;  // So we don't confuse ourselves with a repeat call

        // Find the root rule.
        xs_RuleElement  CurrentRuleElement = null;
        for (int RuleIndex = 0; RuleIndex < InputRuleVector.size(); RuleIndex++)
        {
            CurrentRuleElement = (xs_RuleElement)InputRuleVector.elementAt(RuleIndex);
            if (CurrentRuleElement.isRoot() )
            {
                if (RootRuleElement == null)
                    RootRuleElement = CurrentRuleElement;
                else
                    throw new xg_VerificationException("xsl stylesheet contains more than one root rule"
                                                         + " (this is the second one)",
                                                       CurrentRuleElement.getStartOffset(),
                                                       CurrentRuleElement.getEndOffset() );
            }

            CurrentRuleElement.setRuleNumber(RuleIndex + 1);  // For cosmetic use only
        }

        // Make sure there is a root rule.
        if (RootRuleElement == null)
            throw new xg_VerificationException("xsl stylesheet does not contain a root rule",
                                               getStartOffset(),
                                               getEndOffset() );
    }

    //*****************************************************************************
    /** Allocate a sequential number to each style rule.
     *
     *  @param      InputStyleRuleVector  The style rules
     *  @exception  xs_StyleException     Error applying style
     */
    private void numberStyleRules(Vector  InputStyleRuleVector)
                                             throws xg_VerificationException
    {
        eh_Debug.add(6, "xs_XslElement.numberStyleRules:");
        xs_StyleRuleElement  CurrentRuleElement = null;
        for (int RuleIndex = 0; RuleIndex < InputStyleRuleVector.size(); RuleIndex++)
        {
            CurrentRuleElement = (xs_StyleRuleElement)InputStyleRuleVector.elementAt(RuleIndex);
            CurrentRuleElement.setRuleNumber(RuleIndex + 1);  // For cosmetic use only
        }
    }

    //*****************************************************************************
    /** Apply this style to the given source root element, and write the results to
     *  the InputResultsWriter.
     *
     *  @param      InputSourceRootElement  The pre-parsed source document
     *  @param      InputResultsWriter      The writer to which to write the
     *                                       results of applying a stylesheet to
     *                                       the source node
     *  @param      InputProgressBar        Bar on which to show progress (as
     *                                       measured by the end offset of each
     *                                       node after it is processed).
     *  @exception  xs_StyleException       Error applying style
     */
    public void applyStyle(xg_Element    InputSourceRootElement,
                           Writer        InputResultsWriter,
                           JProgressBar  InputProgressBar)
                                               throws xs_StyleException
    {
        eh_Debug.add(6, "xs_XslElement.applyStyle:");
        xs_Stylist  RootStylist = new xs_Stylist(TheStylesheetDefn);

        try
        {
            // Invoke each of the root rule's actions in turn.
            RootStylist.invokeRuleActions(RootRuleElement,
                                          InputSourceRootElement,
                                          InputResultsWriter,
                                          InputProgressBar);
        }
        catch (IOException  InputException)
        {
            eh_Debug.add(2, "xs_XslElement.applyStyle: Error writing to results writer: "
                                     + InputException);
        }
    }

    //*****************************************************************************
    /** Get the stylesheet definition.
     *
     *  @return  The stylesheet definition
     */
    public xs_StylesheetDefn getStylesheetDefn()
    {
        return TheStylesheetDefn;
    }

    //*****************************************************************************
    /** Return a string representation of the element (intended for use as debug
     *  output).
     *
     *  @return  String representing the node's name.
     */
    public String toString()
    {
        return("XSL stylesheet");
    }
}

//*****************************************************************************
