001/* 002 * Copyright 2002-2013 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.util.xml; 018 019import java.util.ArrayList; 020import java.util.Arrays; 021import java.util.Collection; 022import java.util.List; 023 024import org.w3c.dom.CharacterData; 025import org.w3c.dom.Comment; 026import org.w3c.dom.Element; 027import org.w3c.dom.EntityReference; 028import org.w3c.dom.Node; 029import org.w3c.dom.NodeList; 030import org.xml.sax.ContentHandler; 031 032import org.springframework.util.Assert; 033 034/** 035 * Convenience methods for working with the DOM API, 036 * in particular for working with DOM Nodes and DOM Elements. 037 * 038 * @author Juergen Hoeller 039 * @author Rob Harrop 040 * @author Costin Leau 041 * @author Arjen Poutsma 042 * @author Luke Taylor 043 * @since 1.2 044 * @see org.w3c.dom.Node 045 * @see org.w3c.dom.Element 046 */ 047public abstract class DomUtils { 048 049 /** 050 * Retrieves all child elements of the given DOM element that match any of the given element names. 051 * Only looks at the direct child level of the given element; do not go into further depth 052 * (in contrast to the DOM API's {@code getElementsByTagName} method). 053 * @param ele the DOM element to analyze 054 * @param childEleNames the child element names to look for 055 * @return a List of child {@code org.w3c.dom.Element} instances 056 * @see org.w3c.dom.Element 057 * @see org.w3c.dom.Element#getElementsByTagName 058 */ 059 public static List<Element> getChildElementsByTagName(Element ele, String... childEleNames) { 060 Assert.notNull(ele, "Element must not be null"); 061 Assert.notNull(childEleNames, "Element names collection must not be null"); 062 List<String> childEleNameList = Arrays.asList(childEleNames); 063 NodeList nl = ele.getChildNodes(); 064 List<Element> childEles = new ArrayList<Element>(); 065 for (int i = 0; i < nl.getLength(); i++) { 066 Node node = nl.item(i); 067 if (node instanceof Element && nodeNameMatch(node, childEleNameList)) { 068 childEles.add((Element) node); 069 } 070 } 071 return childEles; 072 } 073 074 /** 075 * Retrieves all child elements of the given DOM element that match the given element name. 076 * Only look at the direct child level of the given element; do not go into further depth 077 * (in contrast to the DOM API's {@code getElementsByTagName} method). 078 * @param ele the DOM element to analyze 079 * @param childEleName the child element name to look for 080 * @return a List of child {@code org.w3c.dom.Element} instances 081 * @see org.w3c.dom.Element 082 * @see org.w3c.dom.Element#getElementsByTagName 083 */ 084 public static List<Element> getChildElementsByTagName(Element ele, String childEleName) { 085 return getChildElementsByTagName(ele, new String[] {childEleName}); 086 } 087 088 /** 089 * Utility method that returns the first child element identified by its name. 090 * @param ele the DOM element to analyze 091 * @param childEleName the child element name to look for 092 * @return the {@code org.w3c.dom.Element} instance, or {@code null} if none found 093 */ 094 public static Element getChildElementByTagName(Element ele, String childEleName) { 095 Assert.notNull(ele, "Element must not be null"); 096 Assert.notNull(childEleName, "Element name must not be null"); 097 NodeList nl = ele.getChildNodes(); 098 for (int i = 0; i < nl.getLength(); i++) { 099 Node node = nl.item(i); 100 if (node instanceof Element && nodeNameMatch(node, childEleName)) { 101 return (Element) node; 102 } 103 } 104 return null; 105 } 106 107 /** 108 * Utility method that returns the first child element value identified by its name. 109 * @param ele the DOM element to analyze 110 * @param childEleName the child element name to look for 111 * @return the extracted text value, or {@code null} if no child element found 112 */ 113 public static String getChildElementValueByTagName(Element ele, String childEleName) { 114 Element child = getChildElementByTagName(ele, childEleName); 115 return (child != null ? getTextValue(child) : null); 116 } 117 118 /** 119 * Retrieves all child elements of the given DOM element 120 * @param ele the DOM element to analyze 121 * @return a List of child {@code org.w3c.dom.Element} instances 122 */ 123 public static List<Element> getChildElements(Element ele) { 124 Assert.notNull(ele, "Element must not be null"); 125 NodeList nl = ele.getChildNodes(); 126 List<Element> childEles = new ArrayList<Element>(); 127 for (int i = 0; i < nl.getLength(); i++) { 128 Node node = nl.item(i); 129 if (node instanceof Element) { 130 childEles.add((Element) node); 131 } 132 } 133 return childEles; 134 } 135 136 /** 137 * Extracts the text value from the given DOM element, ignoring XML comments. 138 * <p>Appends all CharacterData nodes and EntityReference nodes into a single 139 * String value, excluding Comment nodes. Only exposes actual user-specified 140 * text, no default values of any kind. 141 * @see CharacterData 142 * @see EntityReference 143 * @see Comment 144 */ 145 public static String getTextValue(Element valueEle) { 146 Assert.notNull(valueEle, "Element must not be null"); 147 StringBuilder sb = new StringBuilder(); 148 NodeList nl = valueEle.getChildNodes(); 149 for (int i = 0; i < nl.getLength(); i++) { 150 Node item = nl.item(i); 151 if ((item instanceof CharacterData && !(item instanceof Comment)) || item instanceof EntityReference) { 152 sb.append(item.getNodeValue()); 153 } 154 } 155 return sb.toString(); 156 } 157 158 /** 159 * Namespace-aware equals comparison. Returns {@code true} if either 160 * {@link Node#getLocalName} or {@link Node#getNodeName} equals 161 * {@code desiredName}, otherwise returns {@code false}. 162 */ 163 public static boolean nodeNameEquals(Node node, String desiredName) { 164 Assert.notNull(node, "Node must not be null"); 165 Assert.notNull(desiredName, "Desired name must not be null"); 166 return nodeNameMatch(node, desiredName); 167 } 168 169 /** 170 * Returns a SAX {@code ContentHandler} that transforms callback calls to DOM {@code Node}s. 171 * @param node the node to publish events to 172 * @return the content handler 173 */ 174 public static ContentHandler createContentHandler(Node node) { 175 return new DomContentHandler(node); 176 } 177 178 /** 179 * Matches the given node's name and local name against the given desired name. 180 */ 181 private static boolean nodeNameMatch(Node node, String desiredName) { 182 return (desiredName.equals(node.getNodeName()) || desiredName.equals(node.getLocalName())); 183 } 184 185 /** 186 * Matches the given node's name and local name against the given desired names. 187 */ 188 private static boolean nodeNameMatch(Node node, Collection<?> desiredNames) { 189 return (desiredNames.contains(node.getNodeName()) || desiredNames.contains(node.getLocalName())); 190 } 191 192}