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

import xm.xm_NodeTypeRegistry;

import xg.xg_Document;
import xg.xg_Element;
import xg.xg_Node;

import eh.eh_Debug;

import com.sun.java.swing.JProgressBar;

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

//*****************************************************************************
/** The engine for applying an XSL stylesheet to an XML document. Given a
 *  pre-parsed XSL stylesheet and a pre-parsed XML document, it will generate
 *  the results.
 */
public class xs_XslEngine
{
    /** The writer to which the results of applying an XSL stylesheet to an XML
     *  document will be placed. */
    Writer           ResultsWriter         = null;

    /** The root node of the stylesheet currently being applied. */
    xs_XslElement    StylesheetRootElement = null;

    /** The root node of the source document currently being processed. */
    xg_Element       SourceRootElement     = null;

    /** A textual explanation of the most recent style status. */
//    String           StatusMessage = null;

    //*****************************************************************************
    /** Default constructor - which simply ensures all XSL-related element classes
     *  are registered.
     */
    public xs_XslEngine()
    {
        ensureClassesRegistered();
    }

    //*****************************************************************************
    /** Ensure the XSL-related element classes are registered. That is, check to
     *  see if the first one is regstered, and if it isn't, register all of them.
     */
    static public void ensureClassesRegistered()
    {
        if (xm_NodeTypeRegistry.getDefn(xs_XslElement.RegisteredName) == null)
            registerElementClasses();
    }

    //*****************************************************************************
    /** <p>Register special types of element classes used to represent particular
     *  XSL constructs.</p>
     *
     *  <p>The present approach isn't ideal: it's static, so affects <b>all</b> XML
     *  parsing, and it doesn't permit the unregistering of all the special
     *  registrations.</p>
     */
    static public void registerElementClasses()
    {
        eh_Debug.add(8, "xs_XslEngine.registerElementClasses:");
        xm_NodeTypeRegistry.setDefaultParserClassName("xe.xe_ElementParser");
        xm_NodeTypeRegistry.setDefaultCustomizerClassName("xc.xc_ElementCustomizer");
        xm_NodeTypeRegistry.setDefaultViewClassName("xv.xv_BoxViewX");

        // Register core XSL element types.
        xm_NodeTypeRegistry.register(xs_XslElement.RegisteredName,
                                     "Main XSL element",
                                     "xs.xs_XslElement");
        xm_NodeTypeRegistry.register(xs_RuleElement.RegisteredName,
                                     "XSL rule",
                                     "xs.xs_RuleElement");
        xm_NodeTypeRegistry.register(xs_StyleRuleElement.RegisteredName,
                                     "XSL style rule",
                                     "xs.xs_StyleRuleElement");

        // Register XSL pattern types.
        xm_NodeTypeRegistry.setDefaultCustomizerClassName("xs.xs_PatternElementCustomizer");
        xm_NodeTypeRegistry.register("root",
                                     "XSL root pattern",
                                     "xs.xs_PatternElement");
        xm_NodeTypeRegistry.register(xs_ElementElement.RegisteredName,
                                     "XSL element",
                                     "xs.xs_ElementElement");
        xm_NodeTypeRegistry.register(xs_TargetElementElement.RegisteredName,
                                     "XSL target element",
                                     "xs.xs_TargetElementElement");

        // Register XSL action types.
        xm_NodeTypeRegistry.setDefaultCustomizerClassName("xc.xc_ElementCustomizer");
        xm_NodeTypeRegistry.register(xs_ChildrenElement.RegisteredName,
                                     "XSL children element",
                                     "xs.xs_ChildrenElement");
        xm_NodeTypeRegistry.register(xs_EmptyElement.RegisteredName,
                                     "XSL empty element",
                                     "xs.xs_EmptyElement");
        xm_NodeTypeRegistry.register(xs_EvalElement.RegisteredName,
                                     "XSL eval element",
                                     "xs.xs_EvalElement");
    }

    //*****************************************************************************
    /** Apply the style defined in the given stylesheet to the given source
     *  document, and write the results to the given writer.
     *
     *  @param      InputStylesheetDocument  Pre-parsed XSL stylesheet
     *  @param      InputSourceDocument      The pre-parsed source document
     *  @param      InputProgressBar         Bar on which to show progress (as
     *                                        measured by the end offset of each
     *                                        node after it is processed).
     *  @return     A string representing the resulting document
     *  @exception  xs_StyleException        Error applying style
     */
    public String applyStyle(xg_Document   InputStylesheetDocument,
                             xg_Document   InputSourceDocument,
                             JProgressBar  InputProgressBar)
                                                  throws xs_StyleException
    {
        eh_Debug.add(6, "xs_XslEngine.applyStyle (to return string):");

        // Create a StringWriter to which to write the results.
        StringWriter  ResultsStringWriter = new StringWriter();
        ResultsWriter = ResultsStringWriter;

        applyStyleInternal(InputStylesheetDocument,
                           InputSourceDocument,
                           InputProgressBar);
        return ResultsStringWriter.toString();
    }

