http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench-common-activities/blob/163747de/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/XPathActivityConfigurationPanel.java
----------------------------------------------------------------------
diff --git 
a/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/XPathActivityConfigurationPanel.java
 
b/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/XPathActivityConfigurationPanel.java
new file mode 100644
index 0000000..efe7fbc
--- /dev/null
+++ 
b/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/XPathActivityConfigurationPanel.java
@@ -0,0 +1,1202 @@
+package org.apache.taverna.activities.xpath.ui.config;
+
+import java.awt.AWTEvent;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.GridLayout;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Toolkit;
+import java.awt.event.AWTEventListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.geom.Area;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.prefs.Preferences;
+
+import javax.swing.AbstractAction;
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSeparator;
+import javax.swing.JTabbedPane;
+import javax.swing.JTable;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.JToggleButton;
+import javax.swing.ListSelectionModel;
+import javax.swing.Popup;
+import javax.swing.PopupFactory;
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+import javax.swing.event.CaretEvent;
+import javax.swing.event.CaretListener;
+import javax.swing.filechooser.FileFilter;
+import javax.swing.table.DefaultTableModel;
+
+import org.apache.taverna.activities.xpath.XPathActivityConfigurationBean;
+import org.apache.taverna.activities.xpath.ui.config.xmltree.TableCellListener;
+import 
org.apache.taverna.activities.xpath.ui.config.xmltree.XPathActivityXMLTree;
+import 
org.apache.taverna.activities.xpath.ui.servicedescription.XPathActivityIcon;
+import org.apache.taverna.workbench.icons.WorkbenchIcons;
+
+import org.apache.log4j.Logger;
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.DocumentHelper;
+import org.dom4j.InvalidXPathException;
+import org.dom4j.Node;
+import org.dom4j.XPath;
+import org.dom4j.XPathException;
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+@SuppressWarnings("serial")
+public class XPathActivityConfigurationPanel extends JPanel {
+
+       private Logger logger = 
Logger.getLogger(XPathActivityConfigurationPanel.class);
+
+       // --- CONSTANTS ---
+       public static final int 
MAX_NUMBER_OF_MATCHING_NODES_TO_HIGHLIGHT_IN_THE_TREE = 100;
+
+       private static final Color INACTIVE_PANEL_BACKGROUND_COLOR = new 
Color(215,
+                       215, 215);
+
+       private static final String EXAMPLE_XML_PROMPT = "Paste example XML 
here...";
+
+       private static final String 
XPATH_XML_DOCUMENT_DIR_PROPERTY="XPathXMLDocumentDir";
+
+       private XPathActivityConfigurationPanel thisPanel;
+
+       // --- COMPONENTS FOR ACTIVITY CONFIGURATION PANEL ---
+       private JPanel jpActivityConfiguration;
+
+       private JPanel jpLeft;
+       private JPanel jpRight;
+
+       private JToggleButton bShowXMLTreeSettings;
+       private Popup xmlTreeSettingsMenu;
+       private long xmlTreeSettingsMenuLastShownAt;
+       private JButton bGenerateXPathExpression;
+       private JPanel jpXMLTreeSettingsMenuContents;
+       private JCheckBoxMenuItem miIncludeAttributes;
+       private JCheckBoxMenuItem miIncludeValues;
+       private JCheckBoxMenuItem miIncludeNamespaces;
+
+       private JTextArea taSourceXML;
+       private JButton bLoadXMLDocument;
+       private JButton bParseXML;
+       private XPathActivityXMLTree xmlTree;
+       private JScrollPane spXMLTreePlaceholder;
+
+       // --- COMPONENTS FOR XPATH EDITING PANEL ---
+       private JLabel jlXPathExpressionStatus;
+       private JLabel jlXPathExpression;
+       private JTextField tfXPathExpression;
+       private Map<String, String> xpathNamespaceMap;
+       private JButton bRunXPath;
+
+       private JLabel jlShowHideNamespaceMappings;
+       private JTable jtXPathNamespaceMappings;
+       private JButton bAddMapping;
+       private JButton bRemoveMapping;
+       private JPanel jpNamespaceMappingsWithButton;
+
+       // --- COMPONENTS FOR XPATH TESTING PANEL ---
+       private JPanel jpXPathTesting;
+
+       private JTextField tfExecutedXPathExpression;
+       private JTextField tfMatchingElementCount;
+
+       private JTabbedPane tpExecutedXPathExpressionResults;
+       private JTextArea taExecutedXPathExpressionResultsAsText;
+       private JScrollPane spExecutedXPathExpressionResultsAsText;
+       private JTextArea taExecutedXPathExpressionResultsAsXML;
+       private JScrollPane spExecutedXPathExpressionResultsAsXML;
+
+       public XPathActivityConfigurationPanel() {
+               this.thisPanel = this;
+
+               this.setLayout(new GridBagLayout());
+               GridBagConstraints c = new GridBagConstraints();
+
+               c.gridx = 0;
+               c.gridy = 0;
+               c.fill = GridBagConstraints.BOTH;
+               c.weightx = 1.0;
+               c.weighty = 0.50;
+               c.insets = new Insets(0, 10, 10, 10);
+               this.jpActivityConfiguration = 
createActivityConfigurationPanel();
+               this.add(this.jpActivityConfiguration, c);
+
+               c.gridy++;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weighty = 0;
+               c.insets = new Insets(0, 10, 0, 10);
+               this.add(new JSeparator(), c);
+
+               // XPath expression editing panel
+               c.gridy++;
+               c.fill = GridBagConstraints.BOTH;
+               c.weighty = 0.05;
+               c.insets = new Insets(5, 10, 5, 10);
+               this.add(createXPathExpressionEditingPanel(), c);
+
+               c.gridy++;
+               ;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weighty = 0;
+               c.insets = new Insets(0, 10, 0, 10);
+               this.add(new JSeparator(), c);
+
+               // XPath expression testing panel
+               c.gridy++;
+               c.fill = GridBagConstraints.BOTH;
+               c.weighty = 0.35;
+               c.insets = new Insets(5, 10, 0, 10);
+               this.jpXPathTesting = createXPathExpressionTestingPanel();
+               this.add(this.jpXPathTesting, c);
+       }
+
+       private JPanel createActivityConfigurationPanel() {
+               JPanel jpConfig = new JPanel(new GridBagLayout());
+               GridBagConstraints c = new GridBagConstraints();
+
+               // text area for example XML document
+               c.gridx = 0;
+               c.gridy = 0;
+               c.fill = GridBagConstraints.BOTH;
+               c.weightx = 0.5;
+               c.weighty = 1.0;
+               c.insets = new Insets(5, 0, 0, 5);
+               taSourceXML = new JTextArea(10, 30);
+               taSourceXML
+                               .setToolTipText("<html>Use this text area to 
paste or load an example XML document.<br>"
+                                               + "This document can then be 
parsed by clicking the button<br>"
+                                               + "with a green arrow in order 
to see its tree structure.</html>");
+               taSourceXML.setText(EXAMPLE_XML_PROMPT);
+               taSourceXML.addFocusListener(new FocusListener() {
+                       public void focusGained(FocusEvent e) {
+                               taSourceXML.selectAll();
+                       }
+
+                       public void focusLost(FocusEvent e) { /* do nothing */
+                       }
+               });
+               taSourceXML.addCaretListener(new CaretListener() {
+                       public void caretUpdate(CaretEvent e) {
+                               // make sure that it is only allowed to "parse 
example XML"
+                               // when something is actually present in the 
text area
+                               
bParseXML.setEnabled(taSourceXML.getText().trim().length() > 0
+                                               && 
!taSourceXML.getText().trim().equals(
+                                                               
EXAMPLE_XML_PROMPT));
+                       }
+               });
+               jpLeft = new JPanel(new GridLayout(1, 1));
+               jpLeft.add(new JScrollPane(taSourceXML));
+               jpConfig.add(jpLeft, c);
+
+               // button to parse example XML document
+
+               c.gridx++;
+               c.fill = GridBagConstraints.NONE;
+               c.weightx = 0;
+               c.weighty = 0;
+               c.insets = new Insets(0, 0, 0, 0);
+               bParseXML = new JButton(
+                               XPathActivityIcon
+                                               
.getIconById(XPathActivityIcon.XPATH_ACTIVITY_CONFIGURATION_PARSE_XML_ICON));
+               bParseXML
+                               .setToolTipText("Parse example XML document and 
generate its tree structure");
+               bParseXML.setEnabled(false);
+               bParseXML.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               parseXML();
+                       }
+               });
+               jpConfig.add(bParseXML, c);
+
+               // placeholder for XML tree (will be replaced by a real tree 
when the
+               // parsing is done)
+
+               c.gridx++;
+               c.fill = GridBagConstraints.BOTH;
+               c.weightx = 0.5;
+               c.weighty = 1.0;
+               c.insets = new Insets(5, 5, 0, 0);
+               JTextArea taXMLTreePlaceholder = new JTextArea(10, 30);
+               taXMLTreePlaceholder
+                               .setToolTipText("<html>This area will show tree 
structure of the example XML after you<br>"
+                                               + "paste it into the space on 
the left-hand side and press 'Parse'<br>"
+                                               + "button with the green 
arrow.</html>");
+               taXMLTreePlaceholder.setEditable(false);
+               
taXMLTreePlaceholder.setBackground(INACTIVE_PANEL_BACKGROUND_COLOR);
+               spXMLTreePlaceholder = new JScrollPane(taXMLTreePlaceholder);
+               jpRight = new JPanel(new GridLayout(1, 1));
+               jpRight.add(spXMLTreePlaceholder);
+               jpConfig.add(jpRight, c);
+
+               // Button to load XML document from a file
+               
+               bLoadXMLDocument = new JButton("Load XML from file", 
WorkbenchIcons.openIcon);  
+               bLoadXMLDocument.addActionListener(new ActionListener() {
+                       
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               JFileChooser fileChooser = new JFileChooser();
+                               Preferences prefs = 
Preferences.userNodeForPackage(getClass());
+                               String curDir = 
prefs.get(XPATH_XML_DOCUMENT_DIR_PROPERTY, System.getProperty("user.home"));
+                               fileChooser.setDialogTitle("Select file to load 
XML from");
+                               fileChooser.setFileFilter(new FileFilter() {  
+                                       public boolean accept(File f) {
+                                       return f.isDirectory() || 
f.getName().toLowerCase().endsWith(".xml");
+                                   }
+                                   
+                                   public String getDescription() {
+                                       return ".xml files";
+                                   }
+                               });
+                               fileChooser.setCurrentDirectory(new 
File(curDir));              
+                               int returnVal = 
fileChooser.showOpenDialog(((JButton) e
+                                               .getSource()).getParent());
+                               if (returnVal == JFileChooser.APPROVE_OPTION) {
+                                       
prefs.put(XPATH_XML_DOCUMENT_DIR_PROPERTY, fileChooser
+                                                       
.getCurrentDirectory().toString());
+                                       File file = 
fileChooser.getSelectedFile();
+                                       // Read the contents of a file into a 
string
+                                       // and set the value of the XML 
document text area to it
+                                       FileInputStream fis = null;
+                                       try{
+                                               byte[] fileBytes = new 
byte[(int)file.length()];
+                                               fis = new FileInputStream(file);
+                                               fis.read(fileBytes);
+                                               String xmlDocument = new 
String(fileBytes, "UTF-8");
+                                               setSourceXML(xmlDocument);
+                                       }
+                                       catch(Exception ex){
+                                               logger.error("An error occured 
while trying to read the XML document from file " + file.getAbsolutePath(), ex);
+                                               JOptionPane.showMessageDialog(
+                                                               ((JButton) 
e.getSource()).getParent(), 
+                                                               "There was an 
error while trying to read the file", 
+                                                               "XPath 
Activity", 
+                                                               
JOptionPane.ERROR_MESSAGE);
+                                       }
+                                       finally{
+                                               try {
+                                                       fis.close();
+                                               } catch (IOException e1) {
+                                                       // Ignore
+                                               }
+                                       }
+
+                               }
+                       }
+               });
+               c.gridx = 0;
+               c.gridy++;
+               c.gridwidth = 1;
+               c.fill = GridBagConstraints.NONE;
+               c.weightx = 0;
+               c.weighty = 0;
+               c.insets = new Insets(5, 0, 0, 5);
+               c.anchor = GridBagConstraints.EAST;
+               jpConfig.add(bLoadXMLDocument, c);
+               
+               // settings for the view of XML tree from example XML document
+
+               miIncludeAttributes = new JCheckBoxMenuItem("Show XML node 
attributes");
+               miIncludeAttributes.setSelected(true);
+               miIncludeAttributes.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               refreshXMLTreeUI();
+                       }
+               });
+
+               miIncludeValues = new JCheckBoxMenuItem(
+                               "Show values of XML elements and attributes");
+               miIncludeValues.setSelected(true);
+               miIncludeValues.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               refreshXMLTreeUI();
+                       }
+               });
+
+               miIncludeNamespaces = new JCheckBoxMenuItem(
+                               "Show namespaces of XML elements");
+               miIncludeNamespaces.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               refreshXMLTreeUI();
+                       }
+               });
+
+               jpXMLTreeSettingsMenuContents = new JPanel();
+               jpXMLTreeSettingsMenuContents.setBorder(BorderFactory
+                               .createRaisedBevelBorder());
+               jpXMLTreeSettingsMenuContents.setLayout(new BoxLayout(
+                               jpXMLTreeSettingsMenuContents, 
BoxLayout.Y_AXIS));
+               jpXMLTreeSettingsMenuContents.add(miIncludeAttributes);
+               jpXMLTreeSettingsMenuContents.add(miIncludeValues);
+               jpXMLTreeSettingsMenuContents.add(miIncludeNamespaces);
+
+               bShowXMLTreeSettings = new JToggleButton("Show XML tree 
settings...",
+                               
XPathActivityIcon.getIconById(XPathActivityIcon.UNFOLD_ICON));
+               bShowXMLTreeSettings.setSelectedIcon(XPathActivityIcon
+                               .getIconById(XPathActivityIcon.FOLD_ICON));
+               bShowXMLTreeSettings.setEnabled(false);
+               bShowXMLTreeSettings.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               if (xmlTreeSettingsMenu == null) {
+                                       xmlTreeSettingsMenuLastShownAt = 
System.currentTimeMillis();
+
+                                       Point parentPosition = 
bShowXMLTreeSettings
+                                                       .getLocationOnScreen();
+                                       xmlTreeSettingsMenu = 
PopupFactory.getSharedInstance()
+                                                       .getPopup(
+                                                                       
bShowXMLTreeSettings,
+                                                                       
jpXMLTreeSettingsMenuContents,
+                                                                       
parentPosition.x,
+                                                                       
parentPosition.y
+                                                                               
        + bShowXMLTreeSettings.getHeight());
+                                       xmlTreeSettingsMenu.show();
+                               } else {
+                                       bShowXMLTreeSettings.setSelected(false);
+                               }
+                       }
+               });
+               
+               bGenerateXPathExpression = new JButton("Generate XPath 
expression",
+                               XPathActivityIcon
+                                               
.getIconById(XPathActivityIcon.XML_TREE_NODE_ICON));
+               bGenerateXPathExpression.addActionListener(new ActionListener() 
{
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                            updateXPathEditingPanelValues();   
+                       }       
+               });
+               
+               JPanel xmlTreeButtonPanel = new JPanel();
+               xmlTreeButtonPanel.add(bGenerateXPathExpression);
+               xmlTreeButtonPanel.add(bShowXMLTreeSettings);
+               
+               c.gridx = 2;
+               c.gridwidth = 1;
+               c.fill = GridBagConstraints.NONE;
+               c.weightx = 0;
+               c.weighty = 0;
+               c.insets = new Insets(5, 0, 0, 0);
+               c.anchor = GridBagConstraints.EAST;
+               jpConfig.add(xmlTreeButtonPanel, c);
+
+               // register a new listener for all AWT mouse events - this will 
be used
+               // to identify clicks outside of the XML tree popup menu and 
the toggle
+               // button used to show/hide it
+               Toolkit.getDefaultToolkit().addAWTEventListener(new 
AWTEventListener() {
+                       public void eventDispatched(AWTEvent event) {
+                               if (event instanceof MouseEvent && 
xmlTreeSettingsMenu != null) {
+                                       MouseEvent e = (MouseEvent) event;
+                                       if (e.getClickCount() > 0
+                                                       && (e.getWhen() - 
xmlTreeSettingsMenuLastShownAt) > 100) {
+                                               // convert a point where mouse 
click was made from
+                                               // relative coordinates of the 
source component
+                                               // to the coordinates of the 
panel that represents the
+                                               // contents of the popup menu
+                                               Point clickRelativeToOverlay = 
SwingUtilities
+                                                               
.convertPoint((Component) e.getSource(), e
+                                                                               
.getPoint(),
+                                                                               
jpXMLTreeSettingsMenuContents);
+
+                                               Area 
areaOfPopupPanelAndToggleButton = new Area(
+                                                               
jpXMLTreeSettingsMenuContents.getBounds());
+
+                                               // only hide the popup menu if 
a click was made outside
+                                               // of the calculated area --
+                                               // plus not on one of the 
associated toggle buttons
+                                               if 
(!areaOfPopupPanelAndToggleButton
+                                                               
.contains(clickRelativeToOverlay)) {
+                                                       
xmlTreeSettingsMenu.hide();
+                                                       
bShowXMLTreeSettings.setSelected(false);
+
+                                                       // if the popup menu 
was dismissed by a click on the
+                                                       // toggle button that
+                                                       // has made it visible, 
this timer makes sure that
+                                                       // this click doesn't
+                                                       // re-show the popup 
menu
+                                                       new Timer(100, new 
ActionListener() {
+                                                               public void 
actionPerformed(ActionEvent e) {
+                                                                       
((Timer) e.getSource()).stop();
+                                                                       
xmlTreeSettingsMenu = null;
+                                                               }
+                                                       }).start();
+
+                                               }
+                                       }
+                               }
+                       }
+               }, AWTEvent.MOUSE_EVENT_MASK);
+
+               return (jpConfig);
+       }
+
+       private JPanel createXPathExpressionEditingPanel() {
+               this.jlXPathExpressionStatus = new JLabel();
+
+               this.jlXPathExpression = new JLabel("XPath expression");
+
+               this.bRunXPath = new JButton("Run XPath");
+               this.bRunXPath.setEnabled(false);
+               this.bRunXPath.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               runXPath(true);
+                       }
+               });
+
+               this.tfXPathExpression = new JTextField(30);
+               this.tfXPathExpression.setPreferredSize(new Dimension(0, 
this.bRunXPath
+                               .getPreferredSize().height));
+               this.tfXPathExpression.setMinimumSize(new Dimension(0, 
this.bRunXPath
+                               .getPreferredSize().height));
+               this.tfXPathExpression.addCaretListener(new CaretListener() {
+                       public void caretUpdate(CaretEvent e) {
+                               validateXPathAndUpdateUI();
+                       }
+               });
+               this.tfXPathExpression.addKeyListener(new KeyListener() {
+                       public void keyPressed(KeyEvent e) {
+                               if (e.getKeyCode() == KeyEvent.VK_ENTER) {
+                                       if (bRunXPath.isEnabled()) {
+                                               // it is safe to check that 
ENTER key may execute the
+                                               // XPath expression if the
+                                               // "Run XPath" button is 
enabled, as expression
+                                               // validation is responsible for
+                                               // enabling / disabling the 
button as the expression
+                                               // changes
+                                               runXPath(true);
+                                       }
+                               }
+                       }
+
+                       public void keyReleased(KeyEvent e) { /* not in use */
+                       }
+
+                       public void keyTyped(KeyEvent e) { /* not in use */
+                       }
+               });
+
+               JPanel jpXPath = new JPanel(new GridBagLayout());
+               GridBagConstraints c = new GridBagConstraints();
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weighty = 0;
+
+               c.gridx = 0;
+               c.gridy = 0;
+               c.weightx = 0;
+               jpXPath.add(jlXPathExpressionStatus);
+
+               c.gridx++;
+               c.weightx = 0.0;
+               c.insets = new Insets(0, 10, 0, 0);
+               jpXPath.add(jlXPathExpression, c);
+               
+               c.gridx++;
+               c.weightx = 1.0;
+               c.insets = new Insets(0, 10, 0, 10);
+               jpXPath.add(tfXPathExpression, c);
+
+               c.gridx++;
+               c.weightx = 0;
+               c.insets = new Insets(0, 0, 0, 0);
+               jpXPath.add(bRunXPath, c);
+
+               c.gridx = 2;
+               c.gridy++;
+               c.weightx = 1.0;
+               c.weighty = 0;
+               c.gridwidth = 2;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.WEST;
+               c.insets = new Insets(0, 10, 0, 10);
+               jlShowHideNamespaceMappings = new JLabel("Show namespace 
mappings...");
+               jlShowHideNamespaceMappings.setForeground(Color.BLUE);
+               jlShowHideNamespaceMappings.setCursor(new 
Cursor(Cursor.HAND_CURSOR));
+               jlShowHideNamespaceMappings.addMouseListener(new MouseAdapter() 
{
+                       public void mouseClicked(MouseEvent e) {
+                               jpNamespaceMappingsWithButton
+                                               
.setVisible(!jpNamespaceMappingsWithButton.isVisible());
+                               jlShowHideNamespaceMappings
+                                               
.setText((jpNamespaceMappingsWithButton.isVisible() ? "Hide"
+                                                               : "Show")
+                                                               + " namespace 
mappings...");
+                               thisPanel.validate();
+                       }
+               });
+               jpXPath.add(jlShowHideNamespaceMappings, c);
+
+               // namespace mapping table
+               DefaultTableModel tableModel = new DefaultTableModel();
+               tableModel.addColumn("Namespace Prefix");
+               tableModel.addColumn("Namespace URI");
+
+               jtXPathNamespaceMappings = new JTable();
+               jtXPathNamespaceMappings.setModel(tableModel);
+               // 
((DefaultCellEditor)jtXPathNamespaceMappings.getDefaultEditor(String.class)).setClickCountToStart(1);
+               // // TODO - enable if one-click-to-start-editing behaviour is 
required
+               // TODO - next line is to be enabled when Taverna is migrated 
to Java
+               // 1.6; for now it's fine to run without this
+               // jtXPathNamespaceMappings.setFillsViewportHeight(true); // 
makes sure
+               // that when the dedicated area is larger than the table, the 
latter is
+               // stretched vertically to fill the empty space
+               jtXPathNamespaceMappings
+                               
.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); // only one row can be 
selected at a time
+               jtXPathNamespaceMappings
+                               .setPreferredScrollableViewportSize(new 
Dimension(200, 50)); // NB! this prevents the table from occupying most of the 
space in the panel when screen is maximized
+               jtXPathNamespaceMappings.addKeyListener(new KeyAdapter() {
+                       public void keyReleased(KeyEvent e) {
+                               if (e.getKeyCode() == KeyEvent.VK_DELETE) {
+                                       removeNamespaceMapping();
+                               }
+                       }
+               });
+
+               TableCellListener cellListener = new TableCellListener(
+                               jtXPathNamespaceMappings, new AbstractAction() {
+                                       public void actionPerformed(ActionEvent 
e) {
+                                               TableCellListener tcl = 
(TableCellListener) e
+                                                               .getSource();
+
+                                               if (tcl.getColumn() == 0) {
+                                                       // prefix was modified
+                                                       String newPrefix = 
(String) tcl.getNewValue();
+                                                       if 
(xpathNamespaceMap.containsKey(newPrefix)) {
+                                                               // such prefix 
already exists - change won't be
+                                                               // saved
+                                                               JOptionPane
+                                                                               
.showMessageDialog(
+                                                                               
                thisPanel,
+                                                                               
                "Cannot update namespace prefix: "
+                                                                               
                                + "updated value already exists",
+                                                                               
                "XPath Activity",
+                                                                               
                JOptionPane.WARNING_MESSAGE);
+                                                       } else {
+                                                               // update the 
map with the new prefix for the
+                                                               // same URI 
value
+                                                               String 
oldPrefix = (String) tcl.getOldValue();
+                                                               
xpathNamespaceMap.put(newPrefix,
+                                                                               
xpathNamespaceMap.remove(oldPrefix));
+                                                       }
+                                               } else {
+                                                       // simple case - just 
the URI value has changed:
+                                                       // just overwrite the 
value in the namespace map
+                                                       String 
prefixOfUpdatedURI = (String) jtXPathNamespaceMappings
+                                                                       
.getModel().getValueAt(tcl.getRow(), 0);
+                                                       
xpathNamespaceMap.put(prefixOfUpdatedURI,
+                                                                       
(String) tcl.getNewValue());
+                                               }
+
+                                               // either way - reload from the 
local map (map could be
+                                               // not updated if the 
validation didn't succeed)
+                                               
reloadNamespaceMappingTableFromLocalMap();
+                                       }
+                               });
+
+               jtXPathNamespaceMappings.getColumnModel().getColumn(0)
+                               .setPreferredWidth(20); // set relative sizes 
of columns
+               jtXPathNamespaceMappings.getColumnModel().getColumn(1)
+                               .setPreferredWidth(300);
+
+               JScrollPane spXPathNamespaceMappings = new JScrollPane(
+                               jtXPathNamespaceMappings);
+               spXPathNamespaceMappings.setAlignmentY(TOP_ALIGNMENT);
+               spXPathNamespaceMappings.setMinimumSize(new Dimension(200, 
50)); // makes the table to have at least two rows visible in all cases - no 
matter how small the parent panel is
+
+               bAddMapping = new JButton("Add Mapping");
+               bAddMapping.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               addNamespaceMapping();
+                       }
+               });
+
+               bRemoveMapping = new JButton("Remove Mapping");
+               bRemoveMapping.setEnabled(false);
+               bRemoveMapping.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               removeNamespaceMapping();
+                       }
+               });
+
+               bAddMapping.setMinimumSize(bRemoveMapping.getPreferredSize()); 
// make sure that the 'Add Mapping' button is of the same size as 'Remove 
Mapping'
+                                                                               
                                        
