/*
* #%L
* BSD implementations of Bio-Formats readers and writers
* %%
* Copyright (C) 2005 - 2015 Open Microscopy Environment:
* - Board of Regents of the University of Wisconsin-Madison
* - Glencoe Software, Inc.
* - University of Dundee
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
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.
*
* @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
*/
@Override
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. */
@Override
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;
}
}
}