/*
* DSS - Digital Signature Services
*
* Copyright (C) 2013 European Commission, Directorate-General Internal Market and Services (DG MARKT), B-1049 Bruxelles/Brussel
*
* Developed by: 2013 ARHS Developments S.A. (rue Nicolas Bové 2B, L-1253 Luxembourg) http://www.arhs-developments.com
*
* This file is part of the "DSS - Digital Signature Services" project.
*
* "DSS - Digital Signature Services" is free software: you can redistribute it and/or modify it under the terms of
* the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the
* License, or (at your option) any later version.
*
* DSS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* "DSS - Digital Signature Services". If not, see <http://www.gnu.org/licenses/>.
*/
package eu.europa.ec.markt.dss.applet.view.validationpolicy;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.*;
import javax.swing.event.TreeModelEvent;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreePath;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
import eu.europa.ec.markt.dss.DSSUtils;
import eu.europa.ec.markt.dss.applet.component.model.XmlDomAdapterNode;
import eu.europa.ec.markt.dss.applet.component.model.XsdNode;
import eu.europa.ec.markt.dss.applet.component.model.XsdNodeCardinality;
import eu.europa.ec.markt.dss.applet.component.model.XsdNodeType;
import eu.europa.ec.markt.dss.applet.component.model.validation.ValidationPolicyTreeModel;
import eu.europa.ec.markt.dss.applet.component.model.validation.XMLTreeCellRenderer;
import eu.europa.ec.markt.dss.applet.model.ValidationPolicyModel;
import eu.europa.ec.markt.dss.applet.util.ComponentFactory;
import eu.europa.ec.markt.dss.applet.util.ResourceUtils;
import eu.europa.ec.markt.dss.applet.wizard.validationpolicy.ValidationPolicyWizardController;
import eu.europa.ec.markt.dss.commons.swing.mvc.applet.AppletCore;
import eu.europa.ec.markt.dss.commons.swing.mvc.applet.wizard.WizardView;
import eu.europa.ec.markt.dss.validation102853.xml.XmlDom;
/**
* TODO
* <p/>
* <p/>
* DISCLAIMER: Project owner DG-MARKT.
*
* @author <a href="mailto:dgmarkt.Project-DSS@arhs-developments.com">ARHS Developments</a>
* @version $Revision: 1016 $ - $Date: 2011-06-17 15:30:45 +0200 (Fri, 17 Jun 2011) $
*/
public class EditView extends WizardView<ValidationPolicyModel, ValidationPolicyWizardController> {
private JTree validationPolicyTree;
private JScrollPane scrollPane;
private ValidationPolicyTreeModel validationPolicyTreeModel;
final TreeCellRenderer treeCellRenderer = new XMLTreeCellRenderer();
/**
* The default constructor for EditView.
*
* @param core
* @param controller
* @param model
*/
public EditView(AppletCore core, ValidationPolicyWizardController controller, ValidationPolicyModel model) {
super(core, controller, model);
validationPolicyTree = ComponentFactory.tree("tree", null, treeCellRenderer);
scrollPane = ComponentFactory.createScrollPane(validationPolicyTree);
}
@Override
public void doInit() {
validationPolicyTreeModel = new ValidationPolicyTreeModel(getModel().getValidationPolicy());
validationPolicyTree = ComponentFactory.tree("tree", validationPolicyTreeModel, treeCellRenderer);
scrollPane = ComponentFactory.createScrollPane(validationPolicyTree);
registerMouseListener(validationPolicyTree);
}
/**
* fully expand the tree
*
* @param tree
*/
private void expandTree(JTree tree) {
// expand all
for (int i = 0; i < tree.getRowCount(); i++) {
tree.expandRow(i);
}
}
private void registerMouseListener(final JTree tree) {
MouseListener mouseAdapter = new MouseAdapter() {
public void mousePressed(MouseEvent mouseEvent) {
if (mouseEvent.getButton() == MouseEvent.BUTTON3) {
final int selectedRow = tree.getRowForLocation(mouseEvent.getX(), mouseEvent.getY());
final TreePath selectedPath = tree.getPathForLocation(mouseEvent.getX(), mouseEvent.getY());
if (selectedRow != -1) {
final XmlDomAdapterNode clickedItem = (XmlDomAdapterNode) selectedPath.getLastPathComponent();
final boolean isLeaf = tree.getModel().isLeaf(selectedPath.getLastPathComponent());
// Do nothing on root element
if (selectedPath.getPathCount() > 1) {
// find the allowed actions, to know if a popup menu should be displayed and the content of the popup menu + action handlers
if (clickedItem.node instanceof Element) {
nodeActionAdd(mouseEvent, selectedRow, selectedPath, clickedItem, tree);
} else if (isLeaf) {
valueLeafActionEdit(mouseEvent, selectedPath, clickedItem, tree);
}
}
}
}
}
};
tree.addMouseListener(mouseAdapter);
}
String getXPath(Node node) {
Node parent = node.getParentNode();
if (parent == null || parent instanceof Document) {
return node.getNodeName();
}
return getXPath(parent) + "/" + node.getNodeName();
}
/**
* @param element
* @param xsdTree
* @return The list of XsdNode that are possible to add as childs to this node
*/
private List<XsdNode> getChildrenToAdd(Element element, Map<XsdNode, Object> xsdTree) {
String xPathClickedItem = getXPath(element);
List<XsdNode> result = new ArrayList<XsdNode>();
//Get children
Map<XsdNode, Object> childrenMap = getChild(xPathClickedItem, xsdTree);
//We found some children
if (childrenMap != null) {
for (Map.Entry<XsdNode, Object> entry : childrenMap.entrySet()) {
final XsdNode xsdNode = entry.getKey();
final String xmlName = xsdNode.getLastNameOfPath();
XsdNode xsdNodeAddable = null;
final boolean elementExists;
if (xsdNode.getType() == XsdNodeType.ATTRIBUTE) {
//Check if this attribute is already present
final String attribute = element.getAttribute(xmlName);
if (DSSUtils.isEmpty(attribute)) {
elementExists = false;
xsdNodeAddable = xsdNode;
} else {
elementExists = true;
}
} else if (xsdNode.getType() == XsdNodeType.ELEMENT) {
if (xsdNode.getCardinality() == XsdNodeCardinality.ONCE_EXACTLY || xsdNode.getCardinality() == XsdNodeCardinality.ONCE_OPTIONALY) {
// check if this item already exist as a child of this item. If not, it can be added.
elementExists = getModel().getValidationPolicy().getXmlDom().exists(xsdNode.getName());
if (!elementExists) {
xsdNodeAddable = xsdNode;
}
} else {
// multiple element, we can add more of it
elementExists = false;
xsdNodeAddable = xsdNode;
}
} else if (xsdNode.getType() == XsdNodeType.TEXT) {
final XmlDom xmlDomElement = new XmlDom(element);
if (element != null && xmlDomElement.getText() != null && xmlDomElement.getText().length() > 0) {
elementExists = true;
} else {
elementExists = false;
xsdNodeAddable = xsdNode;
}
} else {
throw new IllegalArgumentException("Unknown type " + xsdNode.getType());
}
if (!elementExists && xsdNodeAddable != null) {
result.add(xsdNodeAddable);
}
}
}
return result;
}
private Map<XsdNode, Object> getChild(String xPath, Map<XsdNode, Object> xsdTree) {
final Set<Map.Entry<XsdNode, Object>> entries = xsdTree.entrySet();
for (final Map.Entry<XsdNode, Object> entry : entries) {
if (xPath.startsWith(entry.getKey().getName())) {
if (xPath.equals(entry.getKey().getName())) {
return (Map<XsdNode, Object>) entry.getValue();
} else {
return getChild(xPath, (Map<XsdNode, Object>) entry.getValue());
}
}
}
return null;
}
private void nodeActionAdd(MouseEvent mouseEvent, final int selectedRow, final TreePath selectedPath, final XmlDomAdapterNode clickedItem, final JTree tree) {
final Element clickedElement = (Element) clickedItem.node;
// popup menu for list -> add
final JPopupMenu popup = new JPopupMenu();
//delete node
final JMenuItem menuItemToDelete = new JMenuItem(ResourceUtils.getI18n("DELETE"));
menuItemToDelete.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
// find the order# of the child to delete
final int index = clickedItem.getParent().index(clickedItem);
Node oldChild = clickedElement.getParentNode().removeChild(clickedElement);
if (index > -1) {
validationPolicyTreeModel.fireTreeNodesRemoved(selectedPath.getParentPath(), index, oldChild);
}
}
});
popup.add(menuItemToDelete);
//Add node/value
final Map<XsdNode, Object> xsdTree = validationPolicyTreeModel.getXsdTree();
final List<XsdNode> children = getChildrenToAdd(clickedElement, xsdTree);
for (final XsdNode xsdChild : children) {
final String xmlName = xsdChild.getLastNameOfPath();
final JMenuItem menuItem = new JMenuItem(ResourceUtils.getI18n("ADD") + " (" + xmlName + " " + xsdChild.getType().toString().toLowerCase() + ")");
popup.add(menuItem);
menuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
Document document = getModel().getValidationPolicy().getDocument();
final Node newElement;
if (xsdChild.getType() == XsdNodeType.TEXT) {
// TEXT element always appended (last child)
newElement = clickedElement.appendChild(document.createTextNode("VALUE"));
} else if (xsdChild.getType() == XsdNodeType.ATTRIBUTE) {
newElement = document.createAttributeNS(null, xmlName);
((Attr) newElement).setValue("VALUE");
clickedElement.setAttributeNode((Attr) newElement);
} else if (xsdChild.getType() == XsdNodeType.ELEMENT) {
final Element childToAdd = document.createElementNS("http://dss.markt.ec.europa.eu/validation/diagnostic", xmlName);
// find the correct possition to add the child
// Get all allowed children
Map<XsdNode, Object> childrenMap = getChild(getXPath(clickedElement), xsdTree);
boolean toAddSeen = false;
Element elementIsToAddBeforeThisOne = null;
for (final XsdNode allowed : childrenMap.keySet()) {
if (!toAddSeen && allowed == xsdChild) {
toAddSeen = true;
continue;
}
if (toAddSeen) {
final NodeList elementsByTagNameNS = clickedElement
.getElementsByTagNameNS("http://dss.markt.ec.europa.eu/validation/diagnostic", allowed.getLastNameOfPath());
if (elementsByTagNameNS.getLength() > 0) {
// we found an element that is supposed to be after the one to add
elementIsToAddBeforeThisOne = (Element) elementsByTagNameNS.item(0);
break;
}
}
}
if (elementIsToAddBeforeThisOne != null) {
newElement = clickedElement.insertBefore(childToAdd, elementIsToAddBeforeThisOne);
} else {
newElement = clickedElement.appendChild(childToAdd);
}
} else {
throw new IllegalArgumentException("Unknow XsdNode NodeType " + xsdChild.getType());
}
document.normalizeDocument();
int indexOfAddedItem = 0;
final int childCount = clickedItem.childCount();
for (int i = 0; i < childCount; i++) {
if (clickedItem.child(i).node == newElement) {
indexOfAddedItem = i;
break;
}
}
TreeModelEvent event = new TreeModelEvent(validationPolicyTreeModel, selectedPath, new int[]{indexOfAddedItem}, new Object[]{newElement});
validationPolicyTreeModel.fireTreeNodesInserted(event);
tree.expandPath(selectedPath);
//Update model
getModel().getValidationPolicy().setXmlDom(new XmlDom(document));
}
});
}
popup.show(tree, mouseEvent.getX(), mouseEvent.getY());
}
private void valueLeafActionEdit(final MouseEvent mouseEvent, final TreePath selectedPath, final XmlDomAdapterNode clickedItem, final JTree tree) {
final JPopupMenu popup = new JPopupMenu();
// Basic type : edit
final JMenuItem menuItem = new JMenuItem(ResourceUtils.getI18n("EDIT"));
menuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
final String newValue = JOptionPane.showInputDialog(ResourceUtils.getI18n("EDIT"), clickedItem.node.getNodeValue());
if (newValue != null) {
try {
if (clickedItem.node instanceof Attr) {
((Attr) clickedItem.node).setValue(newValue);
} else {
clickedItem.node.setNodeValue(newValue);
}
//clickedItem.setNewValue(newValue);
} catch (NumberFormatException e) {
showErrorMessage(newValue, tree);
}
validationPolicyTreeModel.fireTreeChanged(selectedPath);
}
}
});
popup.add(menuItem);
popup.show(tree, mouseEvent.getX(), mouseEvent.getY());
}
private void showErrorMessage(String newValue, JTree tree) {
JOptionPane.showMessageDialog(tree, ResourceUtils.getI18n("INVALID_VALUE") + " (" + newValue + ")");
}
@Override
protected Container doLayout() {
final FormLayout layout = new FormLayout("5dlu, fill:default:grow, 5dlu", "5dlu, fill:default:grow, 5dlu");
final PanelBuilder builder = ComponentFactory.createBuilder(layout);
final CellConstraints cc = new CellConstraints();
builder.add(scrollPane, cc.xy(2, 2));
return ComponentFactory.createPanel(builder);
}
}