package com.rapidminer.operator.nio.xml;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.rapidminer.gui.tools.ExtendedJScrollPane;
import com.rapidminer.gui.tools.ExtendedJTable;
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;
/**
* This wizard steps lets the user specify the mapping of namespace URIs to namespace ids.
*
* @author Marius Helf
*
*/
public class XMLNamespaceMapWizardStep extends WizardStep {
private static final String NO_DEFAULT_NAMESPACE = "<none>";
private XMLResultSetConfiguration configuration;
private JPanel component = new JPanel(new GridBagLayout());
private JComboBox defaultNamespaceComboBox = new JComboBox();
private NamespaceMapTableModel namespaceMapModel;
private JLabel statusLabel;
public XMLNamespaceMapWizardStep(AbstractWizard parent, final XMLResultSetConfiguration configuration) {
super("importwizard.xml.namespace_mapping");
this.configuration = configuration;
// init model for namespace map and add action listener
namespaceMapModel = new NamespaceMapTableModel(null);
// fire state changed whenever the namespace map table changes
namespaceMapModel.addTableModelListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
fireStateChanged();
}
});
// add components
ExtendedJTable namespaceMapTable = new ExtendedJTable();
namespaceMapTable.setModel(namespaceMapModel);
GridBagConstraints gridConstraint = new GridBagConstraints();
gridConstraint.insets = new Insets(0, 5, 5, 5);
gridConstraint.fill = GridBagConstraints.BOTH;
gridConstraint.weightx = 1;
gridConstraint.weighty = 1;
gridConstraint.gridwidth = GridBagConstraints.REMAINDER;
component.add(new ExtendedJScrollPane(namespaceMapTable), gridConstraint);
// init default namespace controls
gridConstraint.gridwidth = 1;
gridConstraint.weightx = 0;
gridConstraint.weighty = 0;
component.add(new JLabel(I18N.getGUILabel("importwizard.xml.namespace_mapping.default_namespace")), gridConstraint);
gridConstraint.weightx = 1;
gridConstraint.gridwidth = GridBagConstraints.REMAINDER;
component.add(defaultNamespaceComboBox, gridConstraint);
statusLabel = new JLabel("");
component.add(statusLabel);
this.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
try {
configuration.getDocumentObjectModel();
} catch (OperatorException e1) {
statusLabel.setText(I18N.getGUILabel("xml_reader.wizard.cannot_load_dom"));
statusLabel.setForeground(Color.RED);
return;
}
if (namespaceMapModel.getIdNamespaceMap().size() != namespaceMapModel.getRowCount()) {
// duplicate or undefined namespace prefix
statusLabel.setText(I18N.getGUILabel("xml_reader.wizard.undefined_or_duplicate_namespace_prefix"));
statusLabel.setForeground(Color.RED);
return;
} else if (defaultNamespaceComboBox.getSelectedItem() == null) {
// no default namespace
statusLabel.setText(I18N.getGUILabel("xml_reader.wizard.undefined_default_namespace"));
statusLabel.setForeground(Color.RED);
return;
}
statusLabel.setText(I18N.getGUILabel("xml_reader.wizard.status_ok"));
statusLabel.setForeground(Color.BLACK);
}
});
}
@Override
protected JComponent getComponent() {
return component;
}
@Override
protected boolean canProceed() {
try {
return namespaceMapModel.getIdNamespaceMap().size() == namespaceMapModel.getRowCount() && defaultNamespaceComboBox.getSelectedItem() != null && configuration.getDocumentObjectModel() != null;
} catch (OperatorException e) {
// if xml document cannot be read, return false:
return false;
}
}
@Override
protected boolean canGoBack() {
return true;
}
@Override
protected boolean performEnteringAction(WizardStepDirection direction) {
if (direction != WizardStepDirection.BACKWARD) {
configuration.setNamespaceAware(true);
// get all namespaces
Element rootElement = null;
try {
rootElement = configuration.getDocumentObjectModel().getDocumentElement();
} catch (OperatorException e) {
// do nothing here, the error will be detected at the change listener triggered by
// fireStateChanged() at the end of this method
}
// get namespace mappings from document
Map<String,String> namespaceUriToIdMap = getNamespaces(rootElement);
// get mapping from configuration
Map<String,String> namespaceIdToUriMap = configuration.getNamespacesMap();
for(Entry<String,String> idToUri : namespaceIdToUriMap.entrySet()) {
namespaceUriToIdMap.put(idToUri.getValue(), idToUri.getKey());
}
// search for default namespace
String defaultNamespaceUri = null;
if (configuration.getDefaultNamespaceURI() == null) {
for(Entry<String,String> idToUri : namespaceUriToIdMap.entrySet()) {
if (idToUri.getValue() == null) {
defaultNamespaceUri = idToUri.getKey();
break;
}
}
}
namespaceMapModel.initializeData(namespaceUriToIdMap);
// init default namespace combobox
defaultNamespaceComboBox.removeAllItems();
defaultNamespaceComboBox.addItem(NO_DEFAULT_NAMESPACE);
String[] namespaces = new String[0];
namespaces = namespaceUriToIdMap.keySet().toArray(namespaces);
Arrays.sort(namespaces);
for (String namespace : namespaces) {
defaultNamespaceComboBox.addItem(namespace);
}
if (configuration.getDefaultNamespaceURI() != null) {
defaultNamespaceComboBox.setSelectedItem(configuration.getDefaultNamespaceURI());
} else if (defaultNamespaceUri != null) {
defaultNamespaceComboBox.setSelectedItem(defaultNamespaceUri);
} else {
defaultNamespaceComboBox.setSelectedItem(NO_DEFAULT_NAMESPACE);
}
fireStateChanged();
}
return true;
}
/**
* Returns a map containing all namespaces defined in element and (recursively) its child-elements as keys
* and the corresponding namespace id/prefix as value.
*
*/
protected Map<String,String> getNamespaces(Node node) {
Map<String,String> namespaceUriToIdMap = new HashMap<String, String>();
if (node == null) {
return namespaceUriToIdMap;
}
String namespace = node.getNamespaceURI();
if (namespace != null) {
String id = node.getPrefix();
if ((id != null && !id.isEmpty()) || !namespaceUriToIdMap.containsKey(namespace)) {
namespaceUriToIdMap.put(namespace, id);
}
}
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); ++i) {
Node child = children.item(i);
for (Entry<String,String> uriToId : getNamespaces(child).entrySet()) {
if ((uriToId.getValue() != null && !uriToId.getValue().isEmpty()) || !namespaceUriToIdMap.containsKey(uriToId.getKey())) {
namespaceUriToIdMap.put(uriToId.getKey(), uriToId.getValue());
}
}
}
// get namespaces from xmlns:xxx attributes
NamedNodeMap attributes = node.getAttributes();
if (attributes != null ) {
for (int i = 0; i < attributes.getLength(); ++i) {
Node attributeNode = attributes.item(i);
if (attributeNode instanceof Attr) {
Attr attribute = (Attr)attributeNode;
if (attribute.getPrefix() != null && attribute.getPrefix().equalsIgnoreCase("xmlns")) {
String id = attribute.getLocalName();
String namespaceFromAttribute = attribute.getValue();
if ((id != null && !id.isEmpty()) || !namespaceUriToIdMap.containsKey(namespaceFromAttribute)) {
namespaceUriToIdMap.put(namespaceFromAttribute, id);
}
}
}
}
}
return namespaceUriToIdMap;
}
@Override
protected boolean performLeavingAction(WizardStepDirection direction) {
configuration.setNamespacesMap(namespaceMapModel.getIdNamespaceMap());
String selectedNamespace = (String)defaultNamespaceComboBox.getSelectedItem();
if (selectedNamespace != NO_DEFAULT_NAMESPACE) {
configuration.setDefaultNamespaceURI(selectedNamespace);
} else {
configuration.setDefaultNamespaceURI(null);
}
return true;
}
}