/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

package com.sun.star.xml.security.uno;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import java.io.File;
import java.io.IOException;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/* Basic GUI components */
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JFileChooser;
import javax.swing.ToolTipManager;
import javax.swing.JTable;
import javax.swing.JLabel;
import javax.swing.BorderFactory;

/* GUI components for right-hand side */
import javax.swing.JSplitPane;
import javax.swing.JOptionPane;
import javax.swing.JTabbedPane;


/* GUI support classes */
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Container;
import java.awt.Toolkit;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/* For creating borders */
import javax.swing.border.EmptyBorder;
import javax.swing.border.BevelBorder;
import javax.swing.border.CompoundBorder;

/* For creating a TreeModel */
import javax.swing.tree.TreePath;
import java.util.Vector;

/* UNO classes */
import com.sun.star.uno.UnoRuntime;
import com.sun.star.bridge.XUnoUrlResolver;
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.beans.XPropertySet;
import com.sun.star.uno.XComponentContext;
import com.sun.star.xml.sax.XDocumentHandler;

import com.sun.star.xml.crypto.*;
import com.sun.star.xml.crypto.sax.*;

public class TestTool extends JFrame implements ActionListener
{
    /*
     * xml security framewrok component names
     */
    public static String SIGNATURECREATOR_COMPONENT = "com.sun.star.xml.crypto.sax.SignatureCreator";
    public static String SIGNATUREVERIFIER_COMPONENT = "com.sun.star.xml.crypto.sax.SignatureVerifier";
    public static String ENCRYPTOR_COMPONENT = "com.sun.star.xml.crypto.sax.Encryptor";
    public static String DECRYPTOR_COMPONENT = "com.sun.star.xml.crypto.sax.Decryptor";
    public static String SAXEVENTKEEPER_COMPONENT = "com.sun.star.xml.crypto.sax.SAXEventKeeper";

    /*
     * Java-based component names
     */
    public static String SEINITIALIZER_COMPONENT_JAVA = "com.sun.star.xml.security.bridge.jxsec.SEInitializer_JxsecImpl";
    public static String XMLSIGNATURE_COMPONENT_JAVA = "com.sun.star.xml.security.bridge.jxsec.XMLSignature_JxsecImpl";
    public static String XMLENCRYPTION_COMPONENT_JAVA = "com.sun.star.xml.security.bridge.jxsec.XMLEncryption_JxsecImpl";
    public static String XMLDOCUMENTWRAPPER_COMPONENT_JAVA = "com.sun.star.xml.security.bridge.jxsec.XMLDocumentWrapper_JxsecImpl";

    /*
     * C-based component names
     */
    public static String SEINITIALIZER_COMPONENT_C = "com.sun.star.xml.crypto.SEInitializer";
    public static String XMLSIGNATURE_COMPONENT_C = "com.sun.star.xml.crypto.XMLSignature";
    public static String XMLENCRYPTION_COMPONENT_C = "com.sun.star.xml.crypto.XMLEncryption";
    public static String XMLDOCUMENTWRAPPER_COMPONENT_C = "com.sun.star.xml.wrapper.XMLDocumentWrapper";

    /* url resolver name */
    public static String UNOURLRESOLVER = "com.sun.star.bridge.UnoUrlResolver";

    /*
     * connection URL
     */
    private String m_unoURL = "uno:socket,host=localhost,port=2002;urp;StarOffice.ServiceManager";

    /* key file */
    private String m_javaTokenFile = null;
    private String m_nssTokenPath = null;

    /* User Interfaces */
    private JButton m_goButton;
    private JButton m_stepButton;
    private JButton m_startButton;
    private JButton m_openButton;
    private JCheckBox m_isExportingButton;
    private JCheckBox m_isJavaComponentButton;
    private JButton m_saveButton;
    private JButton m_batchButton;
    private JTree m_leftTree;
    private JTextArea m_leftTextArea;
    private JTree m_middleTree;
    private JTree m_rightTree;
    private JTabbedPane m_leftTabPane;
    private JTextArea m_bufferNodeTextArea;
    private JLabel m_saxChainLabel;
    private JTextField m_saxEventText;
    private JTable m_unsolvedReferenceTable;

    /*
     * whether a batch file is running,
     * if so, no message box is popped up
     */
    private boolean m_bIsBatchRunning = false;

    /*
     * whether the UI needs to be updated.
     * when user click the "go" button, the UI needs
     * not to be updated step by step for performance
     * reason
     */
    private boolean m_bIsUIUpdateSuppressed = false;

    /*
     * three DOM tree adapter
     */
    private DomToTreeModelAdapter m_leftTreeModelAdapter;
    private DomToTreeModelAdapter m_middleTreeModelAdapter;
    private DomToTreeModelAdapter m_rightTreeModelAdapter;

