Moving common code out of remote rpc to clustering common bundle
authorHarman Singh <harmasin@cisco.com>
Sat, 16 Aug 2014 00:50:58 +0000 (17:50 -0700)
committerHarman Singh <harmasin@cisco.com>
Mon, 18 Aug 2014 22:58:29 +0000 (15:58 -0700)
Change-Id: Iad469f809ac55b2d873ff4d48e912ad66184c68d
Signed-off-by: Harman Singh <harmasin@cisco.com>
java/org/opendaylight/controller/xml/codec/InstanceIdentifierForXmlCodec.java [new file with mode: 0644]
java/org/opendaylight/controller/xml/codec/RandomPrefix.java [new file with mode: 0644]
java/org/opendaylight/controller/xml/codec/XmlDocumentUtils.java [new file with mode: 0644]
java/org/opendaylight/controller/xml/codec/XmlStreamUtils.java [new file with mode: 0644]
java/org/opendaylight/controller/xml/codec/XmlUtils.java [new file with mode: 0644]

diff --git a/java/org/opendaylight/controller/xml/codec/InstanceIdentifierForXmlCodec.java b/java/org/opendaylight/controller/xml/codec/InstanceIdentifierForXmlCodec.java
new file mode 100644 (file)
index 0000000..a9a4914
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.xml.codec;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Element;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class InstanceIdentifierForXmlCodec {
+  private static final Pattern PREDICATE_PATTERN = Pattern.compile("\\[(.*?)\\]");
+  private static final Splitter SLASH_SPLITTER = Splitter.on('/');
+  private static final Splitter COLON_SPLITTER = Splitter.on(':');
+  private static final Splitter AT_SPLITTER = Splitter.on('@');
+  private static final Logger logger = LoggerFactory.getLogger(InstanceIdentifierForXmlCodec.class);
+
+  private InstanceIdentifierForXmlCodec() {
+    throw new UnsupportedOperationException("Utility class");
+  }
+
+  public static YangInstanceIdentifier deserialize(final Element element, final SchemaContext schemaContext) {
+    Preconditions.checkNotNull(element, "Value of element for deserialization can't be null");
+    Preconditions.checkNotNull(schemaContext,
+        "Schema context for deserialization of instance identifier type can't be null");
+
+    final String valueTrimmed = element.getTextContent().trim();
+    logger.debug("Instance identifier derserialize: splitting the text {} with Slash to find path arguments", valueTrimmed);
+    final Iterator<String> xPathParts = SLASH_SPLITTER.split(valueTrimmed).iterator();
+
+    // must be at least "/pr:node"
+    if (!xPathParts.hasNext() || !xPathParts.next().isEmpty() || !xPathParts.hasNext()) {
+      logger.debug("Instance identifier derserialize: No path argument found for element.");
+      return null;
+    }
+
+    List<PathArgument> result = new ArrayList<>();
+    while (xPathParts.hasNext()) {
+      String xPathPartTrimmed = xPathParts.next().trim();
+
+      PathArgument pathArgument = toPathArgument(xPathPartTrimmed, element, schemaContext);
+      if (pathArgument != null) {
+        result.add(pathArgument);
+      }
+    }
+    return YangInstanceIdentifier.create(result);
+  }
+
+  public static Element serialize(final YangInstanceIdentifier id, final Element element) {
+    Preconditions.checkNotNull(id, "Variable should contain instance of instance identifier and can't be null");
+    Preconditions.checkNotNull(element, "DOM element can't be null");
+
+    final RandomPrefix prefixes = new RandomPrefix();
+    final String str = XmlUtils.encodeIdentifier(prefixes, id);
+
+    for (Entry<URI, String> e: prefixes.getPrefixes()) {
+      element.setAttribute("xmlns:" + e.getValue(), e.getKey().toString());
+    }
+    element.setTextContent(str);
+    return element;
+  }
+
+  private static String getIdAndPrefixAsStr(final String pathPart) {
+    int predicateStartIndex = pathPart.indexOf('[');
+    return predicateStartIndex == -1 ? pathPart : pathPart.substring(0, predicateStartIndex);
+  }
+
+  private static PathArgument toPathArgument(final String xPathArgument, final Element element, final SchemaContext schemaContext) {
+    final QName mainQName = toIdentity(xPathArgument, element, schemaContext);
+
+    // predicates
+    final Matcher matcher = PREDICATE_PATTERN.matcher(xPathArgument);
+    final Map<QName, Object> predicates = new HashMap<>();
+    QName currentQName = mainQName;
+
+    while (matcher.find()) {
+      final String predicateStr = matcher.group(1).trim();
+      final int indexOfEqualityMark = predicateStr.indexOf('=');
+      if (indexOfEqualityMark != -1) {
+        final Object predicateValue = toPredicateValue(predicateStr.substring(indexOfEqualityMark + 1));
+        if (predicateValue == null) {
+          return null;
+        }
+
+        if (predicateStr.charAt(0) != '.') {
+          // target is not a leaf-list
+          currentQName = toIdentity(predicateStr.substring(0, indexOfEqualityMark), element, schemaContext);
+          if (currentQName == null) {
+            return null;
+          }
+        }
+        logger.debug("Instance identifier derserialize: finding predicates of node {}", predicateValue);
+        predicates.put(currentQName, predicateValue);
+      }
+    }
+
+    if (predicates.isEmpty()) {
+      return new YangInstanceIdentifier.NodeIdentifier(mainQName);
+    } else {
+      return new YangInstanceIdentifier.NodeIdentifierWithPredicates(mainQName, predicates);
+    }
+
+  }
+
+  public static QName toIdentity(final String xPathArgument, final Element element, final SchemaContext schemaContext) {
+    final String xPathPartTrimmed = getIdAndPrefixAsStr(xPathArgument).trim();
+    final Iterator<String> it = COLON_SPLITTER.split(xPathPartTrimmed).iterator();
+
+    // Empty string
+    if (!it.hasNext()) {
+      return null;
+    }
+
+    final String prefix = it.next().trim();
+    if (prefix.isEmpty()) {
+      return null;
+    }
+
+    // it is not "prefix:value"
+    if (!it.hasNext()) {
+      return null;
+    }
+
+    final String identifier = it.next().trim();
+    if (identifier.isEmpty()) {
+      return null;
+    }
+
+    URI namespace = null;
+    String namespaceStr = null;
+    try {
+      namespaceStr = element.getAttribute("xmlns:"+prefix);
+      namespace = new URI(namespaceStr);
+    } catch (URISyntaxException e) {
+      throw new IllegalArgumentException("It wasn't possible to convert " + namespaceStr + " to URI object.");
+    } catch (NullPointerException e) {
+      throw new IllegalArgumentException("I wasn't possible to get namespace for prefix " + prefix);
+    }
+
+    Module module = schemaContext.findModuleByNamespaceAndRevision(namespace, null);
+    return QName.create(module.getQNameModule(), identifier);
+  }
+
+  private static String trimIfEndIs(final String str, final char end) {
+    final int l = str.length() - 1;
+    if (str.charAt(l) != end) {
+      return null;
+    }
+
+    return str.substring(1, l);
+  }
+
+  private static Object toPredicateValue(final String predicatedValue) {
+    logger.debug("Instance identifier derserialize: converting the predicate vstring to object {}", predicatedValue);
+    final String predicatedValueTrimmed = predicatedValue.trim();
+    if (predicatedValue.isEmpty()) {
+      return null;
+    }
+    String updatedValue = null;
+    switch (predicatedValueTrimmed.charAt(0)) {
+      case '"':
+        updatedValue =  trimIfEndIs(predicatedValueTrimmed, '"');
+        break;
+      case '\'':
+        updatedValue =  trimIfEndIs(predicatedValueTrimmed, '\'');
+        break;
+      default:
+        updatedValue =  predicatedValueTrimmed;
+    }
+    Iterator<String> it = AT_SPLITTER.split(updatedValue).iterator();
+    // Empty string
+    if (!it.hasNext()) {
+      return null;
+    }
+
+    final String value = it.next().trim();
+    if (value.isEmpty()) {
+      return null;
+    }
+
+    if (!it.hasNext()) {
+      return value;
+    }
+
+    final String type = it.next().trim();
+    if (type.isEmpty()) {
+      return value;
+    }
+    Object predicateObject = null;
+    try {
+      logger.debug("Instance identifier derserialize: converting the predicate value {{}}to correct object type {{}}", value, type);
+      predicateObject = Class.forName(type).getConstructor(String.class).newInstance(value);
+    } catch (Exception e) {
+      logger.error("Could not convert to valid type of value", e);
+    }
+    return predicateObject;
+  }
+}
diff --git a/java/org/opendaylight/controller/xml/codec/RandomPrefix.java b/java/org/opendaylight/controller/xml/codec/RandomPrefix.java
new file mode 100644 (file)
index 0000000..1349e1e
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.xml.codec;
+
+import org.opendaylight.yangtools.yang.common.QName;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * Picked from Yang tools because it was not public class
+ */
+
+final class RandomPrefix {
+    final Map<URI, String> prefixes = new HashMap<>();
+
+    Iterable<Entry<URI, String>> getPrefixes() {
+        return prefixes.entrySet();
+    }
+
+    String encodeQName(final QName qname) {
+        String prefix = prefixes.get(qname.getNamespace());
+        if (prefix == null) {
+            prefix = qname.getPrefix();
+            if (prefix == null || prefix.isEmpty() || prefixes.containsValue(prefix)) {
+                final ThreadLocalRandom random = ThreadLocalRandom.current();
+                do {
+                    final StringBuilder sb = new StringBuilder();
+                    for (int i = 0; i < 4; i++) {
+                        sb.append((char)('a' + random.nextInt(25)));
+                    }
+
+                    prefix = sb.toString();
+                } while (prefixes.containsValue(prefix));
+            }
+
+            prefixes.put(qname.getNamespace(), prefix);
+        }
+
+        return prefix + ':' + qname.getLocalName();
+    }
+}
diff --git a/java/org/opendaylight/controller/xml/codec/XmlDocumentUtils.java b/java/org/opendaylight/controller/xml/codec/XmlDocumentUtils.java
new file mode 100644 (file)
index 0000000..8af6a31
--- /dev/null
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.xml.codec;
+
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.ModifyAction;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.InstanceIdentifierType;
+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.NodeList;
+
+import javax.activation.UnsupportedDataTypeException;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.dom.DOMResult;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkState;
+
+public class XmlDocumentUtils {
+  private static class ElementWithSchemaContext {
+    Element element;
+    SchemaContext schemaContext;
+
+    ElementWithSchemaContext(final Element element,final SchemaContext schemaContext) {
+      this.schemaContext = schemaContext;
+      this.element = element;
+    }
+
+    Element getElement() {
+      return element;
+    }
+
+    SchemaContext getSchemaContext() {
+      return schemaContext;
+    }
+  }
+
+  public static final QName OPERATION_ATTRIBUTE_QNAME = QName.create(URI.create("urn:ietf:params:xml:ns:netconf:base:1.0"), null, "operation");
+  private static final Logger logger = LoggerFactory.getLogger(XmlDocumentUtils.class);
+  private static final XMLOutputFactory FACTORY = XMLOutputFactory.newFactory();
+
+  /**
+   * Converts Data DOM structure to XML Document for specified XML Codec Provider and corresponding
+   * Data Node Container schema. The CompositeNode data parameter enters as root of Data DOM tree and will
+   * be transformed to root in XML Document. Each element of Data DOM tree is compared against specified Data
+   * Node Container Schema and transformed accordingly.
+   *
+   * @param data Data DOM root element
+   * @param schema Data Node Container Schema
+   * @param codecProvider XML Codec Provider
+   * @return new instance of XML Document
+   * @throws javax.activation.UnsupportedDataTypeException
+   */
+  public static Document toDocument(final CompositeNode data, final DataNodeContainer schema, final XmlCodecProvider codecProvider)
+      throws UnsupportedDataTypeException {
+    Preconditions.checkNotNull(data);
+    Preconditions.checkNotNull(schema);
+
+    if (!(schema instanceof ContainerSchemaNode || schema instanceof ListSchemaNode)) {
+      throw new UnsupportedDataTypeException("Schema can be ContainerSchemaNode or ListSchemaNode. Other types are not supported yet.");
+    }
+
+    final DOMResult result = new DOMResult(getDocument());
+    try {
+      final XMLStreamWriter writer = FACTORY.createXMLStreamWriter(result);
+      XmlStreamUtils.create(codecProvider).writeDocument(writer, data, (SchemaNode)schema);
+      writer.close();
+      return (Document)result.getNode();
+    } catch (XMLStreamException e) {
+      logger.error("Failed to serialize data {}", data, e);
+      return null;
+    }
+  }
+
+  public static Document getDocument() {
+    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+    Document doc = null;
+    try {
+      DocumentBuilder bob = dbf.newDocumentBuilder();
+      doc = bob.newDocument();
+    } catch (ParserConfigurationException e) {
+      throw new RuntimeException(e);
+    }
+    return doc;
+  }
+
+
+  public static QName qNameFromElement(final Element xmlElement) {
+    String namespace = xmlElement.getNamespaceURI();
+    String localName = xmlElement.getLocalName();
+    return QName.create(namespace != null ? URI.create(namespace) : null, null, localName);
+  }
+
+  private static Node<?> toNodeWithSchema(final Element xmlElement, final DataSchemaNode schema, final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) {
+    checkQName(xmlElement, schema.getQName());
+    if (schema instanceof DataNodeContainer) {
+      return toCompositeNodeWithSchema(xmlElement, schema.getQName(), (DataNodeContainer) schema, schemaCtx);
+    } else if (schema instanceof LeafSchemaNode) {
+      return toSimpleNodeWithType(xmlElement, (LeafSchemaNode) schema, codecProvider,schemaCtx);
+    } else if (schema instanceof LeafListSchemaNode) {
+      return toSimpleNodeWithType(xmlElement, (LeafListSchemaNode) schema, codecProvider,schemaCtx);
+    }
+    return null;
+  }
+
+
+
+  private static Node<?> toSimpleNodeWithType(final Element xmlElement, final LeafSchemaNode schema,
+                                              final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) {
+    TypeDefinitionAwareCodec<? extends Object, ? extends TypeDefinition<?>> codec = codecProvider.codecFor(schema.getType());
+    String text = xmlElement.getTextContent();
+    Object value = null;
+    if (codec != null) {
+      logger.debug("toSimpleNodeWithType: found codec, deserializing text {}", text);
+      value = codec.deserialize(text);
+    }
+
+    final TypeDefinition<?> baseType = XmlUtils.resolveBaseTypeFrom(schema.getType());
+    if (baseType instanceof InstanceIdentifierType) {
+      logger.debug("toSimpleNodeWithType: base type of node is instance identifier, deserializing element", xmlElement);
+      value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx);
+
+    } else if(baseType instanceof IdentityrefTypeDefinition){
+      logger.debug("toSimpleNodeWithType: base type of node is IdentityrefTypeDefinition, deserializing element", xmlElement);
+      value = InstanceIdentifierForXmlCodec.toIdentity(xmlElement.getTextContent(), xmlElement, schemaCtx);
+
+    }
+
+    if (value == null) {
+      logger.debug("toSimpleNodeWithType: no type found for element, returning just the text string value of element {}", xmlElement);
+      value = xmlElement.getTextContent();
+    }
+
+    Optional<ModifyAction> modifyAction = getModifyOperationFromAttributes(xmlElement);
+    return new SimpleNodeTOImpl<>(schema.getQName(), null, value, modifyAction.orNull());
+  }
+
+  private static Node<?> toSimpleNodeWithType(final Element xmlElement, final LeafListSchemaNode schema,
+                                              final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) {
+    TypeDefinitionAwareCodec<? extends Object, ? extends TypeDefinition<?>> codec = codecProvider.codecFor(schema.getType());
+    String text = xmlElement.getTextContent();
+    Object value = null;
+    if (codec != null) {
+      logger.debug("toSimpleNodeWithType: found codec, deserializing text {}", text);
+      value = codec.deserialize(text);
+    }
+
+    final TypeDefinition<?> baseType = XmlUtils.resolveBaseTypeFrom(schema.getType());
+    if (baseType instanceof InstanceIdentifierType) {
+      logger.debug("toSimpleNodeWithType: base type of node is instance identifier, deserializing element", xmlElement);
+      value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx);
+    }
+
+    if (value == null) {
+      logger.debug("toSimpleNodeWithType: no type found for element, returning just the text string value of element {}", xmlElement);
+      value = xmlElement.getTextContent();
+    }
+
+    Optional<ModifyAction> modifyAction = getModifyOperationFromAttributes(xmlElement);
+    return new SimpleNodeTOImpl<>(schema.getQName(), null, value, modifyAction.orNull());
+  }
+
+  private static Node<?> toCompositeNodeWithSchema(final Element xmlElement, final QName qName, final DataNodeContainer schema,
+                                                   final SchemaContext schemaCtx) {
+    List<Node<?>> values = toDomNodes(xmlElement, Optional.fromNullable(schema.getChildNodes()),schemaCtx);
+    Optional<ModifyAction> modifyAction = getModifyOperationFromAttributes(xmlElement);
+    return ImmutableCompositeNode.create(qName, values, modifyAction.orNull());
+  }
+
+  private static Optional<ModifyAction> getModifyOperationFromAttributes(final Element xmlElement) {
+    Attr attributeNodeNS = xmlElement.getAttributeNodeNS(OPERATION_ATTRIBUTE_QNAME.getNamespace().toString(), OPERATION_ATTRIBUTE_QNAME.getLocalName());
+    if(attributeNodeNS == null) {
+      return Optional.absent();
+    }
+
+    ModifyAction action = ModifyAction.fromXmlValue(attributeNodeNS.getValue());
+    Preconditions.checkArgument(action.isOnElementPermitted(), "Unexpected operation %s on %s", action, xmlElement);
+
+    return Optional.of(action);
+  }
+
+  private static void checkQName(final Element xmlElement, final QName qName) {
+    checkState(Objects.equal(xmlElement.getNamespaceURI(), qName.getNamespace().toString()));
+    checkState(qName.getLocalName().equals(xmlElement.getLocalName()));
+  }
+
+  public static final Optional<DataSchemaNode> findFirstSchema(final QName qname, final Collection<DataSchemaNode> dataSchemaNode) {
+    if (dataSchemaNode != null && !dataSchemaNode.isEmpty() && qname != null) {
+      for (DataSchemaNode dsn : dataSchemaNode) {
+        if (qname.isEqualWithoutRevision(dsn.getQName())) {
+          return Optional.<DataSchemaNode> of(dsn);
+        } else if (dsn instanceof ChoiceNode) {
+          for (ChoiceCaseNode choiceCase : ((ChoiceNode) dsn).getCases()) {
+            Optional<DataSchemaNode> foundDsn = findFirstSchema(qname, choiceCase.getChildNodes());
+            if (foundDsn != null && foundDsn.isPresent()) {
+              return foundDsn;
+            }
+          }
+        }
+      }
+    }
+    return Optional.absent();
+  }
+
+  private static Node<?> toDomNode(Element element) {
+    QName qname = qNameFromElement(element);
+
+    ImmutableList.Builder<Node<?>> values = ImmutableList.<Node<?>> builder();
+    NodeList nodes = element.getChildNodes();
+    boolean isSimpleObject = true;
+    String value = null;
+    for (int i = 0; i < nodes.getLength(); i++) {
+      org.w3c.dom.Node child = nodes.item(i);
+      if (child instanceof Element) {
+        isSimpleObject = false;
+        values.add(toDomNode((Element) child));
+      }
+      if (isSimpleObject && child instanceof org.w3c.dom.Text) {
+        value = element.getTextContent();
+        if (!Strings.isNullOrEmpty(value)) {
+          isSimpleObject = true;
+        }
+      }
+    }
+    if (isSimpleObject) {
+      return new SimpleNodeTOImpl<>(qname, null, value);
+    }
+    return ImmutableCompositeNode.create(qname, values.build());
+  }
+
+  public static List<Node<?>> toDomNodes(final Element element, final Optional<Collection<DataSchemaNode>> context,SchemaContext schemaCtx) {
+    return forEachChild(element.getChildNodes(),schemaCtx, new Function<ElementWithSchemaContext, Optional<Node<?>>>() {
+
+      @Override
+      public Optional<Node<?>> apply(ElementWithSchemaContext input) {
+        if (context.isPresent()) {
+          QName partialQName = qNameFromElement(input.getElement());
+          Optional<DataSchemaNode> schemaNode = XmlDocumentUtils.findFirstSchema(partialQName, context.get());
+          if (schemaNode.isPresent()) {
+            return Optional.<Node<?>> fromNullable(//
+                toNodeWithSchema(input.getElement(), schemaNode.get(), XmlDocumentUtils.defaultValueCodecProvider(),input.getSchemaContext()));
+          }
+        }
+        return Optional.<Node<?>> fromNullable(toDomNode(input.getElement()));
+      }
+
+    });
+
+  }
+
+  private static final <T> List<T> forEachChild(final NodeList nodes, final SchemaContext schemaContext, final Function<ElementWithSchemaContext, Optional<T>> forBody) {
+    final int l = nodes.getLength();
+    if (l == 0) {
+      return ImmutableList.of();
+    }
+
+    final List<T> list = new ArrayList<>(l);
+    for (int i = 0; i < l; i++) {
+      org.w3c.dom.Node child = nodes.item(i);
+      if (child instanceof Element) {
+        Optional<T> result = forBody.apply(new ElementWithSchemaContext((Element) child,schemaContext));
+        if (result.isPresent()) {
+          list.add(result.get());
+        }
+      }
+    }
+    return ImmutableList.copyOf(list);
+  }
+
+  public static final XmlCodecProvider defaultValueCodecProvider() {
+    return XmlUtils.DEFAULT_XML_CODEC_PROVIDER;
+  }
+}
diff --git a/java/org/opendaylight/controller/xml/codec/XmlStreamUtils.java b/java/org/opendaylight/controller/xml/codec/XmlStreamUtils.java
new file mode 100644 (file)
index 0000000..c9d5e89
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.xml.codec;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.AttributesContainer;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider;
+import org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import java.net.URI;
+import java.util.Map.Entry;
+
+/**
+ * Utility class for bridging JAXP Stream and YANG Data APIs. Note that the definition of this class
+ * by no means final and subject to change as more functionality is centralized here.
+ */
+@Beta
+public class XmlStreamUtils {
+  private static final Logger LOG = LoggerFactory.getLogger(XmlStreamUtils.class);
+  private final XmlCodecProvider codecProvider;
+
+  protected XmlStreamUtils(final XmlCodecProvider codecProvider) {
+    this.codecProvider = Preconditions.checkNotNull(codecProvider);
+  }
+
+  /**
+   * Create a new instance encapsulating a particular codec provider.
+   *
+   * @param codecProvider XML codec provider
+   * @return A new instance
+   */
+  public static XmlStreamUtils create(final XmlCodecProvider codecProvider) {
+    return new XmlStreamUtils(codecProvider);
+  }
+
+  /**
+   * Check if a particular data element can be emitted as an empty element, bypassing value encoding. This
+   * functionality is optional, as valid XML stream is produced even if start/end element is produced unconditionally.
+   *
+   * @param data Data node
+   * @return True if the data node will result in empty element body.
+   */
+  public static boolean isEmptyElement(final Node<?> data) {
+    if (data == null) {
+      return true;
+    }
+
+    if (data instanceof CompositeNode) {
+      return ((CompositeNode) data).getValue().isEmpty();
+    }
+    if (data instanceof SimpleNode) {
+      return data.getValue() == null;
+    }
+
+    // Safe default
+    return false;
+  }
+
+  /**
+   * Write an InstanceIdentifier into the output stream. Calling corresponding {@link javax.xml.stream.XMLStreamWriter#writeStartElement(String)}
+   * and {@link javax.xml.stream.XMLStreamWriter#writeEndElement()} is the responsibility of the caller.
+   *
+   * @param writer XML Stream writer
+   * @param id InstanceIdentifier
+   * @throws javax.xml.stream.XMLStreamException
+   */
+  public static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull YangInstanceIdentifier id) throws XMLStreamException {
+    Preconditions.checkNotNull(writer, "Writer may not be null");
+    Preconditions.checkNotNull(id, "Variable should contain instance of instance identifier and can't be null");
+    LOG.debug("Writing Instance identifier with Random prefix");
+    final RandomPrefix prefixes = new RandomPrefix();
+    final String str = XmlUtils.encodeIdentifier(prefixes, id);
+
+    for (Entry<URI, String> e: prefixes.getPrefixes()) {
+      writer.writeNamespace(e.getValue(), e.getKey().toString());
+    }
+    LOG.debug("Instance identifier with Random prefix is now {}", str);
+    writer.writeCharacters(str);
+  }
+
+  /**
+   * Write a full XML document corresponding to a CompositeNode into an XML stream writer.
+   *
+   * @param writer XML Stream writer
+   * @param data data node
+   * @param schema corresponding schema node, may be null
+   * @throws javax.xml.stream.XMLStreamException if an encoding problem occurs
+   */
+  public void writeDocument(final @Nonnull XMLStreamWriter writer, final @Nonnull CompositeNode data, final @Nullable SchemaNode schema) throws XMLStreamException {
+    // final Boolean repairing = (Boolean) writer.getProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES);
+    // Preconditions.checkArgument(repairing == true, "XML Stream Writer has to be repairing namespaces");
+
+    writer.writeStartDocument();
+    writeElement(writer, data, schema);
+    writer.writeEndDocument();
+    writer.flush();
+  }
+
+
+  /**
+   * Write an element into a XML stream writer. This includes the element start/end tags and
+   * the value of the element.
+   *
+   * @param writer XML Stream writer
+   * @param data data node
+   * @param schema Schema node
+   * @throws javax.xml.stream.XMLStreamException if an encoding problem occurs
+   */
+  public void writeElement(final XMLStreamWriter writer, final @Nonnull Node<?> data, final SchemaNode schema) throws XMLStreamException {
+    final QName qname = data.getNodeType();
+    final String pfx = qname.getPrefix() != null ? qname.getPrefix() : "";
+    final String ns = qname.getNamespace() != null ? qname.getNamespace().toString() : "";
+
+    if (isEmptyElement(data)) {
+      writer.writeEmptyElement(pfx, qname.getLocalName(), ns);
+      return;
+    }
+
+    writer.writeStartElement(pfx, qname.getLocalName(), ns);
+    if (data instanceof AttributesContainer && ((AttributesContainer) data).getAttributes() != null) {
+      for (Entry<QName, String> attribute : ((AttributesContainer) data).getAttributes().entrySet()) {
+        writer.writeAttribute(attribute.getKey().getNamespace().toString(), attribute.getKey().getLocalName(), attribute.getValue());
+      }
+    }
+
+    if (data instanceof SimpleNode<?>) {
+      LOG.debug("writeElement : node is of type SimpleNode");
+      // Simple node
+      if (schema instanceof LeafListSchemaNode) {
+        writeValue(writer, ((LeafListSchemaNode) schema).getType(), data.getValue());
+      } else if (schema instanceof LeafSchemaNode) {
+        writeValue(writer, ((LeafSchemaNode) schema).getType(), data.getValue());
+      } else {
+        Object value = data.getValue();
+        if (value != null) {
+          writer.writeCharacters(String.valueOf(value));
+        }
+      }
+    } else {
+      LOG.debug("writeElement : node is of type CompositeNode");
+      // CompositeNode
+      for (Node<?> child : ((CompositeNode) data).getValue()) {
+        DataSchemaNode childSchema = null;
+        if (schema instanceof DataNodeContainer) {
+          childSchema = SchemaUtils.findFirstSchema(child.getNodeType(), ((DataNodeContainer) schema).getChildNodes()).orNull();
+          if (childSchema == null) {
+            LOG.debug("Probably the data node \"{}\" does not conform to schema", child == null ? "" : child.getNodeType().getLocalName());
+          }
+        }
+
+        writeElement(writer, child, childSchema);
+      }
+    }
+
+    writer.writeEndElement();
+  }
+
+  /**
+   * Write a value into a XML stream writer. This method assumes the start and end of element is
+   * emitted by the caller.
+   *
+   * @param writer XML Stream writer
+   * @param type type definitions
+   * @param value object value
+   * @throws javax.xml.stream.XMLStreamException if an encoding problem occurs
+   */
+  public void writeValue(final @Nonnull XMLStreamWriter writer, final @Nonnull TypeDefinition<?> type, final Object value) throws XMLStreamException {
+    if (value == null) {
+      LOG.debug("Value of {}:{} is null, not encoding it", type.getQName().getNamespace(), type.getQName().getLocalName());
+      return;
+    }
+
+    final TypeDefinition<?> baseType = XmlUtils.resolveBaseTypeFrom(type);
+    if (baseType instanceof IdentityrefTypeDefinition) {
+      write(writer, (IdentityrefTypeDefinition) baseType, value);
+    } else if (baseType instanceof InstanceIdentifierTypeDefinition) {
+      write(writer, (InstanceIdentifierTypeDefinition) baseType, value);
+    } else {
+      final TypeDefinitionAwareCodec<Object, ?> codec = codecProvider.codecFor(baseType);
+      String text;
+      if (codec != null) {
+        try {
+          text = codec.serialize(value);
+        } catch (ClassCastException e) {
+          LOG.error("Provided node value {} did not have type {} required by mapping. Using stream instead.", value, baseType, e);
+          text = String.valueOf(value);
+        }
+      } else {
+        LOG.error("Failed to find codec for {}, falling back to using stream", baseType);
+        text = String.valueOf(value);
+      }
+      writer.writeCharacters(text);
+    }
+  }
+
+  private static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull IdentityrefTypeDefinition type, final @Nonnull Object value) throws XMLStreamException {
+    if (value instanceof QName) {
+      final QName qname = (QName) value;
+      final String prefix;
+      if (qname.getPrefix() != null && !qname.getPrefix().isEmpty()) {
+        prefix = qname.getPrefix();
+      } else {
+        prefix = "x";
+      }
+
+      writer.writeNamespace(prefix, qname.getNamespace().toString());
+      writer.writeCharacters(prefix + ':' + qname.getLocalName());
+    } else {
+      LOG.debug("Value of {}:{} is not a QName but {}", type.getQName().getNamespace(), type.getQName().getLocalName(), value.getClass());
+      writer.writeCharacters(String.valueOf(value));
+    }
+  }
+
+  private static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull InstanceIdentifierTypeDefinition type, final @Nonnull Object value) throws XMLStreamException {
+    if (value instanceof YangInstanceIdentifier) {
+      LOG.debug("Writing InstanceIdentifier object {}", value);
+      write(writer, (YangInstanceIdentifier)value);
+    } else {
+      LOG.debug("Value of {}:{} is not an InstanceIdentifier but {}", type.getQName().getNamespace(), type.getQName().getLocalName(), value.getClass());
+      writer.writeCharacters(String.valueOf(value));
+    }
+  }
+}
diff --git a/java/org/opendaylight/controller/xml/codec/XmlUtils.java b/java/org/opendaylight/controller/xml/codec/XmlUtils.java
new file mode 100644 (file)
index 0000000..5848561
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.xml.codec;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.XmlTreeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import javax.activation.UnsupportedDataTypeException;
+import javax.annotation.Nonnull;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Common XML-related utility methods, which are not specific to a particular
+ * JAXP API.
+ */
+public class XmlUtils {
+
+  public static final XmlCodecProvider DEFAULT_XML_CODEC_PROVIDER = new XmlCodecProvider() {
+    @Override
+    public TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> codecFor(final TypeDefinition<?> baseType) {
+      return TypeDefinitionAwareCodec.from(baseType);
+    }
+  };
+
+  private XmlUtils() {
+  }
+
+  private static final String BLANK = "";
+  private static final Logger LOG = LoggerFactory.getLogger(XmlUtils.class);
+
+  /**
+   * Converts the composite node to xml using rpc input schema node
+   * @param cNode
+   * @param schemaContext
+   * @return xml String
+   */
+  public static String inputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){
+    LOG.debug("Converting input composite node to xml {}", cNode);
+    if (cNode == null) return BLANK;
+
+    if(schemaContext == null) return BLANK;
+
+    Document domTree = null;
+    try {
+      Set<RpcDefinition> rpcs =  schemaContext.getOperations();
+      for(RpcDefinition rpc : rpcs) {
+        if(rpc.getQName().equals(cNode.getNodeType())){
+          LOG.debug("Found the rpc definition from schema context matching with input composite node  {}", rpc.getQName());
+
+          CompositeNode inputContainer = cNode.getFirstCompositeByName(QName.create(cNode.getNodeType(), "input"));
+          domTree = XmlDocumentUtils.toDocument(inputContainer, rpc.getInput(), XmlDocumentUtils.defaultValueCodecProvider());
+
+          LOG.debug("input composite node to document conversion complete, document is   {}", domTree);
+          break;
+        }
+      }
+
+    } catch (UnsupportedDataTypeException e) {
+      LOG.error("Error during translation of CompositeNode to Document", e);
+    }
+    return domTransformer(domTree);
+  }
+
+  /**
+   * Converts the composite node to xml String using rpc output schema node
+   * @param cNode
+   * @param schemaContext
+   * @return xml string
+   */
+  public static String outputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){
+    LOG.debug("Converting output composite node to xml {}", cNode);
+    if (cNode == null) return BLANK;
+
+    if(schemaContext == null) return BLANK;
+
+    Document domTree = null;
+    try {
+      Set<RpcDefinition> rpcs =  schemaContext.getOperations();
+      for(RpcDefinition rpc : rpcs) {
+        if(rpc.getQName().equals(cNode.getNodeType())){
+          LOG.debug("Found the rpc definition from schema context matching with output composite node  {}", rpc.getQName());
+
+          CompositeNode outputContainer = cNode.getFirstCompositeByName(QName.create(cNode.getNodeType(), "output"));
+          domTree = XmlDocumentUtils.toDocument(outputContainer, rpc.getOutput(), XmlDocumentUtils.defaultValueCodecProvider());
+
+          LOG.debug("output composite node to document conversion complete, document is   {}", domTree);
+          break;
+        }
+      }
+
+    } catch (UnsupportedDataTypeException e) {
+      LOG.error("Error during translation of CompositeNode to Document", e);
+    }
+    return domTransformer(domTree);
+  }
+
+  private static String domTransformer(Document domTree) {
+    StringWriter writer = new StringWriter();
+    try {
+      TransformerFactory tf = TransformerFactory.newInstance();
+      Transformer transformer = tf.newTransformer();
+      transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+      transformer.transform(new DOMSource(domTree), new StreamResult(writer));
+    } catch (TransformerException e) {
+
+      LOG.error("Error during translation of Document to OutputStream", e);
+    }
+    LOG.debug("Document to string conversion complete, xml string is  {} ",  writer.toString());
+
+    return writer.toString();
+  }
+
+  public static CompositeNode xmlToCompositeNode(String xml){
+    if (xml==null || xml.length()==0) return null;
+
+    Node<?> dataTree;
+    try {
+      dataTree = XmlTreeBuilder.buildDataTree(new ByteArrayInputStream(xml.getBytes()));
+    } catch (XMLStreamException e) {
+      LOG.error("Error during building data tree from XML", e);
+      return null;
+    }
+    if (dataTree == null) {
+      LOG.error("data tree is null");
+      return null;
+    }
+    if (dataTree instanceof SimpleNode) {
+      LOG.error("RPC XML was resolved as SimpleNode");
+      return null;
+    }
+    return (CompositeNode) dataTree;
+  }
+
+  /**
+   * Converts the xml to composite node using rpc input schema node
+   * @param rpc
+   * @param xml
+   * @param schemaContext
+   * @return CompositeNode object based on the input, if any of the input parameter is null, a null object is returned
+   */
+  public static CompositeNode inputXmlToCompositeNode(QName rpc, String xml,  SchemaContext schemaContext){
+    LOG.debug("Converting input xml to composite node {}", xml);
+    if (xml==null || xml.length()==0) return null;
+
+    if(rpc == null) return null;
+
+    if(schemaContext == null) return null;
+
+    CompositeNode compositeNode = null;
+    try {
+
+      Document doc = XmlUtil.readXmlToDocument(xml);
+      Set<RpcDefinition> rpcs =  schemaContext.getOperations();
+      for(RpcDefinition rpcDef : rpcs) {
+        if(rpcDef.getQName().equals(rpc)){
+          LOG.debug("found the rpc definition from schema context matching rpc  {}", rpc);
+
+          if(rpcDef.getInput() == null) {
+            LOG.warn("found rpc definition's input is null");
+            return null;
+          }
+
+          QName input = rpcDef.getInput().getQName();
+          NodeList nodeList = doc.getElementsByTagNameNS(input.getNamespace().toString(), "input");
+          if(nodeList == null || nodeList.getLength() < 1) {
+            LOG.warn("xml does not have input entry. {}", xml);
+            return null;
+          }
+          Element xmlData = (Element)nodeList.item(0);
+
+          List<Node<?>> dataNodes = XmlDocumentUtils.toDomNodes(xmlData,
+              Optional.of(rpcDef.getInput().getChildNodes()), schemaContext);
+
+          LOG.debug("Converted xml input to list of nodes  {}", dataNodes);
+
+          final CompositeNodeBuilder<ImmutableCompositeNode> it = ImmutableCompositeNode.builder();
+          it.setQName(input);
+          it.add(ImmutableCompositeNode.create(input, dataNodes));
+          compositeNode = it.toInstance();
+          break;
+        }
+      }
+    } catch (SAXException e) {
+      LOG.error("Error during building data tree from XML", e);
+    } catch (IOException e) {
+      LOG.error("Error during building data tree from XML", e);
+    }
+
+    LOG.debug("Xml to composite node conversion complete {} ", compositeNode);
+    return compositeNode;
+  }
+
+  public static TypeDefinition<?> resolveBaseTypeFrom(final @Nonnull TypeDefinition<?> type) {
+    TypeDefinition<?> superType = type;
+    while (superType.getBaseType() != null) {
+      superType = superType.getBaseType();
+    }
+    return superType;
+  }
+
+  /**
+   * This code is picked from yangtools and modified to add type of instance identifier
+   * output of instance identifier something like below for a flow ref composite node of type instance identifier,
+   * which has path arguments with predicates, whose value is of type java.lang.short
+   * <flow-ref xmlns:bgkj="urn:opendaylight:flow:inventory" xmlns:jdlk="urn:opendaylight:inventory">
+   *   /jdlk:nodes/jdlk:node[jdlk:id='openflow:205558455098190@java.lang.String']
+   *   /bgkj:table[bgkj:id='3@java.lang.Short']
+   *   /bgkj:flow[bgkj:id='156@java.lang.String']
+   * </flow-ref>
+   *
+   */
+
+  public static String encodeIdentifier(final RandomPrefix prefixes, final YangInstanceIdentifier id) {
+    StringBuilder textContent = new StringBuilder();
+    for (PathArgument pathArgument : id.getPathArguments()) {
+      textContent.append('/');
+      textContent.append(prefixes.encodeQName(pathArgument.getNodeType()));
+      if (pathArgument instanceof NodeIdentifierWithPredicates) {
+        Map<QName, Object> predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues();
+
+        for (QName keyValue : predicates.keySet()) {
+          Object value = predicates.get(keyValue);
+          String type = value.getClass().getName();
+          String predicateValue = String.valueOf(value);
+          textContent.append('[');
+          textContent.append(prefixes.encodeQName(keyValue));
+          textContent.append("='");
+          textContent.append(predicateValue);
+          textContent.append("@");
+          textContent.append(type);
+          textContent.append("']");
+        }
+      } else if (pathArgument instanceof NodeWithValue) {
+        textContent.append("[.='");
+        textContent.append(((NodeWithValue) pathArgument).getValue());
+        textContent.append("']");
+      }
+    }
+
+    return textContent.toString();
+  }
+}