/* * 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); } }