//*****************************************************************************
/*
** FILE:   xm_ParseThread.java
**
** (c) 1998 Steve Withall.
**
** HISTORY:
**    11Apr98  stevew  Created.
**    03Jul98  stevew  Moved from xt to xm.
**    14Jul98  stevew  Extracted xm_Thread into a separate class.
*/
package xm;

import xm.xm_ParseException;
import xm.xm_StatusBar;
import xm.xm_XmlEngine;

import xg.xg_Document;

import eh.eh_Debug;

import com.sun.java.swing.JProgressBar;
import com.sun.java.swing.JTextArea;
import com.sun.java.swing.text.BadLocationException;
import com.sun.java.swing.text.Document;

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

//*****************************************************************************
/** Thread to load an XML source from a file and/or parse an XML source. The
 *  reason the loading and parsing are in the same class is to allow both to
 *  be performed one after the other (which would be complicated if they were
 *  not in the same thread).
 */
public class xm_ParseThread extends xm_Thread
{
    /** Defines the actions the thread is required to take. It must be set to
     *  LOAD, LOAD_AND_PARSE_STRING, PARSE_STRING or PARSE_FILE. (Note that the
     *  default value is none of these, so it must be explcitly set.) */
    int               ThreadMode     = 0;

    /** The pathname of the XML source file to read. This is not required if in
     *  PARSE_STRING mode. */
    String            SourcePathname = null;

    /** The model which holds the document to be parsed, and all the supporting
     *  paraphernalia. */
    xm_DocumentModel  TheDocumentModel;

    /** The XML engine to be used to parse the document. This is extracted from
     *  TheDocumentModel. */
    xm_XmlEngine      TheXmlEngine;

    /** Text area which assumed to be displaying the document in TheDocumentModel.
     *  Any error in the parse is highlighted by selecting the appropriate part
     *  of the source herein. */
    JTextArea         SourceTextArea;

    /** The file object from which to read. */
    File              SourceFile;

    // Allowed modes.
    /** We will load the source file into the source string. */
    public static final int    LOAD                  =  1;

    /** We will load the source file into the source string and then parse it.*/
    public static final int    LOAD_AND_PARSE_STRING =  2;

    /** We will parse the source string. */
    public static final int    PARSE_STRING          =  3;

    /** We will parse the source file directly, without loading it into the
     *  source string. */
    public static final int    PARSE_FILE            =  4;

    //*****************************************************************************
    /** Constructor.
     *
     *  @param InputThreadMode      Defines what actions we wish to take
     *  @param InputSourcePathname  Name of file to load
     *  @param InputDocumentModel   Model holding document to be parsed
     *  @param InputSourceTextArea  Source text area, to highlight errors
     */
    public xm_ParseThread(int               InputThreadMode,
                          String            InputSourcePathname,
                          xm_DocumentModel  InputDocumentModel,
                          JTextArea         InputSourceTextArea)
    {
        super(InputDocumentModel.getStatusBar() );
    	setThreadMode(InputThreadMode);
    	SourcePathname   = InputSourcePathname;
    	TheDocumentModel = InputDocumentModel;
    	SourceTextArea   = InputSourceTextArea;
    	TheXmlEngine     = TheDocumentModel.getXmlEngine();
    }

    //*****************************************************************************
    /** Constructor, omitting file pathname. Without the pathname, the only
     *  allowed mode is PARSE_STRING - so that's what we set the mode to.
     *
     *  @param InputDocumentModel   Model holding document to be parsed
     *  @param InputSourceTextArea  Source text area, to highlight errors
     */
    public xm_ParseThread(xm_DocumentModel  InputDocumentModel,
                          JTextArea         InputSourceTextArea)
    {
        super(InputDocumentModel.getStatusBar() );
    	TheDocumentModel = InputDocumentModel;
    	SourceTextArea   = InputSourceTextArea;
    	TheXmlEngine     = TheDocumentModel.getXmlEngine();
    	setThreadMode(PARSE_STRING);
    }

    //*****************************************************************************
    /** Load the file and/or execute the parse (depending on mode).
     */
    public void run()
    {
        try
        {
    	    sleep(50);   // Avoid deadlock with focus setting code

    	    // Load file if required.
    	    if (    ThreadMode == LOAD
    	         || ThreadMode == LOAD_AND_PARSE_STRING)
                loadSourceFromFile();

    	    // Parse document from string if required.
    	    if (    ThreadMode == LOAD_AND_PARSE_STRING
    	         || ThreadMode == PARSE_STRING)
    	        parseString();

    	    // Parse document from file if required.
    	    if (ThreadMode == PARSE_FILE)
    	        parseFile();
        }
	    catch (InterruptedException InputException)
	    {
    	    setStatusBarText(2, "Load/parse process interrupted. Exception = "
    	                           + InputException);
    	}

    	removeProgressBar();
    }

