//*****************************************************************************
/*
** FILE:   xm_XmlEngine.java
**
** (c) 1997, 1998 Steve Withall.
**
** HISTORY:
**    05Oct97  stevew  Created.
**    06Apr98  stevew  Added xe_ParseListener.
**    03Jul98  stevew  Split from xe_XmlEngine.
*/
package xm;

import xg.xg_AttList;
import xg.xg_Document;
import xg.xg_Node;

import eh.eh_Debug;

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;

//*****************************************************************************
/** Base class for an engine for processing an XML source.
 *
 *  Given the name of an XML file, initiate the parsing of it, and return a tree
 *  of xg_Node-derived classes representing it. The root xg_Node will currently
 *  always be an xg_Document, but xg_Node is used so that in future we may be
 *  able to parse partial documents.
 */
abstract public class xm_XmlEngine
{
    /** List of configuration attributes for this engine. This is typically
     *  loaded from a configuration file (although the way it is set up is of no
     *  concern to us). */
    protected xg_AttList  EngineAttList = null;

    /** The origin of the XML source being parsed. */
    protected Reader      SourceReader  = null;

    // Parse control flags.
    /** Flag to specify whether the document should be validated (if a validating
     *  parser is being used). */
    protected boolean     ValidateFlag  = true;

    /** Flag to specify whether the document should be verified (if a parser
     *  supporting verification is being used). */
    protected boolean     VerifyFlag    = true;

    /** Parsing attempts to verify the internal consistency of each node after
     *  it has been parsed. This is only possible in special derived classes;
     *  standard xg nodes can do no more than check well-formedness and validate
     *  them. xg nodes therefore never return a verification error. If true,
     *  parsing stops if a node has a verification error (and can result in an
     *  otherwise well-formed or valid XML document failing to parse, because
     *  this sort of checking goes beyond XML's realm of control); if false, a
     *  warning is issued and parsing continues. */
    boolean               StopIfVerifyErrorFlag = true;

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

    //*****************************************************************************
    /** Constructor.
     */
    public xm_XmlEngine()
    {
        xm_NodeTypeRegistry.ensureInitialised();
    }

    //*****************************************************************************
    /** Parse the file represented by InputFileName.
     *
     *  @param      InputFileName      Name of XML file to process
     *  @return                        The xg_Node representing the document parsed
     *  @exception  xm_ParseException  The XML source contains an error
     *  @exception  IOException        An error occurred reading from named file
     */
    public xg_Node parseFile(String  InputFileName) throws xm_ParseException,
                                                           IOException
    {
        return parseFile(InputFileName, null);
    }

    //*****************************************************************************
    /** Parse the file represented by InputFileName.
     *
     *  @param      InputFileName      Name of XML file to process.
     *  @param      OutputDocument     Document into which to place the parse results
     *  @return                        The xg_Node representing the specification
     *  @exception  xm_ParseException  The XML source contains an error
     *  @exception  IOException        An error occurred reading from named file
     */
    public xg_Node parseFile(String       InputFileName,
                             xg_Document  OutputDocument) throws xm_ParseException,
                                                                 IOException
    {
        try
        {
            // Create a FileReader to read the given file.
            SourceReader = new FileReader(InputFileName);
        }
        catch (IOException  InputException)
        {
            eh_Debug.add(2, "xm_XmlEngine: Error opening XML file " + InputFileName
                                      + ". Exception = " + InputException);
            throw InputException;
        }

        // Having successfully opened the file, parse it.
        return parseSource(SourceReader, OutputDocument);
    }

    //*****************************************************************************
    /** Parse an XML source which is held in string InputSourceString.
     *
     *  @param      InputSourceString  The source itself - as a string
     *  @return                        The xg_Node representing the specification
     *  @exception  xm_ParseException  The XML source contains an error
     *  @exception  IOException        An error occurred reading from InputSourceString
     */
    public xg_Node parseString(String  InputSourceString) throws xm_ParseException,
                                                                 IOException
    {
        return parseString(InputSourceString, null);
    }

