package org.opendaylight.controller.netconf.util.xml;
import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
+import com.google.common.base.Strings;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.util.exception.MissingNameSpaceException;
+import org.opendaylight.controller.netconf.util.exception.UnexpectedElementException;
+import org.opendaylight.controller.netconf.util.exception.UnexpectedNamespaceException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;
-import javax.annotation.Nullable;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+public final class XmlElement {
-public class XmlElement {
+ public static final String DEFAULT_NAMESPACE_PREFIX = "";
- public final Element element;
+ private final Element element;
+ private static final Logger logger = LoggerFactory.getLogger(XmlElement.class);
private XmlElement(Element element) {
this.element = element;
return new XmlElement(xml.getDocumentElement());
}
- public static XmlElement fromString(String s) {
+ public static XmlElement fromString(String s) throws NetconfDocumentedException {
try {
return new XmlElement(XmlUtil.readXmlToElement(s));
} catch (IOException | SAXException e) {
- throw new IllegalArgumentException("Unable to create from " + s, e);
+ throw NetconfDocumentedException.wrap(e);
}
}
- public static XmlElement fromDomElementWithExpected(Element element, String expectedName) {
+ public static XmlElement fromDomElementWithExpected(Element element, String expectedName) throws NetconfDocumentedException {
XmlElement xmlElement = XmlElement.fromDomElement(element);
xmlElement.checkName(expectedName);
return xmlElement;
}
- public static XmlElement fromDomElementWithExpected(Element element, String expectedName, String expectedNamespace) {
+ public static XmlElement fromDomElementWithExpected(Element element, String expectedName, String expectedNamespace) throws NetconfDocumentedException {
XmlElement xmlElement = XmlElement.fromDomElementWithExpected(element, expectedName);
xmlElement.checkNamespace(expectedNamespace);
return xmlElement;
}
- private static Map<String, String> extractNamespaces(Element typeElement) {
+ private Map<String, String> extractNamespaces() throws NetconfDocumentedException {
Map<String, String> namespaces = new HashMap<>();
- NamedNodeMap attributes = typeElement.getAttributes();
+ NamedNodeMap attributes = element.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Node attribute = attributes.item(i);
String attribKey = attribute.getNodeName();
if (attribKey.startsWith(XmlUtil.XMLNS_ATTRIBUTE_KEY)) {
String prefix;
if (attribKey.equals(XmlUtil.XMLNS_ATTRIBUTE_KEY)) {
- prefix = "";
+ prefix = DEFAULT_NAMESPACE_PREFIX;
} else {
- Preconditions.checkState(attribKey.startsWith(XmlUtil.XMLNS_ATTRIBUTE_KEY + ":"));
+ if (!attribKey.startsWith(XmlUtil.XMLNS_ATTRIBUTE_KEY + ":")){
+ throw new NetconfDocumentedException("Attribute doesn't start with :",
+ NetconfDocumentedException.ErrorType.application,
+ NetconfDocumentedException.ErrorTag.invalid_value,
+ NetconfDocumentedException.ErrorSeverity.error);
+ }
prefix = attribKey.substring(XmlUtil.XMLNS_ATTRIBUTE_KEY.length() + 1);
}
namespaces.put(prefix, attribute.getNodeValue());
}
}
+
+ // namespace does not have to be defined on this element but inherited
+ if(!namespaces.containsKey(DEFAULT_NAMESPACE_PREFIX)) {
+ Optional<String> namespaceOptionally = getNamespaceOptionally();
+ if(namespaceOptionally.isPresent()) {
+ namespaces.put(DEFAULT_NAMESPACE_PREFIX, namespaceOptionally.get());
+ }
+ }
+
return namespaces;
}
- public void checkName(String expectedName) {
- Preconditions.checkArgument(getName().equals(expectedName), "Expected %s xml element but was %s", expectedName,
- getName());
+ public void checkName(String expectedName) throws UnexpectedElementException {
+ if (!getName().equals(expectedName)){
+ throw new UnexpectedElementException(String.format("Expected %s xml element but was %s", expectedName,
+ getName()),
+ NetconfDocumentedException.ErrorType.application,
+ NetconfDocumentedException.ErrorTag.operation_failed,
+ NetconfDocumentedException.ErrorSeverity.error);
+ }
}
- public void checkNamespaceAttribute(String expectedNamespace) {
- Preconditions.checkArgument(getNamespaceAttribute().equals(expectedNamespace),
- "Unexpected namespace %s for element %s, should be %s", getNamespaceAttribute(), expectedNamespace);
+ public void checkNamespaceAttribute(String expectedNamespace) throws UnexpectedNamespaceException, MissingNameSpaceException {
+ if (!getNamespaceAttribute().equals(expectedNamespace))
+ {
+ throw new UnexpectedNamespaceException(String.format("Unexpected namespace %s should be %s",
+ getNamespaceAttribute(),
+ expectedNamespace),
+ NetconfDocumentedException.ErrorType.application,
+ NetconfDocumentedException.ErrorTag.operation_failed,
+ NetconfDocumentedException.ErrorSeverity.error);
+ }
}
- public void checkNamespace(String expectedNamespace) {
- Preconditions.checkArgument(getNamespace().equals(expectedNamespace),
- "Unexpected namespace %s for element %s, should be %s", getNamespace(), expectedNamespace);
+ public void checkNamespace(String expectedNamespace) throws UnexpectedNamespaceException, MissingNameSpaceException {
+ if (!getNamespace().equals(expectedNamespace))
+ {
+ throw new UnexpectedNamespaceException(String.format("Unexpected namespace %s should be %s",
+ getNamespace(),
+ expectedNamespace),
+ NetconfDocumentedException.ErrorType.application,
+ NetconfDocumentedException.ErrorTag.operation_failed,
+ NetconfDocumentedException.ErrorSeverity.error);
+ }
}
public String getName() {
- if (element.getLocalName()!=null && !element.getLocalName().equals("")){
+ if (element.getLocalName()!=null && !element.getLocalName().equals(DEFAULT_NAMESPACE_PREFIX)){
return element.getLocalName();
}
return element.getTagName();
public void appendChild(Element element) {
this.element.appendChild(element);
- // Element newElement = (Element) element.cloneNode(true);
- // newElement.appendChild(configElement);
- // return XmlElement.fromDomElement(newElement);
}
public Element getDomElement() {
final List<XmlElement> result = new ArrayList<>();
for (int i = 0; i < childNodes.getLength(); i++) {
Node item = childNodes.item(i);
- if (item instanceof Element == false)
+ if (!(item instanceof Element)) {
continue;
- if (strat.accept((Element) item))
+ }
+ if (strat.accept((Element) item)) {
result.add(new XmlElement((Element) item));
+ }
}
return result;
return Lists.newArrayList(Collections2.filter(getChildElementsWithinNamespace(namespace),
new Predicate<XmlElement>() {
@Override
- public boolean apply(@Nullable XmlElement xmlElement) {
+ public boolean apply(XmlElement xmlElement) {
return xmlElement.getName().equals(childName);
}
}));
return getChildElementsInternal(new ElementFilteringStrategy() {
@Override
public boolean accept(Element e) {
- return XmlElement.fromDomElement(e).getNamespace().equals(namespace);
+ try {
+ return XmlElement.fromDomElement(e).getNamespace().equals(namespace);
+ } catch (MissingNameSpaceException e1) {
+ return false;
+ }
}
});
}
+ /**
+ *
+ * @param tagName tag name without prefix
+ * @return
+ */
public List<XmlElement> getChildElements(final String tagName) {
return getChildElementsInternal(new ElementFilteringStrategy() {
@Override
public boolean accept(Element e) {
- return e.getTagName().equals(tagName);
+ // localName returns pure localName without prefix
+ return e.getLocalName().equals(tagName);
}
});
}
- public XmlElement getOnlyChildElement(String childName) {
+ public XmlElement getOnlyChildElement(String childName) throws NetconfDocumentedException {
List<XmlElement> nameElements = getChildElements(childName);
- Preconditions.checkState(nameElements.size() == 1, "One element " + childName + " expected in " + toString());
+ if (nameElements.size() != 1){
+ throw new NetconfDocumentedException("One element " + childName + " expected in " + toString(),
+ NetconfDocumentedException.ErrorType.application,
+ NetconfDocumentedException.ErrorTag.invalid_value,
+ NetconfDocumentedException.ErrorSeverity.error);
+ }
return nameElements.get(0);
}
}
}
- public XmlElement getOnlyChildElementWithSameNamespace(String childName) {
+ public XmlElement getOnlyChildElementWithSameNamespace(String childName) throws NetconfDocumentedException {
return getOnlyChildElement(childName, getNamespace());
}
}
}
- public XmlElement getOnlyChildElementWithSameNamespace() {
+ public XmlElement getOnlyChildElementWithSameNamespace() throws NetconfDocumentedException {
XmlElement childElement = getOnlyChildElement();
childElement.checkNamespace(getNamespace());
return childElement;
}
}
- public XmlElement getOnlyChildElement(final String childName, String namespace) {
+ public XmlElement getOnlyChildElement(final String childName, String namespace) throws NetconfDocumentedException {
List<XmlElement> children = getChildElementsWithinNamespace(namespace);
children = Lists.newArrayList(Collections2.filter(children, new Predicate<XmlElement>() {
@Override
- public boolean apply(@Nullable XmlElement xmlElement) {
+ public boolean apply(XmlElement xmlElement) {
return xmlElement.getName().equals(childName);
}
}));
- Preconditions.checkState(children.size() == 1, "One element %s:%s expected in %s but was %s", namespace,
- childName, toString(), children.size());
+ if (children.size() != 1){
+ throw new NetconfDocumentedException(String.format("One element %s:%s expected in %s but was %s", namespace,
+ childName, toString(), children.size()),
+ NetconfDocumentedException.ErrorType.application,
+ NetconfDocumentedException.ErrorTag.invalid_value,
+ NetconfDocumentedException.ErrorSeverity.error);
+ }
+
return children.get(0);
}
- public XmlElement getOnlyChildElement() {
+ public XmlElement getOnlyChildElement() throws NetconfDocumentedException {
List<XmlElement> children = getChildElements();
- Preconditions.checkState(children.size() == 1, "One element expected in %s but was %s", toString(),
- children.size());
+ if (children.size() != 1){
+ throw new NetconfDocumentedException(String.format( "One element expected in %s but was %s", toString(),
+ children.size()),
+ NetconfDocumentedException.ErrorType.application,
+ NetconfDocumentedException.ErrorTag.invalid_value,
+ NetconfDocumentedException.ErrorSeverity.error);
+ }
return children.get(0);
}
- public String getTextContent() {
- Node textChild = element.getFirstChild();
- Preconditions.checkNotNull(textChild, "Child node expected, got null for " + getName() + " : " + element);
- Preconditions.checkState(textChild instanceof Text, getName() + " should contain text." +
- Text.class.getName() + " expected, got " + textChild);
- String content = textChild.getTextContent();
- // Trim needed
- return content.trim();
+ public String getTextContent() throws NetconfDocumentedException {
+ NodeList childNodes = element.getChildNodes();
+ if (childNodes.getLength() == 0) {
+ return DEFAULT_NAMESPACE_PREFIX;
+ }
+ for(int i = 0; i < childNodes.getLength(); i++) {
+ Node textChild = childNodes.item(i);
+ if (textChild instanceof Text) {
+ String content = textChild.getTextContent();
+ return content.trim();
+ }
+ }
+ throw new NetconfDocumentedException(getName() + " should contain text.",
+ NetconfDocumentedException.ErrorType.application,
+ NetconfDocumentedException.ErrorTag.invalid_value,
+ NetconfDocumentedException.ErrorSeverity.error
+ );
+ }
+
+ public Optional<String> getOnlyTextContentOptionally() {
+ // only return text content if this node has exactly one Text child node
+ if (element.getChildNodes().getLength() == 1) {
+ Node item = element.getChildNodes().item(0);
+ if (item instanceof Text) {
+ return Optional.of(((Text) item).getWholeText());
+ }
+ }
+ return Optional.absent();
}
- public String getNamespaceAttribute() {
+ public String getNamespaceAttribute() throws MissingNameSpaceException {
String attribute = element.getAttribute(XmlUtil.XMLNS_ATTRIBUTE_KEY);
- Preconditions.checkState(attribute != null && !attribute.equals(""), "Element %s must specify namespace",
- toString());
+ if (attribute == null || attribute.equals(DEFAULT_NAMESPACE_PREFIX)){
+ throw new MissingNameSpaceException(String.format("Element %s must specify namespace",
+ toString()),
+ NetconfDocumentedException.ErrorType.application,
+ NetconfDocumentedException.ErrorTag.operation_failed,
+ NetconfDocumentedException.ErrorSeverity.error);
+ }
return attribute;
}
- public String getNamespace() {
+ public Optional<String> getNamespaceOptionally() {
String namespaceURI = element.getNamespaceURI();
- Preconditions.checkState(namespaceURI != null, "No namespace defined for %s", this);
- return namespaceURI.toString();
+ if (Strings.isNullOrEmpty(namespaceURI)) {
+ return Optional.absent();
+ } else {
+ return Optional.of(namespaceURI);
+ }
+ }
+
+ public String getNamespace() throws MissingNameSpaceException {
+ Optional<String> namespaceURI = getNamespaceOptionally();
+ if (namespaceURI.isPresent() == false){
+ throw new MissingNameSpaceException(String.format("No namespace defined for %s", this),
+ NetconfDocumentedException.ErrorType.application,
+ NetconfDocumentedException.ErrorTag.operation_failed,
+ NetconfDocumentedException.ErrorSeverity.error);
+ }
+ return namespaceURI.get();
}
@Override
public String toString() {
- final StringBuffer sb = new StringBuffer("XmlElement{");
+ final StringBuilder sb = new StringBuilder("XmlElement{");
sb.append("name='").append(getName()).append('\'');
if (element.getNamespaceURI() != null) {
- sb.append(", namespace='").append(getNamespace()).append('\'');
+ try {
+ sb.append(", namespace='").append(getNamespace()).append('\'');
+ } catch (MissingNameSpaceException e) {
+ logger.trace("Missing namespace for element.");
+ }
}
sb.append('}');
return sb.toString();
* namespace is returned with empty string as key. If no default namespace
* is found value will be null.
*/
- public Map.Entry<String/* prefix */, String/* namespace */> findNamespaceOfTextContent() {
- Map<String, String> namespaces = extractNamespaces(element);
+ public Map.Entry<String/* prefix */, String/* namespace */> findNamespaceOfTextContent() throws NetconfDocumentedException {
+ Map<String, String> namespaces = extractNamespaces();
String textContent = getTextContent();
- int indexOfColon = textContent.indexOf(":");
+ int indexOfColon = textContent.indexOf(':');
String prefix;
if (indexOfColon > -1) {
prefix = textContent.substring(0, indexOfColon);
} else {
- prefix = "";
+ prefix = DEFAULT_NAMESPACE_PREFIX;
}
- if (namespaces.containsKey(prefix) == false) {
+ if (!namespaces.containsKey(prefix)) {
throw new IllegalArgumentException("Cannot find namespace for " + XmlUtil.toString(element) + ". Prefix from content is "
+ prefix + ". Found namespaces " + namespaces);
}
return Maps.immutableEntry(prefix, namespaces.get(prefix));
}
- public List<XmlElement> getChildElementsWithSameNamespace(final String childName) {
+ public List<XmlElement> getChildElementsWithSameNamespace(final String childName) throws MissingNameSpaceException {
List<XmlElement> children = getChildElementsWithinNamespace(getNamespace());
return Lists.newArrayList(Collections2.filter(children, new Predicate<XmlElement>() {
@Override
- public boolean apply(@Nullable XmlElement xmlElement) {
+ public boolean apply(XmlElement xmlElement) {
return xmlElement.getName().equals(childName);
}
}));
}
public void checkUnrecognisedElements(List<XmlElement> recognisedElements,
- XmlElement... additionalRecognisedElements) {
+ XmlElement... additionalRecognisedElements) throws NetconfDocumentedException {
List<XmlElement> childElements = getChildElements();
childElements.removeAll(recognisedElements);
for (XmlElement additionalRecognisedElement : additionalRecognisedElements) {
childElements.remove(additionalRecognisedElement);
}
- Preconditions.checkState(childElements.isEmpty(), "Unrecognised elements %s in %s", childElements, this);
+ if (!childElements.isEmpty()){
+ throw new NetconfDocumentedException(String.format("Unrecognised elements %s in %s", childElements, this),
+ NetconfDocumentedException.ErrorType.application,
+ NetconfDocumentedException.ErrorTag.invalid_value,
+ NetconfDocumentedException.ErrorSeverity.error);
+ }
}
- public void checkUnrecognisedElements(XmlElement... additionalRecognisedElements) {
- checkUnrecognisedElements(Collections.<XmlElement> emptyList(), additionalRecognisedElements);
+ public void checkUnrecognisedElements(XmlElement... additionalRecognisedElements) throws NetconfDocumentedException {
+ checkUnrecognisedElements(Collections.<XmlElement>emptyList(), additionalRecognisedElements);
}
@Override
public boolean equals(Object o) {
- if (this == o)
+ if (this == o) {
return true;
- if (o == null || getClass() != o.getClass())
+ }
+ if (o == null || getClass() != o.getClass()) {
return false;
+ }
XmlElement that = (XmlElement) o;
- if (!element.isEqualNode(that.element))
+ if (!element.isEqualNode(that.element)) {
return false;
+ }
return true;
}
public boolean hasNamespace() {
try {
getNamespaceAttribute();
- } catch (IllegalStateException e) {
+ } catch (MissingNameSpaceException e) {
try {
getNamespace();
- } catch (IllegalStateException e1) {
+ } catch (MissingNameSpaceException e1) {
return false;
}
return true;
return true;
}
- private static interface ElementFilteringStrategy {
+ private interface ElementFilteringStrategy {
boolean accept(Element e);
}
}