org.wso2.developerstudio.eclipse.esb.presentation.ui.XPathSelectorDialog.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.developerstudio.eclipse.esb.presentation.ui.XPathSelectorDialog.java

Source

/*
 * Copyright 2009-2010 WSO2, Inc. (http://wso2.com)
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package org.wso2.developerstudio.eclipse.esb.presentation.ui;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Dialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.progress.UIJob;
import org.eclipse.wst.xml.xpath.core.util.XSLTXPathHelper;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.wso2.developerstudio.eclipse.esb.presentation.EsbEditorPlugin;
import org.wso2.developerstudio.eclipse.esb.util.EsbUtils;

/**
 * A dialog used to select an xpath from a given xml document.
 */
public class XPathSelectorDialog extends Dialog {
    /**
     * Attribute icon path.
     */
    private static final String ATTRIBUTE_ICON_PATH = "./icons/full/xpath/attribute12.png";

    /**
     * Element icon path.
     */
    private static final String ELEMENT_ICON_PATH = "./icons/full/xpath/element12.png";

    /**
     * Key used to store dom nodes as data within tree items.
     */
    private static final String TREE_ITEM_DATA_KEY = "dom_user_data_key";

    /**
     * Dialog shell.
     */
    private Shell dialogShell;

    /**
     * Label providing an informative message.
     */
    private Label infoLabel;

    /**
     * Selected file path text filed.
     */
    private Text filePathTextField;

    /**
     * Browse button.
     */
    private Button browseButton;

    /**
     * Tree viewer.
     */
    private Tree treeViewWidget;

    /**
     * Xpath label.
     */
    private Label xpathLabel;

    /**
     * XPath expression text field.
     */
    private Text xpathTextField;

    /**
     * Cancel button.
     */
    private Button cancelButton;

    /**
     * Ok button.
     */
    private Button okButton;

    /**
     * Selected xpath.
     */
    private String selectedXpath;

    /**
     * XML namespaces.
     */
    private Map<String, String> nameSpaces;

    /**
     * A utility class used to track {@link TreeItem} state as well as store
     * user data in it.
     */
    private class TreeItemData {
        /**
         * Whether the associated {@link TreeItem} has already been explored
         * once.
         */
        private boolean markAsExplored;

        /**
         * Dom {@link Node} attached to the {@link TreeItem}.
         */
        private Node domNode;

        /**
         * Instantiates a new {@link TreeItemData} instance which is in
         * 'unexplored' state.
         * 
         * @param domNode associated dom {@link Node}.
         */
        public TreeItemData(Node domNode) {
            this.domNode = domNode;
        }

        /**
         * @return whether the corresponding {@link TreeItem} has already been explored.
         */
        public boolean isMarkedAsExplored() {
            return markAsExplored;
        }

        /**
         * marks the corresponding {@link TreeItem} as explored. 
         */
        public void setMarkedAsExplored() {
            this.markAsExplored = true;
        }

        /**
         * @return associated dom {@link Node} instance.
         */
        public Node getDomNode() {
            return domNode;
        }
    }

    /**
     * Creates a new {@link XPathSelectorDialog} instance.
     * 
     * @param parent parent shell.
     */
    public XPathSelectorDialog(Shell parent) {
        super(parent);
        this.dialogShell = new Shell(parent);
    }

    /**
     * A main method for testing the dialog ui.
     * 
     * @param args arguments.
     */
    public static void main(String[] args) {
        XPathSelectorDialog dialog = new XPathSelectorDialog(new Shell(Display.getDefault()));
        dialog.open();
    }

