/* * Licensed Materials - Property of Perforce Software, Inc. * © Copyright Perforce Software, Inc. 2014, 2021 * © Copyright IBM Corp. 2009, 2014 * © Copyright ILOG 1996, 2009 * All Rights Reserved. * * Note to U.S. Government Users Restricted Rights: * The Software and Documentation were developed at private expense and * are "Commercial Items" as that term is defined at 48 CFR 2.101, * consisting of "Commercial Computer Software" and * "Commercial Computer Software Documentation", as such terms are * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4, * as applicable. */ package xml; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.GridLayout; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; import java.net.URL; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.SwingUtilities; import javax.swing.border.TitledBorder; import javax.swing.event.TreeModelEvent; import javax.swing.event.TreeModelListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellEditor; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.xml.sax.InputSource; import ilog.views.chart.IlvChart; import ilog.views.chart.IlvChartLayout; import ilog.views.chart.IlvChartRenderer; import ilog.views.chart.IlvLegend; import ilog.views.chart.data.IlvDataSet; import ilog.views.chart.data.xml.IlvXMLDataReader; import ilog.views.chart.data.xml.IlvXMLDataSource; import ilog.views.chart.graphic.IlvMarkerFactory; import ilog.views.chart.renderer.IlvPolylineChartRenderer; import ilog.views.chart.renderer.IlvSinglePolylineRenderer; import ilog.views.util.IlvImageUtil; import ilog.views.util.xml.XMLUtil; import shared.AbstractChartExample; /** * An example that shows how to import data contained in an XML document. The * document is displayed in three different graphic representations: * <ul> * <li>As a chart of polylines</li> * <li>As a tree</li> * <li>As text</li> * </ul> */ public class XMLDemo extends AbstractChartExample { /** The name of the default file. */ static final String DEFAULT_FILE = "data.xml"; private ImageIcon attrIcon; private ImageIcon textIcon; /** The chart data source containing the data. */ IlvXMLDataSource xmlDS; /** The data reader. */ IlvXMLDataReader xmlReader; /** The TreeModel used to visualize the DOM in a tree. */ DefaultTreeModel treeModel; /** The TextArea used to visualize the document as text. */ JTextArea textArea; /** The document ID. */ String documentId; /** The document currently being viewed in the tree. */ Document document; private boolean internalUpdate = false; /** * A <code>TreeModelListener</code> used to track any modification performed * on the tree DOM. */ TreeModelListener treeModelListener = new TreeModelListener() { Override public void treeNodesChanged(TreeModelEvent evt) { handleTreeModelEvent(evt); } Override public void treeNodesInserted(TreeModelEvent evt) { treeNodesChanged(evt); } Override public void treeNodesRemoved(TreeModelEvent evt) { treeNodesChanged(evt); } Override public void treeStructureChanged(TreeModelEvent evt) { treeNodesChanged(evt); } }; /** * Initializes a new <code>XMLSample</code>. */ Override public void init(Container container) { super.init(container); container.setLayout(new GridLayout(2, 1)); try { attrIcon = new ImageIcon(IlvImageUtil.loadImageFromFile(getClass(), "attr.gif")); textIcon = new ImageIcon(IlvImageUtil.loadImageFromFile(getClass(), "text.gif")); } catch (Exception e) { System.err.println("Cannot load images. " + e.getMessage()); e.printStackTrace(); } // The absolute path of the default file is memorized to be able to // set a systemId when a new InputSource is created from a stream. URL documentURL = getClass().getResource(DEFAULT_FILE); documentId = (documentURL == null) ? null : documentURL.toString(); // ======================= Chart initialization ======================= IlvChart chart = new IlvChart(); chart.setAntiAliasing(true); // The chart data source. An IlvXMLDataSource is a specialized data source // used in association with an IlvXMLDataReader to load data from an // XML document. The document must be conform to the Perforce JViews // Charts DTD // (more information in the Reference Manual). xmlDS = new IlvXMLDataSource(); xmlReader = new IlvXMLDataReader(); xmlReader.setValidating(true); // Load the data contained in the data.xml file in the data source. loadDefaultFile(); // Each data set contained in the XML file is rendered as a polyline. IlvPolylineChartRenderer renderer = new IlvPolylineChartRenderer() { Override protected IlvChartRenderer createChild(IlvDataSet dataSet) { IlvSinglePolylineRenderer child = new IlvSinglePolylineRenderer(); child.setMarker(IlvMarkerFactory.getSquareMarker()); return child; } }; renderer.setDataSource(xmlDS); chart.addRenderer(renderer); // Add a legend to the chart on top of the chart area. IlvLegend legend = new IlvLegend(); chart.addLegend(legend, IlvChartLayout.NORTH_BOTTOM); // ======================== UI Initialization ========================= JPanel panel = new JPanel(new GridLayout(1, 2)); JPanel left = new JPanel(new BorderLayout()); // The text area is used to display the XML document as text. textArea = new JTextArea(); try { BufferedReader in = new BufferedReader(new InputStreamReader(((new URL(documentId)).openStream()))); textArea.read(in, null); in.close(); } catch (IOException e) { e.printStackTrace(); } left.add(new JScrollPane(textArea), BorderLayout.CENTER); JButton button = new JButton("Send to chart"); button.addActionListener(new ActionListener() { Override public void actionPerformed(ActionEvent evt) { sendToChart(); } }); left.add(button, BorderLayout.SOUTH); left.setBorder(new TitledBorder(null, "Text view (Edit text + \"Send\" to update)", TitledBorder.CENTER, TitledBorder.DEFAULT_POSITION)); // The JTree is used to display the DOM as a tree. treeModel = new DefaultTreeModel(new DefaultMutableTreeNode("<No document>")); Document document = xmlReader.getDocument(); fillTreeModel(document); treeModel.addTreeModelListener(treeModelListener); JTree tree = new JTree(treeModel); configureTree(tree); panel.add(left); JPanel right = new JPanel(new BorderLayout()); right.setBorder(new TitledBorder(null, "Tree view (Click on a node to edit)", TitledBorder.CENTER, TitledBorder.DEFAULT_POSITION)); right.add(new JScrollPane(tree), BorderLayout.CENTER); panel.add(right); // A button to reload the original document. button = new JButton("Reset to default data"); button.addActionListener(new ActionListener() { Override public void actionPerformed(ActionEvent evt) { loadDefaultFile(); fillTreeModel(xmlReader.getDocument()); } }); // Add the button to the chart as its footer. The footer is a JComponent // that can be added at the bottom of the chart area. chart.setFooter(button); // The header is a JComponent that can be added on the top of the chart. // As a convenience, the setHeaderText method offers an easy way to // have just a label as header: a JLabel initialized with the specified // string is automatically created and added to the chart. chart.setHeaderText("Chart view"); container.add(chart); container.add(panel); } /** * Loads the document edited in the text area in the chart. This method also * refreshes the tree view. */ protected void sendToChart() { // The contents of the text area is first saved in a StringWriter. Then // an InputSource that will read the writer buffer is created. Finally, // The InputSource is read by the data source reader to initialize the // data sets. Writer writer = new StringWriter(); try { textArea.write(writer); InputSource src = new InputSource(new StringReader(writer.toString())); src.setSystemId(documentId); xmlDS.load(src, xmlReader); // Disable the internal TreeModel listener. internalUpdate = true; // Refresh the tree according to the new document that has just been // read. fillTreeModel(xmlReader.getDocument()); internalUpdate = false; } catch (Exception e) { JOptionPane.showMessageDialog(null, "Not a valid JViews Charts XML document.", "Cannot load data !", JOptionPane.ERROR_MESSAGE); } } /** * Load the contents of the default <i>data.xml</i> file. */ protected void loadDefaultFile() { try { // Load the chart data contained in the given document. This method // parses the XML document (calling the IlvXMLDataReader.read() method) // and initializes its data sets. xmlDS.load(new InputSource(documentId), xmlReader); xmlReader.getDocument().getDoctype(); } catch (Exception e) { e.printStackTrace(); } } // ==================== JTree handling =============================== /** * Returns a string representation of the current DOM viewed in the tree. May * return <code>null</code> if the transformation failed. */ private String documentToString() { String doc = null; try { ByteArrayOutputStream writer = new ByteArrayOutputStream(); XMLUtil.WriteDocument(document, writer, document.getDoctype().getPublicId(), document.getDoctype().getSystemId(), null); doc = writer.toString(); writer.close(); } catch (IOException e) { e.printStackTrace(); } // System.err.println("doc : \n" + doc); return doc; } /** * Called when an event occurs on the <code>TreeModel</code>. This methods * loads the new DOM in the chart and text views. */ protected void handleTreeModelEvent(TreeModelEvent evt) { if (internalUpdate) return; // The new DOM is saved as a string that is used to create a reader // needed to initialize a new InputSource. This InputSource is then // loaded by the XML data source using the XML reader. String doc = documentToString(); StringReader reader = new StringReader(doc); try { InputSource src = new InputSource(reader); // Set the system ID to find the DTD. src.setSystemId(documentId.toString()); xmlDS.load(src, xmlReader); } catch (Exception e) { JOptionPane.showMessageDialog(null, "Not a valid JViews Charts XML document.", "Cannot load data !", JOptionPane.ERROR_MESSAGE); } finally { reader.close(); } // Refresh the text area contents. textArea.setText(doc); } /** * Configures the specified tree to use a custom <code>CellRenderer</code> and * <code>CellEditor</code> able to handle <code>org.w3c.dom.Node</code> user * objects. */ protected void configureTree(JTree tree) { tree.setEditable(true); // Create a custom cell renderer. // The text of a tree node is initialized to: // - the Node value for a Node of type TEXT // - the Node name + the Node value for a Node of type ATTRIBUTE // - the Node name for a Node of another type DefaultTreeCellRenderer cellRenderer = new DefaultTreeCellRenderer() { Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); Node node = (Node) ((DefaultMutableTreeNode) value).getUserObject(); if (node.getNodeType() == Node.TEXT_NODE) { setText(node.getNodeValue()); if (textIcon != null) setIcon(textIcon); } else if (node.getNodeType() == Node.ATTRIBUTE_NODE) { setText(node.getNodeName() + " : " + node.getNodeValue()); if (attrIcon != null) setIcon(attrIcon); } else { setText(node.getNodeName()); } return this; } }; tree.setCellRenderer(cellRenderer); // Create a custom editor able to handle org.w3c.dom.Node object. tree.setCellEditor(new DefaultTreeCellEditor(tree, cellRenderer) { // During editing, the text field text is initialized to the // Node value for a Node of type TEXT or ATTRIBUTE or the Node name // for other types. Override public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) { Component comp = super.getTreeCellEditorComponent(tree, value, isSelected, expanded, leaf, row); Node node = (Node) ((DefaultMutableTreeNode) value).getUserObject(); if ((node.getNodeType() == Node.TEXT_NODE) || (node.getNodeType() == Node.ATTRIBUTE_NODE)) ((JTextField) editingComponent).setText(node.getNodeValue()); else ((JTextField) editingComponent).setText(node.getNodeName()); return comp; } // After a cell has been edited, set the value of the corresponding // Node to the text field text. Override public Object getCellEditorValue() { String value = (String) super.getCellEditorValue(); TreePath path = this.tree.getPathForRow(lastRow); if (path != null) { DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) path.getLastPathComponent(); Node node = (Node) treeNode.getUserObject(); if ((node.getNodeType() == Node.TEXT_NODE) || (node.getNodeType() == Node.ATTRIBUTE_NODE)) node.setNodeValue(value); return node; } return value; } }); } /** * Populates the tree model according to the specified Document. */ protected void fillTreeModel(Document doc) { document = doc; Element root = document.getDocumentElement(); DefaultMutableTreeNode treeRoot = createTreeNode(root); treeModel.setRoot(treeRoot); addToTree(treeRoot, root); } private void addToTree(DefaultMutableTreeNode parent, Node node) { // Every node is associated with its corresponding DOM element. String value = null; Node child = node.getFirstChild(); while (child != null) { if (child.getNodeType() == Node.ELEMENT_NODE) { DefaultMutableTreeNode childTreeNode = createTreeNode(child); parent.add(childTreeNode); NamedNodeMap attributes = child.getAttributes(); for (int i = 0; i < attributes.getLength(); ++i) { Node attr = attributes.item(i); DefaultMutableTreeNode attrNode = createTreeNode(attr); childTreeNode.add(attrNode); } addToTree(childTreeNode, child); } else if ((child.getNodeType() == Node.TEXT_NODE) && ((value = child.getNodeValue()).length() > 0 && Character.isJavaIdentifierPart(value.charAt(0)))) { parent.add(createTreeNode(child)); } child = child.getNextSibling(); } } /** * Factory method that returns a <code>DefaultMutableTreeNode</code> instance. */ protected DefaultMutableTreeNode createTreeNode(Node node) { return new DefaultMutableTreeNode(node); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { Override public void run() { JFrame frame = new JFrame("XMLDemo"); XMLDemo sample = new XMLDemo(); sample.init(frame.getContentPane()); frame.setSize(800, 600); Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); frame.setLocation(d.width / 2 - frame.getWidth() / 2, d.height / 2 - frame.getHeight() / 2); frame.setVisible(true); } }); } }