    /*
     * the current directory, which reserves the default
     * location when user open/save a file.
     */
    private File m_currentDirectory = null;

    /*
     * the log file
     */
    private FileOutputStream m_logFileOutputStream = null;

    /*
     * the thread which is parsing the current XML
     * file
     */
    private ParsingThread m_parsingThread;

    /*
     * whether is exporting or importing
     */
    private boolean m_bIsExporting;

    /*
     * whether java based component or c based component
     * is used now
     */
    private boolean m_bIsJavaBased;

    /*
     * XML security component interface
     */
    private XComponentContext m_xRemoteContext = null;
    private XMultiComponentFactory m_xRemoteServiceManager = null;
    private XXMLSecurityContext m_xXMLSecurityContext = null;
    private XXMLSignature m_xXMLSignature = null;
    private XXMLEncryption m_xXMLEncryption = null;
    private XSEInitializer m_xSEInitializer = null;

    /*
     * SAX event collector for the middle tree and the right tree
     */
    private SAXEventCollector m_rightTreeEventCollector = null;
    private SAXEventCollector m_middleTreeEventCollector = null;

    /*
     * security framework controller
     */
    private XMLSecurityFrameworkController m_xmlSecurityFrameworkController = null;

    /* org.w3c.dom.Document */
    private Document m_document;

    /* represents whether "Go" or "Step" */
    private boolean stepMode = true;

/**************************************************************************************
 * private methods
 **************************************************************************************/


    /******************************************************************************
     * UI related methods
     ******************************************************************************/

    /*
     * initalizes the UI.
     */
    private void initUI()
    {
        m_leftTreeModelAdapter = new DomToTreeModelAdapter(m_document);
        m_middleTreeModelAdapter = new DomToTreeModelAdapter(m_document);
        m_rightTreeModelAdapter = new DomToTreeModelAdapter(m_document);

        m_parsingThread = null;

        m_leftTree.setModel(m_leftTreeModelAdapter);
        m_middleTree.setModel(m_middleTreeModelAdapter);
        m_rightTree.setModel(m_rightTreeModelAdapter);
    }

