2 * Copyright (c) 2015 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
8 package org.opendaylight.yangtools.yang.data.jaxen;
10 import com.google.common.base.Joiner;
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import com.google.common.base.Verify;
14 import com.google.common.collect.Iterators;
15 import com.google.common.collect.UnmodifiableIterator;
16 import com.google.common.io.BaseEncoding;
17 import java.util.AbstractMap.SimpleImmutableEntry;
18 import java.util.Iterator;
20 import java.util.Map.Entry;
21 import java.util.NoSuchElementException;
23 import javax.annotation.Nonnull;
24 import org.jaxen.DefaultNavigator;
25 import org.jaxen.NamedAccessNavigator;
26 import org.jaxen.Navigator;
27 import org.jaxen.UnsupportedAxisException;
28 import org.jaxen.XPath;
29 import org.jaxen.saxpath.SAXPathException;
30 import org.opendaylight.yangtools.yang.common.QName;
31 import org.opendaylight.yangtools.yang.common.QNameModule;
32 import org.opendaylight.yangtools.yang.data.api.AttributesContainer;
33 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
34 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
40 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
43 * A {@link Navigator} implementation for YANG XPaths instantiated on a particular root {@link NormalizedNode}.
45 final class NormalizedNodeNavigator extends DefaultNavigator implements NamedAccessNavigator {
46 private static final long serialVersionUID = 1L;
47 private static final Joiner JOINER = Joiner.on(" ").skipNulls();
48 private final ConverterNamespaceContext namespaceContext;
49 private final JaxenDocument document;
51 NormalizedNodeNavigator(final ConverterNamespaceContext context, final JaxenDocument document) {
52 this.namespaceContext = Preconditions.checkNotNull(context);
53 this.document = document;
56 private static NormalizedNodeContext cast(final Object context) {
57 Verify.verify(context instanceof NormalizedNodeContext, "Unhandled context node %s", context);
58 return (NormalizedNodeContext) context;
61 private static NormalizedNode<?, ?> contextNode(final Object context) {
62 return cast(context).getNode();
65 private QName resolveQName(final NormalizedNode<?, ?> node, final String prefix, final String localName) {
66 final QNameModule module;
67 if (prefix.isEmpty()) {
68 module = node.getNodeType().getModule();
70 module = namespaceContext.convert(prefix);
73 return QName.create(module, localName);
76 @SuppressWarnings("unchecked")
77 private static Entry<QName, String> attribute(final Object attr) {
78 Verify.verify(attr instanceof Entry, "Unhandled attribute %s", attr);
79 return (Entry<QName, String>) attr;
83 public String getElementNamespaceUri(final Object element) {
84 return contextNode(element).getNodeType().getNamespace().toString();
88 public String getElementName(final Object element) {
89 return contextNode(element).getNodeType().getLocalName();
93 public String getElementQName(final Object element) {
94 return namespaceContext.jaxenQName(contextNode(element).getNodeType());
98 public String getAttributeNamespaceUri(final Object attr) {
99 return attribute(attr).getKey().getNamespace().toString();
103 public String getAttributeName(final Object attr) {
104 return attribute(attr).getKey().getLocalName();
108 public String getAttributeQName(final Object attr) {
109 return namespaceContext.jaxenQName(attribute(attr).getKey());
113 public NormalizedNodeContext getDocumentNode(final Object contextNode) {
114 NormalizedNodeContext ctx = cast(contextNode);
115 while (ctx.getParent() != null) {
116 ctx = ctx.getParent();
123 public boolean isDocument(final Object object) {
124 return cast(object).getParent() == null;
128 public boolean isElement(final Object object) {
129 return object instanceof NormalizedNodeContext;
133 public boolean isAttribute(final Object object) {
134 return object instanceof Entry;
138 public boolean isNamespace(final Object object) {
143 public boolean isComment(final Object object) {
148 public boolean isText(final Object object) {
149 return object instanceof String;
153 public boolean isProcessingInstruction(final Object object) {
158 public String getCommentStringValue(final Object comment) {
159 throw new UnsupportedOperationException();
163 public String getElementStringValue(final Object element) {
164 final NormalizedNode<?, ?> node = contextNode(element);
165 if (node instanceof LeafNode || node instanceof LeafSetEntryNode) {
166 final Object value = node.getValue();
168 // TODO: This is a rather poor approximation of what the codec infrastructure, but it should be sufficient
169 // to work for now. Tracking SchemaPath will mean we will need to wrap each NormalizedNode with a
170 // corresponding SchemaPath. That in turn would complicate this class and result in a lot of object
172 if (value instanceof byte[]) {
174 return BaseEncoding.base64().encode((byte[]) value);
176 if (value instanceof Set) {
178 return JOINER.join((Set<?>)value);
181 // Everything else...
182 return String.valueOf(value);
190 public String getAttributeStringValue(final Object attr) {
191 return attribute(attr).getValue();
195 public String getNamespaceStringValue(final Object ns) {
196 throw new UnsupportedOperationException();
200 public String getTextStringValue(final Object text) {
201 return text.toString();
205 public String getNamespacePrefix(final Object ns) {
206 throw new UnsupportedOperationException();
210 public XPath parseXPath(final String xpath) throws SAXPathException {
211 // FIXME: need to bind YangXPath probably
212 throw new UnsupportedOperationException();
216 public Iterator<NormalizedNodeContext> getChildAxisIterator(final Object contextNode, final String localName,
217 final String namespacePrefix, final String namespaceURI) {
218 final NormalizedNodeContext ctx = cast(contextNode);
219 final NormalizedNode<?, ?> node = ctx.getNode();
220 if (!(node instanceof DataContainerNode)) {
224 final QName qname = resolveQName(node, namespacePrefix, localName);
225 @SuppressWarnings({ "unchecked", "rawtypes" })
226 final Optional<NormalizedNode<?, ?>> maybeChild = ((DataContainerNode)node).getChild(new NodeIdentifier(qname));
227 if (!maybeChild.isPresent()) {
231 // The child may be a structural node
232 final NormalizedNode<?, ?> child = maybeChild.get();
233 if (child instanceof MapNode) {
234 return Iterators.transform(((MapNode)child).getValue().iterator(), ctx);
236 if (child instanceof LeafSetNode) {
237 return Iterators.transform(((LeafSetNode<?>)child).getValue().iterator(), ctx);
240 return Iterators.singletonIterator(ctx.apply(child));
244 public Iterator<? extends Entry<?, ?>> getAttributeAxisIterator(final Object contextNode, final String localName, final String namespacePrefix,
245 final String namespaceURI) {
246 final NormalizedNode<?, ?> node = contextNode(contextNode);
247 if (node instanceof AttributesContainer) {
248 final Map<QName, String> attributes = ((AttributesContainer) node).getAttributes();
249 if (attributes.isEmpty()) {
253 final QName qname = resolveQName(node, namespacePrefix, localName);
254 final String value = attributes.get(qname);
255 return value == null ? null : Iterators.singletonIterator(new SimpleImmutableEntry<>(qname, value));
262 public Iterator<NormalizedNodeContext> getChildAxisIterator(final Object contextNode) {
263 final NormalizedNodeContext ctx = cast(contextNode);
264 final NormalizedNode<?, ?> node = ctx.getNode();
265 if (node instanceof DataContainerNode) {
266 return Iterators.transform(((DataContainerNode<?>) node).getValue().iterator(), ctx);
273 public Iterator<NormalizedNodeContext> getParentAxisIterator(final Object contextNode) {
274 final NormalizedNodeContext parent = cast(contextNode).getParent();
275 return parent == null ? null : Iterators.singletonIterator(parent);
279 public Iterator<NormalizedNodeContext> getAncestorAxisIterator(final Object contextNode) throws UnsupportedAxisException {
280 final NormalizedNodeContext parent = cast(contextNode).getParent();
281 return parent == null ? null : new NormalizedNodeContextIterator(parent);
285 public Iterator<? extends Entry<?, ?>> getAttributeAxisIterator(final Object contextNode) {
286 final NormalizedNode<?, ?> node = contextNode(contextNode);
287 if (node instanceof AttributesContainer) {
288 final Map<QName, String> attributes = ((AttributesContainer) node).getAttributes();
289 if (attributes.isEmpty()) {
293 return attributes.entrySet().iterator();
300 public Iterator<NormalizedNodeContext> getSelfAxisIterator(final Object contextNode) throws UnsupportedAxisException {
301 return Iterators.singletonIterator(cast(contextNode));
305 public Iterator<NormalizedNodeContext> getAncestorOrSelfAxisIterator(final Object contextNode) throws UnsupportedAxisException {
306 return new NormalizedNodeContextIterator(cast(contextNode));
310 public NormalizedNodeContext getParentNode(final Object contextNode) throws UnsupportedAxisException {
311 return cast(contextNode).getParent();
314 NormalizedNode<?, ?> getRootNode() {
315 return document.getRootNode();
319 SchemaContext getSchemaContext() {
320 return document.getSchemaContext();
323 private static final class NormalizedNodeContextIterator extends UnmodifiableIterator<NormalizedNodeContext> {
324 private NormalizedNodeContext next;
326 NormalizedNodeContextIterator(final NormalizedNodeContext initial) {
327 this.next = Preconditions.checkNotNull(initial);
331 public boolean hasNext() {
336 public NormalizedNodeContext next() {
338 throw new NoSuchElementException();
341 final NormalizedNodeContext ret = next;
342 next = next.getParent();