    /**
     * Layout ui elements and open dialog window.
     */
    public void open() {
        // Dialog shell.
        dialogShell.setText("XPath Navigator");
        {
            FormLayout dialogShellLayout = new FormLayout();
            dialogShellLayout.marginHeight = 5;
            dialogShellLayout.marginWidth = 5;
            dialogShell.setLayout(dialogShellLayout);
        }

        // Ok button.
        okButton = new Button(dialogShell, SWT.NONE);
        {
            okButton.setText("OK");
            FormData okButtonLayoutData = new FormData();
            okButtonLayoutData.right = new FormAttachment(100);
            okButtonLayoutData.bottom = new FormAttachment(100);
            okButtonLayoutData.width = 80;
            okButton.setLayoutData(okButtonLayoutData);
        }

        // Cancel button.
        cancelButton = new Button(dialogShell, SWT.NONE);
        {
            cancelButton.setText("Cancel");
            cancelButton.setToolTipText("click here to exit");
            FormData cancelButtonLayoutData = new FormData();
            cancelButtonLayoutData.top = new FormAttachment(okButton, 0, SWT.CENTER);
            cancelButtonLayoutData.right = new FormAttachment(okButton, -5);
            cancelButtonLayoutData.width = 80;
            cancelButton.setLayoutData(cancelButtonLayoutData);
        }

        // XPath label.
        xpathLabel = new Label(dialogShell, SWT.NONE);
        {
            xpathLabel.setText("XPath:");
            FormData xpathLabelLayoutData = new FormData();
            xpathLabelLayoutData.top = new FormAttachment(okButton, 0, SWT.CENTER);
            xpathLabelLayoutData.left = new FormAttachment(0);
            xpathLabel.setLayoutData(xpathLabelLayoutData);
        }

        // XPath text field.
        xpathTextField = new Text(dialogShell, SWT.BORDER);
        {
            xpathTextField.setEditable(false);
            FormData xPathTextLayoutData = new FormData();
            xPathTextLayoutData.top = new FormAttachment(okButton, 0, SWT.CENTER);
            xPathTextLayoutData.left = new FormAttachment(xpathLabel, 5);
            xPathTextLayoutData.right = new FormAttachment(cancelButton, -5);
            xpathTextField.setLayoutData(xPathTextLayoutData);
        }

        // Browse button.
        browseButton = new Button(dialogShell, SWT.NONE);
        {
            browseButton.setText("Browse");
            FormData browseButtonLayoutData = new FormData();
            browseButtonLayoutData.top = new FormAttachment(0);
            browseButtonLayoutData.right = new FormAttachment(100);
            browseButtonLayoutData.width = 80;
            browseButton.setLayoutData(browseButtonLayoutData);
        }

        // Information label.
        infoLabel = new Label(dialogShell, SWT.NONE);
        {
            infoLabel.setText("XML document:");
            FormData infoLabelLayoutData = new FormData();
            infoLabelLayoutData.top = new FormAttachment(browseButton, 0, SWT.CENTER);
            infoLabelLayoutData.left = new FormAttachment(0);
            infoLabel.setLayoutData(infoLabelLayoutData);
        }

        // File path text field.
        filePathTextField = new Text(dialogShell, SWT.BORDER);
        {
            filePathTextField.setEnabled(false);
            FormData filePathTextFieldLayoutData = new FormData();
            filePathTextFieldLayoutData.top = new FormAttachment(browseButton, 0, SWT.CENTER);
            filePathTextFieldLayoutData.left = new FormAttachment(infoLabel, 5);
            filePathTextFieldLayoutData.right = new FormAttachment(browseButton, -5);
            filePathTextField.setLayoutData(filePathTextFieldLayoutData);
        }

        // Tree view.
        treeViewWidget = new Tree(dialogShell, SWT.BORDER);
        {
            FormData treeViewLayoutData = new FormData();
            treeViewLayoutData.top = new FormAttachment(filePathTextField, 5);
            treeViewLayoutData.left = new FormAttachment(0);
            treeViewLayoutData.right = new FormAttachment(100);
            treeViewLayoutData.bottom = new FormAttachment(okButton, -5);
            treeViewLayoutData.width = 500;
            treeViewLayoutData.height = 400;
            treeViewWidget.setLayoutData(treeViewLayoutData);
        }

        initializeActions();
        centerDialog();
        setTabOrder();

        dialogShell.pack();
        dialogShell.open();
        Display display = getParent().getDisplay();
        while (!dialogShell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }
    }

