//*****************************************************************************
/*
** FILE:   xe_Parser.java
**
** (c) 1997, 1998 Steve Withall.
**
** HISTORY:
**    06Oct97  stevew  Created.
**    20Oct97  stevew  Merged in xe_BaseEntityParser.
**    18Apr98  stevew  Renamed from xe_BaseParser to xe_Parser.
*/
package xe;

import xm.xm_ParseException;

import xg.xg_Attribute;
import xg.xg_Node;

import eh.eh_Debug;

import java.io.IOException;

//*****************************************************************************
/** Abstract base class for the parsing of any part of an XML document. It
 *  implements some common operations required by most parser derived classes,
 *  including high level logic to parse the next entity, and the ability to
 *  parse an attribute.
 */
public abstract class xe_Parser
{
    // Information about the entity currently being parsed, which is supplied by
    // the parser factory (via Set...() calls).
    /** The type of the entity currently being parsed. */
    protected int              EntityType;

    /** The name of the entity currently being parsed. (Note that this may bear
     *  no direct relationship to the EntityType.) */
    protected String           EntityName;

    /** If the node currently being parsed was preceded by whitespace in the
     *  source, this is it. */
    protected String           NodePrecedingWhitespace = null;

    /** The parent of the node currently being parsed. */
    protected xg_Node          ParentNode;

    /** Name of the class used to represent entities of this type. */
    protected String           EntityClassName;

    /** The parse manager controlling the current parse. */
    protected xe_ParseManager  TheParseManager = null;

    //*****************************************************************************
    /** Attempt to parse the next entity.
     *
     *  Any parse errors result in an IOException. This will either be
     *  IOException or xm_ParseException.
     *
     *  @param      InputParseManager  Controls parsing
     *  @return                        The parsed xg_Node derived class
     *  @exception  xm_ParseException  XML wellformedness error
     *  @exception  IOException        Error reading from source reader
     */
    public xg_Node parse(xe_ParseManager InputParseManager)
                               throws xm_ParseException, IOException
    {
        TheParseManager = InputParseManager;
        return parse();
    }

    //*****************************************************************************
    /** Attempt to parse the next entity.
     *
     *  Any parse errors result in either an IOException or xm_ParseException. This
     *  allows calling classes to handle errors in whatever way they see fit.
     *
     *  @return     The parsed xg_Node derived class
     *  @exception  xm_ParseException  XML wellformedness error
     *  @exception  IOException        Error reading from source reader
     */
    abstract public xg_Node parse() throws xm_ParseException, IOException;

    //*****************************************************************************
    /** Parse the next entity, assuming its start (ie. its name) has already been
     *  parsed. identified by the given node name, and then add it to its
     *  InputParentNode.
     *
     *  @param      InputEntityStartToken  The first token of the new node, which
     *                                      has already been parsed
     *  @param      InputParentNode        The parent to which the newly-parsed
     *                                      node belongs
     *  @return                            The node parsed
     *  @exception  xm_ParseException      XML wellformedness error
     *  @exception  IOException            Error reading from source reader
     */
    protected xg_Node parseNextEntity(xe_Token  InputEntityStartToken,
                                      xg_Node   InputParentNode)
                                           throws xm_ParseException, IOException
    {
        String  NextEntityName = InputEntityStartToken.getStringValue();
        int     TokenType      = InputEntityStartToken.getType();

        // Find the type of node associated with this type of token.
        xe_TokenTypeDefn  EntityTokenTypeDefn = xe_TokenType.getDefn(TokenType);
        int     NodeType   = EntityTokenTypeDefn.getNodeType();
        if (NodeType == 0)
            TheParseManager.throwParseException("No parser associated with node '"
                                                      + NextEntityName + "'");

        eh_Debug.add(7, "xe_Parser.parseNextEntity: Entity name = " + NextEntityName);

        // Create a parser suitable to parse this entity.
        xe_Parser  NewParser = TheParseManager.createParser(NextEntityName, NodeType);

        // Parse the entity and add it to its parent. Assume that any error
        // will throw an exception - so no error handling is required here.
        NewParser.setNodePrecedingWhitespace(InputEntityStartToken.getPrecedingWhitespace());
        NewParser.setParentNode(InputParentNode);
        xg_Node  NewNode = NewParser.parse(TheParseManager);

        // Extract the preceding whitespace from this entity's start token.
        NewNode.setPrecedingWhitespace(InputEntityStartToken.getPrecedingWhitespace());
        return NewNode;
    }

