//*****************************************************************************
/*
** FILE:   xt_NxpEngine.java
**
** (c) 1997, 1998 Steve Withall.
**
** HISTORY:
**    28Sep97  stevew  Created this file, based on NXP.Esis_Stdout.
**    05Jul98  stevew  Renamed from xt_NxpDebugEsis to xt_NxpEngine, and
**                      restructured to extend xm_XmlEngine.
*/
package xt;

import NXP.Esis;
import NXP.Esis_Stdout;
import NXP.Notation;
import NXP.XML;
import NXP.XMLParser;

import xm.xm_NodeFactory;
import xm.xm_NodeTypeRegistry;
import xm.xm_ParseException;
import xm.xm_ParseListener;
import xm.xm_XmlEngine;

import xg.xg_Document;
import xg.xg_Element;
import xg.xg_Node;
import xg.xg_PI;
import xg.xg_Text;

import xa.xa_NodeTypeChoiceList;

import eh.eh_Debug;

import java.net.URL;

import java.util.Enumeration;
import java.util.Vector;

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

//*****************************************************************************
/** Engine for processing an XML source using Norbert's NXP processor.
 */
public class xt_NxpEngine extends xm_XmlEngine implements Esis
{
    /** The XML document created by the most recent parse. */
    xg_Document     CurrentDocument = null;

    /** The node currently being parsed (if we are in the middle of parsing). */
    xg_Node         CurrentNode     = null;

    /** Factory for creating xg_Nodes of the right type. (Note that while its
     *  methods are currently all static, we use a proper factory object since its
     *  implementation may change). */
    xm_NodeFactory  TheNodeFactory  = new xm_NodeFactory();

    //*****************************************************************************
    /** Constructor.
     */
    public xt_NxpEngine()
    {
        super();
    }

    //*****************************************************************************
    /** 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
    {
        // Work on the supplied document; create a new one if it's null.
        if (OutputDocument == null)
            CurrentDocument = new xg_Document();
        else
            CurrentDocument = OutputDocument;
        CurrentNode = CurrentDocument;

        boolean  VerboseFlag = false;
        boolean  catalog     = false;

        // Create parser
        eh_Debug.add(3, "NXP: Create file parser");
        XMLParser  NxpParser = new XMLParser(InputFileName,
                                             XML.FILE_INPS,
                                             VerboseFlag,
                                             ValidateFlag,
                                             catalog);

        NxpParser.getParser().setEsis(this);

        long  StartTime = System.currentTimeMillis();
        eh_Debug.add(3, "NXP: Start parsing");
        NxpParser.startParsing();

        StatusMessage = new String("Processing using NXP completed successfully "
                                        + getParseStatsText(StartTime));
        return CurrentDocument;
    }

    //*****************************************************************************
    /** 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
    {
        throw new xm_ParseException("Norbert's NXP processor is unable to parse a string");
    }

    //*****************************************************************************
    /** 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
     */
    public xg_Node parseSource(Reader       InputSourceReader,
                               xg_Document  OutputDocument) throws xm_ParseException,
                                                                   IOException
    {
        throw new xm_ParseException("Norbert's NXP processor is unable to parse from a Reader");
    }

    //*****************************************************************************
    /** Set the parse listener which is to be informed of parse events. The main
     *  purpose of this is to be able to show progress to the user; since NXP
     *  doesn't provide any information which allows us to do this, this
     *  implementation does nothing.
     *
     *  @param  InputParseListener  Listener to be informed of certain parse
     *                               events
     */
    public void setParseListener(xm_ParseListener  InputParseListener)
    {
    }

    //*****************************************************************************
    /** 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
     */
    private String getParseStatsText(long  InputParseStartTime)
    {
        long    ParseTimeMillis = System.currentTimeMillis() - InputParseStartTime;
        float   ParseTime       = ((float)ParseTimeMillis) / 1000;
//        float   ParseRate       = TheParseManager.getTotalCharCount() / ParseTime;
        String  ParseStatsText  = new String("in " + ParseTime + " seconds");
//        String  ParseStatsText  = new String(TheParseManager.getTotalCharCount()
//                                             + " characters/" + TheParseManager.getLineCount()
//                                             + " lines in " + ParseTime
//                                             + " seconds (" + ParseRate + " characters per second)");
        return ParseStatsText;
    }

