//*****************************************************************************
/*
** FILE:   xe_ParseManager.java
**
** (c) 1997, 1998 Steve Withall.
**
** HISTORY:
**    06Oct97  stevew  Created.
**    25Oct97  stevew  Introduced hashtable-driven parsing in AutoParseNextToken().
**    06Apr98  stevew  Added xe_ParseListener.
**    17Apr98  stevew  Removed auto-parse functionality - and moved it to new
**                      sub-class xe_ParseManagerAuto.
**    06May98  stevew  Split main parsing into separate class xe_Tokenizer.
*/
package xe;

import xm.xm_ParseException;
import xm.xm_ParseListener;

import xg.xg_ValidationException;
import xg.xg_VerificationException;
import xg.xg_Node;

import eh.eh_Debug;

import java.util.Enumeration;
import java.io.IOException;
import java.io.Reader;

//*****************************************************************************
/** <p>Perform low-level parsing of an XML source and keep the state of the
 *  current parse (by inheriting from xe_Tokenizer). Also provide access to the
 *  factory for creating parsers and manages the parse listener (if there is
 *  one).</p>
 *
 *  <p>The parsing of the different parts of an XML source are created by an
 *  xe_ParserFactory held herein. Specialised parsers can be added by registering
 *  with xe_TokenType the keywords to which they relate.</p>
 */
public class xe_ParseManager extends xe_Tokenizer
{
    /** Factory for parsers of all types */
    xe_ParserFactory  TheParserFactory;

    /** Object to be notified of significant events during the parse process. */
    xm_ParseListener  TheParseListener = null;

    //*****************************************************************************
    /** Default constructor.
     */
    public xe_ParseManager()
    {
        TheParserFactory = new xe_ParserFactory();  // Create parser factory
    }

    //*****************************************************************************
    // Methods related to the parser factory.
    //*****************************************************************************
    /** Create a parser suitable for parser the entity whose name is giving in
     *  InputEntityName.
     *
     *  @param      InputNodeName  The name of the node for which we need a parser
     *  @param      InputNodeType  The type of node for which we need a parser
     *  @return                    The newly-created parser for the named entity
     *  @exception  xm_ParseException  Either no parser class is registered
     *                                  or it cannot be instantiated
     */
    public xe_Parser createParser(String InputNodeName,
                                  int    InputNodeType) throws xm_ParseException
    {
        xe_Parser  NewParser = null;
        try
        {
            // Create a parser.
            NewParser = TheParserFactory.createParser(InputNodeName, InputNodeType);
            NewParser.setParseManager(this);
        }
        catch (xm_ParseException  InputException)
        {
            // Throw an exception the same as the one intercepted, but with status
            // details (about the current positions in the source) added.
            throwParseException(InputException.getMessage() );
        }
        return NewParser;
    }

    //*****************************************************************************
    /** <p>Create an entity of the type suitable for the entity whose type is
     *  'InputEntityName'. The entity created is guaranteed to be an instance of
     *  the class whose name is InputUsualClassName - or a derived class.</p>
     *
     *  <p>Non-standard entities can be added by registering with xe_NodeTypeRegistry
     *  the names to which they relate.</p>
     *
     *  @param  InputEntityName      The name of the node we to create
     *  @param  InputNodeType        The type of node we need to create
     *  @param  InputNodePrecedingWhitespace  The whitespace which precedes the new
     *                                         node
     *  @param  InputUsualClassName  The name of the class which is usually used to
     *                                 represent entities of this type. The class
     *                               actually used must be this or a derived from it.
     *  @param  InputParentNode      The parent of the new entity. The newly-created
     *                                entity is added as a child of this parent.
     *  @return                      The newly-created entity
     *  @exception  xm_ParseException  XML wellformedness error
     */
    public xg_Node createEntity(String   InputEntityName,
                                int      InputNodeType,
                                String   InputNodePrecedingWhitespace,
                                String   InputUsualClassName,
                                xg_Node  InputParentNode) throws xm_ParseException
    {
        xg_Node  NewNode = TheParserFactory.createNode(InputEntityName,
                                                       InputNodeType,
                                                       InputUsualClassName);
        NewNode.setPrecedingWhitespace(InputNodePrecedingWhitespace);
        InputParentNode.addChild(NewNode);
        fireStartNodeEvent(NewNode, LastSignificantOffset - 1);
        return NewNode;
    }

    //*****************************************************************************
    /** Add InputNewNode to its parent (hence the name of this method: we're issuing
     *  its birth certificate), and then tell the outside world about this joyous
     *  event.
     *
     *  @param  InputNewNode         The new node being certified.
     *  @param  InputParentNode      The parent of the new node. The newly-created
     *                                node is added as a child of this parent.
     *  @exception  xm_ParseException  XML wellformedness error
     */
    public void certifyNewNode(xg_Node  InputNewNode,
                               xg_Node  InputParentNode) throws xm_ParseException
    {
        InputParentNode.addChild(InputNewNode);
        fireStartNodeEvent(InputNewNode, LastSignificantOffset - 1);
    }

    //*****************************************************************************
    // Methods related to the parse listener.
    //*****************************************************************************
    /** Set the parse listener which is to be informed of parse events.
     */
    public void setParseListener(xm_ParseListener  InputParseListener)
    {
        TheParseListener = InputParseListener;
    }

    //*****************************************************************************
    /** Send a 'start node' event to the parse listener.
     *
     *  @param  InputNewNode        The node whose start has just been parsed
     *  @param  InputStartPosition  The offset from the start of the source at
     *                               which this node starts
     */
    public void fireStartNodeEvent(xg_Node  InputNewNode,
                                   int      InputStartPosition)
     {
        if (TheParseListener != null)
            TheParseListener.startNode(InputNewNode, InputStartPosition);
    }

    //*****************************************************************************
    /** Send an 'end node' event to the parse listener.
     *
     *  @param  InputNewNode  The node which has just been parsed
     */
    public void fireEndNodeEvent(xg_Node  InputNewNode) throws xm_ParseException
    {
        if (TheParseListener != null)
            TheParseListener.endNode(InputNewNode, TotalCharCount - 1);

        // Validate the new node.
        if (ValidateFlag)
        {
            try
            {
                InputNewNode.validate();
            }
            catch (xg_ValidationException  InputException)
            {
                throwParseException(InputException.getMessageID(),
                                    InputNewNode.getStartOffset(),
                                    InputNewNode.getEndOffset() );
//                                    InputException.getStartPosition(),
//                                    InputException.getEndPosition() );
                //TBD We're not yet creating a proper message.
            }
        }

        // Ask the new node to verify that it is semantically OK. It can use this
        // as an opportunity to perform its own processing to become internally
        // consistent.
        if (VerifyFlag)
        {
            try
            {
                InputNewNode.verify();
            }
            catch (xg_VerificationException  InputException)
            {
                if (StopIfVerifyErrorFlag)
                    throwParseException(InputException.getMessageID(),
                                        InputNewNode.getStartOffset(),
                                        InputNewNode.getEndOffset() );
//                                        InputException.getStartPosition(),
//                                        InputException.getEndPosition() );
                    //TBD We're not yet creating a proper message.
                else
                    reportWarning(InputException.getMessageID() );
                    //TBD We're not yet creating a proper message.
            }
        }
    }
}

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