2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.controller.netconf.util.xml;
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import com.google.common.base.Predicate;
14 import com.google.common.collect.Collections2;
15 import com.google.common.collect.Lists;
16 import com.google.common.collect.Maps;
17 import org.w3c.dom.Attr;
18 import org.w3c.dom.Document;
19 import org.w3c.dom.Element;
20 import org.w3c.dom.NamedNodeMap;
21 import org.w3c.dom.Node;
22 import org.w3c.dom.NodeList;
23 import org.w3c.dom.Text;
24 import org.xml.sax.SAXException;
26 import javax.annotation.Nullable;
27 import java.io.IOException;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.HashMap;
31 import java.util.List;
34 public final class XmlElement {
36 private final Element element;
38 private XmlElement(Element element) {
39 this.element = element;
42 public static XmlElement fromDomElement(Element e) {
43 return new XmlElement(e);
46 public static XmlElement fromDomDocument(Document xml) {
47 return new XmlElement(xml.getDocumentElement());
50 public static XmlElement fromString(String s) {
52 return new XmlElement(XmlUtil.readXmlToElement(s));
53 } catch (IOException | SAXException e) {
54 throw new IllegalArgumentException("Unable to create from " + s, e);
58 public static XmlElement fromDomElementWithExpected(Element element, String expectedName) {
59 XmlElement xmlElement = XmlElement.fromDomElement(element);
60 xmlElement.checkName(expectedName);
64 public static XmlElement fromDomElementWithExpected(Element element, String expectedName, String expectedNamespace) {
65 XmlElement xmlElement = XmlElement.fromDomElementWithExpected(element, expectedName);
66 xmlElement.checkNamespace(expectedNamespace);
70 private static Map<String, String> extractNamespaces(Element typeElement) {
71 Map<String, String> namespaces = new HashMap<>();
72 NamedNodeMap attributes = typeElement.getAttributes();
73 for (int i = 0; i < attributes.getLength(); i++) {
74 Node attribute = attributes.item(i);
75 String attribKey = attribute.getNodeName();
76 if (attribKey.startsWith(XmlUtil.XMLNS_ATTRIBUTE_KEY)) {
78 if (attribKey.equals(XmlUtil.XMLNS_ATTRIBUTE_KEY)) {
81 Preconditions.checkState(attribKey.startsWith(XmlUtil.XMLNS_ATTRIBUTE_KEY + ":"));
82 prefix = attribKey.substring(XmlUtil.XMLNS_ATTRIBUTE_KEY.length() + 1);
84 namespaces.put(prefix, attribute.getNodeValue());
90 public void checkName(String expectedName) {
91 Preconditions.checkArgument(getName().equals(expectedName), "Expected %s xml element but was %s", expectedName,
95 public void checkNamespaceAttribute(String expectedNamespace) {
96 Preconditions.checkArgument(getNamespaceAttribute().equals(expectedNamespace),
97 "Unexpected namespace %s for element %s, should be %s", getNamespaceAttribute(), expectedNamespace);
100 public void checkNamespace(String expectedNamespace) {
101 Preconditions.checkArgument(getNamespace().equals(expectedNamespace),
102 "Unexpected namespace %s for element %s, should be %s", getNamespace(), expectedNamespace);
105 public String getName() {
106 if (element.getLocalName()!=null && !element.getLocalName().equals("")){
107 return element.getLocalName();
109 return element.getTagName();
112 public String getAttribute(String attributeName) {
113 return element.getAttribute(attributeName);
116 public String getAttribute(String attributeName, String namespace) {
117 return element.getAttributeNS(namespace, attributeName);
120 public NodeList getElementsByTagName(String name) {
121 return element.getElementsByTagName(name);
124 public void appendChild(Element element) {
125 this.element.appendChild(element);
128 public Element getDomElement() {
132 public Map<String, Attr> getAttributes() {
134 Map<String, Attr> mappedAttributes = Maps.newHashMap();
136 NamedNodeMap attributes = element.getAttributes();
137 for (int i = 0; i < attributes.getLength(); i++) {
138 Attr attr = (Attr) attributes.item(i);
139 mappedAttributes.put(attr.getNodeName(), attr);
142 return mappedAttributes;
148 private List<XmlElement> getChildElementsInternal(ElementFilteringStrategy strat) {
149 NodeList childNodes = element.getChildNodes();
150 final List<XmlElement> result = new ArrayList<>();
151 for (int i = 0; i < childNodes.getLength(); i++) {
152 Node item = childNodes.item(i);
153 if (item instanceof Element == false) {
156 if (strat.accept((Element) item)) {
157 result.add(new XmlElement((Element) item));
164 public List<XmlElement> getChildElements() {
165 return getChildElementsInternal(new ElementFilteringStrategy() {
167 public boolean accept(Element e) {
173 public List<XmlElement> getChildElementsWithinNamespace(final String childName, String namespace) {
174 return Lists.newArrayList(Collections2.filter(getChildElementsWithinNamespace(namespace),
175 new Predicate<XmlElement>() {
177 public boolean apply(@Nullable XmlElement xmlElement) {
178 return xmlElement.getName().equals(childName);
183 public List<XmlElement> getChildElementsWithinNamespace(final String namespace) {
184 return getChildElementsInternal(new ElementFilteringStrategy() {
186 public boolean accept(Element e) {
187 return XmlElement.fromDomElement(e).getNamespace().equals(namespace);
193 public List<XmlElement> getChildElements(final String tagName) {
194 return getChildElementsInternal(new ElementFilteringStrategy() {
196 public boolean accept(Element e) {
197 return e.getTagName().equals(tagName);
202 public XmlElement getOnlyChildElement(String childName) {
203 List<XmlElement> nameElements = getChildElements(childName);
204 Preconditions.checkState(nameElements.size() == 1, "One element " + childName + " expected in " + toString());
205 return nameElements.get(0);
208 public Optional<XmlElement> getOnlyChildElementOptionally(String childName) {
210 return Optional.of(getOnlyChildElement(childName));
211 } catch (Exception e) {
212 return Optional.absent();
216 public Optional<XmlElement> getOnlyChildElementOptionally(String childName, String namespace) {
218 return Optional.of(getOnlyChildElement(childName, namespace));
219 } catch (Exception e) {
220 return Optional.absent();
224 public XmlElement getOnlyChildElementWithSameNamespace(String childName) {
225 return getOnlyChildElement(childName, getNamespace());
228 public Optional<XmlElement> getOnlyChildElementWithSameNamespaceOptionally(String childName) {
230 return Optional.of(getOnlyChildElement(childName, getNamespace()));
231 } catch (Exception e) {
232 return Optional.absent();
236 public XmlElement getOnlyChildElementWithSameNamespace() {
237 XmlElement childElement = getOnlyChildElement();
238 childElement.checkNamespace(getNamespace());
242 public Optional<XmlElement> getOnlyChildElementWithSameNamespaceOptionally() {
244 XmlElement childElement = getOnlyChildElement();
245 childElement.checkNamespace(getNamespace());
246 return Optional.of(childElement);
247 } catch (Exception e) {
248 return Optional.absent();
252 public XmlElement getOnlyChildElement(final String childName, String namespace) {
253 List<XmlElement> children = getChildElementsWithinNamespace(namespace);
254 children = Lists.newArrayList(Collections2.filter(children, new Predicate<XmlElement>() {
256 public boolean apply(@Nullable XmlElement xmlElement) {
257 return xmlElement.getName().equals(childName);
260 Preconditions.checkState(children.size() == 1, "One element %s:%s expected in %s but was %s", namespace,
261 childName, toString(), children.size());
262 return children.get(0);
265 public XmlElement getOnlyChildElement() {
266 List<XmlElement> children = getChildElements();
267 Preconditions.checkState(children.size() == 1, "One element expected in %s but was %s", toString(),
269 return children.get(0);
272 public String getTextContent() {
273 Node textChild = element.getFirstChild();
274 Preconditions.checkNotNull(textChild, "Child node expected, got null for " + getName() + " : " + element);
275 Preconditions.checkState(textChild instanceof Text, getName() + " should contain text." +
276 Text.class.getName() + " expected, got " + textChild);
277 String content = textChild.getTextContent();
279 return content.trim();
282 public String getNamespaceAttribute() {
283 String attribute = element.getAttribute(XmlUtil.XMLNS_ATTRIBUTE_KEY);
284 Preconditions.checkState(attribute != null && !attribute.equals(""), "Element %s must specify namespace",
289 public String getNamespace() {
290 String namespaceURI = element.getNamespaceURI();
291 Preconditions.checkState(namespaceURI != null, "No namespace defined for %s", this);
296 public String toString() {
297 final StringBuilder sb = new StringBuilder("XmlElement{");
298 sb.append("name='").append(getName()).append('\'');
299 if (element.getNamespaceURI() != null) {
300 sb.append(", namespace='").append(getNamespace()).append('\'');
303 return sb.toString();
307 * Search for element's attributes defining namespaces. Look for the one
308 * namespace that matches prefix of element's text content. E.g.
312 * xmlns:th-java="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">th-java:threadfactory-naming</type>
315 * returns {"th-java","urn:.."}. If no prefix is matched, then default
316 * namespace is returned with empty string as key. If no default namespace
317 * is found value will be null.
319 public Map.Entry<String/* prefix */, String/* namespace */> findNamespaceOfTextContent() {
320 Map<String, String> namespaces = extractNamespaces(element);
321 String textContent = getTextContent();
322 int indexOfColon = textContent.indexOf(':');
324 if (indexOfColon > -1) {
325 prefix = textContent.substring(0, indexOfColon);
329 if (namespaces.containsKey(prefix) == false) {
330 throw new IllegalArgumentException("Cannot find namespace for " + XmlUtil.toString(element) + ". Prefix from content is "
331 + prefix + ". Found namespaces " + namespaces);
333 return Maps.immutableEntry(prefix, namespaces.get(prefix));
336 public List<XmlElement> getChildElementsWithSameNamespace(final String childName) {
337 List<XmlElement> children = getChildElementsWithinNamespace(getNamespace());
338 return Lists.newArrayList(Collections2.filter(children, new Predicate<XmlElement>() {
340 public boolean apply(@Nullable XmlElement xmlElement) {
341 return xmlElement.getName().equals(childName);
346 public void checkUnrecognisedElements(List<XmlElement> recognisedElements,
347 XmlElement... additionalRecognisedElements) {
348 List<XmlElement> childElements = getChildElements();
349 childElements.removeAll(recognisedElements);
350 for (XmlElement additionalRecognisedElement : additionalRecognisedElements) {
351 childElements.remove(additionalRecognisedElement);
353 Preconditions.checkState(childElements.isEmpty(), "Unrecognised elements %s in %s", childElements, this);
356 public void checkUnrecognisedElements(XmlElement... additionalRecognisedElements) {
357 checkUnrecognisedElements(Collections.<XmlElement> emptyList(), additionalRecognisedElements);
361 public boolean equals(Object o) {
365 if (o == null || getClass() != o.getClass()) {
369 XmlElement that = (XmlElement) o;
371 if (!element.isEqualNode(that.element)) {
379 public int hashCode() {
380 return element.hashCode();
383 public boolean hasNamespace() {
385 getNamespaceAttribute();
386 } catch (IllegalStateException e) {
389 } catch (IllegalStateException e1) {
397 private interface ElementFilteringStrategy {
398 boolean accept(Element e);