// // XMLCellRenderer.java // /* OME Bio-Formats package for reading and converting biological file formats. Copyright (C) 2005-@year@ UW-Madison LOCI and Glencoe Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package loci.formats.gui; import java.awt.Component; import javax.swing.JLabel; import javax.swing.JTree; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; import org.w3c.dom.Comment; 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.Text; /** * A tree cell renderer for displaying syntax highlighted XML in a JTree. * * <dl><dt><b>Source code:</b></dt> * <dd><a href="http://trac.openmicroscopy.org.uk/ome/browser/bioformats.git/components/bio-formats/src/loci/formats/gui/XMLCellRenderer.java">Trac</a>, * <a href="http://git.openmicroscopy.org/?p=bioformats.git;a=blob;f=components/bio-formats/src/loci/formats/gui/XMLCellRenderer.java;hb=HEAD">Gitweb</a></dd></dl> * * @author Curtis Rueden ctrueden at wisc.edu */ public class XMLCellRenderer extends DefaultTreeCellRenderer { private static final String ELEMENT_STYLE_START = "<font color=\"#7f007f\"><b>"; private static final String ELEMENT_STYLE_END = "</b></font>"; private static final String ATTR_NAME_STYLE_START = "<b>"; private static final String ATTR_NAME_STYLE_END = "</b>"; private static final String ATTR_VALUE_STYLE_START = "<font color=\"blue\">"; private static final String ATTR_VALUE_STYLE_END = "</font>"; private static final String COMMENT_STYLE_START = "<font color=\"green\"><i>"; private static final String COMMENT_STYLE_END = "</i></font>"; // -- XMLCellRenderer API methods -- /** Constructs a Swing JTree that displays the given XML DOM document. */ public static JTree makeJTree(Document doc) { Element rootNode = doc.getDocumentElement(); DefaultMutableTreeNode rootTreeNode = makeTreeNode(rootNode); JTree tree = new JTree(rootTreeNode); tree.setCellRenderer(new XMLCellRenderer()); tree.setRowHeight(0); return tree; } // -- TreeCellRenderer API methods -- /* * @see javax.swing.tree.TreeCellRenderer#getTreeCellRendererComponent(JTree, * Object, boolean, boolean, boolean, int, boolean */ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { Component c = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); // generate syntax highlighted text, modeled after Mozilla Firefox DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) value; XMLItem item = (XMLItem) treeNode.getUserObject(); JLabel l = (JLabel) c; l.setText(item.toString(true)); return c; } // -- Helper methods -- /** Recursively builds JTree node structure from DOM node structure. */ private static DefaultMutableTreeNode makeTreeNode(Node node) { DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(new XMLItem(node)); NodeList nodeList = node.getChildNodes(); for (int i=0; i<nodeList.getLength(); i++) { Node n = nodeList.item(i); if (!(n instanceof Text)) treeNode.add(makeTreeNode(n)); } return treeNode; } // -- Helper classes -- /** Cell renderer item for use as DefaultMutableTreeNode user object. */ private static class XMLItem { private Node node; public XMLItem(Node node) { this.node = node; } public String toString(boolean html) { StringBuffer sb = new StringBuffer(); if (node instanceof Element) { if (html) sb.append("<html>"); sb.append(html ? "<" : "<"); if (html) sb.append(ELEMENT_STYLE_START); sb.append(node.getNodeName()); if (html) sb.append(ELEMENT_STYLE_END); // append attributes NamedNodeMap attr = node.getAttributes(); for (int i=0; i<attr.getLength(); i++) { Node attrNode = attr.item(i); sb.append(" "); if (html) sb.append(ATTR_NAME_STYLE_START); sb.append(attrNode.getNodeName()); sb.append("="); if (html) sb.append(ATTR_NAME_STYLE_END); if (html) sb.append(ATTR_VALUE_STYLE_START); sb.append(html ? """ : "\""); sb.append(attrNode.getNodeValue()); sb.append(html ? """ : "\""); if (html) sb.append(ATTR_VALUE_STYLE_END); } int numChildren = node.getChildNodes().getLength(); if (numChildren == 0) sb.append("/"); sb.append(html ? ">" : ">"); // check for single child text node if (numChildren == 1) { Node n = node.getFirstChild(); if (n instanceof Text) { sb.append(sanitize(n.getNodeValue(), html)); sb.append(html ? "<" : "<"); sb.append("/"); if (html) sb.append(ELEMENT_STYLE_START); sb.append(node.getNodeName()); if (html) sb.append(ELEMENT_STYLE_END); sb.append(html ? ">" : ">"); } } } else if (node instanceof Comment) { if (html) sb.append("<html>"); if (html) sb.append(COMMENT_STYLE_START); sb.append(html ? "<" : "<"); sb.append("!--"); sb.append(html ? "<br>" : "\n"); sb.append(sanitize(node.getNodeValue(), html)); sb.append(html ? "<br>" : "\n"); sb.append("--"); sb.append(html ? ">" : ">"); if (html) sb.append(COMMENT_STYLE_END); } else sb.append(node.getNodeValue()); return sb.toString(); } /** Allows copy and paste from tree component. */ public String toString() { return toString(false); } /** Sanitizes strings to appear correctly in HTML. */ private String sanitize(String s, boolean html) { if (!html) return s; s = s.replaceAll("&", "&"); s = s.replaceAll("\"", """); s = s.replaceAll("<", "<"); s = s.replaceAll(">", ">"); s = s.replaceAll("[\n\r]", "<br>"); return s; } } }