Remove SchemaContextProvider
[yangtools.git] / yang / yang-data-jaxen / src / main / java / org / opendaylight / yangtools / yang / data / jaxen / NormalizedNodeNavigator.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.yangtools.yang.data.jaxen;
9
10 import static java.util.Objects.requireNonNull;
11
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;
20 import java.util.Set;
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;
35
36 /**
37  * A {@link Navigator} implementation for YANG XPaths instantiated on a particular root {@link NormalizedNode}.
38  */
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;
45
46     NormalizedNodeNavigator(final ConverterNamespaceContext context, final JaxenDocument document) {
47         this.namespaceContext = requireNonNull(context);
48         this.document = document;
49     }
50
51     private static NormalizedNodeContext cast(final Object context) {
52         Verify.verify(context instanceof NormalizedNodeContext, "Unhandled context node %s", context);
53         return (NormalizedNodeContext) context;
54     }
55
56     private static NormalizedNode<?, ?> contextNode(final Object context) {
57         return cast(context).getNode();
58     }
59
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();
64         } else {
65             module = namespaceContext.convert(prefix);
66         }
67
68         return QName.create(module, localName);
69     }
70
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;
75     }
76
77     @Override
78     public String getElementNamespaceUri(final Object element) {
79         return contextNode(element).getNodeType().getNamespace().toString();
80     }
81
82     @Override
83     public String getElementName(final Object element) {
84         return contextNode(element).getNodeType().getLocalName();
85     }
86
87     @Override
88     public String getElementQName(final Object element) {
89         return namespaceContext.jaxenQName(contextNode(element).getNodeType());
90     }
91
92     @Override
93     public String getAttributeNamespaceUri(final Object attr) {
94         return attribute(attr).getKey().getNamespace().toString();
95     }
96
97     @Override
98     public String getAttributeName(final Object attr) {
99         return attribute(attr).getKey().getLocalName();
100     }
101
102     @Override
103     public String getAttributeQName(final Object attr) {
104         return namespaceContext.jaxenQName(attribute(attr).getKey());
105     }
106
107     @Override
108     public NormalizedNodeContext getDocumentNode(final Object contextNode) {
109         NormalizedNodeContext ctx = cast(contextNode);
110         while (ctx.getParent() != null) {
111             ctx = ctx.getParent();
112         }
113
114         return ctx;
115     }
116
117     @Override
118     public boolean isDocument(final Object object) {
119         return cast(object).getParent() == null;
120     }
121
122     @Override
123     public boolean isElement(final Object object) {
124         return object instanceof NormalizedNodeContext;
125     }
126
127     @Override
128     public boolean isAttribute(final Object object) {
129         return object instanceof Entry;
130     }
131
132     @Override
133     public boolean isNamespace(final Object object) {
134         return false;
135     }
136
137     @Override
138     public boolean isComment(final Object object) {
139         return false;
140     }
141
142     @Override
143     public boolean isText(final Object object) {
144         return object instanceof String;
145     }
146
147     @Override
148     public boolean isProcessingInstruction(final Object object) {
149         return false;
150     }
151
152     @Override
153     public String getCommentStringValue(final Object comment) {
154         throw new UnsupportedOperationException();
155     }
156
157     @Override
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();
162
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
166             //       allocations.
167             if (value instanceof byte[]) {
168                 // Binary
169                 return Base64.getEncoder().encodeToString((byte[]) value);
170             }
171             if (value instanceof Set) {
172                 // Bits
173                 return JOINER.join((Set<?>)value);
174             }
175             if (value != null) {
176                 // Everything else...
177                 return String.valueOf(value);
178             }
179         }
180
181         return "";
182     }
183
184     @Override
185     public String getAttributeStringValue(final Object attr) {
186         return attribute(attr).getValue();
187     }
188
189     @Override
190     public String getNamespaceStringValue(final Object ns) {
191         throw new UnsupportedOperationException();
192     }
193
194     @Override
195     public String getTextStringValue(final Object text) {
196         return text.toString();
197     }
198
199     @Override
200     public String getNamespacePrefix(final Object ns) {
201         throw new UnsupportedOperationException();
202     }
203
204     @Override
205     public XPath parseXPath(final String xpath) throws SAXPathException {
206         // FIXME: need to bind YangXPath probably
207         throw new UnsupportedOperationException();
208     }
209
210     @Override
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;
215     }
216
217     @Override
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))
224                         : null;
225     }
226
227     @Override
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()) {
234         //              return null;
235         //          }
236         //
237         //          return attributes.entrySet().iterator();
238         //      }
239         return null;
240     }
241
242     @Override
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()) {
250         //              return null;
251         //          }
252         //
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));
256         //      }
257         return null;
258     }
259
260     @Override
261     public Iterator<NormalizedNodeContext> getParentAxisIterator(final Object contextNode) {
262         final NormalizedNodeContext parent = cast(contextNode).getParent();
263         return parent == null ? null : Iterators.singletonIterator(parent);
264     }
265
266     @Override
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);
271     }
272
273     @Override
274     public Iterator<NormalizedNodeContext> getSelfAxisIterator(final Object contextNode)
275             throws UnsupportedAxisException {
276         return Iterators.singletonIterator(cast(contextNode));
277     }
278
279     @Override
280     public Iterator<NormalizedNodeContext> getAncestorOrSelfAxisIterator(final Object contextNode)
281             throws UnsupportedAxisException {
282         return new NormalizedNodeContextIterator(cast(contextNode));
283     }
284
285     @Override
286     public NormalizedNodeContext getParentNode(final Object contextNode) throws UnsupportedAxisException {
287         return cast(contextNode).getParent();
288     }
289
290     @Override
291     public EffectiveModelContext getEffectiveModelContext() {
292         return document.getEffectiveModelContext();
293     }
294
295     JaxenDocument getDocument() {
296         return document;
297     }
298
299     private static final class NormalizedNodeContextIterator extends UnmodifiableIterator<NormalizedNodeContext> {
300         private NormalizedNodeContext next;
301
302         NormalizedNodeContextIterator(final NormalizedNodeContext initial) {
303             this.next = requireNonNull(initial);
304         }
305
306         @Override
307         public boolean hasNext() {
308             return next != null;
309         }
310
311         @Override
312         public NormalizedNodeContext next() {
313             if (next == null) {
314                 throw new NoSuchElementException();
315             }
316
317             final NormalizedNodeContext ret = next;
318             next = next.getParent();
319             return ret;
320         }
321     }
322 }