    //*****************************************************************************
    /** Apply the style defined in the given stylesheet to the given source
     *  document, and write the results to the given writer.
     *
     *  @param      InputStylesheetDocument  Pre-parsed XSL stylesheet
     *  @param      InputSourceDocument      The pre-parsed source document
     *  @param      OutputResultsWriter      The results
     *  @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_Document   InputStylesheetDocument,
                           xg_Document   InputSourceDocument,
                           Writer        OutputResultsWriter,
                           JProgressBar  InputProgressBar)
                                                throws xs_StyleException
    {
        eh_Debug.add(6, "xs_XslEngine.applyStyle (writing to given writer):");
        ResultsWriter = OutputResultsWriter;
        applyStyleInternal(InputStylesheetDocument,
                           InputSourceDocument,
                           InputProgressBar);
    }

    //*****************************************************************************
    /** Apply the style defined in the given stylesheet to the given source
     *  document, and write the results to attribute ResultsWriter.
     *
     *  @param      InputStylesheetDocument  Pre-parsed XSL stylesheet
     *  @param      InputSourceDocument      The pre-parsed source document
     *  @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
     */
    private void applyStyleInternal(xg_Document   InputStylesheetDocument,
                                    xg_Document   InputSourceDocument,
                                    JProgressBar  InputProgressBar)
                                                     throws xs_StyleException
    {
        eh_Debug.add(6, "xs_XslEngine.applyStyleInternal:");

        // Make sure the input documents are suitable for processing.
        checkDocuments(InputStylesheetDocument, InputSourceDocument);

        try
        {
            // Ask the stylesheet to apply style to the source root element.
            StylesheetRootElement.applyStyle(SourceRootElement,
                                             ResultsWriter,
                                             InputProgressBar);
            ResultsWriter.flush();
            ResultsWriter.close();
        }
        catch (IOException  InputException)
        {
            eh_Debug.add(2, "xs_XslEngine.applyStyleInternal: Error writing to results writer: "
                                     + InputException);
        }
    }

    //*****************************************************************************
    /** Check that the given stylesheet and the given source document are
     *  suitable for applying the former to the latter. That is:
     *
     *  <p>* They must not be null.</p>
     *  <p>* They must each contain a root node which is not null.</p>
     *  <p>* The stylesheet's root node must be of type 'xsl'.</p>
     *
     *  @param      InputStylesheetDocument  Pre-parsed XSL stylesheet
     *  @param      InputSourceDocument      The pre-parsed source document
     *  @exception  xs_StyleException        One of the documents is unacceptable
     */
    public void checkDocuments(xg_Document  InputStylesheetDocument,
                               xg_Document  InputSourceDocument)
                                                    throws xs_StyleException
    {
        eh_Debug.add(6, "xs_XslEngine.checkDocuments:");

        if (InputStylesheetDocument == null)
            throw new xs_StyleException("Stylesheet document is null");

        if (InputSourceDocument == null)
            throw new xs_StyleException("Source document is null");

        xg_Element  StylesheetRootxgElement = InputStylesheetDocument.getRootElement();
        if (StylesheetRootxgElement == null)
            throw new xs_StyleException("Stylesheet's root element is null");

        SourceRootElement = InputSourceDocument.getRootElement();
        if (SourceRootElement == null)
            throw new xs_StyleException("Source document's root element is null");

        String  StylesheetRootElementName = StylesheetRootxgElement.getName();
        if (!StylesheetRootElementName.equals(xs_XslElement.RegisteredName) )
            throw new xs_StyleException("Is the stylesheet really a stylesheet? (Its root element is '"
                                             + StylesheetRootElementName
                                             + "', when it should be '"
                                             + xs_XslElement.RegisteredName + "')");

        if (!(StylesheetRootxgElement instanceof xs_XslElement) )
            throw new xs_StyleException("Stylesheet root element is not an instance of xs_XslElement."
                                             + " (Has xs_XslElement not been registered?)");

        StylesheetRootElement = (xs_XslElement)StylesheetRootxgElement;
    }

    //*****************************************************************************
    /** Prepare and return a string describing how many objects style was applied
     *  to, and how fast.
     *
     *  This method is private because it uses the current time as the time at which
     *  the processing is deemed to have completed - so if called from outside later,
     *  a misleading result will be given.
     *
     *  @param  InputParseStartTime  The system time at which the style processing
     *                                started
     *  @return                      A string description of the style processing
     *                                statistics
     */
/*    private String getParseStatsText(long  InputParseStartTime)
    {
        long    ParseTimeMillis = System.currentTimeMillis() - InputParseStartTime;
        float   ParseTime       = ((float)ParseTimeMillis) / 1000;
        float   ParseRate       = TheParseManager.getTotalCharCount() / ParseTime;
        String  ParseStatsText  = new String(TheParseManager.getTotalCharCount()
                                             + " characters/" + TheParseManager.getLineCount()
                                             + " lines in " + ParseTime
                                             + " seconds (" + ParseRate + " characters per second)");
        return ParseStatsText;
    }
*/
    //*****************************************************************************
    /** Get a text message explaining the last status message which was set.
     *
     *  @return  The last status message
     */
/*    public String getStatusMessage()
    {
        return StatusMessage;
    }
*/
}

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