    /*
     * constructs the user interface.
     */
    private Container buildUI(int width, int height)
    {
        JPanel mainPanel = new JPanel();

        int frameHeight = height-40;
        int leftWindowWidth = (width-40)/3;
        int middleWindowWidth = leftWindowWidth;
        int rightWindowWidth = leftWindowWidth;
        int leftPaneWidth = leftWindowWidth+middleWindowWidth;
        int frameWidth = leftPaneWidth + rightWindowWidth;

        /* Make a nice border */
        EmptyBorder emptyBorder = new EmptyBorder(5,5,5,5);
        BevelBorder bevelBorder = new BevelBorder(BevelBorder.LOWERED);
        CompoundBorder compoundBorder = new CompoundBorder(emptyBorder,bevelBorder);
        mainPanel.setBorder(new CompoundBorder(compoundBorder,emptyBorder));

        /* Set up the tree */
        m_leftTreeModelAdapter = new DomToTreeModelAdapter(m_document);
        m_middleTreeModelAdapter = new DomToTreeModelAdapter(m_document);
        m_rightTreeModelAdapter = new DomToTreeModelAdapter(m_document);

        m_leftTree = new JTree(m_leftTreeModelAdapter);
        m_leftTextArea = new JTextArea();
        m_middleTree = new JTree(m_middleTreeModelAdapter);
        m_rightTree = new JTree(m_rightTreeModelAdapter);

        ToolTipManager.sharedInstance().registerComponent(m_leftTree);
        ToolTipManager.sharedInstance().registerComponent(m_middleTree);
        ToolTipManager.sharedInstance().registerComponent(m_rightTree);

        /* Builds left tab pane */
        JScrollPane leftTreePane = new JScrollPane(m_leftTree);
        JScrollPane leftTextPane = new JScrollPane(m_leftTextArea);
        m_leftTabPane= new JTabbedPane();
        m_leftTabPane.add("Tree View",leftTreePane);
        m_leftTabPane.add("Text View",leftTextPane);

        /* Builds middle tree pane */
        JScrollPane middleTreePane = new JScrollPane(m_middleTree);

        /* Builds right tree pane */
        JScrollPane rightTreePane = new JScrollPane(m_rightTree);
        rightTreePane.setBorder(BorderFactory.createCompoundBorder(
                        BorderFactory.createTitledBorder("Result"),
                        BorderFactory.createEmptyBorder(8,8,8,8)));

        m_leftTabPane.setPreferredSize(
            new Dimension( leftWindowWidth, frameHeight ));
        middleTreePane.setPreferredSize(
            new Dimension( middleWindowWidth, frameHeight ));
        rightTreePane.setPreferredSize(
            new Dimension( rightWindowWidth, frameHeight ));

        /* Builds the SAX event text box */
        m_saxEventText = new JTextField();

        /* Builds the unsolved reference table */
        m_unsolvedReferenceTable = new JTable(
                new UnsolvedReferenceTableModel(this));

        /* Builds the BufferNode information text area */
        m_bufferNodeTextArea = new JTextArea();

        /* Builds the SAX chain information label */
        m_saxChainLabel = new JLabel();

        /* Builds the left pane */
        JPanel tabPaneWithSaxEventPane = new JPanel();
        tabPaneWithSaxEventPane.setLayout(new BorderLayout());
        tabPaneWithSaxEventPane.add("Center",m_leftTabPane);
        tabPaneWithSaxEventPane.add("South",new JScrollPane(m_saxEventText));

        JSplitPane leftPane =
            new JSplitPane( JSplitPane.VERTICAL_SPLIT,
                tabPaneWithSaxEventPane,
                new JScrollPane(m_unsolvedReferenceTable));
        leftPane.setBorder(BorderFactory.createCompoundBorder(
                        BorderFactory.createTitledBorder("Original"),
                        BorderFactory.createEmptyBorder(8,8,8,8)));

        leftPane.setContinuousLayout( true );
        leftPane.setDividerLocation( frameHeight*2/3 );
        leftPane.setPreferredSize(
            new Dimension( leftWindowWidth, frameHeight ));

        /* Builds the middle pane */
        JPanel bufferNodeWithSaxChainPane = new JPanel();
        bufferNodeWithSaxChainPane.setLayout(new BorderLayout());
        bufferNodeWithSaxChainPane.add("Center",m_bufferNodeTextArea);
        bufferNodeWithSaxChainPane.add("South",new JScrollPane(m_saxChainLabel));

        JSplitPane middlePane =
            new JSplitPane( JSplitPane.VERTICAL_SPLIT,
                middleTreePane,
                new JScrollPane(bufferNodeWithSaxChainPane));

        middlePane.setBorder(BorderFactory.createCompoundBorder(
                        BorderFactory.createTitledBorder("Insight SAXEventKeeper"),
                        BorderFactory.createEmptyBorder(8,8,8,8)));

        middlePane.setContinuousLayout( true );
        middlePane.setDividerLocation( frameHeight/2+5 );
        middlePane.setPreferredSize(
            new Dimension( middleWindowWidth, frameHeight ));

        /* Builds the whole frame pane */
        JSplitPane leftWithMiddlePane =
            new JSplitPane( JSplitPane.HORIZONTAL_SPLIT,
                leftPane,
                middlePane );
        leftWithMiddlePane.setContinuousLayout( true );
        leftWithMiddlePane.setDividerLocation( leftWindowWidth );
        leftWithMiddlePane.setPreferredSize(
            new Dimension( leftPaneWidth + 10, frameHeight+10 ));

        JSplitPane framePane =
            new JSplitPane( JSplitPane.HORIZONTAL_SPLIT,
                leftWithMiddlePane,
                rightTreePane );


        framePane.setContinuousLayout( true );
        framePane.setDividerLocation(leftPaneWidth+10 );
        framePane.setPreferredSize(
            new Dimension( frameWidth + 20, frameHeight+10 ));

        /* Adds all GUI components to the main panel */
        mainPanel.setLayout(new BorderLayout());
        mainPanel.add("Center", framePane );

        m_openButton = new JButton("Open...");
        m_openButton.addActionListener(this);

        m_goButton = new JButton("Go!");
        m_goButton.addActionListener(this);

        m_stepButton = new JButton("Step");
        m_stepButton.addActionListener(this);

        m_startButton = new JButton("Start");
        m_startButton.addActionListener(this);
        m_startButton.setEnabled(false);

        m_isExportingButton = new JCheckBox("export, not import", true);
        m_isJavaComponentButton = new JCheckBox("use java component", false);

        m_saveButton = new JButton("Save...");
        m_saveButton.addActionListener(this);

        m_batchButton = new JButton("Batch...");
        m_batchButton.addActionListener(this);

        JPanel buttonPanel = new JPanel();
        buttonPanel.add(m_batchButton);
        buttonPanel.add(m_openButton);
        buttonPanel.add(m_startButton);
        buttonPanel.add(m_goButton);
        buttonPanel.add(m_stepButton);
        buttonPanel.add(m_isExportingButton);
        buttonPanel.add(m_isJavaComponentButton);
        buttonPanel.add(m_saveButton);

        mainPanel.add("South", buttonPanel);

        enableGoButton(false);

        return mainPanel;
    }