    /**
     * Initialize action listeners.
     */
    private void initializeActions() {
        treeViewWidget.addListener(SWT.Selection, new Listener() {
            public void handleEvent(Event event) {
                TreeItemData treeItemData = (TreeItemData) event.item.getData(TREE_ITEM_DATA_KEY);
                xpathTextField.setText(XSLTXPathHelper.calculateXPathToNode(treeItemData.getDomNode()));
            }
        });

        treeViewWidget.addListener(SWT.Expand, new Listener() {
            public void handleEvent(Event event) {
                TreeItem currentItem = (TreeItem) event.item;
                TreeItemData treeItemData = (TreeItemData) currentItem.getData(TREE_ITEM_DATA_KEY);

                // Explore if not already explored.
                if (!treeItemData.isMarkedAsExplored()) {
                    // Make sure to remove the dummy child item.
                    currentItem.removeAll();

                    Node node = treeItemData.getDomNode();

                    // Attributes.
                    if (node.hasAttributes()) {
                        NamedNodeMap attributesMap = node.getAttributes();
                        for (int i = 0; i < attributesMap.getLength(); i++) {
                            Node childNode = attributesMap.item(i);
                            addTreeItem(currentItem, childNode);
                        }
                    }

                    // Children.
                    if (node.hasChildNodes()) {
                        NodeList childNodes = node.getChildNodes();
                        for (int i = 0; i < childNodes.getLength(); i++) {
                            Node childNode = childNodes.item(i);
                            if (childNode.getNodeType() == Node.ELEMENT_NODE) {
                                addTreeItem(currentItem, childNode);
                            }
                        }
                    }

                    // Done exploring.
                    treeItemData.setMarkedAsExplored();
                }
            }
        });

        treeViewWidget.addMouseListener(new MouseListener() {
            public void mouseUp(MouseEvent e) {
            }

            public void mouseDown(MouseEvent e) {
            }

            public void mouseDoubleClick(MouseEvent e) {
                TreeItem[] selection = treeViewWidget.getSelection();
                if (selection.length > 0) {
                    Node selectedNode = ((TreeItemData) selection[0].getData(TREE_ITEM_DATA_KEY)).getDomNode();
                    setSelectedXpath(XSLTXPathHelper.calculateXPathToNode(selectedNode));
                    dialogShell.dispose();
                }
            }
        });

        okButton.addListener(SWT.Selection, new Listener() {
            public void handleEvent(Event event) {
                setSelectedXpath(xpathTextField.getText());
                dialogShell.dispose();
            }
        });

        cancelButton.addListener(SWT.Selection, new Listener() {
            public void handleEvent(Event arg0) {
                dialogShell.dispose();
            }
        });

        browseButton.addListener(SWT.Selection, new Listener() {
            public void handleEvent(Event event) {
                // Configure file dialog.
                FileDialog fileBrowserDialog = new FileDialog(dialogShell, SWT.OPEN);
                fileBrowserDialog.setText("Select XML Document");
                fileBrowserDialog.setFilterExtensions(new String[] { "*.xml" });

                // Let the user browse for the input xml file.
                final String filePath = fileBrowserDialog.open();

                // If the user selected a file.
                if (!StringUtils.isBlank(filePath)) {
                    // Clear tree view.
                    treeViewWidget.removeAll();

                    // Update the selected path
                    filePathTextField.setText(filePath);

                    final File selectedFile = new File(filePath);
                    if (selectedFile.exists() && selectedFile.canRead()) {
                        // Create a new runnable that can be executed with a progress bar.
                        IRunnableWithProgress runnable = new IRunnableWithProgress() {
                            public void run(IProgressMonitor monitor)
                                    throws InvocationTargetException, InterruptedException {
                                // We have no clue how long this would take.
                                monitor.beginTask("Parsing xml document...", IProgressMonitor.UNKNOWN);

                                // Parse the xml document into a dom object.
                                try {
                                    Document document = EsbUtils.parseDocument(selectedFile);
                                    final Node rootNode = document.getDocumentElement();

                                    // Adding a tree item is a ui related
                                    // operation which must be executed on the
                                    // ui thread. Since we are currently
                                    // executing inside a background thread, we
                                    // need to explicitly invoke the ui thread.
                                    new UIJob("add-root-tree-item-job") {
                                        public IStatus runInUIThread(IProgressMonitor monitor) {
                                            addTreeItem(null, rootNode);
                                            addNameSpaces(rootNode);
                                            return Status.OK_STATUS;
                                        }
                                    }.schedule();

                                    // Done.
                                    monitor.done();
                                } catch (Exception ex) {
                                    // Stop progress.
                                    monitor.done();

                                    // Report error.
                                    throw new InvocationTargetException(ex);
                                }
                            }
                        };

                        // Run the operation within a progress monitor dialog,
                        // make sure to fork-off from the ui thread so that we
                        // don't cause the ui to hang.
                        try {
                            new ProgressMonitorDialog(dialogShell).run(true, false, runnable);
                        } catch (Exception ex) {
                            MessageDialog.openError(dialogShell, "Parse Error",
                                    "Error while parsing specified xml file.");
                        }
                    } else {
                        MessageDialog.openError(dialogShell, "I/O Error", "Unable to read specified input file.");
                    }
                }
            }
        });
    }

