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 static java.util.Objects.requireNonNull;
12 import com.google.common.base.Joiner;
13 import com.google.common.base.Verify;
14 import com.google.common.collect.Iterators;
15 import com.google.common.collect.UnmodifiableIterator;
16 import java.util.Base64;
17 import java.util.Iterator;
18 import java.util.Map.Entry;
19 import java.util.NoSuchElementException;
21 import org.jaxen.DefaultNavigator;
22 import org.jaxen.NamedAccessNavigator;
23 import org.jaxen.Navigator;
24 import org.jaxen.UnsupportedAxisException;
25 import org.jaxen.XPath;
26 import org.jaxen.saxpath.SAXPathException;
27 import org.opendaylight.yangtools.yang.common.QName;
28 import org.opendaylight.yangtools.yang.common.QNameModule;
29 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
33 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
34 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContextProvider;
37 * A {@link Navigator} implementation for YANG XPaths instantiated on a particular root {@link NormalizedNode}.
39 final class NormalizedNodeNavigator extends DefaultNavigator
40 implements NamedAccessNavigator, EffectiveModelContextProvider {
41 private static final long serialVersionUID = 1L;
42 private static final Joiner JOINER = Joiner.on(" ").skipNulls();
43 private final ConverterNamespaceContext namespaceContext;
44 private final JaxenDocument document;
46 NormalizedNodeNavigator(final ConverterNamespaceContext context, final JaxenDocument document) {
47 this.namespaceContext = requireNonNull(context);
48 this.document = document;
51 private static NormalizedNodeContext cast(final Object context) {
52 Verify.verify(context instanceof NormalizedNodeContext, "Unhandled context node %s", context);
53 return (NormalizedNodeContext) context;
56 private static NormalizedNode<?, ?> contextNode(final Object context) {
57 return cast(context).getNode();
60 private QName resolveQName(final NormalizedNode<?, ?> node, final String prefix, final String localName) {
61 final QNameModule module;
62 if (prefix.isEmpty()) {
63 module = node.getNodeType().getModule();
65 module = namespaceContext.convert(prefix);
68 return QName.create(module, localName);
71 @SuppressWarnings("unchecked")
72 private static Entry<QName, String> attribute(final Object attr) {
73 Verify.verify(attr instanceof Entry, "Unhandled attribute %s", attr);
74 return (Entry<QName, String>) attr;
78 public String getElementNamespaceUri(final Object element) {
79 return contextNode(element).getNodeType().getNamespace().toString();
83 public String getElementName(final Object element) {
84 return contextNode(element).getNodeType().getLocalName();
88 public String getElementQName(final Object element) {
89 return namespaceContext.jaxenQName(contextNode(element).getNodeType());
93 public String getAttributeNamespaceUri(final Object attr) {
94 return attribute(attr).getKey().getNamespace().toString();
98 public String getAttributeName(final Object attr) {
99 return attribute(attr).getKey().getLocalName();
103 public String getAttributeQName(final Object attr) {
104 return namespaceContext.jaxenQName(attribute(attr).getKey());
108 public NormalizedNodeContext getDocumentNode(final Object contextNode) {
109 NormalizedNodeContext ctx = cast(contextNode);
110 while (ctx.getParent() != null) {
111 ctx = ctx.getParent();
118 public boolean isDocument(final Object object) {
119 return cast(object).getParent() == null;
123 public boolean isElement(final Object object) {
124 return object instanceof NormalizedNodeContext;
128 public boolean isAttribute(final Object object) {
129 return object instanceof Entry;
133 public boolean isNamespace(final Object object) {
138 public boolean isComment(final Object object) {
143 public boolean isText(final Object object) {
144 return object instanceof String;
148 public boolean isProcessingInstruction(final Object object) {
153 public String getCommentStringValue(final Object comment) {
154 throw new UnsupportedOperationException();
158 public String getElementStringValue(final Object element) {
159 final NormalizedNode<?, ?> node = contextNode(element);
160 if (node instanceof LeafNode || node instanceof LeafSetEntryNode) {
161 final Object value = node.getValue();
163 // TODO: This is a rather poor approximation of what the codec infrastructure, but it should be sufficient
164 // to work for now. Tracking SchemaPath will mean we will need to wrap each NormalizedNode with a
165 // corresponding SchemaPath. That in turn would complicate this class and result in a lot of object
167 if (value instanceof byte[]) {
169 return Base64.getEncoder().encodeToString((byte[]) value);
171 if (value instanceof Set) {
173 return JOINER.join((Set<?>)value);
176 // Everything else...
177 return String.valueOf(value);
185 public String getAttributeStringValue(final Object attr) {
186 return attribute(attr).getValue();
190 public String getNamespaceStringValue(final Object ns) {
191 throw new UnsupportedOperationException();
195 public String getTextStringValue(final Object text) {
196 return text.toString();
200 public String getNamespacePrefix(final Object ns) {
201 throw new UnsupportedOperationException();
205 public XPath parseXPath(final String xpath) throws SAXPathException {
206 // FIXME: need to bind YangXPath probably
207 throw new UnsupportedOperationException();
211 public Iterator<NormalizedNodeContext> getChildAxisIterator(final Object contextNode) {
212 final NormalizedNodeContext ctx = cast(contextNode);
213 final NormalizedNode<?, ?> node = ctx.getNode();
214 return node instanceof DataContainerNode ? ctx.iterateChildren((DataContainerNode<?>) node) : null;
218 public Iterator<NormalizedNodeContext> getChildAxisIterator(final Object contextNode, final String localName,
219 final String namespacePrefix, final String namespaceURI) {
220 final NormalizedNodeContext ctx = cast(contextNode);
221 final NormalizedNode<?, ?> node = ctx.getNode();
222 return node instanceof DataContainerNode
223 ? ctx.iterateChildrenNamed((DataContainerNode<?>)node, resolveQName(node, namespacePrefix, localName))
228 public Iterator<? extends Entry<?, ?>> getAttributeAxisIterator(final Object contextNode) {
229 // FIXME: how do we mix in metadata?
230 // final NormalizedNode<?, ?> node = contextNode(contextNode);
231 // if (node instanceof AttributesContainer) {
232 // final Map<QName, String> attributes = ((AttributesContainer) node).getAttributes();
233 // if (attributes.isEmpty()) {
237 // return attributes.entrySet().iterator();
243 public Iterator<? extends Entry<?, ?>> getAttributeAxisIterator(final Object contextNode, final String localName,
244 final String namespacePrefix, final String namespaceURI) {
245 // FIXME: how do we mix in metadata?
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));
261 public Iterator<NormalizedNodeContext> getParentAxisIterator(final Object contextNode) {
262 final NormalizedNodeContext parent = cast(contextNode).getParent();
263 return parent == null ? null : Iterators.singletonIterator(parent);
267 public Iterator<NormalizedNodeContext> getAncestorAxisIterator(final Object contextNode)
268 throws UnsupportedAxisException {
269 final NormalizedNodeContext parent = cast(contextNode).getParent();
270 return parent == null ? null : new NormalizedNodeContextIterator(parent);
274 public Iterator<NormalizedNodeContext> getSelfAxisIterator(final Object contextNode)
275 throws UnsupportedAxisException {
276 return Iterators.singletonIterator(cast(contextNode));
280 public Iterator<NormalizedNodeContext> getAncestorOrSelfAxisIterator(final Object contextNode)
281 throws UnsupportedAxisException {
282 return new NormalizedNodeContextIterator(cast(contextNode));
286 public NormalizedNodeContext getParentNode(final Object contextNode) throws UnsupportedAxisException {
287 return cast(contextNode).getParent();
291 public EffectiveModelContext getEffectiveModelContext() {
292 return document.getEffectiveModelContext();
295 JaxenDocument getDocument() {
299 private static final class NormalizedNodeContextIterator extends UnmodifiableIterator<NormalizedNodeContext> {
300 private NormalizedNodeContext next;
302 NormalizedNodeContextIterator(final NormalizedNodeContext initial) {
303 this.next = requireNonNull(initial);
307 public boolean hasNext() {
312 public NormalizedNodeContext next() {
314 throw new NoSuchElementException();
317 final NormalizedNodeContext ret = next;
318 next = next.getParent();