| /* |
| |
| Copyright 2000,2002-2003,2005 The Apache Software Foundation |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| |
| */ |
| package org.apache.batik.util.gui; |
| |
| import java.awt.BorderLayout; |
| import java.awt.Component; |
| import java.awt.GridLayout; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ItemEvent; |
| import java.awt.event.ItemListener; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.ResourceBundle; |
| |
| import javax.swing.AbstractAction; |
| import javax.swing.Action; |
| import javax.swing.BorderFactory; |
| import javax.swing.ImageIcon; |
| import javax.swing.JCheckBox; |
| import javax.swing.JFrame; |
| import javax.swing.JPanel; |
| import javax.swing.JScrollPane; |
| import javax.swing.JSplitPane; |
| import javax.swing.JTable; |
| import javax.swing.JTextArea; |
| import javax.swing.JTree; |
| import javax.swing.event.TreeSelectionEvent; |
| import javax.swing.event.TreeSelectionListener; |
| import javax.swing.table.AbstractTableModel; |
| import javax.swing.tree.DefaultMutableTreeNode; |
| import javax.swing.tree.DefaultTreeCellRenderer; |
| import javax.swing.tree.DefaultTreeModel; |
| import javax.swing.tree.MutableTreeNode; |
| import javax.swing.tree.TreeNode; |
| |
| import org.apache.batik.bridge.svg12.ContentManager; |
| import org.apache.batik.bridge.svg12.DefaultXBLManager; |
| import org.apache.batik.dom.AbstractDocument; |
| import org.apache.batik.dom.svg12.XBLOMContentElement; |
| import org.apache.batik.dom.xbl.NodeXBL; |
| import org.apache.batik.dom.xbl.XBLManager; |
| import org.apache.batik.util.gui.resource.ActionMap; |
| import org.apache.batik.util.gui.resource.ButtonFactory; |
| import org.apache.batik.util.gui.resource.MissingListenerException; |
| import org.apache.batik.util.gui.resource.ResourceManager; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.NamedNodeMap; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.w3c.dom.css.CSSStyleDeclaration; |
| import org.w3c.dom.css.ViewCSS; |
| |
| /** |
| * The components of this class are used to view a DOM tree. |
| * |
| * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a> |
| * @version $Id$ |
| */ |
| public class DOMViewer extends JFrame implements ActionMap { |
| /** |
| * The resource file name |
| */ |
| protected final static String RESOURCE = |
| "org.apache.batik.util.gui.resources.DOMViewerMessages"; |
| |
| /** |
| * The resource bundle |
| */ |
| protected static ResourceBundle bundle; |
| |
| /** |
| * The resource manager |
| */ |
| protected static ResourceManager resources; |
| |
| static { |
| bundle = ResourceBundle.getBundle(RESOURCE, Locale.getDefault()); |
| resources = new ResourceManager(bundle); |
| } |
| |
| /** |
| * The map that contains the listeners |
| */ |
| protected Map listeners = new HashMap(); |
| |
| /** |
| * The panel. |
| */ |
| protected Panel panel = new Panel(); |
| |
| /** |
| * Whether to show text nodes that contain only whitespace in the tree. |
| */ |
| protected boolean showWhitespace = true; |
| |
| /** |
| * Creates a new DOMViewer panel. |
| */ |
| public DOMViewer() { |
| super(resources.getString("Frame.title")); |
| setSize(resources.getInteger("Frame.width"), |
| resources.getInteger("Frame.height")); |
| |
| listeners.put("CloseButtonAction", new CloseButtonAction()); |
| |
| getContentPane().add(panel); |
| |
| JPanel p = new JPanel(new BorderLayout()); |
| |
| JCheckBox cb = new JCheckBox("Show Whitespace Text Nodes"); |
| cb.setSelected(showWhitespace); |
| cb.addItemListener(new ItemListener() { |
| public void itemStateChanged(ItemEvent ie) { |
| setShowWhitespace |
| (ie.getStateChange() == ItemEvent.SELECTED); |
| } |
| }); |
| |
| p.add(cb, BorderLayout.WEST); |
| |
| |
| ButtonFactory bf = new ButtonFactory(bundle, this); |
| p.add(bf.createJButton("CloseButton"), BorderLayout.EAST); |
| getContentPane().add("South", p); |
| } |
| |
| /** |
| * Sets whether to show text nodes that contain only whitespace |
| * in the tree. |
| */ |
| public void setShowWhitespace(boolean state) { |
| showWhitespace = state; |
| if (panel.document != null) |
| panel.setDocument(panel.document); |
| } |
| |
| /** |
| * Sets the document to display. |
| */ |
| public void setDocument(Document doc) { |
| panel.setDocument(doc); |
| } |
| |
| /** |
| * Sets the document to display and its ViewCSS. |
| */ |
| public void setDocument(Document doc, ViewCSS view) { |
| panel.setDocument(doc, view); |
| } |
| |
| /** |
| * Returns the action associated with the given string |
| * or null on error |
| * @param key the key mapped with the action to get |
| * @throws MissingListenerException if the action is not found |
| */ |
| public Action getAction(String key) throws MissingListenerException { |
| return (Action)listeners.get(key); |
| } |
| |
| /** |
| * The action associated with the 'Close' button of the viewer panel |
| */ |
| protected class CloseButtonAction extends AbstractAction { |
| public void actionPerformed(ActionEvent e) { |
| dispose(); |
| } |
| } |
| |
| /** |
| * The panel that contains the viewer. |
| */ |
| public class Panel extends JPanel { |
| /** |
| * The DOM document. |
| */ |
| protected Document document; |
| |
| /** |
| * The ViewCSS object associated with the document. |
| */ |
| protected ViewCSS viewCSS; |
| |
| /** |
| * The tree. |
| */ |
| protected JTree tree; |
| |
| /** |
| * The split pane. |
| */ |
| protected JSplitPane splitPane; |
| |
| /** |
| * The right panel. |
| */ |
| protected JPanel rightPanel = new JPanel(new BorderLayout()); |
| |
| /** |
| * The attributes table. |
| */ |
| protected JTable attributesTable = new JTable(); |
| |
| /** |
| * The properties table. |
| */ |
| protected JTable propertiesTable = new JTable(); |
| |
| /** |
| * The element panel. |
| */ |
| protected JPanel elementPanel = new JPanel(new GridLayout(2, 1)); |
| { |
| JScrollPane pane = new JScrollPane(); |
| pane.setBorder(BorderFactory.createCompoundBorder |
| (BorderFactory.createEmptyBorder(2, 0, 2, 2), |
| BorderFactory.createCompoundBorder |
| (BorderFactory.createTitledBorder |
| (BorderFactory.createEmptyBorder(), |
| resources.getString("AttributesPanel.title")), |
| BorderFactory.createLoweredBevelBorder()))); |
| pane.getViewport().add(attributesTable); |
| |
| JScrollPane pane2 = new JScrollPane(); |
| pane2.setBorder(BorderFactory.createCompoundBorder |
| (BorderFactory.createEmptyBorder(2, 0, 2, 2), |
| BorderFactory.createCompoundBorder |
| (BorderFactory.createTitledBorder |
| (BorderFactory.createEmptyBorder(), |
| resources.getString("CSSValuesPanel.title")), |
| BorderFactory.createLoweredBevelBorder()))); |
| pane2.getViewport().add(propertiesTable); |
| |
| elementPanel.add(pane); |
| elementPanel.add(pane2); |
| } |
| |
| /** |
| * The CharacterData panel text area. |
| */ |
| protected JTextArea characterData = new JTextArea(); |
| |
| /** |
| * The CharacterData node panel. |
| */ |
| protected JPanel characterDataPanel = new JPanel(new BorderLayout()); |
| { |
| characterDataPanel.setBorder |
| (BorderFactory.createCompoundBorder |
| (BorderFactory.createEmptyBorder(2, 0, 2, 2), |
| BorderFactory.createCompoundBorder |
| (BorderFactory.createTitledBorder |
| (BorderFactory.createEmptyBorder(), |
| resources.getString("CDataPanel.title")), |
| BorderFactory.createLoweredBevelBorder()))); |
| JScrollPane pane = new JScrollPane(); |
| pane.getViewport().add(characterData); |
| characterDataPanel.add(pane); |
| characterData.setEditable(false); |
| } |
| |
| /** |
| * The documentInfo panel text area. |
| */ |
| protected JTextArea documentInfo = new JTextArea(); |
| |
| /** |
| * The documentInfo node panel. |
| */ |
| protected JPanel documentInfoPanel = new JPanel(new BorderLayout()); |
| { |
| documentInfoPanel.setBorder |
| (BorderFactory.createCompoundBorder |
| (BorderFactory.createEmptyBorder(2, 0, 2, 2), |
| BorderFactory.createCompoundBorder |
| (BorderFactory.createTitledBorder |
| (BorderFactory.createEmptyBorder(), |
| resources.getString("DocumentInfoPanel.title")), |
| BorderFactory.createLoweredBevelBorder()))); |
| JScrollPane pane = new JScrollPane(); |
| pane.getViewport().add(documentInfo); |
| documentInfoPanel.add(pane); |
| documentInfo.setEditable(false); |
| } |
| |
| /** |
| * Creates a new Panel object. |
| */ |
| public Panel() { |
| super(new BorderLayout()); |
| setBorder(BorderFactory.createTitledBorder |
| (BorderFactory.createEmptyBorder(), |
| resources.getString("DOMViewerPanel.title"))); |
| |
| TreeNode root; |
| root = new DefaultMutableTreeNode |
| (resources.getString("EmptyDocument.text")); |
| tree = new JTree(root); |
| tree.setCellRenderer(new NodeRenderer()); |
| tree.putClientProperty("JTree.lineStyle", "Angled"); |
| |
| JScrollPane treePane = new JScrollPane(); |
| treePane.setBorder(BorderFactory.createCompoundBorder |
| (BorderFactory.createEmptyBorder(2, 2, 2, 0), |
| BorderFactory.createCompoundBorder |
| (BorderFactory.createTitledBorder |
| (BorderFactory.createEmptyBorder(), |
| resources.getString("DOMViewer.title")), |
| BorderFactory.createLoweredBevelBorder()))); |
| treePane.getViewport().add(tree); |
| splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, |
| true, // Continuous layout |
| treePane, |
| rightPanel); |
| int loc = resources.getInteger("SplitPane.dividerLocation"); |
| splitPane.setDividerLocation(loc); |
| add(splitPane); |
| |
| tree.addTreeSelectionListener(new DOMTreeSelectionListener()); |
| } |
| |
| /** |
| * Sets the document to display. |
| */ |
| public void setDocument(Document doc) { |
| setDocument(doc, null); |
| } |
| |
| /** |
| * Sets the document to display and its ViewCSS. |
| */ |
| public void setDocument(Document doc, ViewCSS view) { |
| document = doc; |
| viewCSS = view; |
| TreeNode root = createTree(doc, showWhitespace); |
| ((DefaultTreeModel)tree.getModel()).setRoot(root); |
| if (rightPanel.getComponentCount() != 0) { |
| rightPanel.remove(0); |
| splitPane.revalidate(); |
| splitPane.repaint(); |
| } |
| } |
| |
| /** |
| * Creates a swing tree from a DOM document. |
| */ |
| protected MutableTreeNode createTree(Node node, |
| boolean showWhitespace) { |
| DefaultMutableTreeNode result; |
| result = new DefaultMutableTreeNode(new NodeInfo(node)); |
| for (Node n = node.getFirstChild(); |
| n != null; |
| n = n.getNextSibling()) { |
| if (!showWhitespace && (n instanceof org.w3c.dom.Text)) { |
| String txt = n.getNodeValue(); |
| if (txt.trim().length() == 0) |
| continue; |
| } |
| result.add(createTree(n, showWhitespace)); |
| } |
| if (node instanceof NodeXBL) { |
| Element shadowTree = ((NodeXBL) node).getXblShadowTree(); |
| if (shadowTree != null) { |
| DefaultMutableTreeNode shadowNode |
| = new DefaultMutableTreeNode |
| (new ShadowNodeInfo(shadowTree)); |
| shadowNode.add(createTree(shadowTree, showWhitespace)); |
| result.add(shadowNode); |
| } |
| } |
| if (node instanceof XBLOMContentElement) { |
| AbstractDocument doc |
| = (AbstractDocument) node.getOwnerDocument(); |
| XBLManager xm = doc.getXBLManager(); |
| if (xm instanceof DefaultXBLManager) { |
| DefaultMutableTreeNode selectedContentNode |
| = new DefaultMutableTreeNode(new ContentNodeInfo(node)); |
| DefaultXBLManager dxm = (DefaultXBLManager) xm; |
| ContentManager cm = dxm.getContentManager(node); |
| if (cm != null) { |
| NodeList nl |
| = cm.getSelectedContent((XBLOMContentElement) node); |
| for (int i = 0; i < nl.getLength(); i++) { |
| selectedContentNode.add(createTree(nl.item(i), |
| showWhitespace)); |
| } |
| result.add(selectedContentNode); |
| } |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * To listen to the tree selection. |
| */ |
| protected class DOMTreeSelectionListener |
| implements TreeSelectionListener { |
| /** |
| * Called when the selection changes. |
| */ |
| public void valueChanged(TreeSelectionEvent ev) { |
| DefaultMutableTreeNode mtn; |
| mtn = |
| (DefaultMutableTreeNode)tree.getLastSelectedPathComponent(); |
| if (mtn == null) { |
| return; |
| } |
| |
| if (rightPanel.getComponentCount() != 0) { |
| rightPanel.remove(0); |
| } |
| |
| Object nodeInfo = mtn.getUserObject(); |
| if (nodeInfo instanceof NodeInfo) { |
| Node node = ((NodeInfo)nodeInfo).getNode(); |
| switch (node.getNodeType()) { |
| case Node.DOCUMENT_NODE: |
| documentInfo.setText |
| (createDocumentText((Document)node)); |
| rightPanel.add(documentInfoPanel); |
| break; |
| case Node.ELEMENT_NODE: |
| attributesTable.setModel(new NodeAttributesModel(node)); |
| propertiesTable.setModel(new NodeCSSValuesModel(node)); |
| rightPanel.add(elementPanel); |
| break; |
| case Node.COMMENT_NODE: |
| case Node.TEXT_NODE: |
| case Node.CDATA_SECTION_NODE: |
| characterData.setText(node.getNodeValue()); |
| rightPanel.add(characterDataPanel); |
| } |
| } |
| splitPane.revalidate(); |
| splitPane.repaint(); |
| } |
| |
| protected String createDocumentText(Document doc) { |
| StringBuffer sb = new StringBuffer(); |
| sb.append("Nodes: "); |
| sb.append(nodeCount(doc)); |
| return sb.toString(); |
| } |
| |
| protected int nodeCount(Node node) { |
| int result = 1; |
| for (Node n = node.getFirstChild(); |
| n != null; |
| n = n.getNextSibling()) { |
| result += nodeCount(n); |
| } |
| return result; |
| } |
| } |
| |
| /** |
| * To render the tree nodes. |
| */ |
| protected class NodeRenderer extends DefaultTreeCellRenderer { |
| /** |
| * The icon used to represent elements. |
| */ |
| ImageIcon elementIcon; |
| |
| /** |
| * The icon used to represent comments. |
| */ |
| ImageIcon commentIcon; |
| |
| /** |
| * The icon used to represent processing instructions. |
| */ |
| ImageIcon piIcon; |
| |
| /** |
| * The icon used to represent text. |
| */ |
| ImageIcon textIcon; |
| |
| /** |
| * Creates a new NodeRenderer object. |
| */ |
| public NodeRenderer() { |
| String s; |
| s = resources.getString("Element.icon"); |
| elementIcon = new ImageIcon(getClass().getResource(s)); |
| s = resources.getString("Comment.icon"); |
| commentIcon = new ImageIcon(getClass().getResource(s)); |
| s = resources.getString("PI.icon"); |
| piIcon = new ImageIcon(getClass().getResource(s)); |
| s = resources.getString("Text.icon"); |
| textIcon = new ImageIcon(getClass().getResource(s)); |
| } |
| |
| /** |
| * Sets the value of the current tree cell. |
| */ |
| public Component getTreeCellRendererComponent(JTree tree, |
| Object value, |
| boolean sel, |
| boolean expanded, |
| boolean leaf, |
| int row, |
| boolean hasFocus) { |
| super.getTreeCellRendererComponent(tree, value, sel, expanded, |
| leaf, row, hasFocus); |
| switch (getNodeType(value)) { |
| case Node.ELEMENT_NODE: |
| setIcon(elementIcon); |
| break; |
| case Node.COMMENT_NODE: |
| setIcon(commentIcon); |
| break; |
| case Node.PROCESSING_INSTRUCTION_NODE: |
| setIcon(piIcon); |
| break; |
| case Node.TEXT_NODE: |
| case Node.CDATA_SECTION_NODE: |
| setIcon(textIcon); |
| break; |
| } |
| return this; |
| } |
| |
| /** |
| * Returns the DOM type of the given object. |
| * @return the type or -1. |
| */ |
| protected short getNodeType(Object value) { |
| DefaultMutableTreeNode mtn = (DefaultMutableTreeNode)value; |
| Object obj = mtn.getUserObject(); |
| if (obj instanceof NodeInfo) { |
| Node node = ((NodeInfo)obj).getNode(); |
| return node.getNodeType(); |
| } |
| return -1; |
| } |
| } |
| |
| /** |
| * To display the attributes of a DOM node attributes in a table. |
| */ |
| protected class NodeAttributesModel extends AbstractTableModel { |
| /** |
| * The node. |
| */ |
| protected Node node; |
| |
| /** |
| * Creates a new NodeAttributesModel object. |
| */ |
| public NodeAttributesModel(Node n) { |
| node = n; |
| } |
| |
| /** |
| * Returns the name to give to a column. |
| */ |
| public String getColumnName(int col) { |
| if (col == 0) { |
| return resources.getString("AttributesTable.column1"); |
| } else { |
| return resources.getString("AttributesTable.column2"); |
| } |
| } |
| |
| /** |
| * Returns the number of columns in the table. |
| */ |
| public int getColumnCount() { |
| return 2; |
| } |
| |
| /** |
| * Returns the number of rows in the table. |
| */ |
| public int getRowCount() { |
| return node.getAttributes().getLength(); |
| } |
| |
| /** |
| * Whether the given cell is editable. |
| */ |
| public boolean isCellEditable(int row, int col) { |
| return false; |
| } |
| |
| /** |
| * Returns the value of the given cell. |
| */ |
| public Object getValueAt(int row, int col) { |
| NamedNodeMap map = node.getAttributes(); |
| Node n = map.item(row); |
| if (col == 0) { |
| return n.getNodeName(); |
| } else { |
| return n.getNodeValue(); |
| } |
| } |
| } |
| |
| /** |
| * To display the CSS properties of a DOM node in a table. |
| */ |
| protected class NodeCSSValuesModel extends AbstractTableModel { |
| /** |
| * The node. |
| */ |
| protected Node node; |
| |
| /** |
| * The computed style. |
| */ |
| protected CSSStyleDeclaration style; |
| |
| /** |
| * The property names. |
| */ |
| protected java.util.List propertyNames; |
| |
| /** |
| * Creates a new NodeAttributesModel object. |
| */ |
| public NodeCSSValuesModel(Node n) { |
| node = n; |
| if (viewCSS != null) { |
| style = viewCSS.getComputedStyle((Element)n, null); |
| propertyNames = new ArrayList(); |
| if (style != null) { |
| for (int i = 0; i < style.getLength(); i++) { |
| propertyNames.add(style.item(i)); |
| } |
| Collections.sort(propertyNames); |
| } |
| } |
| } |
| |
| /** |
| * Returns the name to give to a column. |
| */ |
| public String getColumnName(int col) { |
| if (col == 0) { |
| return resources.getString("CSSValuesTable.column1"); |
| } else { |
| return resources.getString("CSSValuesTable.column2"); |
| } |
| } |
| |
| /** |
| * Returns the number of columns in the table. |
| */ |
| public int getColumnCount() { |
| return 2; |
| } |
| |
| /** |
| * Returns the number of rows in the table. |
| */ |
| public int getRowCount() { |
| if (style == null) { |
| return 0; |
| } |
| return style.getLength(); |
| } |
| |
| /** |
| * Whether the given cell is editable. |
| */ |
| public boolean isCellEditable(int row, int col) { |
| return false; |
| } |
| |
| /** |
| * Returns the value of the given cell. |
| */ |
| public Object getValueAt(int row, int col) { |
| String prop = (String)propertyNames.get(row); |
| if (col == 0) { |
| return prop; |
| } else { |
| return style.getPropertyValue(prop); |
| } |
| } |
| } |
| |
| } // class Panel |
| |
| /** |
| * To store the nodes informations |
| */ |
| protected static class NodeInfo { |
| /** |
| * The DOM node. |
| */ |
| protected Node node; |
| |
| /** |
| * Creates a new NodeInfo object. |
| */ |
| public NodeInfo(Node n) { |
| node = n; |
| } |
| |
| /** |
| * Returns the DOM Node associated with this node info. |
| */ |
| public Node getNode() { |
| return node; |
| } |
| |
| /** |
| * Returns a printable representation of the object. |
| */ |
| public String toString() { |
| if (node instanceof Element) { |
| String id = ((Element)node).getAttribute("id"); |
| if (id.length() != 0) { |
| return node.getNodeName() + " \"" + id + "\""; |
| } |
| } |
| return node.getNodeName(); |
| } |
| } |
| |
| /** |
| * To store the node information for a shadow tree. |
| */ |
| protected static class ShadowNodeInfo extends NodeInfo { |
| /** |
| * Creates a new ShadowNodeInfo object. |
| */ |
| public ShadowNodeInfo(Node n) { |
| super(n); |
| } |
| |
| /** |
| * Returns a printable representation of the object. |
| */ |
| public String toString() { |
| return "shadow tree"; |
| } |
| } |
| |
| /** |
| * To store the node information for an xbl:content node's |
| * selected content. |
| */ |
| protected static class ContentNodeInfo extends NodeInfo { |
| /** |
| * Creates a new ContentNodeInfo object. |
| */ |
| public ContentNodeInfo(Node n) { |
| super(n); |
| } |
| |
| /** |
| * Returns a printable representation of the object. |
| */ |
| public String toString() { |
| return "selected content"; |
| } |
| } |
| } |