    //*****************************************************************************
    /** Load the file from the SourcePathname.
     */
    private void loadSourceFromFile()
    {
        eh_Debug.add(6, "xm_ParseThread.loadSourceFromFileFile: Load source from file '"
                              + SourcePathname + "'");
        try
        {
       	    SourceFile = new File(SourcePathname);
       	    if (!SourceFile.exists())
       	    {
                eh_Debug.add(3, "File " + SourcePathname + " not found");
                return;
   	        }

    	    // Initialize the progress bar.
            JProgressBar  LoadProgressBar = prepareProgressBar((int)SourceFile.length(),
                                                               8,
                                                               "Loading file " + SourcePathname);

            // Read the source file.
            eh_Debug.add(3, "Load file: " + SourcePathname);
            Reader  SourceFileReader = new FileReader(SourceFile);
    	    char[]  ReadBuffer       = new char[4096];
            int     NumCharsRead     = 0;
            int     TotalCharCount   = 0;
            while ((NumCharsRead = SourceFileReader.read(ReadBuffer, 0, ReadBuffer.length)) != -1)
            {
                TheDocumentModel.insertString(TheDocumentModel.getLength(),
                                              new String(ReadBuffer, 0, NumCharsRead),
                                              null);
        		LoadProgressBar.setValue(LoadProgressBar.getValue() + NumCharsRead);
                TotalCharCount += NumCharsRead;
            }

            eh_Debug.add(3, "File contains " + TotalCharCount + " characters");
        }
        catch (IOException InputException)
        {
            eh_Debug.add(2, "Error opening file " + SourcePathname
                               + ". Exception = " + InputException);
        }
    	catch (BadLocationException InputException)
    	{
            eh_Debug.add(2, "Location of file " + SourcePathname
                                + "is bad. Exception = " + InputException);
    	}

        setStatusBarText(8, "File " + SourcePathname + " loaded");
    }

    //*****************************************************************************
    /** Parse the source string which is in TheDocumentModel.
     */
    private void parseString()
    {
        eh_Debug.add(6, "xm_ParseThread.parse: Parse string");
    	xg_Document  CurrentDocument = TheDocumentModel.getCurrentDocument();
        String       SourceString    = TheDocumentModel.getSourceString();

        // Prepare the status bar to display the progress of the parse.
        prepareProgressBar((int)SourceString.length(),
                           3,
                           "Processing XML source");

        try
        {
            TheXmlEngine.parseString(SourceString, CurrentDocument);
    	    removeProgressBar(8, TheXmlEngine.getStatusMessage() );
        }
        catch (xm_ParseException InputException)
        {
            // Highlight the part of the source which is in error (if the XML engine
            // has supplied this information).
            if (InputException.getParsePosition() > 0)
                SourceTextArea.select(InputException.getLastSignificantPosition(),
                                      InputException.getParsePosition() );
            if (TheStatusBar != null)
   	            TheStatusBar.setText(3, InputException.getMessage() );
        }
	    catch (IOException InputException)
	    {
            setStatusBarText(2, "Error in XML processing: " + InputException);
    	}
    }

    //*****************************************************************************
    /** Parse the file whose pathname is in SourcePathname.
     */
    private void parseFile()
    {
        eh_Debug.add(6, "xm_ParseThread.parseFile: Parse file '" + SourcePathname + "'");
    	xg_Document   CurrentDocument = TheDocumentModel.getCurrentDocument();

        // Prepare the status bar to display the progress of the parse.
        //TBD We currently can't display a progress bar, because we don't go to the
        //TBD trouble to find out how long the file is.
//        prepareProgressBar((int)SourceString.length(),
//                           3,
//                           "Processing XML source directly from file '"
//                                + SourcePathname + "'");
        if (TheStatusBar != null)
            TheStatusBar.setText(3, "Processing XML source directly from file '"
                                         + SourcePathname + "'");

        try
        {
            TheXmlEngine.parseFile(SourcePathname, CurrentDocument);
        }
        catch (xm_ParseException InputException)
        {
            // We can't highlight the source in error, since the source isn't loaded.
        }
	    catch (IOException InputException)
	    {
            eh_Debug.add(2, "Error in XML processing. Exception = " + InputException);
    	}

        setStatusBarText(8, TheXmlEngine.getStatusMessage() );
    }

    //*****************************************************************************
    /** Set this thread's mode, which defines which actions it will take.
     *
     *  @param  InputMode  The new mode
     */
    public void setThreadMode(int  InputMode)
    {
        if (InputMode < LOAD || InputMode > PARSE_FILE)
            eh_Debug.add(2, "xm_ParseThread.setMode: Mode value " + InputMode
                                  + " is invalid; it must be between "
                                  + LOAD + " and " + PARSE_FILE);
    	else
            ThreadMode = InputMode;
    }
}

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