    //*****************************************************************************
    /** Parse an XML source which is held in string InputSourceString.
     *
     *  @param      InputSourceString  The source itself - as a string
     *  @param      OutputDocument     Document into which to place the parse results
     *  @return                        The xg_Node representing the specification
     *  @exception  xm_ParseException  The XML source contains an error
     *  @exception  IOException        An error occurred reading from InputSourceString
     */
    public xg_Node parseString(String       InputSourceString,
                               xg_Document  OutputDocument) throws xm_ParseException,
                                                                   IOException
    {
        // Create a StringReader to read the given string.
        SourceReader = new StringReader(InputSourceString);

        // Parse the string.
        return parseSource(SourceReader, OutputDocument);
    }

    //*****************************************************************************
    /** Parse the source supplied by the InputSourceReader.
     *
     *  @param      InputSourceReader  The reader from which to read the XML source
     *  @param      OutputDocument     Document into which to place the parse results
     *  @return                        The xg_Node representing the parse results
     *  @exception  xm_ParseException  The XML source contains an error
     *  @exception  IOException        An error occurred reading from InputSourceReader
     */
    abstract public xg_Node parseSource(Reader       InputSourceReader,
                                        xg_Document  OutputDocument)
                                              throws xm_ParseException, IOException;

    //*****************************************************************************
    /** Prepare and return a string describing how much was parsed, and how fast.
     *
     *  This method is private because it uses the current time as the time at which
     *  the parse 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 parse started
     *  @return                      A string description of the parse statistics
     */
    abstract protected String getParseStatsText(long  InputParseStartTime);

    //*****************************************************************************
    /** Set the parse listener which is to be informed of parse events. This
     *  version does nothing; it must be overridden in a derived class if parse
     *  listening is to be used.
     */
    public void setParseListener(xm_ParseListener  InputParseListener)
    {
    }

    //*****************************************************************************
    /** Give this engine a set of attributes. The way in which these attributes are
     *  used (and, indeed, whether any of them are) is entirely up to a specific
     *  engine implementation (ie. derived class).
     *
     *  @param  InputAttList  A list of attributes associated with this engine.
     */
    public void setAttList(xg_AttList  InputAttList)
    {
        EngineAttList = InputAttList;
    }

    //*****************************************************************************
    /** Set the flag to control whether to validate the document.
     *
     *  @param  InputValidateFlag  true means validate; false means do not.
     */
    public void setValidateFlag(boolean  InputValidateFlag)
    {
        ValidateFlag = InputValidateFlag;
    }

    //*****************************************************************************
    /** Set the flag to control whether to verify the document.
     *
     *  @param  InputVerifyFlag  true means verify; false means do not.
     */
    public void setVerifyFlag(boolean  InputVerifyFlag)
    {
        VerifyFlag = InputVerifyFlag;
    }

    //*****************************************************************************
    /** Specify whether we check the semantics of the source.
     *
     *  @param InputStopIfVerifyErrorFlag  True means we check semantics
     */
    public void setStopIfVerifyErrorFlag(boolean  InputStopIfVerifyErrorFlag)
    {
        StopIfVerifyErrorFlag = InputStopIfVerifyErrorFlag;
    }

    //*****************************************************************************
    /** Get this engine's attributes.
     *
     *  @return  A list of attributes associated with this engine.
     */
    public xg_AttList getAttList()
    {
        return EngineAttList;
    }

    //*****************************************************************************
    /** Get the flag to control whether to validate the document.
     *
     *  @return  true means validate; false means do not.
     */
    public boolean getValidateFlag()
    {
        return ValidateFlag;
    }

    //*****************************************************************************
    /** Get the flag to control whether to verify the document.
     *
     *  @return  true means verify; false means do not.
     */
    public boolean getVerifyFlag()
    {
        return VerifyFlag;
    }

    //*****************************************************************************
    /** Find out whether we check the semantics of the source.
     *
     *  @returns  True means we check semantics
     */
    public boolean getStopIfVerifyErrorFlag()
    {
        return StopIfVerifyErrorFlag;
    }

    //*****************************************************************************
    /** Get a text message explaining the last status message which was set.
     *
     *  @return  The last status message
     */
    public String getStatusMessage()
    {
        return StatusMessage;
    }
}

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