    /*
     * enables/disables the Go(and Step) button.
     */
    private void enableGoButton(boolean enabled)
    {
        m_goButton.setEnabled(enabled);
        m_stepButton.setEnabled(enabled);
    }

    /*
     * updates the unsolved reference information.
     */
    private void updatesUnsolvedReferencesInformation()
    {
        m_unsolvedReferenceTable.setModel(new UnsolvedReferenceTableModel(this));
    }

    /*
     * adjusts the view of the tree in order to make the
     * particular Node into the focus tree leaf.
     */
    private void updatesTree(Node node, JTree tree)
    {
        int i=0;
        int currentLine = 0;

        while (i<tree.getRowCount())
        {
            TreePath treePath = tree.getPathForRow(i);
            tree.expandPath(treePath);

            AdapterNode adapterNode = (AdapterNode)treePath.getLastPathComponent();

            if (node == adapterNode.getNode())
            {
                tree.addSelectionPath(treePath);
                currentLine = i;
            }

            ++i;
        }

        tree.setCellRenderer(new XMLTreeCellRanderer(node));
        tree.scrollRowToVisible(currentLine);
    }

    /******************************************************************************
     * action listener related methods.
     ******************************************************************************/

    /*
     * reads in a document, either the document is a file or
     * is a text paragraph.
     */
    private void openDocument()
    {
        if (m_leftTabPane.getSelectedIndex() == 0)
        {
            File f = openFile();
            if (f != null)
            {
                parseFile(f);
            }
        }
        else
        {
            String text = m_leftTextArea.getText();

            try
            {
                parseStream(new ByteArrayInputStream(text.getBytes("UTF-8")));
            }
            catch(UnsupportedEncodingException e)
            {
                e.printStackTrace();
            }

            m_leftTabPane.setSelectedIndex(0);
        }
    }

    /*
     * save the result tree to a file.
     */
    private void saveResult()
    {
        saveFile();
    }

    /*
     * selects a batch file to excute.
     */
    private void openBatch()
    {
        File f = openFile();
        if (f != null)
        {
            runBatch(f);
        }
    }

    /*
     * makes the current operation to an end.
     */
    private void endMission()
    {
        enableGoButton(false);
        m_parsingThread = null;

        if (m_xmlSecurityFrameworkController != null)
        {
            m_xmlSecurityFrameworkController.endMission();
        }

        updatesUIs();

        m_xmlSecurityFrameworkController = null;
        freeComponents();

        System.gc();
    }


    /******************************************************************************
     * UNO component related methods
     ******************************************************************************/

