一、前言
关于icefaces源码包(ice-3.4.2.jar)中org.icefaces.impl.util.DOMUtils文档结构工具类,对org.w3c.dom.Document文档数据解析转换处理,详情参见源码说明。
二、源码说明
package org.icefaces.impl.util;@b@@b@import java.io.IOException;@b@import java.io.StringWriter;@b@import java.io.Writer;@b@import java.lang.reflect.Method;@b@import java.util.ArrayList;@b@import java.util.Arrays;@b@import java.util.Collection;@b@import java.util.HashMap;@b@import java.util.HashSet;@b@import java.util.Iterator;@b@import java.util.List;@b@import java.util.Map;@b@import java.util.Set;@b@import java.util.logging.Level;@b@import java.util.logging.Logger;@b@import java.util.regex.Matcher;@b@import java.util.regex.Pattern;@b@import javax.faces.component.UIComponent;@b@import javax.xml.parsers.DocumentBuilder;@b@import javax.xml.parsers.DocumentBuilderFactory;@b@import javax.xml.parsers.ParserConfigurationException;@b@import org.w3c.dom.Attr;@b@import org.w3c.dom.CDATASection;@b@import org.w3c.dom.Comment;@b@import org.w3c.dom.Document;@b@import org.w3c.dom.Element;@b@import org.w3c.dom.Entity;@b@import org.w3c.dom.NamedNodeMap;@b@import org.w3c.dom.Node;@b@import org.w3c.dom.NodeList;@b@import org.w3c.dom.Text;@b@@b@public class DOMUtils@b@{@b@ private static Logger log = Logger.getLogger("org.icefaces.util.DOMUtil");@b@ private static HashSet<String> TAGS_THAT_CAN_CLOSE_SHORT = new HashSet(Arrays.asList(new String[] { "img", "input", "br", "hr", "meta", "base", "link", "frame", "col", "area" }));@b@ private static HashSet<String> TAGS_THAT_ALLOW_NEWLINE = new HashSet(Arrays.asList(new String[] { "img", "input", "td" }));@b@ private static Pattern CDATA_END = Pattern.compile("]]>");@b@ private static DocumentBuilder DOCUMENT_BUILDER;@b@ private static boolean isDOMChecking = true;@b@ private static String[] ansiCharacters;@b@@b@ public static Document getNewDocument()@b@ {@b@ Document doc = DOCUMENT_BUILDER.newDocument();@b@ applyDocumentSettings(doc);@b@ return doc;@b@ }@b@@b@ private static void applyDocumentSettings(Document doc) {@b@ if (!(isDOMChecking)) {@b@ Method setErrorCheckingMethod = null;@b@ try {@b@ setErrorCheckingMethod = doc.getClass().getMethod("setErrorChecking", new Class[] { Boolean.TYPE });@b@@b@ setErrorCheckingMethod.invoke(doc, new Object[] { Boolean.FALSE });@b@ } catch (Exception e) {@b@ if (log.isLoggable(Level.FINE))@b@ log.log(Level.FINE, "DOM error checking not disabled ", e);@b@ }@b@ }@b@ }@b@@b@ public static String DocumentTypetoString(String publicID, String systemID, String root)@b@ {@b@ return "<!DOCTYPE " + root + " PUBLIC \"" + publicID + "\" \"" + systemID + "\">";@b@ }@b@@b@ public static String nodeToString(Node node) throws IOException@b@ {@b@ StringWriter writer = new StringWriter();@b@ try {@b@ printNode(node, writer);@b@@b@ return writer.toString();@b@ }@b@ finally@b@ {@b@ writer.flush(); }@b@ return writer.toString();@b@ }@b@@b@ public static String childrenToString(Node node) throws IOException@b@ {@b@ StringWriter writer = new StringWriter();@b@ try {@b@ printChildNodes(node, writer);@b@@b@ return writer.toString();@b@ }@b@ finally@b@ {@b@ writer.flush(); }@b@ return writer.toString();@b@ }@b@@b@ public static void printChildNodes(Node node, Writer writer) throws IOException@b@ {@b@ NodeList children = node.getChildNodes();@b@ int l = children.getLength();@b@ for (int i = 0; i < l; ++i)@b@ printNode(children.item(i), writer);@b@ }@b@@b@ public static void printNodeCDATA(Node node, Writer writer) throws IOException@b@ {@b@ printNode(node, writer, 0, true, false, true);@b@ }@b@@b@ public static void printNode(Node node, Writer writer) throws IOException {@b@ printNode(node, writer, 0, true, false, false);@b@ }@b@@b@ private static void printNode(Node node, Writer writer, int depth, boolean allowAddingWhitespace, boolean addTrailingNewline, boolean isInCdata)@b@ throws IOException@b@ {@b@ NodeList nodes;@b@ int i;@b@ switch (node.getNodeType())@b@ {@b@ case 9:@b@ nodes = node.getChildNodes();@b@ if (nodes == null) return;@b@ for (i = 0; i < nodes.getLength(); ++i)@b@ printNode(nodes.item(i), writer, depth + 1, allowAddingWhitespace, false, isInCdata);@b@ break;@b@ case 1:@b@ int childrenLength;@b@ int i;@b@ String name = node.getNodeName();@b@@b@ writer.write("<");@b@ writer.write(name);@b@ NamedNodeMap attributes = node.getAttributes();@b@ for (int i = 0; i < attributes.getLength(); ++i) {@b@ Node current = attributes.item(i);@b@ writer.write(" ");@b@ writer.write(current.getNodeName());@b@ writer.write("=\"");@b@ writer.write(escapeAttribute(current.getNodeValue()));@b@ writer.write("\"");@b@ }@b@@b@ if ((!(node.hasChildNodes())) && (xmlShortClosingAllowed(node))) {@b@ writer.write(" />");@b@ return;@b@ }@b@@b@ writer.write(">");@b@@b@ NodeList children = node.getChildNodes();@b@@b@ if (children != null) {@b@ childrenLength = children.getLength();@b@ for (i = 0; i < childrenLength; ++i) {@b@ boolean childAddTrailingNewline = false;@b@ if ((allowAddingWhitespace) && @b@ (i + 1 < childrenLength)) {@b@ Node nextChild = children.item(i + 1);@b@@b@ childAddTrailingNewline = (!(isWhitespaceText(nextChild))) && (isNewlineAllowedTag(nextChild));@b@ }@b@@b@ printNode(children.item(i), writer, depth + 1, allowAddingWhitespace, childAddTrailingNewline, isInCdata);@b@ }@b@@b@ }@b@@b@ writer.write("</");@b@ writer.write(name);@b@ writer.write(">");@b@@b@ break;@b@ case 3:@b@ if (!(isInCdata)) {@b@ writer.write(node.getNodeValue()); return; }@b@ String value = node.getNodeValue();@b@ String escaped = CDATA_END.matcher(value).replaceAll("]]>]]><![CDATA[");@b@@b@ writer.write(escaped);@b@@b@ break;@b@ case 4:@b@ writer.write("<![CDATA[");@b@ writer.write(node.getNodeValue());@b@ writer.write("]]>");@b@ case 2:@b@ case 5:@b@ case 6:@b@ case 7:@b@ case 8: } }@b@@b@ private static boolean isWhitespaceText(Node node) {@b@ if (node.getNodeType() == 3) {@b@ String val = node.getNodeValue();@b@@b@ for (int i = val.length() - 1; i >= 0; --i)@b@ if (!(Character.isWhitespace(val.charAt(i))))@b@ return false;@b@@b@@b@ return true;@b@ }@b@ return false;@b@ }@b@@b@ private static boolean isNewlineAllowedTag(Node node) {@b@ short nodeType = node.getNodeType();@b@ String nodeName = node.getNodeName().toLowerCase();@b@ return ((nodeType != 1) || (!(TAGS_THAT_ALLOW_NEWLINE.contains(nodeName))));@b@ }@b@@b@ private static boolean xmlShortClosingAllowed(Node node)@b@ {@b@ short nodeType = node.getNodeType();@b@ String nodeName = node.getNodeName().toLowerCase();@b@ return ((nodeType == 1) && (TAGS_THAT_CAN_CLOSE_SHORT.contains(nodeName)));@b@ }@b@@b@ public static Node getChildByNodeName(Node node, String name)@b@ {@b@ NodeList children = node.getChildNodes();@b@@b@ int l = children.getLength();@b@ for (int i = 0; i < l; ++i) {@b@ Node child = children.item(i);@b@ if (child.getNodeName().equalsIgnoreCase(name))@b@ return child;@b@ }@b@@b@ return null;@b@ }@b@@b@ public static Node[] domDiff(Document oldDOM, Document newDOM)@b@ {@b@ return nodeDiff(oldDOM.getDocumentElement(), newDOM.getDocumentElement());@b@ }@b@@b@ public static Node[] nodeDiff(Node oldNode, Node newNode)@b@ {@b@ List nodeDiffs = new ArrayList();@b@ compareNodes(nodeDiffs, oldNode, newNode);@b@@b@ Node[] prunedDiff = null;@b@ try {@b@ prunedDiff = pruneAncestors(nodeDiffs);@b@ } catch (Exception e) {@b@ e.printStackTrace();@b@ }@b@@b@ return ((prunedDiff == null) ? new Node[0] : prunedDiff);@b@ }@b@@b@ public static boolean compareNodes(List<Node> nodeDiffs, Node oldNode, Node newNode)@b@ {@b@ if (!(oldNode.getNodeName().equals(newNode.getNodeName())))@b@ {@b@ nodeDiffs.add(newNode.getParentNode());@b@ return false;@b@ }@b@ if (!(compareIDs(oldNode, newNode)))@b@ {@b@ nodeDiffs.add(newNode.getParentNode());@b@ return false;@b@ }@b@ if (!(compareAttributes(oldNode, newNode))) {@b@ nodeDiffs.add(newNode);@b@ return false;@b@ }@b@ if (!(compareStrings(oldNode.getNodeValue(), newNode.getNodeValue())))@b@ {@b@ nodeDiffs.add(newNode);@b@ return false;@b@ }@b@@b@ NodeList oldChildNodes = oldNode.getChildNodes();@b@ NodeList newChildNodes = newNode.getChildNodes();@b@@b@ int oldChildLength = oldChildNodes.getLength();@b@ int newChildLength = newChildNodes.getLength();@b@@b@ if (oldChildLength != newChildLength) {@b@ nodeDiffs.add(newNode);@b@ return false;@b@ }@b@@b@ boolean allChildrenMatch = true;@b@ for (int i = 0; i < newChildLength; ++i)@b@ if (!(compareNodes(nodeDiffs, oldChildNodes.item(i), newChildNodes.item(i))))@b@ {@b@ allChildrenMatch = false;@b@ }@b@@b@@b@ return allChildrenMatch;@b@ }@b@@b@ private static boolean compareStrings(String oldString, String newString) {@b@ if ((null == oldString) && (null == newString))@b@ return true;@b@ try@b@ {@b@ return oldString.equals(newString);@b@ } catch (NullPointerException e) {@b@ }@b@ return false;@b@ }@b@@b@ public static boolean compareIDs(Node oldNode, Node newNode)@b@ {@b@ if ((!(oldNode instanceof Element)) && (!(newNode instanceof Element)))@b@ {@b@ return true;@b@ }@b@ try {@b@ return ((Element)oldNode).getAttribute("id").equals(((Element)newNode).getAttribute("id"));@b@ }@b@ catch (Exception e) {@b@ }@b@ return false;@b@ }@b@@b@ public static boolean compareAttributes(Node oldNode, Node newNode)@b@ {@b@ boolean oldHasAttributes = oldNode.hasAttributes();@b@ boolean newHasAttributes = newNode.hasAttributes();@b@@b@ if ((!(oldHasAttributes)) && (!(newHasAttributes)))@b@ return true;@b@@b@ if (oldHasAttributes != newHasAttributes) {@b@ return false;@b@ }@b@@b@ NamedNodeMap oldMap = oldNode.getAttributes();@b@ NamedNodeMap newMap = newNode.getAttributes();@b@@b@ int oldLength = oldMap.getLength();@b@ int newLength = newMap.getLength();@b@@b@ if (oldLength != newLength) {@b@ return false;@b@ }@b@@b@ Node newAttribute = null;@b@ Node oldAttribute = null;@b@ for (int i = 0; i < newLength; ++i) {@b@ newAttribute = newMap.item(i);@b@ oldAttribute = oldMap.getNamedItem(newAttribute.getNodeName());@b@ if (null == oldAttribute)@b@ return false;@b@@b@ if (!(String.valueOf(oldAttribute.getNodeValue()).equals(String.valueOf(newAttribute.getNodeValue()))))@b@ {@b@ return false;@b@ }@b@ }@b@@b@ return true;@b@ }@b@@b@ public static Element ascendToNodeWithID(Node start)@b@ {@b@ Node node = start;@b@ while (null != node) {@b@ if (node instanceof Element) {@b@ String id = ((Element)node).getAttribute("id");@b@ if ((null != id) && (!("".equals(id))))@b@ return ((Element)node);@b@ }@b@@b@ node = node.getParentNode();@b@ }@b@@b@ return start.getOwnerDocument().getDocumentElement();@b@ }@b@@b@ public static boolean escapeIsRequired(UIComponent uiComponent)@b@ {@b@ Object escapeAttribute = uiComponent.getAttributes().get("escape");@b@ if (escapeAttribute != null) {@b@ if (escapeAttribute instanceof String)@b@ return Boolean.valueOf((String)escapeAttribute).booleanValue();@b@ if (escapeAttribute instanceof Boolean)@b@ return ((Boolean)escapeAttribute).booleanValue();@b@ }@b@@b@ return true;@b@ }@b@@b@ public static String escapeAttribute(String text)@b@ {@b@ if (null == text)@b@ return "";@b@@b@ char[] chars = text.toCharArray();@b@ StringBuilder buffer = new StringBuilder(chars.length);@b@ for (int index = 0; index < chars.length; ++index) {@b@ char ch = chars[index];@b@ if (ch <= '\31') {@b@ if ((ch == '\t') || (ch == '\n') || (ch == '\r'))@b@ buffer.append(ch);@b@@b@ }@b@ else if (ch == '<')@b@ buffer.append("<");@b@ else if (ch == '>')@b@ buffer.append(">");@b@ else if (ch == '&')@b@ buffer.append("&");@b@ else if (ch == '"')@b@ buffer.append(""");@b@ else@b@ buffer.append(ch);@b@@b@ }@b@@b@ return buffer.toString();@b@ }@b@@b@ public static String escapeAnsi(String text) {@b@ if (null == text)@b@ return "";@b@@b@ char[] chars = text.toCharArray();@b@ StringBuffer buffer = new StringBuffer(chars.length);@b@ for (int index = 0; index < chars.length; ++index) {@b@ char ch = chars[index];@b@@b@ if (ch <= '\31') {@b@ if ((ch == '\t') || (ch == '\n') || (ch == '\r'))@b@ buffer.append(ch);@b@ }@b@ else {@b@ if (ch == '') break label233:@b@@b@ if (ch == '>')@b@ buffer.append(">");@b@ else if (ch == '<')@b@ buffer.append("<");@b@ else if (ch == '&')@b@ buffer.append("&");@b@ else if (ch == '\'')@b@ buffer.append("'");@b@ else if (ch == '"')@b@ buffer.append(""");@b@ else if ((ch >= 160) && (ch <= 255))@b@ buffer.append("&#").append(Integer.toString(ch)).append(";");@b@ else if (ch == 8364)@b@ buffer.append("€");@b@ else@b@ buffer.append(ch);@b@ }@b@ }@b@@b@ label233: return buffer.toString();@b@ }@b@@b@ private static String escapeAnsi(char character)@b@ {@b@ int indexOfEscapedCharacter = character - 160;@b@ return ansiCharacters[indexOfEscapedCharacter];@b@ }@b@@b@ private static Node[] pruneAncestors(List nodeList)@b@ {@b@ boolean reload;@b@ int j;@b@ HashSet dupCheck;@b@ int i;@b@ Node[] changed = (Node[])(Node[])nodeList.toArray(new Node[0]);@b@ HashMap depthMaps = new HashMap();@b@ for (int i = 0; i < changed.length; ++i) {@b@ Element changeRoot = ascendToNodeWithID(changed[i]);@b@@b@ changed[i] = changeRoot;@b@ Integer depth = new Integer(getDepth(changeRoot));@b@ HashSet peers = (HashSet)depthMaps.get(depth);@b@ if (null == peers) {@b@ peers = new HashSet();@b@ depthMaps.put(depth, peers);@b@ }@b@@b@ peers.add(changeRoot);@b@ }@b@ Iterator allDepths = depthMaps.keySet().iterator();@b@ while (allDepths.hasNext()) {@b@ Integer baseDepth = (Integer)allDepths.next();@b@ Iterator checkDepths = depthMaps.keySet().iterator();@b@ while (checkDepths.hasNext()) {@b@ Integer checkDepth = (Integer)checkDepths.next();@b@ if (baseDepth.intValue() < checkDepth.intValue()) {@b@ pruneAncestors(baseDepth, (HashSet)depthMaps.get(baseDepth), checkDepth, (HashSet)depthMaps.get(checkDepth));@b@ }@b@@b@ }@b@@b@ }@b@@b@ HashSet topElements = new HashSet();@b@ Iterator allDepthMaps = depthMaps.values().iterator();@b@ while (allDepthMaps.hasNext()) {@b@ topElements.addAll((HashSet)allDepthMaps.next());@b@ }@b@@b@ Element[] elements = null;@b@ if (!(topElements.isEmpty())) {@b@ reload = false;@b@ j = 0;@b@ elements = new Element[topElements.size()];@b@ dupCheck = new HashSet();@b@@b@ for (i = 0; i < changed.length; ++i) {@b@ Element element = (Element)changed[i];@b@ String tag = element.getTagName();@b@@b@ reload = (reload) || ("html".equalsIgnoreCase(tag)) || ("head".equalsIgnoreCase(tag));@b@ if ((topElements.contains(element)) && @b@ (!(dupCheck.contains(element)))) {@b@ dupCheck.add(element);@b@ elements[(j++)] = element;@b@ }@b@ }@b@@b@ }@b@@b@ return elements;@b@ }@b@@b@ private static void pruneAncestors(Integer parentDepth, Collection parents, Integer childDepth, Collection children)@b@ {@b@ Iterator parentList = parents.iterator();@b@ while (parentList.hasNext()) {@b@ Node parent = (Node)parentList.next();@b@ Iterator childList = children.iterator();@b@ while (childList.hasNext()) {@b@ Node child = (Node)childList.next();@b@ if (isAncestor(parentDepth, parent, childDepth, child))@b@ childList.remove();@b@ }@b@ }@b@ }@b@@b@ private static int getDepth(Node node)@b@ {@b@ int depth = 0;@b@ Node parent = node;@b@ while ((parent = parent.getParentNode()) != null)@b@ ++depth;@b@@b@ return depth;@b@ }@b@@b@ private static boolean isAncestor(Integer parentDepth, Node parent, Integer childDepth, Node child)@b@ {@b@ if (!(parent.hasChildNodes()))@b@ return false;@b@@b@ Node testParent = child;@b@ int testDepth = childDepth.intValue();@b@ int stopDepth = parentDepth.intValue();@b@ do { if (((testParent = testParent.getParentNode()) == null) || (testDepth <= stopDepth))@b@ break label60;@b@ --testDepth; }@b@ while (!(testParent.equals(parent)));@b@ return true;@b@@b@ label60: return false;@b@ }@b@@b@ public static String toDebugStringDeep(Node node)@b@ {@b@ return toDebugStringDeep(node, "");@b@ }@b@@b@ static String toDebugStringDeep(Node node, String indent) {@b@ int i;@b@ String result = toDebugString(node) + "\n";@b@ indent = indent + " ";@b@ NodeList nodes = node.getChildNodes();@b@ if (nodes != null)@b@ for (i = 0; i < nodes.getLength(); ++i)@b@ result = result + indent + toDebugStringDeep(nodes.item(i), indent);@b@@b@@b@ return result;@b@ }@b@@b@ public static String toDebugString(Node node) {@b@ short type = node.getNodeType();@b@ switch (type)@b@ {@b@ case 2:@b@ Attr attr = (Attr)node;@b@ return "attribute[name: " + attr.getName() + "; value: " + attr.getValue() + "]";@b@ case 1:@b@ Element element = (Element)node;@b@ StringBuffer buffer = new StringBuffer();@b@ buffer.append("element[tag: ");@b@ buffer.append(element.getTagName());@b@ buffer.append("; attributes: ");@b@ NamedNodeMap attributes = element.getAttributes();@b@ for (int i = 0; i < attributes.getLength(); ++i) {@b@ Attr attr = (Attr)attributes.item(i);@b@ buffer.append(attr.getName());@b@ buffer.append("=");@b@ buffer.append(attr.getValue());@b@ buffer.append(' ');@b@ }@b@@b@ buffer.append(']');@b@@b@ return buffer.toString();@b@ case 4:@b@ CDATASection cdataSection = (CDATASection)node;@b@ return "cdata[" + cdataSection.getData() + "]";@b@ case 3:@b@ Text text = (Text)node;@b@ return "text[" + text.getData() + "]";@b@ case 8:@b@ Comment comment = (Comment)node;@b@ return "comment[" + comment.getData() + "]";@b@ case 6:@b@ Entity entity = (Entity)node;@b@ return "entity[public: " + entity.getPublicId() + "; system: " + entity.getSystemId() + "]";@b@ case 5:@b@ case 7:@b@ }@b@@b@ return node.getNodeName();@b@ }@b@@b@ static@b@ {@b@ try@b@ {@b@ DOCUMENT_BUILDER = DocumentBuilderFactory.newInstance().newDocumentBuilder();@b@ } catch (ParserConfigurationException e) {@b@ log.log(Level.SEVERE, "unable to acquire a DocumentBuilder", e);@b@ }@b@@b@ ansiCharacters = new String[] { "nbsp", "iexcl", "cent", "pound", "curren", "yen", "brvbar", "sect", "uml", "copy", "ordf", "laquo", "not", "shy", "reg", "macr", "deg", "plusmn", "sup2", "sup3", "acute", "micro", "para", "middot", "cedil", "sup1", "ordm", "raquo", "frac14", "frac12", "frac34", "iquest", "Agrave", "Aacute", "Acirc", "Atilde", "Auml", "Aring", "AElig", "Ccedil", "Egrave", "Eacute", "Ecirc", "Euml", "Igrave", "Iacute", "Icirc", "Iuml", "ETH", "Ntilde", "Ograve", "Oacute", "Ocirc", "Otilde", "Ouml", "times", "Oslash", "Ugrave", "Uacute", "Ucirc", "Uuml", "Yacute", "THORN", "szlig", "agrave", "aacute", "acirc", "atilde", "auml", "aring", "aelig", "ccedil", "egrave", "eacute", "ecirc", "euml", "igrave", "iacute", "icirc", "iuml", "eth", "ntilde", "ograve", "oacute", "ocirc", "otilde", "ouml", "divide", "oslash", "ugrave", "uacute", "ucirc", "uuml", "yacute", "thorn", "yuml" };@b@ }@b@}