+               
bAddMapping.setPreferredSize(bRemoveMapping.getPreferredSize()); // -- both are 
required to achieve desired behaviour when window is resized / namespace 
mapping table is enabled/disabled                                               
                                               
+
+               bRunXPath.setMinimumSize(bRemoveMapping.getPreferredSize()); // 
do the same for 'Run XPath' button
+               
+               bRunXPath.setPreferredSize(bRemoveMapping.getPreferredSize());
+
+               JPanel jpAddRemoveButtons = new JPanel();
+               jpAddRemoveButtons.setLayout(new GridBagLayout());
+               GridBagConstraints cAddRemove = new GridBagConstraints();
+               cAddRemove.gridx = 0;
+               cAddRemove.gridy = 0;
+               cAddRemove.weightx = 1.0;
+               cAddRemove.anchor = GridBagConstraints.NORTH;
+               cAddRemove.fill = GridBagConstraints.HORIZONTAL;
+               jpAddRemoveButtons.add(bAddMapping, cAddRemove);
+               cAddRemove.gridy++;
+               cAddRemove.weighty = 1.0;
+               cAddRemove.insets = new Insets(2, 0, 0, 0);
+               jpAddRemoveButtons.add(bRemoveMapping, cAddRemove);
+
+               jpNamespaceMappingsWithButton = new JPanel();
+               jpNamespaceMappingsWithButton.setVisible(false);
+               jpNamespaceMappingsWithButton.setLayout(new BorderLayout(10, 
0));
+               jpNamespaceMappingsWithButton.add(spXPathNamespaceMappings,
+                               BorderLayout.CENTER);
+               jpNamespaceMappingsWithButton
+                               .add(jpAddRemoveButtons, BorderLayout.EAST);
+
+               c.gridx = 0;
+               c.gridy++;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.BOTH;
+               c.weightx = 1.0;
+               c.weighty = 1.0;
+               c.insets = new Insets(5, 0, 0, 0);
+               jpXPath.add(jpNamespaceMappingsWithButton, c);
+
+               // initialise some values / tooltips
+               resetXPathEditingPanel();
+
+               return (jpXPath);
+       }
+
+       protected void addNamespaceMapping() {
+               TwoFieldQueryPanel queryPanel = new TwoFieldQueryPanel(
+                               "Namespace prefix:", "Namespace URI:");
+               int result = JOptionPane.showConfirmDialog(this, queryPanel,
+                               "XPath Activity - Create new namespace mapping",
+                               JOptionPane.OK_CANCEL_OPTION);
+
+               if (result == JOptionPane.OK_OPTION) {
+                       boolean bInvalidMapping = true;
+                       do {
+                               bInvalidMapping = 
queryPanel.getFirstValue().length() == 0
+                                               || 
queryPanel.getSecondValue().length() == 0
+                                               || 
xpathNamespaceMap.containsKey(queryPanel
+                                                               
.getFirstValue());
+
+                               if (bInvalidMapping) {
+                                       queryPanel = new TwoFieldQueryPanel(
+                                                       "<html><center><font 
color=\"red\">ERROR: you must "
+                                                                       + 
"enter values for both namespace prefix and URI. Prefix must be<br>"
+                                                                       + 
"unique in the mapping table - duplicates are not 
allowed!</font></center></html>",
+                                                       "Namespace prefix:", 
queryPanel.getFirstValue(),
+                                                       "Namespace URI:", 
queryPanel.getSecondValue());
+                                       result = 
JOptionPane.showConfirmDialog(this, queryPanel,
+                                                       "XPath Activity - 
Create new namespace mapping",
+                                                       
JOptionPane.OK_CANCEL_OPTION);
+                               }
+                       } while (bInvalidMapping && result == 
JOptionPane.OK_OPTION);
+
+                       if (result == JOptionPane.OK_OPTION && 
!bInvalidMapping) {
+                               // the value appears to be valid and OK was 
pressed - create new
+                               // mapping
+                               
this.xpathNamespaceMap.put(queryPanel.getFirstValue(),
+                                               queryPanel.getSecondValue());
+                               reloadNamespaceMappingTableFromLocalMap();
+                       }
+               }
+       }
+
+       protected void removeNamespaceMapping() {
+               int selectedRow = jtXPathNamespaceMappings.getSelectedRow();
+               if (selectedRow != -1) {
+                       // some row is selected - need to delete it and refresh 
table's UI
+                       // (but first stop editing to avoid
+                       // problems with cell editor trying to store an edited 
value after
+                       // edited row has been deleted)
+                       if (jtXPathNamespaceMappings.getCellEditor() != null) {
+                               
jtXPathNamespaceMappings.getCellEditor().stopCellEditing();
+                       }
+                       
xpathNamespaceMap.remove(jtXPathNamespaceMappings.getValueAt(
+                                       selectedRow, 0));
+                       reloadNamespaceMappingTableFromLocalMap();
+
+                       // select another row in the table
+                       int rowCount = jtXPathNamespaceMappings.getRowCount();
+                       if (rowCount > 0) {
+                               if (selectedRow < 
jtXPathNamespaceMappings.getRowCount()) {
+                                       // select the row that followed the one 
that was deleted
+                                       
jtXPathNamespaceMappings.getSelectionModel()
+                                                       
.setSelectionInterval(selectedRow, selectedRow);
+                               } else {
+                                       // last row in the table was deleted - 
select the one that
+                                       // is the new last row
+                                       
jtXPathNamespaceMappings.getSelectionModel()
+                                                       
.setSelectionInterval(rowCount - 1, rowCount - 1);
+                               }
+                       }
+               } else {
+                       JOptionPane.showMessageDialog(thisPanel,
+                                       "Please select a mapping to delete in 
the table first!",
+                                       "XPath Activity", 
JOptionPane.WARNING_MESSAGE);
+               }
+       }
+
+       private JPanel createXPathExpressionTestingPanel() {
+               JPanel jpTesting = new JPanel(new GridBagLayout());
+               GridBagConstraints c = new GridBagConstraints();
+
+               c.gridx = 0;
+               c.gridy = 0;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.WEST;
+               c.fill = GridBagConstraints.NONE;
+               c.weightx = 0;
+               c.weighty = 0;
+               c.insets = new Insets(0, 0, 10, 10);
+               jpTesting.add(new JLabel("Executed XPath expression:"), c);
+
+               c.gridx++;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1.0;
+               c.weighty = 0;
+               c.insets = new Insets(0, 0, 10, 10);
+               tfExecutedXPathExpression = new JTextField();
+               tfExecutedXPathExpression.setEditable(false);
+               tfExecutedXPathExpression.setBorder(null);
+               jpTesting.add(tfExecutedXPathExpression, c);
+
+               c.gridx = 0;
+               c.gridy++;
+               c.fill = GridBagConstraints.NONE;
+               c.weightx = 0;
+               c.weighty = 0;
+               c.insets = new Insets(0, 0, 5, 10);
+               jpTesting.add(new JLabel("Number of matching nodes:"), c);
+
+               c.gridx++;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1.0;
+               c.weighty = 0;
+               c.insets = new Insets(0, 0, 5, 10);
+               tfMatchingElementCount = new JTextField();
+               tfMatchingElementCount.setEditable(false);
+               tfMatchingElementCount.setBorder(null);
+               jpTesting.add(tfMatchingElementCount, c);
+
+               c.gridx = 0;
+               c.gridy++;
+               c.gridwidth = 2;
+               c.fill = GridBagConstraints.BOTH;
+               c.weightx = 1.0;
+               c.weighty = 1.0;
+               tpExecutedXPathExpressionResults = new JTabbedPane();
+               jpTesting.add(tpExecutedXPathExpressionResults, c);
+
+               taExecutedXPathExpressionResultsAsText = new JTextArea();
+               taExecutedXPathExpressionResultsAsText.setEditable(false);
+               spExecutedXPathExpressionResultsAsText = new JScrollPane(
+                               taExecutedXPathExpressionResultsAsText);
+               spExecutedXPathExpressionResultsAsText.setPreferredSize(new 
Dimension(
+                               200, 60));
+               spExecutedXPathExpressionResultsAsText.setBorder(BorderFactory
+                               
.createLineBorder(INACTIVE_PANEL_BACKGROUND_COLOR, 3));
+               tpExecutedXPathExpressionResults.add("Results as text",
+                               spExecutedXPathExpressionResultsAsText);
+
+               taExecutedXPathExpressionResultsAsXML = new JTextArea();
+               taExecutedXPathExpressionResultsAsXML.setEditable(false);
+               spExecutedXPathExpressionResultsAsXML = new JScrollPane(
+                               taExecutedXPathExpressionResultsAsXML);
+               spExecutedXPathExpressionResultsAsXML.setPreferredSize(new 
Dimension(
+                               200, 60));
+               spExecutedXPathExpressionResultsAsXML.setBorder(BorderFactory
+                               
.createLineBorder(INACTIVE_PANEL_BACKGROUND_COLOR, 3));
+               tpExecutedXPathExpressionResults.add("Results as XML",
+                               spExecutedXPathExpressionResultsAsXML);
+
+               // initialise some values / tooltips
+               resetXPathTestingPanel();
+
+               return (jpTesting);
+       }
+
+       protected void parseXML() {
+               String xmlData = taSourceXML.getText();
+
+               try {
+                       xmlTree = 
XPathActivityXMLTree.createFromXMLData(xmlData,
+                                       miIncludeAttributes.isSelected(), 
miIncludeValues
+                                                       .isSelected(), 
miIncludeNamespaces.isSelected(),
+                                       this);
+                       xmlTree
+                                       .setToolTipText("<html>This is a tree 
structure of the XML document that you have pasted.<br><br>"
+                                                       + "Clicking on the 
nodes in this tree will automatically generate a<br>"
+                                                       + "corresponding XPath 
expression. Multiple <b>identical</b> nodes can<br>"
+                                                       + "be selected at once 
- in this case <b>wildcards</b> will be used in the<br>"
+                                                       + "generated XPath 
expression to if selected nodes have different<br>"
+                                                       + "ancestors. Other 
nodes that match the generated XPath expression<br>"
+                                                       + "will also be 
selected in the tree.<br><br>"
+                                                       + "Contextual menu 
provides convenience methods for expanding or<br>"
+                                                       + "collapsing the 
tree." + "</html>");
+                       xmlTree.setBorder(BorderFactory.createEmptyBorder(5, 5, 
5, 5));
+                       JScrollPane spXMLTree = new JScrollPane(xmlTree);
+                       
spXMLTree.setPreferredSize(spXMLTreePlaceholder.getPreferredSize());
+                       jpRight.removeAll();
+                       jpRight.add(spXMLTree);
+
+                       // all successful - enable options to modify the tree
+                       this.bShowXMLTreeSettings.setEnabled(true);
+
+                       // data structures inside the XML tree were reset (as 
the tree was
+                       // re-created) -
+                       // now reset the UI to the initial state as well
+                       resetXPathEditingPanel();
+                       resetXPathTestingPanel();
+
+                       // XML tree has pre-populated the namespace map with 
the namespaces
+                       // specified in the
+                       // root element of the tree - load these values
+                       updateXPathEditingPanelValues();
+
+                       this.validate();
+                       this.repaint();
+               } catch (DocumentException e) {
+                       JOptionPane.showMessageDialog(this, e.getMessage(),
+                                       "XPath Activity", 
JOptionPane.ERROR_MESSAGE);
+                       this.taSourceXML.requestFocusInWindow();
+                       return;
+               }
+       }
+
+       /**
+        * Makes the {@link XPathActivityXMLTree} to refresh its UI from the
+        * original XML document that was used to create it in first place.
+        * 
+        * The reason for using this method is to apply new options to the way 
the
+        * tree is rendered - e.g. attributes shown/hidden in the tree, values 
and
+        * namespaces shown/hidden, etc.
+        */
+       protected void refreshXMLTreeUI() {
+               
this.xmlTree.refreshFromExistingDocument(this.miIncludeAttributes
+                               .isSelected(), 
this.miIncludeValues.isSelected(),
+                               this.miIncludeNamespaces.isSelected());
+       }
+
+       /**
+        * Initialises XPath Editing panel: -- resets XPath expression that is 
being
+        * shown; -- resets local copy of namespace map; -- resets UI of 
namespace
+        * mapping table;
+        */
+       private void resetXPathEditingPanel() {
+               tfXPathExpression.setText("");
+               validateXPathAndUpdateUI();
+
+               // clear the local copy of namespace map
+               xpathNamespaceMap = new HashMap<String, String>();
+
+               // clear the namespace mapping table and reload the data from 
the map
+               DefaultTableModel tableModel = (DefaultTableModel) 
jtXPathNamespaceMappings
+                               .getModel();
+               tableModel.getDataVector().removeAllElements();
+       }
+
+       /**
+        * Initialises XPath testing panel which shows results of executing 
current
+        * XPath expression against the example XML - the panel is returned to 
the
+        * way it looks when it is first loaded.
+        */
+       private void resetXPathTestingPanel() {
+               this.tfExecutedXPathExpression.setText("--");
+               this.tfMatchingElementCount.setText("--");
+
+               this.taExecutedXPathExpressionResultsAsText.setText("");
+               this.taExecutedXPathExpressionResultsAsText
+                               .setBackground(INACTIVE_PANEL_BACKGROUND_COLOR);
+
+               this.taExecutedXPathExpressionResultsAsXML.setText("");
+               this.taExecutedXPathExpressionResultsAsXML
+                               .setBackground(INACTIVE_PANEL_BACKGROUND_COLOR);
+       }
+
+       public void updateXPathEditingPanelValues() {
+               if (xmlTree.getCurrentXPathExpression() != null) {
+                       
tfXPathExpression.setText(xmlTree.getCurrentXPathExpression()
+                                       .getText());
+               }
+
+               // clear the local copy of namespace map and update it with all 
values
+               // from
+               // the map in XML tree instance (which was apparently just 
re-generated
+               // on user request)
+               xpathNamespaceMap.clear();
+               xpathNamespaceMap.putAll(xmlTree.getCurrentXPathNamespaces());
+
+               // clear the namespace mapping table and reload the data from 
the map
+               reloadNamespaceMappingTableFromLocalMap();
+       }
+
+       protected void reloadNamespaceMappingTableFromLocalMap() {
+               // clear the namespace mapping table and reload the data from 
the map
+               DefaultTableModel tableModel = (DefaultTableModel) 
jtXPathNamespaceMappings
+                               .getModel();
+               tableModel.getDataVector().removeAllElements();
+               for (Map.Entry<String, String> mapping : this.xpathNamespaceMap
+                               .entrySet()) {
+                       tableModel.addRow(new Object[] { mapping.getKey(),
+                                       mapping.getValue() });
+               }
+
+               
bRemoveMapping.setEnabled(this.xpathNamespaceMap.entrySet().size() > 0);
+
+               repaint();
+       }
+
+       private String getXPathValidationErrorMessage() {
+               try {
+                       // try to parse the XPath expression...
+                       
DocumentHelper.createXPath(tfXPathExpression.getText().trim());
+                       // ...success
+                       return ("");
+               } catch (InvalidXPathException e) {
+                       // ...failed to parse the XPath expression: notify of 
the error
+                       return (e.getMessage());
+               }
+       }
+
+       /**
+        * Validates the current XPath expression and updates UI accordingly: --
+        * XPath status icon is updated; -- tooltip for the icon explains the
+        * status; -- 'Run XPath' button is enabled/disabled depending on 
validity
+        * of XPath expression and existence of example data in the XML tree
+        */
+       protected void validateXPathAndUpdateUI() {
+               String candidatePath = tfXPathExpression.getText();
+               int xpathStatus = XPathActivityConfigurationBean
+                               .validateXPath(candidatePath);
+
+               switch (xpathStatus) {
+               case XPathActivityConfigurationBean.XPATH_VALID:
+                       // success: expression is correct
+                       jlXPathExpressionStatus.setIcon(XPathActivityIcon
+                                       
.getIconById(XPathActivityIcon.XPATH_STATUS_OK_ICON));
+                       jlXPathExpressionStatus
+                                       .setToolTipText("Current XPath 
expression is well-formed and valid");
+
+                       // could allow to execute against example XML, with 
only condition:
+                       // XML tree must be populated
+                       // (that is, there should be something to run the 
expression
+                       // against)
+                       if (xmlTree != null) {
+                               this.bRunXPath.setEnabled(true);
+                               this.bRunXPath
+                                               .setToolTipText("<html>Evaluate 
current XPath expression against the XML document<br>"
+                                                               + "whose 
structure is shown in the tree view above.</html>");
+                       } else {
+                               this.bRunXPath.setEnabled(false);
+                               this.bRunXPath
+                                               .setToolTipText("<html>No XML 
document to evaluate the current XPath expression against.<br><br>"
+                                                               + "Paste some 
example XML into the area in the top-left section of the<br>"
+                                                               + "window, then 
parse it by clicking on the button with the green arrow<br>"
+                                                               + "in order to 
test your XPath expression.</html>");
+                       }
+                       break;
+
+               case XPathActivityConfigurationBean.XPATH_EMPTY:
+                       // no XPath expression - can't tell if it is correct + 
nothing to
+                       // execute
+                       jlXPathExpressionStatus.setIcon(XPathActivityIcon
+                                       
.getIconById(XPathActivityIcon.XPATH_STATUS_UNKNOWN_ICON));
+                       jlXPathExpressionStatus
+                                       .setToolTipText("<html>There is no 
XPath expression to validate.<br><br>"
+                                                       + "<b>Hint:</b> select 
something in the tree view showing the structure<br>"
+                                                       + "of the XML document 
that you have pasted (or type the XPath<br>"
+                                                       + "expression 
manually).</html>");
+                       this.bRunXPath.setEnabled(false);
+                       this.bRunXPath.setToolTipText("No XPath expression to 
execute");
+                       break;
+
+               case XPathActivityConfigurationBean.XPATH_INVALID:
+                       // failed to parse the XPath expression: notify of the 
error
+                       jlXPathExpressionStatus.setIcon(XPathActivityIcon
+                                       
.getIconById(XPathActivityIcon.XPATH_STATUS_ERROR_ICON));
+                       jlXPathExpressionStatus
+                                       
.setToolTipText(getXPathValidationErrorMessage());
+
+                       this.bRunXPath.setEnabled(false);
+                       this.bRunXPath
+                                       .setToolTipText("Cannot execute invalid 
XPath expression");
+                       break;
+               }
+
+       }
+
+       /**
+        * Executes the current XPath expression against the current XML tree.
+        * 
+        * @param displayResults
+        *            <code>true</code> to execute and display results in the 
XPath
+        *            activity configuration panel (this happens when the 'Run
+        *            XPath' button is clicked);<br/>
+        *            <false> to run the expression quietly and simply return 
the
+        *            number of matching nodes.
+        * @return Number of nodes in the XML tree that match the current XPath
+        *         expression. (Or <code>-1</code> if an error has occurred 
during
+        *         the execution -- error messages will only be shown if
+        *         <code>displayResults == true</code>).
+        */
+       public int runXPath(boolean displayResults) {
+               // ----- RUNNING THE XPath EXPRESSION -----
+               XPath expr = null;
+               try {
+                       expr = 
DocumentHelper.createXPath(this.tfXPathExpression.getText());
+                       expr.setNamespaceURIs(this.xpathNamespaceMap);
+               } catch (InvalidXPathException e) {
+                       if (displayResults) {
+                               JOptionPane
+                                               .showMessageDialog(
+                                                               thisPanel,
+                                                               "Incorrect 
XPath Expression\n\n"
+                                                                               
+ "Please check the expression if you have manually modified it;\n"
+                                                                               
+ "Alternatively, try to select another node from the XML tree.\n\n"
+                                                                               
+ 
"------------------------------------------------------------------------------------\n\n"
+                                                                               
+ "XPath processing library reported the following error:\n"
+                                                                               
+ e.getMessage(), "XPath Activity",
+                                                               
JOptionPane.ERROR_MESSAGE);
+                       }
+                       return (-1);
+               }
+
+               Document doc = xmlTree.getDocumentUsedToPopulateTree();
+               List<Node> matchingNodes = null;
+               int matchingNodeCount = -1;
+               try {
+                       matchingNodes = expr.selectNodes(doc);
+                       matchingNodeCount = matchingNodes.size();
+               } catch (XPathException e) {
+                       if (displayResults) {
+                               JOptionPane
+                                               .showMessageDialog(
+                                                               thisPanel,
+                                                               "Unexpected 
error has occurred while executing the XPath expression.\n\n"
+                                                                               
+ "If you have manually modified the XPath expression and/or namespace 
mappings,\n"
+                                                                               
+ "please check you changes. Alternatively, make your selection in the XML tree 
and\n"
+                                                                               
+ "a correct XPath expression with corresponding namespace mapping will be 
generated.\n\n"
+                                                                               
+ 
"-------------------------------------------------------------------------------------------------------------\n\n"
+                                                                               
+ "XPath processing library reported the following error:\n"
+                                                                               
+ e.getMessage(), "XPath Activity",
+                                                               
JOptionPane.ERROR_MESSAGE);
+                       }
+                       return (-1);
+               }
+
+               // ----- DISPLAYING THE RESULTS -----
+               if (displayResults) {
+                       tfExecutedXPathExpression.setText(expr.getText());
+                       tfMatchingElementCount.setText("" + matchingNodeCount);
+
+                       StringBuffer outNodesText = new StringBuffer();
+                       StringBuffer outNodesXML = new StringBuffer();
+                       for (Node n : matchingNodes) {
+                               if (n.getStringValue() != null
+                                               && n.getStringValue().length() 
> 0) {
+                                       outNodesText.append(n.getStringValue() 
+ "\n");
+                               }
+                               outNodesXML.append(n.asXML() + "\n");
+                       }
+
+                       // 
tpExecutedXPathExpressionResults.setSelectedIndex(0); // open the
+                       // first tab (should be the one with textual results) 
// TODO -
+                       // enable if needed
+
+                       
taExecutedXPathExpressionResultsAsText.setText(outNodesText
+                                       .toString());
+                       
taExecutedXPathExpressionResultsAsText.setBackground(Color.WHITE);
+                       
taExecutedXPathExpressionResultsAsText.setCaretPosition(0);
+                       
spExecutedXPathExpressionResultsAsText.setBorder(BorderFactory
+                                       .createLineBorder(Color.WHITE, 3));
+
+                       
taExecutedXPathExpressionResultsAsXML.setText(outNodesXML
+                                       .toString());
+                       
taExecutedXPathExpressionResultsAsXML.setBackground(Color.WHITE);
+                       
taExecutedXPathExpressionResultsAsXML.setCaretPosition(0);
+                       
spExecutedXPathExpressionResultsAsXML.setBorder(BorderFactory
+                                       .createLineBorder(Color.WHITE, 3));
+               }
+
+               return (matchingNodeCount);
+       }
+
+       protected void setSourceXML(String xmlData) {
+               this.taSourceXML.setText(xmlData);
+       }
+
+       protected String getCurrentXPathExpression() {
+               return (this.tfXPathExpression.getText().trim());
+       }
+
+       protected void setCurrentXPathExpression(String xpathExpression) {
+               this.tfXPathExpression.setText(xpathExpression);
+       }
+
+       protected Map<String, String> getCurrentXPathNamespaceMap() {
+               return (this.xpathNamespaceMap);
+       }
+
+       /**
+        * This method doesn't simply set a reference to the passed map, but 
rather
+        * performs a shallow copy of values.
+        * 
+        * This is because the method is used during configuration panel's
+        * initialisation from the values that are held in the configuration 
bean.
+        * In case of simple reference assignment, any changes made to map in 
the
+        * configuration panel are also taking effect on the same map - 
referenced
+        * from the configuration bean, which leads to undesired behaviour.
+        */
+       protected void setCurrentXPathNamespaceMapValues(
+                       Map<String, String> xpathNamespaceMap) {
+               this.xpathNamespaceMap.clear();
+               this.xpathNamespaceMap.putAll(xpathNamespaceMap);
+       }
+
+       protected XPathActivityXMLTree getCurrentXMLTree() {
+               return (this.xmlTree);
+       }
+
+       /**
+        * For testing
+        */
+       public static void main(String[] args) {
+               JFrame frame = new JFrame();
+               frame.getContentPane().add(new 
XPathActivityConfigurationPanel());
+               frame.pack();
+               frame.setSize(new Dimension(900, 600));
+               frame.setLocationRelativeTo(null);
+               frame.setVisible(true);
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench-common-activities/blob/163747de/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/XPathActivityConfigurationPanelProvider.java
----------------------------------------------------------------------
diff --git 
a/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/XPathActivityConfigurationPanelProvider.java
 
b/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/XPathActivityConfigurationPanelProvider.java
new file mode 100644
index 0000000..c652d98
--- /dev/null
+++ 
b/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/XPathActivityConfigurationPanelProvider.java
@@ -0,0 +1,158 @@
+package org.apache.taverna.activities.xpath.ui.config;
+
+import java.awt.BorderLayout;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.swing.BorderFactory;
+import javax.swing.JOptionPane;
+
+import org.apache.taverna.activities.xpath.XPathActivityConfigurationBean;
+import 
org.apache.taverna.workbench.ui.views.contextualviews.activity.ActivityConfigurationPanel;
+import org.apache.taverna.commons.services.ServiceRegistry;
+import org.apache.taverna.scufl2.api.activity.Activity;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+
+/**
+ *
+ * @author Sergejs Aleksejevs
+ * @author David Withers
+ */
+@SuppressWarnings("serial")
+public class XPathActivityConfigurationPanelProvider extends 
ActivityConfigurationPanel {
+
+       private XPathActivityConfigurationPanel configPanel;
+       private final ServiceRegistry serviceRegistry;
+
+       public XPathActivityConfigurationPanelProvider(Activity activity, 
ServiceRegistry serviceRegistry) {
+               super(activity);
+               this.serviceRegistry = serviceRegistry;
+               initialise();
+       }
+
+       @Override
+       protected void initialise() {
+               super.initialise();
+               removeAll();
+               setLayout(new BorderLayout());
+
+               // create actual contents of the config panel
+               this.configPanel = new XPathActivityConfigurationPanel();
+               add(configPanel, BorderLayout.CENTER);
+
+               // place the whole configuration panel into a raised area, so 
that
+               // automatically added 'Apply' / 'Close' buttons visually apply 
to
+               // the whole of the panel, not just part of it
+               this.setBorder(BorderFactory.createCompoundBorder(
+                               BorderFactory.createEmptyBorder(12, 12, 2, 12),
+                               BorderFactory.createRaisedBevelBorder()));
+
+               // Populate fields from activity configuration bean
+               refreshConfiguration();
+       }
+
+       /**
+        * Prepare a new configuration bean from the UI, to be returned with
+        * getConfiguration()
+        */
+       @Override
+       public void noteConfiguration() {
+               if (configPanel.getCurrentXMLTree() != null) {
+                       setProperty("exampleXmlDocument", 
configPanel.getCurrentXMLTree()
+                                       
.getDocumentUsedToPopulateTree().asXML());
+               }
+               setProperty("xpathExpression", 
configPanel.getCurrentXPathExpression());
+
+               Map<String, String> xPathNamespaceMap = 
configPanel.getCurrentXPathNamespaceMap();
+               if (xPathNamespaceMap.isEmpty()) {
+                       json.remove("xpathNamespaceMap");
+               } else {
+                       ArrayNode namespaceMapNode = json.arrayNode();
+                       for (Entry<String, String> namespaceMapping : 
xPathNamespaceMap.entrySet()) {
+                               namespaceMapNode.addObject().put("prefix", 
namespaceMapping.getKey()).put("uri", namespaceMapping.getValue());
+                       }
+                       json.set("xpathNamespaceMap", namespaceMapNode);
+               }
+
+               configureInputPorts(serviceRegistry);
+               configureOutputPorts(serviceRegistry);
+}
+
+       /**
+        * Check that user values in the UI are valid.
+        */
+       @Override
+       public boolean checkValues() {
+               // the only validity condition is the correctness of the XPath
+               // expression -- so checking that
+               int xpathExpressionStatus = 
XPathActivityConfigurationBean.validateXPath(this.configPanel
+                               .getCurrentXPathExpression());
+
+               // show an explicit warning message to explain the problem
+               if (xpathExpressionStatus == 
XPathActivityConfigurationBean.XPATH_EMPTY) {
+                       JOptionPane.showMessageDialog(this, "XPath expression 
should not be empty",
+                                       "XPath Activity", 
JOptionPane.WARNING_MESSAGE);
+               } else if (xpathExpressionStatus == 
XPathActivityConfigurationBean.XPATH_INVALID) {
+                       JOptionPane.showMessageDialog(this,
+                                       "<html><center>XPath expression is 
invalid - hover the mouse over the XPath status<br>"
+                                                       + "icon to get more 
information</center></html>", "XPath Activity",
+                                       JOptionPane.WARNING_MESSAGE);
+               }
+
+               return (xpathExpressionStatus == 
XPathActivityConfigurationBean.XPATH_VALID);
+       }
+
+       /**
+        * Update GUI from a changed configuration bean (perhaps by undo / 
redo).
+        */
+       @Override
+       public void refreshConfiguration() {
+               if (json.has("exampleXmlDocument")) {
+                       
configPanel.setSourceXML(getProperty("exampleXmlDocument"));
+                       configPanel.parseXML();
+               }
+
+               
configPanel.setCurrentXPathExpression(getProperty("xpathExpression"));
+
+               Map<String, String> xpathNamespaceMap = new HashMap<>();
+               if (json.has("xpathNamespaceMap")) {
+                       for (JsonNode namespaceMapping : 
json.get("xpathNamespaceMap")) {
+                               
xpathNamespaceMap.put(namespaceMapping.get("prefix").asText(), 
namespaceMapping.get("uri").asText());
+                       }
+               }
+               
configPanel.setCurrentXPathNamespaceMapValues(xpathNamespaceMap);
+               configPanel.reloadNamespaceMappingTableFromLocalMap();
+
+               // if the XML tree was populated, (re-)run the XPath expression
+               // and restore selection of nodes in the tree, if possible
+               if (configPanel.getCurrentXMLTree() != null) {
+                       configPanel.runXPath(true);
+
+                       // convert the XPath expression into the required list 
form;
+                       // discard the first 'leg', as it's a side effect of
+                       // "String.split()" -
+                       // non-existent string to the left of the first "/"
+                       String[] xpathLegs = 
getProperty("xpathExpression").split("/");
+                       List<String> xpathLegList = new ArrayList<String>();
+                       for (int i = 1; i < xpathLegs.length; i++) {
+                               xpathLegList.add("/" + xpathLegs[i]);
+                       }
+
+                       // if nothing was obtained, we should be looking at the 
root node -
+                       // but add the actual expression as it was, just in case
+                       if (xpathLegList.size() == 0) {
+                               
xpathLegList.add(configPanel.getCurrentXPathExpression());
+                       }
+
+                       // invoke selection handler of the XML tree to do the 
job
+                       
configPanel.getCurrentXMLTree().getXMLTreeSelectionHandler()
+                                       
.selectAllNodesThatMatchTheCurrentXPath(xpathLegList, null);
+               }
+
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench-common-activities/blob/163747de/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/XPathActivityConfigureAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/XPathActivityConfigureAction.java
 
b/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/XPathActivityConfigureAction.java
new file mode 100644
index 0000000..b57d9f7
--- /dev/null
+++ 
b/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/XPathActivityConfigureAction.java
@@ -0,0 +1,52 @@
+package org.apache.taverna.activities.xpath.ui.config;
+
+import java.awt.Frame;
+import java.awt.event.ActionEvent;
+
+import org.apache.taverna.servicedescriptions.ServiceDescriptionRegistry;
+import org.apache.taverna.workbench.activityicons.ActivityIconManager;
+import org.apache.taverna.workbench.edits.EditManager;
+import org.apache.taverna.workbench.file.FileManager;
+import 
org.apache.taverna.workbench.ui.actions.activity.ActivityConfigurationAction;
+import 
org.apache.taverna.workbench.ui.views.contextualviews.activity.ActivityConfigurationDialog;
+import org.apache.taverna.commons.services.ServiceRegistry;
+import org.apache.taverna.scufl2.api.activity.Activity;
+
+/**
+ * @author Sergejs Aleksejevs
+ * @author David Withers
+ */
+@SuppressWarnings("serial")
+public class XPathActivityConfigureAction extends ActivityConfigurationAction {
+
+       private final EditManager editManager;
+       private final FileManager fileManager;
+       private final ServiceRegistry serviceRegistry;
+
+       public XPathActivityConfigureAction(Activity activity, Frame owner,
+                       EditManager editManager, FileManager fileManager,
+                       ActivityIconManager activityIconManager,
+                       ServiceDescriptionRegistry serviceDescriptionRegistry, 
ServiceRegistry serviceRegistry) {
+               super(activity, activityIconManager, 
serviceDescriptionRegistry);
+               this.editManager = editManager;
+               this.fileManager = fileManager;
+               this.serviceRegistry = serviceRegistry;
+       }
+
+       public void actionPerformed(ActionEvent e) {
+               ActivityConfigurationDialog currentDialog = 
ActivityConfigurationAction.getDialog(getActivity());
+
+               if (currentDialog != null) {
+                       currentDialog.toFront();
+                       return;
+               }
+
+               XPathActivityConfigurationPanelProvider panel = new 
XPathActivityConfigurationPanelProvider(
+                               getActivity(), serviceRegistry);
+               ActivityConfigurationDialog dialog = new 
ActivityConfigurationDialog(
+                               getActivity(), panel, editManager);
+
+               ActivityConfigurationAction.setDialog(getActivity(), dialog, 
fileManager);
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench-common-activities/blob/163747de/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/xmltree/TableCellListener.java
----------------------------------------------------------------------
diff --git 
a/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/xmltree/TableCellListener.java
 
b/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/xmltree/TableCellListener.java
new file mode 100644
index 0000000..08c5f42
--- /dev/null
+++ 
b/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/xmltree/TableCellListener.java
@@ -0,0 +1,186 @@
+package org.apache.taverna.activities.xpath.ui.config.xmltree;
+
+import java.awt.event.*;
+import javax.swing.*;
+import java.beans.*;
+
+/**
+ *  This class listens for changes made to the data in the table via the
+ *  TableCellEditor. When editing is started, the value of the cell is saved
+ *  When editing is stopped the new value is saved. When the oold and new
+ *  values are different, then the provided Action is invoked.
+ *
+ *  The source of the Action is a TableCellListener instance.
+ *  
+ *  TODO: update to work with Java 1.6, when Taverna is migrated to that 
version
+ *        (see the next TODO tag - this is where the change needs to be made) 
+ *  
+ *  @author Robert Camick
+ *  
+ *  @see <a 
href="http://tips4java.wordpress.com/2009/06/07/table-cell-listener/";>http://tips4java.wordpress.com/2009/06/07/table-cell-listener/</a>
+ *  @see <a 
href="http://www.camick.com/java/source/TableCellListener.java";>http://www.camick.com/java/source/TableCellListener.java</a>
+ */
+public class TableCellListener implements PropertyChangeListener, Runnable
+{
+  private JTable table;
+  private Action action;
+
+  private int row;
+  private int column;
+  private Object oldValue;
+  private Object newValue;
+
+  /**
+   *  Create a TableCellListener.
+   *
+   *  @param table   the table to be monitored for data changes
+   *  @param action  the Action to invoke when cell data is changed
+   */
+  public TableCellListener(JTable table, Action action)
+  {
+    this.table = table;
+    this.action = action;
+    this.table.addPropertyChangeListener( this );
+  }
+
+  /**
+   *  Create a TableCellListener with a copy of all the data relevant to
+   *  the change of data for a given cell.
+   *
+   *  @param row  the row of the changed cell
+   *  @param column  the column of the changed cell
+   *  @param oldValue  the old data of the changed cell
+   *  @param newValue  the new data of the changed cell
+   */
+  private TableCellListener(JTable table, int row, int column, Object 
oldValue, Object newValue)
+  {
+    this.table = table;
+    this.row = row;
+    this.column = column;
+    this.oldValue = oldValue;
+    this.newValue = newValue;
+  }
+
+  /**
+   *  Get the column that was last edited - value as in table model, not in 
the UI
+   *
+   *  @return the column that was edited
+   */
+  public int getColumn()
+  {
+    return column;
+  }
+
+  /**
+   *  Get the new value in the cell
+   *
+   *  @return the new value in the cell
+   */
+  public Object getNewValue()
+  {
+    return newValue;
+  }
+
+  /**
+   *  Get the old value of the cell
+   *
+   *  @return the old value of the cell
+   */
+  public Object getOldValue()
+  {
+    return oldValue;
+  }
+
+  /**
+   *  Get the row that was last edited - value as in table model, not in the UI
+   *
+   *  @return the row that was edited
+   */
+  public int getRow()
+  {
+    return row;
+  }
+
+  /**
+   *  Get the table of the cell that was changed
+   *
+   *  @return the table of the cell that was changed
+   */
+  public JTable getTable()
+  {
+    return table;
+  }
+  
+  //
+  //  Implement the PropertyChangeListener interface
+  //
+  public void propertyChange(PropertyChangeEvent e)
+  {
+    //  A cell has started/stopped editing
+
+    if ("tableCellEditor".equals(e.getPropertyName()))
+    {
+      if (table.isEditing())
+        processEditingStarted();
+      else
+        processEditingStopped();
+    }
+  }
+
+  /*
+   *  Save information of the cell about to be edited
+   */
+  private void processEditingStarted()
+  {
+    //  The invokeLater is necessary because the editing row and editing
+    //  column of the table have not been set when the "tableCellEditor"
+    //  PropertyChangeEvent is fired.
+    //  This results in the "run" method being invoked
+
+    SwingUtilities.invokeLater( this );
+  }
+  
+  /*
+   *  See above.
+   */
+  public void run()
+  {
+//    TODO - the next line is a correct implementation for Java 1.6    
+//    row = table.convertRowIndexToModel( table.getEditingRow() );
+    
+//    in Java 1.5 which is currently used, tables are not easily sortable
+//    (and that is the case for the XPath Activity, where this class is used) 
--
+//    hence, row numbers in the view and in the model will always be identical
+//    --> HACK: just use the row number from the view...
+    row = table.getEditingRow();
+    
+    column = table.convertColumnIndexToModel( table.getEditingColumn() );
+    oldValue = table.getModel().getValueAt(row, column);
+    newValue = null;
+  }
+
+  /*
+   *  Update the Cell history when necessary
+   */
+  private void processEditingStopped()
+  {
+    newValue = table.getModel().getValueAt(row, column);
+
+    //  The data has changed, invoke the supplied Action
+
+    if (! newValue.equals(oldValue))
+    {
+      //  Make a copy of the data in case another cell starts editing
+      //  while processing this change
+
+      TableCellListener tcl = new TableCellListener(
+        getTable(), getRow(), getColumn(), getOldValue(), getNewValue());
+
+      ActionEvent event = new ActionEvent(
+        tcl,
+        ActionEvent.ACTION_PERFORMED,
+        "");
+      action.actionPerformed(event);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench-common-activities/blob/163747de/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/xmltree/XPathActivityXMLTree.java
----------------------------------------------------------------------
diff --git 
a/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/xmltree/XPathActivityXMLTree.java
 
b/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/xmltree/XPathActivityXMLTree.java
new file mode 100644
index 0000000..e6b2e9f
--- /dev/null
+++ 
b/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/xmltree/XPathActivityXMLTree.java
@@ -0,0 +1,572 @@
+package org.apache.taverna.activities.xpath.ui.config.xmltree;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JLabel;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.JTree;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.text.Position;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreePath;
+
+import 
org.apache.taverna.activities.xpath.ui.config.XPathActivityConfigurationPanel;
+import 
org.apache.taverna.activities.xpath.ui.servicedescription.XPathActivityIcon;
+
+import org.dom4j.Attribute;
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.DocumentHelper;
+import org.dom4j.Element;
+import org.dom4j.Namespace;
+import org.dom4j.QName;
+import org.dom4j.XPath;
+
+
+/**
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class XPathActivityXMLTree extends JTree
+{
+  private XPathActivityXMLTree instanceOfSelf;
+  private XPathActivityXMLTreeRenderer treeRenderer;
+  
+  private JPopupMenu contextualMenu;
+  
+  private TreeSelectionListener[] allSelectionListeners;
+  private XPathActivityXMLTreeSelectionHandler xmlTreeSelectionHandler;
+  
+  /**
+   * 
+   */
+  private XPathActivityConfigurationPanel parentConfigPanel;
+
+  private Document documentUsedToPopulateTree;
+  
+  /**
+   *  holds value of the current XPath expression obtained from 
+   *  the combination of nodes selected in the XML tree 
+   */
+  private XPath currentXPathExpression;
+  
+  private Map<String,String> currentXPathNamespaces;
+  
+  
+  
+  private XPathActivityXMLTree(XPathActivityXMLTreeNode root, Document 
documentUsedToPopulateTree, 
+      boolean bIncludeElementValues, boolean bIncludeElementNamespaces, 
XPathActivityConfigurationPanel parentConfigPanel)
+  {
+    super(root);
+    
+    this.instanceOfSelf = this;
+    this.allSelectionListeners = new TreeSelectionListener[0];
+    
+    this.parentConfigPanel = parentConfigPanel;
+    
+    this.documentUsedToPopulateTree = documentUsedToPopulateTree;
+    this.currentXPathExpression = null;
+    this.currentXPathNamespaces = new HashMap<String,String>();
+    this.prepopulateNamespaceMap();
+    
+    
+    // custom renderer of the nodes in the XML tree
+    this.treeRenderer = new 
XPathActivityXMLTreeRenderer(bIncludeElementValues, bIncludeElementNamespaces);
+    this.setCellRenderer(treeRenderer);
+    
+    
+    // add listener to handle various selections of nodes in the tree 
+    this.xmlTreeSelectionHandler = new 
XPathActivityXMLTreeSelectionHandler(parentConfigPanel, this);
+    this.addTreeSelectionListener(xmlTreeSelectionHandler);
+    
+    
+    // --- CONTEXTUAL MENU FOR EXPANDING / COLLAPSING THE TREE ---
+    
+    // create popup menu for expanding / collapsing all nodes in the tree
+    JMenuItem miExpandAll = new JMenuItem("Expand all", 
XPathActivityIcon.getIconById(XPathActivityIcon.XML_TREE_EXPAND_ALL_ICON));
+    miExpandAll.setToolTipText("Expand all nodes in the filtering tree");
+    miExpandAll.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        for (int i = 0; i < getRowCount(); i++) {
+          instanceOfSelf.expandRow(i);
+        }
+      }
+    });
+    JMenuItem miCollapseAll = new JMenuItem("Collapse all", 
XPathActivityIcon.getIconById(XPathActivityIcon.XML_TREE_COLLAPSE_ALL_ICON));
+    miCollapseAll.setToolTipText("Collapse all expanded nodes in the filtering 
tree");
+    miCollapseAll.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        for (int i = getRowCount() - 1; i >= 0; i--) {
+          instanceOfSelf.collapseRow(i);
+        }
+      }
+    });
+    
+    // populate the popup menu with created menu items
+    contextualMenu = new JPopupMenu();
+    contextualMenu.add(miExpandAll);
+    contextualMenu.add(miCollapseAll);
+    
+    // mouse events may cause the contextual menu to be shown - adding a 
listener
+    this.addMouseListener(new MouseAdapter()
+    {
+      public void mousePressed(MouseEvent e) {
+        if (e.isPopupTrigger()) {
+          contextualMenu.show(instanceOfSelf, e.getX(), e.getY());
+        }
+      }
+      public void mouseReleased(MouseEvent e) {
+        if (e.isPopupTrigger()) {
+          // another way a popup menu may be called on different systems
+          contextualMenu.show(instanceOfSelf, e.getX(), e.getY());
+        }
+      }
+    });
+    
+  }
+  
+  
+  /**
+   * Pre-populates namespace map with the namespaced declared in the root
+   * node of the XML document, which was used to populate the tree.
+   */
+  private void prepopulateNamespaceMap()
+  {
+    Document doc = this.getDocumentUsedToPopulateTree();
+    Element root = doc.getRootElement();
+    
+    // get opening tag of the root node
+    String rootAsXML = root.asXML().substring(0, root.asXML().indexOf(">"));
+    
+    // split the opening tag into tokens (all attributes are separated by a 
space)
+    String[] rootTokens = rootAsXML.split(" ");
+    
+    // for each attribute check if that's a namespace declaration
+    for (String token : rootTokens) {
+      if (token.startsWith("xmlns"))
+      {
+        String[] namespacePrefixAndURI = token.split("=");
+        
+        // a prefix is either given explicitly, or an empty one will be used
+        String prefix = namespacePrefixAndURI[0].indexOf(":") == -1 ?
+                        "" :
+                        namespacePrefixAndURI[0].split(":")[1];
+        
+        // URI is the value of the XML attribute, so need to strip out 
surrounding quotes
+        String URI = namespacePrefixAndURI[1].replaceAll("\"", "");
+        
+        // now add the details of the current namespace to the map
+        this.addNamespaceToXPathMap(new Namespace(prefix, URI));
+      }
+    }
+  }
+
+
+  protected XPathActivityConfigurationPanel getParentConfigPanel() {
+    return parentConfigPanel;
+  }
+  
+  public XPathActivityXMLTreeSelectionHandler getXMLTreeSelectionHandler() {
+    return xmlTreeSelectionHandler;
+  }
+  
+  public Document getDocumentUsedToPopulateTree() {
+    return documentUsedToPopulateTree;
+  }
+  
+  public XPath getCurrentXPathExpression() {
+    return currentXPathExpression;
+  }
+  protected void setCurrentXPathExpression(XPath xpathExpression) {
+    this.currentXPathExpression = xpathExpression;
+  }
+  
+  
+  public Map<String,String> getCurrentXPathNamespaces() {
+    return currentXPathNamespaces;
+  }
+  
+  
+  
+  protected void removeAllSelectionListeners()
+  {
+    this.allSelectionListeners = this.getTreeSelectionListeners();
+    for (TreeSelectionListener listener : this.allSelectionListeners) {
+      this.removeTreeSelectionListener(listener);
+    }
+  }
+  
+  protected void restoreAllSelectionListeners()
+  {
+    for (TreeSelectionListener listener : this.allSelectionListeners) {
+      this.addTreeSelectionListener(listener);
+    }
+  }
+  
+  
+  
+  /**
+   * Creates an instance of the XML tree from provided XML data.
+   * 
+   * @param xmlData XML document in the form of a <code>String</code> to
+   *        derive the tree from.
+   * @param bIncludeAttributesIntoTree
+   * @param bIncludeValuesIntoTree
+   * @param bIncludeElementNamespacesIntoTree
+   * @param parentConfigPanel
+   * @return
+   * @throws DocumentException if <code>xmlData</code> does not
+   *                           contain a valid XML document. 
+   * 
+   */
+  public static XPathActivityXMLTree createFromXMLData(String xmlData, boolean 
bIncludeAttributesIntoTree,
+                   boolean bIncludeValuesIntoTree, boolean 
bIncludeElementNamespacesIntoTree,
+                   XPathActivityConfigurationPanel parentConfigPanel) throws 
DocumentException
+  {
+    // ----- XML DOCUMENT PARSING -----
+    // try to parse the XML document - the next line will throw an exception if
+    // the document is not well-formed; proceed otherwise
+    Document doc = DocumentHelper.parseText(xmlData);
+    Element rootElement = doc.getRootElement();
+    
+    
+    // ----- POPULATE XML TREE -----
+    XPathActivityXMLTreeElementNode rootNode = new 
XPathActivityXMLTreeElementNode(rootElement);
+    populate(rootNode, rootElement, bIncludeAttributesIntoTree);
+    
+    return (new XPathActivityXMLTree(rootNode, doc, bIncludeValuesIntoTree, 
bIncludeElementNamespacesIntoTree, parentConfigPanel));
+  }
+  
+  
+  /**
+   * Worker method for populating the tree recursively from a list of Elements.
+   * 
+   * @param node
+   * @param element
+   */
+  private static void populate(DefaultMutableTreeNode node, Element element,
+                               boolean bIncludeAttributesIntoTree)
+  {
+    Iterator<Element> elementIterator = element.elements().iterator();
+    while (elementIterator.hasNext()) {
+      Element childElement = elementIterator.next();
+      XPathActivityXMLTreeElementNode childNode = new 
XPathActivityXMLTreeElementNode(childElement);
+      node.add(childNode);
+      
+      // recursively repeat for all children of the current child element
+      populate(childNode, childElement, bIncludeAttributesIntoTree);
+    }
+    
+    
+    // add attributes of the element as its children, if necessary
+    if (bIncludeAttributesIntoTree) {
+      List<Attribute> attributes = element.attributes();
+      for (Attribute attribute : attributes) {
+        node.add(new XPathActivityXMLTreeAttributeNode(attribute));
+      }
+    }
+  }
+  
+  
+  // ---------------- RESPONDING TO REQUESTS TO CHANGE APPEARANCE OF EXISTING 
TREE -----------------
+  
+  /**
+   * NB! May be inefficient, as this solution re-generates the whole tree from
+   *     stored XML document and replaces the root node of itself with a newly
+   *     generated root node (that will be populated with updated children,
+   *     according to the new values of options).
+   *  
+   *     However, this is a simple solution that will work for now.
+   * 
+   * @param bIncludeAttributes
+   * @param bIncludeValues
+   * @param bIncludeNamespaces
+   */
+  public void refreshFromExistingDocument(boolean bIncludeAttributes, boolean 
bIncludeValues, boolean bIncludeNamespaces)
+  {
+    this.setEnabled(false);
+    removeAllSelectionListeners();
+    
+    // store expansion and selection state of the XML tree
+    // see documentation for restoreExpandedPaths() for more details
+    //
+    // stored paths to expanded nodes are quite reliable, as paths are 
recorded;
+    // stored selected rows are less reliable, as only indices are kept -- 
however,
+    // the tree is re-created from the same document, so ordering/number of 
nodes
+    // cannot change (apart from attributes that may be added / removed - the 
attributes
+    // appear after other child nodes of some node in the tree, therefore only 
their
+    // selection could be affected)
+    HashMap<String,ArrayList<String>> toExpand = new 
HashMap<String,ArrayList<String>>();
+    ArrayList<Integer> toSelect = new ArrayList<Integer>();
+    for( int i = 1; i < this.getRowCount(); i++) {
+      if( this.isExpanded(i) ) {
+        TreePath path = this.getPathForRow(i);
+        String parentPath = path.getParentPath().toString();
+        ArrayList<String> values = toExpand.get(parentPath);
+        if(values == null) {
+          values = new ArrayList<String>();
+        }
+        values.add(path.getLastPathComponent().toString());
+        toExpand.put(parentPath, values);
+      }
+      if (this.isRowSelected(i)) {
+        toSelect.add(i);
+      }
+    }
+    
+    
+    // update presentation options
+    this.treeRenderer.setIncludeElementValues(bIncludeValues);
+    this.treeRenderer.setIncludeElementNamespaces(bIncludeNamespaces);
+    
+    // re-create the root node of the tree and replace the old one with it
+    Element rootElement = this.documentUsedToPopulateTree.getRootElement();
+    XPathActivityXMLTreeNode newRootNode = new 
XPathActivityXMLTreeElementNode(rootElement);
+    populate(newRootNode, rootElement, bIncludeAttributes);
+    ((DefaultTreeModel)this.getModel()).setRoot(newRootNode);
+    
+    
+    // restore previous state of the tree from saved values
+    restoreExpandedPaths(toExpand, this.getPathForRow(0));
+    restoreSelectedPaths(toSelect);
+    
+    this.restoreAllSelectionListeners();
+    this.setEnabled(true);
+  }
+  
+  
+  /**
+   * This method can only reliably work when the tree is re-generated from the 
same
+   * XML document, so that number / order of nodes would not change.
+   * 
+   * @param toSelect List of indices of rows to re-select after tree was 
re-generated.
+   */
+  private void restoreSelectedPaths(ArrayList<Integer> toSelect)
+  {
+    if (toSelect == null || toSelect.isEmpty()) return;
+    
+    // something definitely needs to be selected, so include root element into 
selection
+    this.addSelectionRow(0);
+    
+    // select all stored rows
+    for (Integer value : toSelect) {
+      this.addSelectionRow(value);
+    }
+  }
+
+
+
+  /**
+   * Taken from: <a 
href="http://java.itags.org/java-core-gui-apis/58504/";>http://java.itags.org/java-core-gui-apis/58504/</a>
+   * 
+   * This method recursively expands all previously stored paths.
+   * Works under assumption that the name of the root node did not change.
+   * Otherwise, it can handle changed structure of the tree.
+   * 
+   * To achieve its goal, it cannot simply use stored TreePath from your the 
original tree,
+   * since the paths are invalid after the tree is refreshed. Instead, a 
HashMap which links
+   * a String representation of the parent tree path to all expanded child 
node names is used.
+   * 
+   * @param toExpand Map which links a String representation of the parent 
tree path to all
+   *                 expanded child node names is used.
+   * @param rootPath Path to root node.
+   */
+  void restoreExpandedPaths(HashMap<String,ArrayList<String>> toExpand, 
TreePath rootPath)
+  {
+    ArrayList<String> values = toExpand.remove(rootPath.toString());
+    if (values == null) return;
+    
+    int row = this.getRowForPath(rootPath);
+    for (String value : values)
+    {
+      TreePath nextMatch = this.getNextMatch(value, row, 
Position.Bias.Forward);
+      this.expandPath(nextMatch);
+      if (toExpand.containsKey(nextMatch.toString())) {
+        restoreExpandedPaths(toExpand, nextMatch);
+      }
+    }
+  }
+  
+  
+  
+  // ---------------- TREE SELECTION MODEL + XPath GENERATION -----------------
+  
+  
+  protected String generateXPathFromTreePath(TreePath path)
+  {
+    StringBuilder xpath = new StringBuilder();
+    
+    for (String leg : generateXPathFromTreePathAsLegList(path)) {
+      xpath.append(leg);
+    }
+    
+    return (xpath.toString());
+  }
+  
+  
+  protected List<String> generateXPathFromTreePathAsLegList(TreePath path)
+  {
+    List<String> pathLegs = new LinkedList<String>();
+    
+    TreePath parentPath = path;
+    for (int i = 0; i < path.getPathCount(); i++)
+    {
+      XPathActivityXMLTreeNode lastXMLTreeNodeInThisPath = 
(XPathActivityXMLTreeNode)parentPath.getLastPathComponent();
+      pathLegs.add(0, 
this.getXMLTreeNodeEffectiveQualifiedNameAsXPathLeg(lastXMLTreeNodeInThisPath));
+      
+      parentPath = parentPath.getParentPath();
+    }
+    
+    return (pathLegs);
+  }
+  
+  
+  protected String 
getXMLTreeNodeEffectiveQualifiedNameAsXPathLeg(XPathActivityXMLTreeNode node)
+  {
+    QName qname = node.getNodeQName();
+    String effectiveNamespacePrefix = 
addNamespaceToXPathMap(qname.getNamespace());
+    
+    return("/" +
+           (node.isAttribute() ? "@" : "") +
+           (effectiveNamespacePrefix.length() > 0 ? (effectiveNamespacePrefix 
+ ":") : "") +
+           qname.getName());
+  }
+  
+  
+  
+  private String addNamespaceToXPathMap(Namespace namespace) 
+  {
+    // EMTPY PREFIX
+    if (namespace.getPrefix().length() == 0) {
+      if (namespace.getURI().length() == 0) {
+        // DEFAULT NAMESPACE with no URI - nothing to worry about
+        return "";
+      }
+      else {
+        // DEFAULT NAMESPACE WITH NO PREFIX, BUT URI IS KNOWN
+        return (addNamespaceToXPathMap(new Namespace("default", 
namespace.getURI())));
+      }
+    }
+    
+    // NEW NON-EMPTY PREFIX
+    if (!this.currentXPathNamespaces.containsKey(namespace.getPrefix())) {
+      this.currentXPathNamespaces.put(namespace.getPrefix(), 
namespace.getURI());
+      return (namespace.getPrefix());
+    }
+    
+    // EXISTING NON-EMPTY PREFIX AND THE SAME URI - NO NEED TO ADD AGAIN
+    else if 
(this.currentXPathNamespaces.get(namespace.getPrefix()).equals(namespace.getURI()))
 {
+      return (namespace.getPrefix());
+    }
+    
+    // EXISTING NON-EMPTY PREFIX, BUT DIFFERENT URI
+    else {
+      String repeatedPrefix = namespace.getPrefix();
+      
+      int i = 0;
+      while (this.currentXPathNamespaces.containsKey(repeatedPrefix + i)) {
+        // check if current alternative prefix wasn't yet applied to current 
URI
+        if (this.currentXPathNamespaces.get(repeatedPrefix + 
i).equals(namespace.getURI())) {
+          return (repeatedPrefix + i);
+        }
+        else {
+          // still another URI for the same prefix, keep trying to increase 
the ID in the prefix
+          i++;
+        }
+      }
+      
+      String modifiedPrefix = repeatedPrefix + i;
+      this.currentXPathNamespaces.put(modifiedPrefix, namespace.getURI());
+      return (modifiedPrefix);
+    }
+  }
+  
+  
+  // ----------------------- Tree Cell Renderer --------------------------
+  
+  /**
+   * 
+   * @author Sergejs Aleksejevs
+   */
+  private class XPathActivityXMLTreeRenderer extends DefaultTreeCellRenderer
+  {
+    private boolean bIncludeElementValues;
+    private boolean bIncludeElementNamespaces;
+    
+    public XPathActivityXMLTreeRenderer(boolean bIncludeElementValues, boolean 
bIncludeElementNamespaces) {
+      super();
+      this.bIncludeElementValues = bIncludeElementValues;
+      this.bIncludeElementNamespaces = bIncludeElementNamespaces;
+    }
+    
+    
+    public boolean getIncludeElementValues() {
+      return bIncludeElementValues;
+    }
+    public void setIncludeElementValues(boolean bIncludeElementValues) {
+      this.bIncludeElementValues = bIncludeElementValues;
+    }
+    
+    public boolean getIncludeElementNamespaces() {
+      return bIncludeElementNamespaces;
+    }
+    public void setIncludeElementNamespaces(boolean bIncludeElementNamespaces) 
{
+      this.bIncludeElementNamespaces = bIncludeElementNamespaces;
+    }
+    
+    
+    public Component getTreeCellRendererComponent(JTree tree, Object value,
+        boolean selected, boolean expanded, boolean leaf, int row,
+        boolean hasFocus)
+    {
+      // obtain the default rendering, we'll then customize it
+      Component defaultRendering = super.getTreeCellRendererComponent(tree, 
value, selected, expanded, leaf, row, hasFocus);
+      
+      // it is most likely that the default rendering will be a JLabel, check 
just to be safe
+      if (defaultRendering instanceof JLabel)
+      {
+        JLabel defaultRenderedLabel = ((JLabel)defaultRendering);
+        
+        // ---------- CHOOSE APPROPRIATE ICON FOR THE NODE ------------
+        if (row == 0) {
+          // set the icon for the XML tree root node
+          
defaultRenderedLabel.setIcon(XPathActivityIcon.getIconById(XPathActivityIcon.XML_TREE_ROOT_ICON));
+        }
+        else {
+          // set the icon for the XML tree node
+          if (value instanceof XPathActivityXMLTreeNode && 
+              ((XPathActivityXMLTreeNode)value).isAttribute())
+          {
+            
defaultRenderedLabel.setIcon(XPathActivityIcon.getIconById(XPathActivityIcon.XML_TREE_ATTRIBUTE_ICON));
+          }
+          else {
+            
defaultRenderedLabel.setIcon(XPathActivityIcon.getIconById(XPathActivityIcon.XML_TREE_NODE_ICON));
+          }
+        }
+        
+        
+        // ----------- CHOOSE THE DISPLAY TITLE FOR THE NODE ------------
+        if (value instanceof XPathActivityXMLTreeNode) {
+          
defaultRenderedLabel.setText(((XPathActivityXMLTreeNode)value).getTreeNodeDisplayLabel(
+              this.bIncludeElementValues, this.bIncludeElementNamespaces, 
true));
+        }
+      }
+      
+      return (defaultRendering);
+    }
+    
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench-common-activities/blob/163747de/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/xmltree/XPathActivityXMLTreeAttributeNode.java
----------------------------------------------------------------------
diff --git 
a/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/xmltree/XPathActivityXMLTreeAttributeNode.java
 
b/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/xmltree/XPathActivityXMLTreeAttributeNode.java
new file mode 100644
index 0000000..4a37839
--- /dev/null
+++ 
b/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/xmltree/XPathActivityXMLTreeAttributeNode.java
@@ -0,0 +1,50 @@
+package org.apache.taverna.activities.xpath.ui.config.xmltree;
+
+import org.dom4j.Attribute;
+
+/**
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class XPathActivityXMLTreeAttributeNode extends XPathActivityXMLTreeNode
+{
+  private Attribute associatedAttribute;
+  
+  public XPathActivityXMLTreeAttributeNode(Attribute associatedAttribute) {
+    super(associatedAttribute, true);
+    this.associatedAttribute = associatedAttribute;
+  }
+  
+  public Attribute getAssociatedAttribute() {
+    return associatedAttribute;
+  }
+  
+  public String getTreeNodeDisplayLabel(boolean bIncludeValue, boolean 
bUseStyling)
+  {
+    StringBuilder label = new StringBuilder();
+    
+    // add qualified attribute name (possibly) with styling
+    label.append((bUseStyling ? "<font color=\"purple\">" : "") +
+                 this.associatedAttribute.getQualifiedName() +
+                 (bUseStyling ? "</font>" : ""));
+    
+    // add attribute value
+    if (bIncludeValue)
+    {
+      String attributeTextValue = this.associatedAttribute.getText();
+      
+      if (attributeTextValue != null && attributeTextValue.length() > 0) {
+        label.append((bUseStyling ? "<font color=\"gray\"> - </font><font 
color=\"green\">" : "") +
+                     
truncateElementTextValue(stripAllHTML(attributeTextValue)) +
+                     (bUseStyling ? "</font>" : ""));
+      }
+    }
+    
+    if (bUseStyling) {
+      label.insert(0, "<html>");
+      label.append("</html>");
+    }
+    
+    return (label.toString());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench-common-activities/blob/163747de/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/xmltree/XPathActivityXMLTreeElementNode.java
----------------------------------------------------------------------
diff --git 
a/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/xmltree/XPathActivityXMLTreeElementNode.java
 
b/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/xmltree/XPathActivityXMLTreeElementNode.java
new file mode 100644
index 0000000..cedcc95
--- /dev/null
+++ 
b/taverna-xpath-activity-ui/src/main/java/org/apache/taverna/activities/xpath/ui/config/xmltree/XPathActivityXMLTreeElementNode.java
@@ -0,0 +1,62 @@
+package org.apache.taverna.activities.xpath.ui.config.xmltree;
+
+import org.dom4j.Element;
+import org.dom4j.Namespace;
+
+
+/**
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class XPathActivityXMLTreeElementNode extends XPathActivityXMLTreeNode
+{
+  private Element associatedElement;
+  
+  public XPathActivityXMLTreeElementNode(Element associatedElement) {
+    super(associatedElement, false);
+    this.associatedElement = associatedElement;
+  }
+  
+  public Element getAssociatedElement() {
+    return associatedElement;
+  }
+  
+  public String getTreeNodeDisplayLabel(boolean bIncludeValue, boolean 
bIncludeNamespace, boolean bUseStyling)
+  {
+    StringBuilder label = new StringBuilder();
+    
+    // add qualified element name
+    label.append(this.associatedElement.getQualifiedName());
+    
+    // add element namespace
+    if (bIncludeNamespace)
+    {
+      Namespace ns = this.associatedElement.getNamespace();
+      
+      label.append((bUseStyling ? "<font color=\"gray\">" : "") +
+          " - xmlns" + (ns.getPrefix().length() > 0 ? (":" + ns.getPrefix()) : 
"") + "=\"" + 
+          this.associatedElement.getNamespaceURI() +
+          (bUseStyling ? "\"</font>" : ""));
+    }
+    
+    // add element value
+    if (bIncludeValue)
+    {
+      String elementTextValue = this.associatedElement.getTextTrim();
+      
+      if (elementTextValue != null && elementTextValue.length() > 0) {
+        label.append((bUseStyling ? "<font color=\"gray\"> - </font><font 
color=\"blue\">" : "") +
+                     truncateElementTextValue(stripAllHTML(elementTextValue)) +
+                     (bUseStyling ? "</font>" : ""));
+      }
+    }
+    
+    if (bUseStyling) {
+      label.insert(0, "<html>");
+      label.append("</html>");
+    }
+    
+    return (label.toString());
+  }
+  
+}
\ No newline at end of file

Reply via email to