    //*****************************************************************************
    // Methods implementing org.xml.sax.DocumentHandler, which receive
    // notification of general document events.
    //*****************************************************************************
    /** A new element has been parsed. Create an element object of a suitable type,
     *  give it its attributes and then add it to its parent.
     *
     *  @param  InputElementName  The name of the new element
     *  @param  InputAttList      The new element's attributes
     */
/*    public void startElement(String InputElementName, AttributeList InputAttList)
                                       throws SAXException
    {
        eh_Debug.add(7, "Start element: " + InputElementName);

        // Create new element.
        xg_Element  NewElement = null;
        try
        {
            NewElement = (xg_Element)TheNodeFactory.createNode(InputElementName,
                                                               xa_NodeTypeChoiceList.ELEMENT_TYPE,
                                                               "xg.xg_Element");
        }
        catch (xm_ParseException  InputException)
        {
            throw new SAXParseException(InputException.getMessage(),
                                        null,
                                        null,
                                        InputException.getLineNum(),
                                        InputException.getColumnNum() );
        }

        // Give the new element its attributes.
        if (InputAttList.getLength() == 0)
        {
            eh_Debug.add(7, "Element " + InputElementName + " has no attributes");
        }
        else
        {
            for (int AttIndex = 0; AttIndex < InputAttList.getLength(); AttIndex++)
            {
                NewElement.addAttribute(InputAttList.getName(AttIndex),
                                        InputAttList.getValue(AttIndex) );
                String AttName  = InputAttList.getName(AttIndex);
                String AttType  = InputAttList.getType(AttIndex);
                String AttValue = InputAttList.getValue(AttIndex);
                eh_Debug.add(7, "Attribute " + AttIndex + ": '" + AttName
                                    + "' = '" + AttValue
                                    + "' (Type '" + AttType + "')");
            }
        }

        CurrentNode.addChild(NewElement);  // Add the new element to its parent
        CurrentNode = NewElement;          // The new element is now the current node
    }
*/
    //*****************************************************************************
    /** The end of the current element has been parsed.
     */
/*    public void endElement (String InputElementName)
    {
        eh_Debug.add(7, "End element: " + InputElementName);
//        CurrentNode.verify();  //TBD Use config flag to switch this off
        CurrentNode = CurrentNode.getParentNode();  // Parent becomes the current node
    }
*/
    //*****************************************************************************
    /** Print a message when characters are found.
     */
/*    public void characters(char ch[], int start, int length)
    {
        eh_Debug.add(7, "Characters: '"
		                   + escapeCharacters(ch, start, length)
		                   + "' (Start = " + start + ", Length = " + length + ")");
        xg_Text  NewText = new xg_Text(new String(ch, start, length) );
        CurrentNode.addChild(NewText);
    }
*/
    //*****************************************************************************
    /** Print a message when a processing instruction is found.
     */
/*    public void processingInstruction (String target, String data)
    {
        eh_Debug.add(7, "Processing instruction: " + target + " " + data);
        xg_PI  NewPI = new xg_PI(new String(target + " " + data) );
        CurrentNode.addChild(NewPI);
    }
*/
    //*****************************************************************************
    /** Escape special characters for display.
     */
/*    private static String escapeCharacters(char ch[], int start, int length)
    {
        StringBuffer out = new StringBuffer();

        for (int i = start; i < start+length; i++)
        {
            if (ch[i] >= 0x20 && ch[i] < 0x7f)
	            out.append(ch[i]);
            else
	            out.append("&#" + (int)ch[i] + ';');
        }

        return out.toString();
    }
*/
    //*****************************************************************************
    //*Esis*methods****************************************************************
    //*****************************************************************************
    /** Indicates that an error has occurred during parsing.
     *
     *  @param  code  The error code
     *  @param  desc  Some additional info
     **/
    public void error(int code, String desc)
    {
        eh_Debug.add(2, new String("NXP error: " + desc) );
    }

    //*****************************************************************************
    /** Indicates the start of a SGML Tag
     *
     *  @param  name  The name of Tag that is opened
     **/
    public void tag_open(String name)
    {
        eh_Debug.add(3, new String("<" + name + ">") );
    }

    //*****************************************************************************
    /** Indicates the end of a SGML Tag
     *
     *  @param  name  The name of Tag that is closed
     **/
    public void tag_close(String name)
    {
        eh_Debug.add(3, new String("</" + name + ">") );
    }

    //*****************************************************************************
    /** Data that is parsed
     *
     *  @param  data  The content
     **/
    public void data(String data)
    {
        eh_Debug.add(3, data);
//        eh_Debug.add(3, new String("\"" + data + "\"") );
    }

    //*****************************************************************************
    /** System data of a processing instruction
     *
     *  @param  target  The target of the processing instruction
     *  @param  pi      The processing instruction
     **/
    public void processing_instruction(String target, String pi)
    {
        eh_Debug.add(3, new String("{" + target + " : " + pi + "}") );
    }

    //*****************************************************************************
    /** Attribute in an element
     *
     *  @param  name             The name of the attribute
     *  @param  decl_val_type    The type of the declared value
     *  @param  decl_val         The declared value
     *  @param  default_value    The default_value
     *  @param  specified_value  The specified value
     **/
    public void Attribute(String name,
                          int    decl_val_type,
                          String decl_val,
                          String default_value,
                          Vector specified_value)
    {
        String decl_val_string = "";

        String spec_val = "";

        if (specified_value != null)
            spec_val = specified_value.toString();

        eh_Debug.add(3, new String("[" + name + " " + decl_val_string + " " + default_value + "  " + spec_val + "]") );
    }

    //*****************************************************************************
    /** SDATA entity
     *
     *  @param  name  The name of the entity
     *  @param  text  The text of the entity
     **/
    public void SDATA_Entity(String name,String text)
    {
        eh_Debug.add(3, new String("?" + text + "?") );
    }

    //*****************************************************************************
    /** CDATA section
     *
     *  @param  data  The data that has been parsed
     **/
    public void CDATA_Section(String data)
    {
        eh_Debug.add(3, new String("[[" + data + "]]") );
    }

    //*****************************************************************************
    /** NDATA entity
     *
     *  @param  name  The name of the entity
     *  @param  not   The notation
     **/
    public void NDATA_Entity(String name, Notation not)
    {
        eh_Debug.add(3, "<|");
        eh_Debug.add(3, name);
        if (not == null)
        {
            eh_Debug.add(3, "Notation NULL???");
        }
        else
        {
            eh_Debug.add(3, not.toString());
        }
        eh_Debug.add(3, "|>");
    }
}

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