/* * RapidMiner * * Copyright (C) 2001-2011 by Rapid-I and the contributors * * Complete list of developers available at our web site: * * http://rapid-i.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.operator.nio.xml; import java.awt.Color; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Vector; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellRenderer; import javax.swing.tree.TreeSelectionModel; import javax.xml.transform.TransformerException; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.w3c.dom.Attr; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.NodeList; import com.rapidminer.gui.tools.ColoredTableCellRenderer; import com.rapidminer.gui.tools.ExtendedJScrollPane; import com.rapidminer.gui.tools.ExtendedJTable; import com.rapidminer.gui.tools.ExtendedJTableSorterModel; import com.rapidminer.gui.tools.ResourceAction; import com.rapidminer.gui.tools.dialogs.wizards.AbstractWizard; import com.rapidminer.gui.tools.dialogs.wizards.AbstractWizard.WizardStepDirection; import com.rapidminer.gui.tools.dialogs.wizards.WizardStep; import com.rapidminer.operator.OperatorException; import com.rapidminer.tools.I18N; import com.rapidminer.tools.xml.MapBasedNamespaceContext; /** * A wizard step for selecting attributes from previously selected examples. * * Gets an XPath expression and lets the user choose attributes and text content of * XML elements matching the XPath. * * @author Marius Helf * */ public class XMLAttributeExpressionWizardStep extends WizardStep { /** * A simple subclass of {@link ExtendedJTable} specifically for use with anErrorAwareTableModel. * Prior to rendering a cell, asks its model if in that cell there exists an error and draws a * red cell background in that case. * * @author Marius Helf * */ private class ExtendedJTableWithErrorIndicator extends ExtendedJTable { private static final long serialVersionUID = 1L; /** * Returns a cell renderer with red background if the model indicates an error in that cell. * Returns the default cell renderer otherwise. * @see com.rapidminer.gui.tools.ExtendedJTable#getCellRenderer(int, int) */ @Override public TableCellRenderer getCellRenderer(int row, int col) { ColoredTableCellRenderer renderer = (ColoredTableCellRenderer)super.getCellRenderer(row, col); ErrorAwareTableModel model = (ErrorAwareTableModel)((ExtendedJTableSorterModel)getModel()).getTableModel(); if (model.hasError(row, col)) { renderer.setColor(Color.RED); } return renderer; } } /** * Interface to use with ExtendendJTableWithErrorIndicator. * * @author Marius Helf * */ private interface ErrorAwareTableModel { boolean hasError(int rowIndex, int columnIndex); } /** * Table model to provide XPath expressions to attributes and their values in a given element. * * @author Marius Helf * */ private class AttributeXPathTableModel extends AbstractTableModel implements ErrorAwareTableModel { private static final long serialVersionUID = 1L; public static final int XPATH_COLUMN = 0; public static final int VALUE_COLUMN = 1; public static final int COLUMN_COUNT = 2; /** * A List of Vectors of Strings, where each Vector represents a row. * Which value is stored in which column is defined by XPATH_COLUMN and VALUE_COLUMN. */ List<Vector<String>> xPaths = new LinkedList<Vector<String>>(); /** * Sets the XPath string for a given row. aValue must be instanceof String and is * interpreted as XPath expression. * * The XPath is evaluated, and in the value column the value of the first match * is set. * * * @see javax.swing.table.AbstractTableModel#setValueAt(java.lang.Object, int, int) */ @Override public void setValueAt(Object aValue, int rowIndex, int columnIndex) { switch (columnIndex) { case XPATH_COLUMN: Vector<String> row = xPaths.get(rowIndex); String xPath = (String)aValue; List<String> valueList = getDataForXPath(xPath); String value = null; if (valueList.size() > 0) { value = valueList.get(0); } row.add(XPATH_COLUMN, xPath); row.add(VALUE_COLUMN, value); fireTableCellUpdated(rowIndex, VALUE_COLUMN); } } @Override public int getRowCount() { if (xPaths != null) { return xPaths.size(); } return 0; } @Override public int getColumnCount() { return COLUMN_COUNT; } @Override public String getColumnName(int column) { switch (column) { case XPATH_COLUMN: return I18N.getGUILabel("importwizard.xml.attribute_expression.xpath_table.xpath_column_header"); case VALUE_COLUMN: return I18N.getGUILabel("importwizard.xml.attribute_expression.xpath_table.value_column_header"); } return ""; } @Override public Object getValueAt(int rowIndex, int columnIndex) { switch (columnIndex) { case XPATH_COLUMN: case VALUE_COLUMN: return xPaths.get(rowIndex).get(columnIndex); } return null; } @Override public boolean isCellEditable(int rowIndex, int columnIndex) { switch (columnIndex) { case XPATH_COLUMN: return true; } return false; } /** * Returns the index of the first row whose XPATH_COLUMN equals the given xPath. * @return */ public int rowIndexOfXPath(String xPath) { int idx = 0; for (Vector<String> row : xPaths) { if (row.get(XPATH_COLUMN).equals(xPath)) { return idx; } ++idx; } return -1; } public boolean containsXPath(String xPath) { return rowIndexOfXPath(xPath) >= 0; } /** * Adds an XPath to the model. Basically behaves like setValueAt(). */ public void addXPath(String xPath) { if (!containsXPath(xPath)) { Vector<String> newRow = new Vector<String>(COLUMN_COUNT); newRow.add(XPATH_COLUMN, xPath); List<String> valueList = getDataForXPath(xPath); String value = null; if (valueList.size() > 0) { value = valueList.get(0); } newRow.add(VALUE_COLUMN, value); xPaths.add(newRow); fireTableRowsInserted(xPaths.size()-1, xPaths.size()-1); attributeTableModel.update(); } } /** * @return A list of all XPaths in the model. */ public List<String> getXPaths() { List<String> result = new LinkedList<String>(); for (Vector<String> row : xPaths) { result.add(row.get(XPATH_COLUMN)); } return result; } /** * Clears the model and then adds all XPaths from attriubteXPaths to the model. * Also evaluates the XPaths and inserts the values, i.e. behaves like addXPath(). * */ public void setXPaths(List<String> attributeXPaths) { xPaths.clear(); if (attributeXPaths != null) { for (String xPath : attributeXPaths) { Vector<String> row = new Vector<String>(COLUMN_COUNT); row.add(XPATH_COLUMN, xPath); row.add(VALUE_COLUMN, null); xPaths.add(row); } updateValuesFromXPaths(); } fireTableDataChanged(); } /** * Evaluates the given XPath in the scope of the currentElement in the XMLAttributeExpressionWizardStep. * Returns the textContent of all matched Nodes as a list of strings. */ public List<String> getDataForXPath(String xPathString) { List<String> resultList = new LinkedList<String>(); XPath xPath = XPathFactory.newInstance().newXPath(); xPath.setNamespaceContext(new MapBasedNamespaceContext(configuration.getNamespacesMap(), configuration.getDefaultNamespaceURI())); try { XPathExpression xPathExpression = xPath.compile(xPathString); NodeList matchedNodes = (NodeList)xPathExpression.evaluate(currentElement, XPathConstants.NODESET); if (configuration.getXmlExampleSourceCompatibilityVersion().compareTo(XMLExampleSource.CHANGE_5_1_013_NODE_OUTPUT) > 0) { try { String resultString; resultString = XMLDomHelper.nodeListToString(matchedNodes); resultList.add(resultString); } catch (TransformerException e) { resultList.add(null); } }else { for (int i = 0; i < matchedNodes.getLength(); ++i) { // TODO operator Version beachten resultList.add(matchedNodes.item(i).getTextContent()); } } } catch (XPathExpressionException e) { return new LinkedList<String>(); } return resultList; } /** * Evaluates each XPath in this model and writes the text content of the first match into the VALUE_COLUMN. */ public void updateValuesFromXPaths() { int rowIndex = 0; for (Vector<String> row: xPaths) { String xPath = row.get(XPATH_COLUMN); List<String> valueList = getDataForXPath(xPath); String value = null; if (valueList.size() > 0) { value = valueList.get(0); } row.set(VALUE_COLUMN, value); fireTableCellUpdated(rowIndex, VALUE_COLUMN); ++rowIndex; } } /** * Returns true iff the XPath in the given row is not valid. * @see com.rapidminer.operator.nio.xml.XMLAttributeExpressionWizardStep.ErrorAwareTableModel#hasError(int, int) */ @Override public boolean hasError(int rowIndex, int columnIndex) { if (getValueAt(rowIndex, VALUE_COLUMN) == null) { return true; } return !isValidXPath(xPaths.get(rowIndex).get(XPATH_COLUMN)); } /** * Returns true if at least one XPath in the model in invalid. */ public boolean hasErrors() { for (int i = 0; i < getRowCount(); ++i) { if (hasError(i, XPATH_COLUMN)) { return true; } } return false; } /** * Removes the rows with the given indices from the model. */ public void removeRows(int[] selectedRows) { Arrays.sort(selectedRows); for (int i = selectedRows.length-1; i >= 0; --i) { int row = selectedRows[i]; xPaths.remove(row); fireTableRowsDeleted(row, row); } } } /** * A model which contains attributes and their values. * In addition to the attributes the special string TEXT_NODE_TEXT can be added, representing * not an attribute but the textContent() of an Element. * * Does not provide attributes which are already contained in the {@link AttributeXPathTableModel} of this * wizard step, since this model is used to display those attributes which the user is still able to add * to the final example set. * * @author Marius Helf * */ private class AttributeTableModel extends DefaultTableModel { private static final long serialVersionUID = 1L; public static final int ATTRIBUTE_COLUMN = 0; public static final int VALUE_COLUMN = 1; public static final String TEXT_NODE_TEXT = "text()"; /** * The element whose attributes are provided by this model. */ private Element element = null; public AttributeTableModel() { addColumn(I18N.getGUILabel("importwizard.xml.attribute_expression.attribute_table.attribute_column_header")); addColumn(I18N.getGUILabel("importwizard.xml.attribute_expression.attribute_table.value_column_header")); } /** * This model is not editable. * @see javax.swing.table.DefaultTableModel#isCellEditable(int, int) */ @Override public boolean isCellEditable(int row, int column) { return false; } /** * Rereads the values of all attributes from the current element reference of this model. * Attributes not yet in the model are added, those already present in the attributeXPathModel are deleted. */ public void update() { // clear model setRowCount(0); if (element == null) { return; } NamedNodeMap attributes = element.getAttributes(); for (int i = 0; i < attributes.getLength(); ++i) { Attr attribute = (Attr)attributes.item(i); String namespace = attribute.getNamespaceURI(); String name = attributes.item(i).getLocalName(); name = (namespace!=null)?configuration.getNamespaceId(namespace)+":"+name:name; if (!attributeXPathModel.containsXPath(getXPathForAttribute(name))) { String value = attribute.getNodeValue(); Vector<String> row = new Vector<String>(); row.add(ATTRIBUTE_COLUMN, name); row.add(VALUE_COLUMN, value); addRow(row); } } // add text() element String xPathForAttribute = getXPathForAttribute(TEXT_NODE_TEXT); if (!attributeXPathModel.containsXPath(xPathForAttribute)) { try { XPathFactory factory = XPathFactory.newInstance(); XPath xpath = factory.newXPath(); final Map<String, String> namespacesMap = configuration.getNamespacesMap(); xpath.setNamespaceContext(new MapBasedNamespaceContext(namespacesMap, configuration.getDefaultNamespaceURI())); XPathExpression expression; expression = xpath.compile("text()"); String result = (String)expression.evaluate(element, XPathConstants.STRING); Vector<String> row = new Vector<String>(); row.add(ATTRIBUTE_COLUMN, TEXT_NODE_TEXT); row.add(VALUE_COLUMN, result); addRow(row); } catch (XPathExpressionException e) { // do nothing (simply don't add text() element) } } fireTableDataChanged(); } /** * Sets the element whose attributes are provided by this model and updates the model. */ public void setElement(Element element) { this.element = element; update(); } public Element getElement() { return element; } } private XMLResultSetConfiguration configuration; /** * Displays the XML structure of the current element. */ private XMLTreeView xmlTreeView; /** * Holds the attributes of the currently selected element. */ private AttributeTableModel attributeTableModel = new AttributeTableModel(); private ExtendedJTable attributeTable = new ExtendedJTable(attributeTableModel, true); private JPanel component = new JPanel(new GridBagLayout()); /** * The list of all elements matched by the XPath provided by the previous wizard step. */ private NodeList elementMatches = null; private int currentMatchIndex = 0; private JButton nextMatchButton; private JButton previousMatchButton; private Element currentElement; /** * Displays all XPaths the user has selected to represent attributes in the resulting example set. */ private ExtendedJTableWithErrorIndicator xPathTable; /** * The model for xPathTable. */ private AttributeXPathTableModel attributeXPathModel = new AttributeXPathTableModel(); private JButton addButton; private JButton deleteFromXPathTableButton; /** * Instantiates a new XMLAttributeExpressionWizardStep and initalizes the GUI elements. */ public XMLAttributeExpressionWizardStep(AbstractWizard parent, final XMLResultSetConfiguration configuration) throws OperatorException { super("importwizard.xml.attribute_expression"); this.configuration = configuration; // only select entire rows attributeTable.setCellSelectionEnabled(false); attributeTable.setRowSelectionAllowed(true); // adding components // left panel contains XML Element view and the attribute table with the attributes of the current element. JPanel leftBarPanel = new JPanel(new GridBagLayout()); { GridBagConstraints leftBarConstraints = new GridBagConstraints(); // add previous/next buttons leftBarConstraints.fill = GridBagConstraints.BOTH; leftBarConstraints.weightx = .5; leftBarConstraints.weighty = 0; leftBarConstraints.insets = new Insets(0, 5, 5, 5); leftBarConstraints.gridwidth = 1; previousMatchButton = new JButton(); leftBarPanel.add(previousMatchButton, leftBarConstraints); nextMatchButton = new JButton(); leftBarConstraints.gridwidth = GridBagConstraints.REMAINDER; leftBarPanel.add(nextMatchButton, leftBarConstraints); // add xml tree view leftBarConstraints.insets = new Insets(5, 5, 5, 5); leftBarConstraints.weightx = 1; leftBarConstraints.weighty = 0; leftBarConstraints.gridwidth = GridBagConstraints.REMAINDER; leftBarConstraints.insets = new Insets(5, 5, 0, 5); JLabel xmlTreeLabel = new JLabel(I18N.getGUILabel("importwizard.xml.attribute_expression.xml_tree_label")); leftBarPanel.add(xmlTreeLabel, leftBarConstraints); leftBarConstraints.weighty = 0.5; leftBarConstraints.insets = new Insets(5, 5, 5, 5); xmlTreeView = new XMLTreeView(configuration.getNamespacesMap()); xmlTreeView.setShowElementIndices(true); xmlTreeView.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); leftBarPanel.add(new ExtendedJScrollPane(xmlTreeView), leftBarConstraints); // add attribute table leftBarConstraints.weighty = 0; leftBarConstraints.insets = new Insets(5, 5, 0, 5); JLabel attributeTableLabel = new JLabel(I18N.getGUILabel("importwizard.xml.attribute_expression.attribute_table_label")); leftBarPanel.add(attributeTableLabel, leftBarConstraints); leftBarConstraints.weighty = 0.5; leftBarPanel.add(new ExtendedJScrollPane(attributeTable), leftBarConstraints); // add add/remove selection button leftBarConstraints.weightx = .5; leftBarConstraints.weighty = 0; leftBarConstraints.insets = new Insets(5, 5, 0, 5); // listeners for buttons previousMatchButton.setAction(new ResourceAction(true, "importwizard.xml.attribute_expression.previous_match") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { setCurrentMatch(currentMatchIndex-1); } }); nextMatchButton.setAction(new ResourceAction(true, "importwizard.xml.attribute_expression.next_match") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { setCurrentMatch(currentMatchIndex+1); } }); attributeXPathModel.addTableModelListener(new TableModelListener() { @Override public void tableChanged(TableModelEvent e) { addButton.setEnabled(attributeTable.getSelectedRows().length > 0); attributeTableModel.update(); fireStateChanged(); } }); attributeTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { addButton.setEnabled(attributeTable.getSelectedRows().length > 0); } }); attributeTable.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { // do nothing } @Override public void keyReleased(KeyEvent e) { // do nothing } @Override public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_ENTER: addSelectedAttributes(); e.consume(); break; } } }); attributeTable.addMouseListener(new MouseListener() { @Override public void mouseClicked(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) { addSelectedAttributes(); } } @Override public void mousePressed(MouseEvent e) { // do nothing } @Override public void mouseReleased(MouseEvent e) { // do nothing } @Override public void mouseEntered(MouseEvent e) { // do nothing } @Override public void mouseExited(MouseEvent e) { // do nothing } }); // tree selection listener, which updates the attribute table on changes of the selection // in the xmlTreeView. xmlTreeView.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() { @Override public void valueChanged(TreeSelectionEvent e) { Set<Element> elements = xmlTreeView.getElementsFromSelection(); // should contain one or zero elements for (Element element : elements) { // for loop should iterate at most once attributeTableModel.setElement(element); attributeTable.getSelectionModel().setSelectionInterval(0, attributeTableModel.getRowCount()-1); } } }); xmlTreeView.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { // do nothing } @Override public void keyReleased(KeyEvent e) { // do nothing } @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { attributeTable.requestFocusInWindow(); e.consume(); } } }); xmlTreeView.addMouseListener(new MouseListener() { @Override public void mouseClicked(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) { attributeTable.requestFocusInWindow(); e.consume(); } } @Override public void mousePressed(MouseEvent e) { // do nothing } @Override public void mouseReleased(MouseEvent e) { // do nothing } @Override public void mouseEntered(MouseEvent e) { // do nothing } @Override public void mouseExited(MouseEvent e) { // do nothing } }); } // mid panel contains the add/remove buttons JPanel midBarPanel = new JPanel(new GridBagLayout()); { GridBagConstraints midBarConstraints = new GridBagConstraints(); midBarConstraints.fill = GridBagConstraints.BOTH; midBarConstraints.weightx = 0; midBarConstraints.weighty = 0; midBarConstraints.gridx = GridBagConstraints.REMAINDER; addButton = new JButton(); addButton.setAction(new ResourceAction(true, "importwizard.xml.attribute_expression.add_xpath") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { addSelectedAttributes(); } }); midBarPanel.add(addButton, midBarConstraints); deleteFromXPathTableButton = new JButton(); midBarConstraints.weighty = 0; midBarPanel.add(deleteFromXPathTableButton, midBarConstraints); deleteFromXPathTableButton.setAction(new ResourceAction(false, "importwizard.xml.attribute_expression.remove_xpath") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { int[] selectedRows = xPathTable.getSelectedRows(); // convert to model coordinates for (int i = 0; i < selectedRows.length; ++i) { selectedRows[i] = xPathTable.getModelIndex(selectedRows[i]); } attributeXPathModel.removeRows(selectedRows); } }); deleteFromXPathTableButton.setEnabled(false); } // right panel contains only the table with the selected XPath JPanel rightBarPanel = new JPanel(new GridBagLayout()); { GridBagConstraints rightBarConstraints = new GridBagConstraints(); rightBarConstraints.fill = GridBagConstraints.BOTH; rightBarConstraints.weightx = 1; rightBarConstraints.weighty = 0; rightBarConstraints.gridx = GridBagConstraints.REMAINDER; rightBarConstraints.insets = new Insets(0, 5, 5, 5); JLabel attributeXPathTableLabel = new JLabel(I18N.getGUILabel("importwizard.xml.attribute_expression.xpath_table_label")); rightBarPanel.add(attributeXPathTableLabel, rightBarConstraints); rightBarConstraints.weighty = 1; xPathTable = new ExtendedJTableWithErrorIndicator(); xPathTable.setModel(attributeXPathModel); rightBarPanel.add(new ExtendedJScrollPane(xPathTable), rightBarConstraints); // only select whole rows xPathTable.setCellSelectionEnabled(false); xPathTable.setRowSelectionAllowed(true); xPathTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { deleteFromXPathTableButton.setEnabled(xPathTable.getSelectedRowCount() > 0); } }); xPathTable.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { // do nothing } @Override public void keyReleased(KeyEvent e) { // do nothing } @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_DELETE) { int[] selectedRows = xPathTable.getSelectedRows(); // convert to model coordinates for (int i = 0; i < selectedRows.length; ++i) { selectedRows[i] = xPathTable.getModelIndex(selectedRows[i]); } attributeXPathModel.removeRows(selectedRows); e.consume(); } } }); } GridBagConstraints c = new GridBagConstraints(); c.insets = new Insets(5, 5, 5, 5); c.fill = GridBagConstraints.BOTH; c.weighty = 1d; c.weightx = 0.3d; component.add(leftBarPanel, c); c.weightx = 0; component.add(midBarPanel, c); c.gridwidth = GridBagConstraints.REMAINDER; c.weightx = 0.7d; component.add(rightBarPanel, c); } /** * Return true iff xPathString is a valid XPath, i.e. can be compiled without errors. * It is NOT checked if the XPath matches anything. */ public boolean isValidXPath(String xPathString) { if (xPathString == null) { return true; } XPath xPath = XPathFactory.newInstance().newXPath(); try { XPathExpression expression = xPath.compile(xPathString); } catch (XPathExpressionException e) { return false; } return true; } @Override protected JComponent getComponent() { return component; } /** * Initializes the models for the gui elements and makes sure that the correct button are enabled etc. * * @see com.rapidminer.gui.tools.dialogs.wizards.WizardStep#performEnteringAction(com.rapidminer.gui.tools.dialogs.wizards.AbstractWizard.WizardStepDirection) */ @Override protected boolean performEnteringAction(WizardStepDirection direction) { XPath exampleXPath = XPathFactory.newInstance().newXPath(); exampleXPath.setNamespaceContext(new MapBasedNamespaceContext(configuration.getNamespacesMap(), configuration.getDefaultNamespaceURI())); try { XPathExpression exampleExpression = exampleXPath.compile(configuration.getExampleXPath()); this.elementMatches = (NodeList) exampleExpression.evaluate(configuration.getDocumentObjectModel(), XPathConstants.NODESET); xmlTreeView.setNamespacesMap(configuration.getNamespacesMap()); setCurrentMatch(0); } catch (OperatorException e) { // should not happen, because the user can only proceed to this step if the previous // step produced a valid XPath-Expression e.printStackTrace(); } catch (XPathExpressionException e) { // should not happen, because the user can only proceed to this step if the previous // step produced a valid XPath-Expression e.printStackTrace(); } attributeXPathModel.setXPaths(configuration.getAttributeXPaths()); return true; } /** * Selects the index-th element from the list of elements matching the XPath from the configuration object * as current element and updates the models of the xml element view and the attribute view accordingly. */ private void setCurrentMatch(int index) { int length = 0; if (elementMatches != null) { length = elementMatches.getLength(); } if (index >= length-1) { index = length-1; nextMatchButton.setEnabled(false); } else { nextMatchButton.setEnabled(true); } if (index <= 0) { previousMatchButton.setEnabled(false); } else { previousMatchButton.setEnabled(true); } currentMatchIndex = index; if (index >= 0) { currentElement = (Element)elementMatches.item(index); } else { currentElement = null; } xmlTreeView.setModel(new XMLTreeModel(currentElement, false)); attributeTableModel.setElement(null); attributeXPathModel.updateValuesFromXPaths(); } /** * Writes the selected XPaths into the configuration object. * @see com.rapidminer.gui.tools.dialogs.wizards.WizardStep#performLeavingAction(com.rapidminer.gui.tools.dialogs.wizards.AbstractWizard.WizardStepDirection) */ @Override protected boolean performLeavingAction(WizardStepDirection direction) { configuration.setAttributeXPaths(attributeXPathModel.getXPaths()); return true; } @Override protected boolean canProceed() { return !attributeXPathModel.hasErrors(); } @Override protected boolean canGoBack() { return true; } /** * Returns an XPath matching the xml attribute named attribute from the currently selected element * in the xml tree view. */ private String getXPathForAttribute(String attribute) { StringBuilder builder = new StringBuilder(); builder.append(XMLDomHelper.getXPath(currentElement, attributeTableModel.getElement(), true, configuration.getNamespacesMap())); if (builder.length() > 0) { builder.append("/"); } if (attribute.equals(AttributeTableModel.TEXT_NODE_TEXT)) { builder.append("text()"); } else { builder.append("attribute::"); builder.append(attribute); } String xPath = builder.toString(); return xPath; } /** * Adds the attributes which are currently selected in the attribute table to the XPath table * and removes them from the attribute table. */ private void addSelectedAttributes() { int[] selectedRows = attributeTable.getSelectedRows(); List<String> selectedAttributes = new LinkedList<String>(); for (int i = 0; i < selectedRows.length; ++i) { int modelRow = attributeTable.getModelIndex(selectedRows[i]); String attribute = (String)attributeTableModel.getValueAt(modelRow, AttributeTableModel.ATTRIBUTE_COLUMN); selectedAttributes.add(attribute); } for (String attribute : selectedAttributes) { attributeXPathModel.addXPath(getXPathForAttribute(attribute)); } } }