    //*****************************************************************************
    /** Parse an attribute (ie. name/value pair), assuming that the attribute's name
     *  has already been parsed. An exception is throw if the tokens parsed do not
     *  constitute an attribute. Attributes have the form:
     *
     *    Name = "Value"
     *
     *  @param      InputFirstToken  The first token of the attribute (which will
     *                                be preceding whitespace and its name)
     *  @return     A new attribute
     *  @exception  xm_ParseException      XML wellformedness error
     *  @exception  IOException            Error reading from source reader
     */
    public xg_Attribute parseAttribute(xe_Token  InputFirstToken)
                                            throws xm_ParseException, IOException
    {
        eh_Debug.add(8, "xe_ElementParser.parseAttribute:");

        // The start of the attribute must be a valid name.
        if (InputFirstToken.getType() != xe_TokenType.NAME)
            TheParseManager.throwParseException("Invalid name for attribute");

        xg_Attribute  NewAttribute = new xg_Attribute();
        NewAttribute.setPrecedingWhitespace(InputFirstToken.getPrecedingWhitespace() );
        NewAttribute.setName(InputFirstToken.getStringValue() );

        // Parse the '=' sign.
        xe_Token  CurrentToken = TheParseManager.parseNextTokenExpected(xe_TokenType.EQUALS_CHAR,
                                                                        true);
        NewAttribute.setWhitespaceBeforeEquals(CurrentToken.getPrecedingWhitespace() );

        // Parse the attribute's value.
        CurrentToken = TheParseManager.parseLiteralToken(true);
        NewAttribute.setWhitespaceAfterEquals(CurrentToken.getPrecedingWhitespace() );
        NewAttribute.setValue(CurrentToken.getStringValue() );

        return NewAttribute;
    }

    //*****************************************************************************
    /** Create an entity of the type suitable for the entity whose type is in the
     *  EntityName attribute. The entity created is guaranteed to be an instance of
     *  the class whose name is InputUsualClassName - or a derived class. This is
     *  no more than a convenience method: derived Parsers can call it with fewer
     *  parameters than the call we make in here.
     *
     *  @param  InputUsualClassName   The name of the class which is usually used
     *                                 to represent entities of the requested type
     *  @return Newly-created node
     *  @exception  xm_ParseException  Error creating the new node
     */
    public xg_Node createNode(String  InputUsualClassName) throws xm_ParseException
    {
        return(TheParseManager.createEntity(EntityName,
                                            EntityType,
                                            NodePrecedingWhitespace,
                                            InputUsualClassName,
                                            ParentNode) );
    }

    //*****************************************************************************
    /** Set the type of the entity currently being parsed.
     *
     *  @param  InputNodeType   Type of this entity
     */
    public void setEntityType(int  InputNodeType)
    {
        EntityType = InputNodeType;
    }

    //*****************************************************************************
    /** Set the name of the entity currently being parsed. (Note that this may bear
     *  no direct relationship to the EntityType.)
     *
     *  @param  InputEntityName   Name of current entity
     */
    public void setEntityName(String  InputEntityName)
    {
        EntityName = InputEntityName;
    }

    //*****************************************************************************
    /** Set the value of the preceding whitespace of the node currently being
     *  parsed.
     *
     *  @param  InputNodePrecedingWhitespace   Value of the preceding whitespace
     */
    public void setNodePrecedingWhitespace(String  InputNodePrecedingWhitespace)
    {
        NodePrecedingWhitespace = InputNodePrecedingWhitespace;
    }

    //*****************************************************************************
    /** Set the parent of the entity currently being parsed.
     *
     *  @param  InputParentNode   Parent of entity currently being parsed
     */
    public void setParentNode(xg_Node  InputParentNode)
    {
        ParentNode = InputParentNode;
    }

    //*****************************************************************************
    /** Set the name of the class used to represent entities of this type.
     *
     *  @param  InputEntityClassName   Name of entity class
     */
    public void setEntityClassName(String  InputEntityClassName)
    {
        EntityClassName = InputEntityClassName;
    }

    //*****************************************************************************
    /** Set the parse manager.
     *
     *  @param  InputParseManager  Controls parsing
     */
    public void setParseManager(xe_ParseManager InputParseManager)
    {
        TheParseManager = InputParseManager;
    }
}

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