    /**
     * Utility method for adding a child {@link TreeItem} into the specified
     * parent {@link TreeItem}.
     * 
     * @param parent parent {@link TreeItem} or null if this is root.
     * @param node dom {@link Node} to be associated with this {@link TreeItem}.
     */
    private void addTreeItem(TreeItem parent, Node node) {
        TreeItem childTreeItem;
        if (null == parent) {
            childTreeItem = new TreeItem(treeViewWidget, SWT.NONE);
        } else {
            childTreeItem = new TreeItem(parent, SWT.NONE);
        }
        childTreeItem.setText(node.getNodeName());

        // Select the icon based on the node type.
        String iconPath = (node.getNodeType() == Node.ELEMENT_NODE) ? ELEMENT_ICON_PATH : ATTRIBUTE_ICON_PATH;
        try {
            URL imageUrl = new URL(EsbEditorPlugin.getPlugin().getBaseURL(), iconPath);
            childTreeItem.setImage(ImageDescriptor.createFromURL(imageUrl).createImage());
        } catch (MalformedURLException ex) {
            // TODO: Log.
        }

        // Associated dom node with the newly created tree item.
        childTreeItem.setData(TREE_ITEM_DATA_KEY, new TreeItemData(node));

        // Here we determine if a dummy grand-child should be added into the
        // newly created child item. The grand-child is needed so that the
        // expand handle (little triangle besides the tree item) appears and the
        // user is able to expand the node (upon which event we explore the rest
        // of the tree dynamically).
        boolean hasChildren = (node.getNodeType() == Node.ELEMENT_NODE) ? node.hasChildNodes() : false;
        if (hasChildren) {
            TreeItem grandChild = new TreeItem(childTreeItem, SWT.NONE);
            grandChild.setText("Working...");
        }
    }

    /**
     * Adding NameSpaces
     * @param node
     */
    private void addNameSpaces(Node node) {
        nameSpaces = new HashMap<String, String>();
        NamedNodeMap nsList = node.getAttributes();
        if (nsList.getLength() > 0) {
            for (int i = 0; i < nsList.getLength(); i++) {
                Node item = nsList.item(i);
                if (null != item.getNodeName() && item.getNodeName().startsWith("xmlns:")) {
                    nameSpaces.put(item.getNodeName().replaceAll("^xmlns:", ""), item.getNodeValue());
                }
            }
        }
    }

    private void centerDialog() {
        Rectangle parentBounds = getParent().getBounds();
        Rectangle dialogBounds = dialogShell.getBounds();
        int centerX, centerY;
        centerX = (parentBounds.width - dialogBounds.width) / 2 + parentBounds.x;
        centerY = (parentBounds.height - dialogBounds.height) / 2 + parentBounds.y;
        dialogShell.setLocation(new Point(centerX, centerY));
    }

    private void setTabOrder() {
        Control[] tabOrder = new Control[] { browseButton, okButton, cancelButton };
        dialogShell.setTabList(tabOrder);
    }

    private void setSelectedXpath(String selectedXpath) {
        this.selectedXpath = selectedXpath;
    }

    /**
     * @return selected xpath.
     */
    public String getSelectedXpath() {
        return selectedXpath;
    }

    public Map<String, String> getNameSpaces() {
        return nameSpaces;
    }
}