    /*
     * connects the SO server.
     */
    private void connectSO(String unoUrlString)
    {
        if (unoUrlString != null)
        {
            m_unoURL = new String(unoUrlString);
        }

        try
        {
            m_xRemoteServiceManager = getRemoteServiceManager(m_unoURL);
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }

    /*
     * creates UNO components.
     */
    private boolean createComponents()
    {
        try
        {
            String SEInitializer_comp;
            String XMLSignature_comp;
            String XMLEncryption_comp;
            String tokenPath;

            if (m_bIsJavaBased)
            {
                SEInitializer_comp = SEINITIALIZER_COMPONENT_JAVA;
                XMLSignature_comp = XMLSIGNATURE_COMPONENT_JAVA;
                XMLEncryption_comp = XMLENCRYPTION_COMPONENT_JAVA;
                tokenPath = m_javaTokenFile;
            }
            else
            {
                SEInitializer_comp = SEINITIALIZER_COMPONENT_C;
                XMLSignature_comp = XMLSIGNATURE_COMPONENT_C;
                XMLEncryption_comp = XMLENCRYPTION_COMPONENT_C;
                tokenPath = m_nssTokenPath;
            }

            Object seInitializerObj = m_xRemoteServiceManager.createInstanceWithContext(
                SEInitializer_comp, m_xRemoteContext);

            if (seInitializerObj == null)
            {
                freeComponents();
                return false;
            }

            m_xSEInitializer = (XSEInitializer)UnoRuntime.queryInterface(
                        XSEInitializer.class, seInitializerObj);

            m_xXMLSecurityContext = m_xSEInitializer.createSecurityContext(tokenPath);

            Object xmlSignatureObj = m_xRemoteServiceManager.createInstanceWithContext(
                XMLSignature_comp, m_xRemoteContext);

            if (xmlSignatureObj == null)
            {
                freeComponents();
                return false;
            }

            m_xXMLSignature = (XXMLSignature)UnoRuntime.queryInterface(
                        XXMLSignature.class, xmlSignatureObj);

            Object xmlEncryptionObj = m_xRemoteServiceManager.createInstanceWithContext(
                XMLEncryption_comp, m_xRemoteContext);

            if (xmlEncryptionObj == null)
            {
                freeComponents();
                return false;
            }

            m_xXMLEncryption = (XXMLEncryption)UnoRuntime.queryInterface(
                        XXMLEncryption.class, xmlEncryptionObj);

            return true;
        }
        catch(Exception e)
        {
            freeComponents();
            e.printStackTrace();
            return false;
        }
    }

        /*
         * frees UNO components.
         */
    private void freeComponents()
    {
        try
        {
            if (m_xXMLSecurityContext != null)
            {
                m_xSEInitializer.freeSecurityContext(m_xXMLSecurityContext);
                m_xXMLSecurityContext = null;
            }

            m_xXMLSignature = null;
            m_xXMLEncryption = null;
            m_xSEInitializer = null;
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }

    /*
     * getRemoteServiceManager
     */
    private XMultiComponentFactory getRemoteServiceManager(String unoUrl) throws java.lang.Exception
    {
        if (m_xRemoteContext == null)
        {
            /*
             * First step: create local component context, get local servicemanager and
             * ask it to create a UnoUrlResolver object with an XUnoUrlResolver interface
             */
            XComponentContext xLocalContext =
                com.sun.star.comp.helper.Bootstrap.createInitialComponentContext(null);
            XMultiComponentFactory xLocalServiceManager = xLocalContext.getServiceManager();
            Object urlResolver = xLocalServiceManager.createInstanceWithContext(
                UNOURLRESOLVER, xLocalContext );
            /*
             * query XUnoUrlResolver interface from urlResolver object
             */
            XUnoUrlResolver xUnoUrlResolver = (XUnoUrlResolver) UnoRuntime.queryInterface(
                XUnoUrlResolver.class, urlResolver );

            /*
             * Second step: use xUrlResolver interface to import the remote StarOffice.ServiceManager,
             * retrieve its property DefaultContext and get the remote servicemanager
             */
            Object initialObject = xUnoUrlResolver.resolve( unoUrl );
            XPropertySet xPropertySet = (XPropertySet)UnoRuntime.queryInterface(
            XPropertySet.class, initialObject);
            Object context = xPropertySet.getPropertyValue("DefaultContext");
            m_xRemoteContext = (XComponentContext)UnoRuntime.queryInterface(
                XComponentContext.class, context);
        }
        return m_xRemoteContext.getServiceManager();
    }


    /******************************************************************************
     * XML related methods
     ******************************************************************************/

    /*
     * removes all empty text node inside the particular element
     */
    private void removeEmptyText(Node node)
    {
        int type = node.getNodeType();
        NodeList children;
        int i;

        switch (type)
        {
        case Node.DOCUMENT_NODE:
        case Node.ELEMENT_NODE:
            Node child = node.getFirstChild();
            while (child!= null)
            {
                Node nextSibling = child.getNextSibling();
                int childType = child.getNodeType();

                if (childType==Node.TEXT_NODE)
                {
                    String message = child.getNodeValue().trim();
                    if (message == null || message.length()<=0)
                    {
                        node.removeChild(child);
                    }
                }
                else if (childType == Node.ELEMENT_NODE)
                {
                    removeEmptyText(child);
                }

                child = nextSibling;
            }
            break;
        }
    }

    /*
     * reads a stream, and parses it into the original tree.
     */
    private void parseStream(InputStream is)
    {
        try
        {
            DocumentBuilderFactory factory =
                DocumentBuilderFactory.newInstance();
            m_document = null;
            m_startButton.setEnabled(false);
            initUI();

            /* factory.setValidating(true); */
            /* factory.setNamespaceAware(true); */

            try
            {
                DocumentBuilder builder = factory.newDocumentBuilder();
                m_document = builder.parse(is);
                m_startButton.setEnabled(true);
                initUI();
            }
            catch (ParserConfigurationException pce)
            {
                pce.printStackTrace();
            }
            catch (IOException ioe)
            {
                ioe.printStackTrace();
            }
        }
        catch(Exception exce)
        {
            System.out.println("input stream Exception");
        }
    }


    /******************************************************************************
     * file operation related methods
     ******************************************************************************/

    /*
     * opens a file, and parses it into the original tree.
     */
    private void parseFile(File name)
    {
        try
        {
            FileInputStream fis = new FileInputStream(name);
            parseStream(fis);
            fis.close();
        }
        catch(Exception exce)
        {
            System.out.println("open file Exception");
        }
    }


    /*
     * selects a file to open
     */
    private File openFile()
    {
        File rc = null;

        JFileChooser fileChooser= new JFileChooser();

        fileChooser.setDialogTitle("Select File To Open");
        fileChooser.setDialogType(JFileChooser.OPEN_DIALOG);

        fileChooser.setApproveButtonText("Ok");

        if (m_currentDirectory == null)
        {
            fileChooser.rescanCurrentDirectory();
        }
        else
        {
            fileChooser.setCurrentDirectory(m_currentDirectory);
        }

        fileChooser.setFileFilter(new XMLFileFilter());

        int result = fileChooser.showDialog(this,null);
        if (result==fileChooser.APPROVE_OPTION)
        {
            m_currentDirectory = fileChooser.getCurrentDirectory();
            rc = fileChooser.getSelectedFile();
        }

        return rc;
    }

    private void saveFile()
    {
        JFileChooser fileChooser= new JFileChooser();

        fileChooser.setDialogTitle("Select File To Save");
        fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);

        fileChooser.setApproveButtonText("Ok");

        if (m_currentDirectory == null)
        {
            fileChooser.rescanCurrentDirectory();
        }
        else
        {
            fileChooser.setCurrentDirectory(m_currentDirectory);
        }

        fileChooser.setFileFilter(new XMLFileFilter());

        int result = fileChooser.showDialog(this,null);
        if (result==fileChooser.APPROVE_OPTION)
        {
            try
            {
                m_currentDirectory = fileChooser.getCurrentDirectory();
                saveFile(fileChooser.getSelectedFile());
            }
            catch(Exception exce)
            {
                System.out.println("save file Exception");
                exce.printStackTrace();
            }
        }
    }

    /*
     * excutes a batch file.
     */
    private void runBatch(File f)
    {
        FileInputStream fis = null;

        try
        {
            fis = new FileInputStream(f);
            StringBuffer commandBuffer = new StringBuffer();

            m_logFileOutputStream = new FileOutputStream("TestTool-log.txt");
            m_bIsBatchRunning = true;
            int ch = 0;

            while (ch != -1)
            {
                ch = fis.read();

                if (ch != 0x0a && ch != -1)
                {
                    if (ch != 0x0d)
                    {
                        commandBuffer.append((char)ch);
                    }
                }
                else
                {
                    String command = new String(commandBuffer);
                    if (command.startsWith("Open "))
                    {
                        m_logFileOutputStream.write(("start \""+command+"\" ...\n").getBytes());
                        String fileName = command.substring(5);
                        parseFile(new File(fileName));
                        m_logFileOutputStream.write("command end \n\n".getBytes());
                    }
                    else if (command.startsWith("Use Java Component"))
                    {
                        m_logFileOutputStream.write(("start \""+command+"\" ...\n").getBytes());
                        m_isJavaComponentButton.setSelected(true);
                        m_logFileOutputStream.write("command end \n\n".getBytes());
                    }
                    else if (command.startsWith("Use C++ Component"))
                    {
                        m_logFileOutputStream.write(("start \""+command+"\" ...\n").getBytes());
                        m_isJavaComponentButton.setSelected(false);
                        m_logFileOutputStream.write("command end \n\n".getBytes());
                    }
                    else if (command.startsWith("Go "))
                    {
                        m_logFileOutputStream.write(("start \""+command+"\" ...\n").getBytes());
                        String opera = command.substring(3);
                        if (opera.equals("Sign") || opera.equals("Encrypt"))
                        {
                            m_isExportingButton.setSelected(true);
                        }
                        else
                        {
                            m_isExportingButton.setSelected(false);
                        }

                        startsUp();
                        if (m_parsingThread != null)
                        {
                            m_bIsUIUpdateSuppressed = true;
                            try{
                                while (m_parsingThread.nextStep());
                                endMission();
                            }
                            catch(Exception e)
                            {
                                System.out.println("exception happen during batch:"+e);
                                e.printStackTrace();
                            }

                            m_bIsUIUpdateSuppressed = false;
                            updatesUIs();
                        }
                        m_logFileOutputStream.write("command end \n\n".getBytes());
                    }
                    else if (command.startsWith("Save "))
                    {
                        m_logFileOutputStream.write(("start \""+command+"\" ...\n").getBytes());
                        String fileName = command.substring(5);
                        saveFile(new File(fileName));
                        m_logFileOutputStream.write("command end \n\n".getBytes());
                    }

                    commandBuffer = new StringBuffer();
                }
            }

            m_bIsBatchRunning = false;
            m_logFileOutputStream.close();
            m_logFileOutputStream = null;

            fis.close();
            fis = null;
        }
        catch(java.io.IOException e)
        {
            e.printStackTrace();
        }
    }

    /*
     * save the current result tree to a particular file.
     */
    private void saveFile(File f)
    {
        try{
            FileOutputStream fos = new FileOutputStream(f);
            SAXEventPrinter.display((Document)m_rightTreeEventCollector.getDocument(),
                0,fos,false);
            fos.close();
        }catch(Exception e)
        {
            e.printStackTrace();
        }
    }

    /******************************************************************************
     * others
     ******************************************************************************/

    /*
     * starts up the operation.
     */
    private void startsUp()
    {
        if (m_parsingThread != null)
        {
            m_parsingThread = null;
        }

        m_bIsExporting = m_isExportingButton.isSelected();
        m_bIsJavaBased = m_isJavaComponentButton.isSelected();

        if (createComponents())
        {
            m_rightTreeEventCollector = new SAXEventCollector(this);

            m_parsingThread = new ParsingThread(
                m_document,
                null,
                this);

            m_xmlSecurityFrameworkController =
                new XMLSecurityFrameworkController(
                    this,
                    m_bIsExporting,
                    m_bIsJavaBased,
                    m_rightTreeEventCollector,
                    m_parsingThread,
                    m_xXMLSecurityContext,
                    m_xXMLSignature,
                    m_xXMLEncryption,
                    m_xRemoteServiceManager,
                    m_xRemoteContext);

            enableGoButton(true);
        }
        else
        {
            showMessage("Error in creating XML Security Components!");
        }
    }

/**************************************************************************************
 * protected methods
 **************************************************************************************/

    /******************************************************************************
     * UI related methods
     ******************************************************************************/

    /*
     * updates the sax chain information.
     */
    protected void updatesSAXChainInformation(String chainStr)
    {
        m_saxChainLabel.setText(chainStr);
    }

    /*
     * update the current SAX event information.
     */
    protected void updatesCurrentSAXEventInformation(String event)
    {
        m_saxEventText.setText(event);
    }

    /*
     * updates all information in the UI.
     */
    protected void updatesUIs()
    {
        if (!m_bIsUIUpdateSuppressed)
        {
            m_leftTree.clearSelection();
            updatesTree(null, m_leftTree);

            if (m_xmlSecurityFrameworkController != null)
            {
                String bufferNodeTreeText = m_xmlSecurityFrameworkController.getBufferNodeTreeInformation();
                if (bufferNodeTreeText == null)
                {
                    m_middleTree.setVisible(false);
                    m_bufferNodeTextArea.setText("No XML Security Related");
                }
                else
                {
                    m_middleTreeEventCollector = new SAXEventCollector(null);
                    m_xmlSecurityFrameworkController.getDocument(m_middleTreeEventCollector);

                    m_middleTreeModelAdapter = new DomToTreeModelAdapter(m_middleTreeEventCollector.getDocument());
                    m_middleTree.setModel(m_middleTreeModelAdapter);
                    updatesTree(null, m_middleTree);
                    m_middleTree.setVisible(true);
                    m_bufferNodeTextArea.setText(bufferNodeTreeText);
                }
            }
            else
            {
                m_middleTree.setVisible(false);
                m_bufferNodeTextArea.setText("No XMLImporter/XMLExporter");
            }

            if (m_rightTreeEventCollector != null)
            {
                m_rightTreeModelAdapter = new DomToTreeModelAdapter((Document)m_rightTreeEventCollector.getDocument());
                m_rightTree.setModel(m_rightTreeModelAdapter);
                updatesTree((Node)m_rightTreeEventCollector.getCurrentElement(), m_rightTree);
            }

            updatesUnsolvedReferencesInformation();
        }
    }

    /*
     * shows a message.
     */
    protected void showMessage(String msg)
    {
        if (m_bIsBatchRunning)
        {
            try
            {
                if (!msg.startsWith("Message from : SAXEventKeeper"))
                {
                    byte [] b = msg.getBytes();
                    m_logFileOutputStream.write("        ".getBytes());

                    for (int i=0; i<b.length; ++i)
                    {
                        m_logFileOutputStream.write(b[i]);
                        if (b[i] == '\n')
                        {
                            m_logFileOutputStream.write("        ".getBytes());
                        }
                    }
                    m_logFileOutputStream.write("\n        ==============================\n".getBytes());
                }
            }
            catch(IOException e)
            {
                e.printStackTrace();
            }
        }
        else
        {
            if (stepMode)
            {
                JOptionPane optionPane = new JOptionPane();
                optionPane.showMessageDialog(this, msg, "TestTool Notification", JOptionPane.INFORMATION_MESSAGE);
            }
            else
            {
                Object[] options = { "OK", "Go back to step mode" };
                if (1 == JOptionPane.showOptionDialog(this, msg, "TestTool Notification",
                    JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE,
                    null, options, options[0]))
                {
                    stepMode = true;
                }
            }
        }
    }


    /******************************************************************************
     * information retrieving
     ******************************************************************************/

    /*
     * gets all unsolved reference ids.
     * a reference id is the value of the id attribute of an
     * referenced element.
     */
    protected Vector getUnsolvedReferenceIds()
    {
        Vector rc;

        if (m_xmlSecurityFrameworkController == null)
        {
            rc = new Vector();
        }
        else
        {
            rc = ((XMLSecurityFrameworkController)m_xmlSecurityFrameworkController).
                getUnsolvedReferenceIds();
        }

        return rc;
    }

    /*
     * gets all unsolved reference keeper ids.
     * a reference keeper id is the id which the SAXEventKeeper uses
     * to identify the corresponding BufferNode.
     */
    protected Vector getUnsolvedReferenceKeeperIds()
    {
        Vector rc;

        if (m_xmlSecurityFrameworkController == null)
        {
            rc = new Vector();
        }
        else
        {
            rc = ((XMLSecurityFrameworkController)m_xmlSecurityFrameworkController).
                getUnsolvedReferenceKeeperIds();
        }

        return rc;
    }

    /*
     * gets all unsolved references' remaining numbers.
     * a remaining number is that how many claims have not been found for
     * a unsolved reference.
     */
    protected Vector getUnsolvedReferenceRefNum()
    {
        Vector rc;

        if (m_xmlSecurityFrameworkController == null)
        {
            rc = new Vector();
        }
        else
        {
            rc = ((XMLSecurityFrameworkController)m_xmlSecurityFrameworkController).
                getUnsolvedReferenceRefNum();
        }

        return rc;
    }


/**************************************************************************************
 * public methods
 **************************************************************************************/

    /******************************************************************************
     * action listener related methods.
     ******************************************************************************/

    /*
     * action listening method.
     */
    public void actionPerformed(ActionEvent e)
    {
        if (e.getSource().equals(m_startButton))
        {
            endMission();
            startsUp();
        }
        if (e.getSource().equals(m_goButton))
        {
            if (m_parsingThread != null)
            {
                stepMode = false;
                boolean notOver;
                while ( notOver = m_parsingThread.nextStep())
                {
                    if (stepMode) break;
                }

                if (!notOver) endMission();
            }
        }
        if (e.getSource().equals(m_stepButton))
        {
            if (m_parsingThread != null)
            {
                if (!m_parsingThread.nextStep())
                {
                    endMission();
                }
            }
        }
        if (e.getSource().equals(m_openButton))
        {
            openDocument();
        }
        if (e.getSource().equals(m_saveButton))
        {
            saveResult();
        }
        if (e.getSource().equals(m_batchButton))
        {
            openBatch();
        }
    }

    /*
     * void-consturctor method
     */
    public TestTool()
    {
            getRootPane().putClientProperty("defeatSystemEventQueueCheck", Boolean.TRUE);

            try
            {
                m_currentDirectory = new File(System.getProperty("user.dir"));
        }
        catch(Exception e)
        {
            System.out.println("getProperty error :"+e);
        }
    }

    /*
     * consturctor method with a specific connection URL
     */
    public TestTool(String connecturl)
    {
        this();
        m_unoURL = new String(connecturl);
    }

    public static void main(String argv[])
    {
        Dimension screenSize =
            Toolkit.getDefaultToolkit().getScreenSize();

        TestTool tt;

        if (argv.length < 1)
        {
            System.out.println("Usage: java TestTool [javaTokenFile] [nssTokenPath] [xml file]?");
            return;
        }

        boolean hasFile = false;
        boolean hasBatch = false;
        String fileName = null;

        if (argv.length >= 3)
        {
            if (argv[2].startsWith("-b"))
            {
                fileName = argv[2].substring(2);
                hasBatch = true;
            }
            else
            {
                fileName = argv[2];
                hasFile = true;
            }
        }

        tt = new TestTool();
        tt.m_javaTokenFile = new String(argv[0]);
        tt.m_nssTokenPath = new String(argv[1]);
        tt.connectSO(null);

        /* Set up a GUI framework */
        JFrame myFrame = new JFrame("XML Security Components Tester");
        myFrame.addWindowListener(
            new WindowAdapter() {
                public void windowClosing(WindowEvent e) {System.exit(0);}
            }
            );

        myFrame.setContentPane(tt.buildUI(screenSize.width, screenSize.height));
        myFrame.pack();
        int w = screenSize.width-30;
        int h = screenSize.height-30;
        myFrame.setLocation(screenSize.width/2 - w/2,
        screenSize.height/2 - h/2);
        myFrame.setSize(w, h);
        myFrame.setVisible(true);

        if (hasFile)
        {
            tt.parseFile(new File(fileName));
        }
        else if (hasBatch)
        {
            tt.runBatch(new File(fileName));
        }
    }
}

