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 java.io.IOException;
14 import javax.annotation.Nullable;
17 import org.xml.sax.SAXException;
19 import com.google.common.base.Optional;
20 import com.google.common.base.Preconditions;
21 import com.google.common.base.Predicate;
22 import com.google.common.collect.Collections2;
23 import com.google.common.collect.Lists;
24 import com.google.common.collect.Maps;
26 public class XmlElement {
28 public final Element element;
30 private XmlElement(Element element) {
31 this.element = element;
34 public static XmlElement fromDomElement(Element e) {
35 return new XmlElement(e);
38 public static XmlElement fromDomDocument(Document xml) {
39 return new XmlElement(xml.getDocumentElement());
42 public static XmlElement fromString(String s) {
44 return new XmlElement(XmlUtil.readXmlToElement(s));
45 } catch (IOException | SAXException e) {
46 throw new IllegalArgumentException("Unable to create from " + s, e);
50 public static XmlElement fromDomElementWithExpected(Element element, String expectedName) {
51 XmlElement xmlElement = XmlElement.fromDomElement(element);
52 xmlElement.checkName(expectedName);
56 public static XmlElement fromDomElementWithExpected(Element element, String expectedName, String expectedNamespace) {
57 XmlElement xmlElement = XmlElement.fromDomElementWithExpected(element, expectedName);
58 xmlElement.checkNamespace(expectedNamespace);
62 private static Map<String, String> extractNamespaces(Element typeElement) {
63 Map<String, String> namespaces = new HashMap<>();
64 NamedNodeMap attributes = typeElement.getAttributes();
65 for (int i = 0; i < attributes.getLength(); i++) {
66 Node attribute = attributes.item(i);
67 String attribKey = attribute.getNodeName();
68 if (attribKey.startsWith(XmlUtil.XMLNS_ATTRIBUTE_KEY)) {
70 if (attribKey.equals(XmlUtil.XMLNS_ATTRIBUTE_KEY)) {
73 Preconditions.checkState(attribKey.startsWith(XmlUtil.XMLNS_ATTRIBUTE_KEY + ":"));
74 prefix = attribKey.substring(XmlUtil.XMLNS_ATTRIBUTE_KEY.length() + 1);
76 namespaces.put(prefix, attribute.getNodeValue());
82 public void checkName(String expectedName) {
83 Preconditions.checkArgument(getName().equals(expectedName), "Expected %s xml element but was %s", expectedName,
87 public void checkNamespaceAttribute(String expectedNamespace) {
88 Preconditions.checkArgument(getNamespaceAttribute().equals(expectedNamespace),
89 "Unexpected namespace %s for element %s, should be %s", getNamespaceAttribute(), expectedNamespace);
92 public void checkNamespace(String expectedNamespace) {
93 Preconditions.checkArgument(getNamespace().equals(expectedNamespace),
94 "Unexpected namespace %s for element %s, should be %s", getNamespace(), expectedNamespace);
97 public String getName() {
98 return element.getTagName();
101 public String getAttribute(String attributeName) {
102 return element.getAttribute(attributeName);
105 public String getAttribute(String attributeName, String namespace) {
106 return element.getAttributeNS(namespace, attributeName);
109 public void appendChild(Element element) {
110 this.element.appendChild(element);
111 // Element newElement = (Element) element.cloneNode(true);
112 // newElement.appendChild(configElement);
113 // return XmlElement.fromDomElement(newElement);
116 public Element getDomElement() {
120 public Map<String, Attr> getAttributes() {
122 Map<String, Attr> mappedAttributes = Maps.newHashMap();
124 NamedNodeMap attributes = element.getAttributes();
125 for (int i = 0; i < attributes.getLength(); i++) {
126 Attr attr = (Attr) attributes.item(i);
127 mappedAttributes.put(attr.getNodeName(), attr);
130 return mappedAttributes;
136 private List<XmlElement> getChildElementsInternal(ElementFilteringStrategy strat) {
137 NodeList childNodes = element.getChildNodes();
138 final List<XmlElement> result = new ArrayList<>();
139 for (int i = 0; i < childNodes.getLength(); i++) {
140 Node item = childNodes.item(i);
141 if (item instanceof Element == false)
143 if (strat.accept((Element) item))
144 result.add(new XmlElement((Element) item));
150 public List<XmlElement> getChildElements() {
151 return getChildElementsInternal(new ElementFilteringStrategy() {
153 public boolean accept(Element e) {
159 public List<XmlElement> getChildElementsWithinNamespace(final String childName, String namespace) {
160 return Lists.newArrayList(Collections2.filter(getChildElementsWithinNamespace(namespace),
161 new Predicate<XmlElement>() {
163 public boolean apply(@Nullable XmlElement xmlElement) {
164 return xmlElement.getName().equals(childName);
169 public List<XmlElement> getChildElementsWithinNamespace(final String namespace) {
170 return getChildElementsInternal(new ElementFilteringStrategy() {
172 public boolean accept(Element e) {
173 return XmlElement.fromDomElement(e).getNamespace().equals(namespace);
179 public List<XmlElement> getChildElements(final String tagName) {
180 return getChildElementsInternal(new ElementFilteringStrategy() {
182 public boolean accept(Element e) {
183 return e.getTagName().equals(tagName);
188 public XmlElement getOnlyChildElement(String childName) {
189 List<XmlElement> nameElements = getChildElements(childName);
190 Preconditions.checkState(nameElements.size() == 1, "One element " + childName + " expected in " + toString());
191 return nameElements.get(0);
194 public Optional<XmlElement> getOnlyChildElementOptionally(String childName) {
196 return Optional.of(getOnlyChildElement(childName));
197 } catch (Exception e) {
198 return Optional.absent();
202 public Optional<XmlElement> getOnlyChildElementOptionally(String childName, String namespace) {
204 return Optional.of(getOnlyChildElement(childName, namespace));
205 } catch (Exception e) {
206 return Optional.absent();
210 public XmlElement getOnlyChildElementWithSameNamespace(String childName) {
211 return getOnlyChildElement(childName, getNamespace());
214 public Optional<XmlElement> getOnlyChildElementWithSameNamespaceOptionally(String childName) {
216 return Optional.of(getOnlyChildElement(childName, getNamespace()));
217 } catch (Exception e) {
218 return Optional.absent();
222 public XmlElement getOnlyChildElementWithSameNamespace() {
223 XmlElement childElement = getOnlyChildElement();
224 childElement.checkNamespace(getNamespace());
228 public Optional<XmlElement> getOnlyChildElementWithSameNamespaceOptionally() {
230 XmlElement childElement = getOnlyChildElement();
231 childElement.checkNamespace(getNamespace());
232 return Optional.of(childElement);
233 } catch (Exception e) {
234 return Optional.absent();
238 public XmlElement getOnlyChildElement(final String childName, String namespace) {
239 List<XmlElement> children = getChildElementsWithinNamespace(namespace);
240 children = Lists.newArrayList(Collections2.filter(children, new Predicate<XmlElement>() {
242 public boolean apply(@Nullable XmlElement xmlElement) {
243 return xmlElement.getName().equals(childName);
246 Preconditions.checkState(children.size() == 1, "One element %s:%s expected in %s but was %s", namespace,
247 childName, toString(), children.size());
248 return children.get(0);
251 public XmlElement getOnlyChildElement() {
252 List<XmlElement> children = getChildElements();
253 Preconditions.checkState(children.size() == 1, "One element expected in %s but was %s", toString(),
255 return children.get(0);
258 public String getTextContent() {
259 Node textChild = element.getFirstChild();
260 Preconditions.checkState(textChild instanceof Text, getName() + " should contain text");
261 String content = textChild.getTextContent();
263 return content.trim();
266 public String getNamespaceAttribute() {
267 String attribute = element.getAttribute(XmlUtil.XMLNS_ATTRIBUTE_KEY);
268 Preconditions.checkState(attribute != null && !attribute.equals(""), "Element %s must specify a %s attribute",
269 toString(), XmlUtil.XMLNS_ATTRIBUTE_KEY);
273 public String getNamespace() {
274 String namespaceURI = element.getNamespaceURI();
275 Preconditions.checkState(namespaceURI != null, "No namespace defined for %s", this);
276 return namespaceURI.toString();
280 public String toString() {
281 final StringBuffer sb = new StringBuffer("XmlElement{");
282 sb.append("name='").append(getName()).append('\'');
283 if (element.getNamespaceURI() != null) {
284 sb.append(", namespace='").append(getNamespace()).append('\'');
287 return sb.toString();
291 * Search for element's attributes defining namespaces. Look for the one
292 * namespace that matches prefix of element's text content. E.g.
296 * xmlns:th-java="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">th-java:threadfactory-naming</type>
299 * returns {"th-java","urn:.."}. If no prefix is matched, then default
300 * namespace is returned with empty string as key. If no default namespace
301 * is found value will be null.
303 public Map.Entry<String/* prefix */, String/* namespace */> findNamespaceOfTextContent() {
304 Map<String, String> namespaces = extractNamespaces(element);
305 String textContent = getTextContent();
306 int indexOfColon = textContent.indexOf(":");
308 if (indexOfColon > -1) {
309 prefix = textContent.substring(0, indexOfColon);
313 if (namespaces.containsKey(prefix) == false) {
314 throw new IllegalArgumentException("Cannot find namespace for " + element + ". Prefix from content is "
315 + prefix + ". Found namespaces " + namespaces);
317 return Maps.immutableEntry(prefix, namespaces.get(prefix));
320 public List<XmlElement> getChildElementsWithSameNamespace(final String childName) {
321 List<XmlElement> children = getChildElementsWithinNamespace(getNamespace());
322 return Lists.newArrayList(Collections2.filter(children, new Predicate<XmlElement>() {
324 public boolean apply(@Nullable XmlElement xmlElement) {
325 return xmlElement.getName().equals(childName);
330 public void checkUnrecognisedElements(List<XmlElement> recognisedElements,
331 XmlElement... additionalRecognisedElements) {
332 List<XmlElement> childElements = getChildElements();
333 childElements.removeAll(recognisedElements);
334 for (XmlElement additionalRecognisedElement : additionalRecognisedElements) {
335 childElements.remove(additionalRecognisedElement);
337 Preconditions.checkState(childElements.isEmpty(), "Unrecognised elements %s in %s", childElements, this);
340 public void checkUnrecognisedElements(XmlElement... additionalRecognisedElements) {
341 checkUnrecognisedElements(Collections.<XmlElement> emptyList(), additionalRecognisedElements);
345 public boolean equals(Object o) {
348 if (o == null || getClass() != o.getClass())
351 XmlElement that = (XmlElement) o;
353 if (!element.isEqualNode(that.element))
360 public int hashCode() {
361 return element.hashCode();
364 private static interface ElementFilteringStrategy {
365 boolean accept(Element e);