Cleanup use of Guava library
[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 com.google.common.io.BaseEncoding;
17 import java.util.AbstractMap.SimpleImmutableEntry;
18 import java.util.Iterator;
19 import java.util.Map;
20 import java.util.Map.Entry;
21 import java.util.NoSuchElementException;
22 import java.util.Optional;
23 import java.util.Set;
24 import javax.annotation.Nonnull;
25 import org.jaxen.DefaultNavigator;
26 import org.jaxen.NamedAccessNavigator;
27 import org.jaxen.Navigator;
28 import org.jaxen.UnsupportedAxisException;
29 import org.jaxen.XPath;
30 import org.jaxen.saxpath.SAXPathException;
31 import org.opendaylight.yangtools.yang.common.QName;
32 import org.opendaylight.yangtools.yang.common.QNameModule;
33 import org.opendaylight.yangtools.yang.data.api.AttributesContainer;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
35 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
40 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
41 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
42
43 /**
44  * A {@link Navigator} implementation for YANG XPaths instantiated on a particular root {@link NormalizedNode}.
45  */
46 final class NormalizedNodeNavigator extends DefaultNavigator implements NamedAccessNavigator {
47     private static final long serialVersionUID = 1L;
48     private static final Joiner JOINER = Joiner.on(" ").skipNulls();
49     private final ConverterNamespaceContext namespaceContext;
50     private final JaxenDocument document;
51
52     NormalizedNodeNavigator(final ConverterNamespaceContext context, final JaxenDocument document) {
53         this.namespaceContext = requireNonNull(context);
54         this.document = document;
55     }
56
57     private static NormalizedNodeContext cast(final Object context) {
58         Verify.verify(context instanceof NormalizedNodeContext, "Unhandled context node %s", context);
59         return (NormalizedNodeContext) context;
60     }
61
62     private static NormalizedNode<?, ?> contextNode(final Object context) {
63         return cast(context).getNode();
64     }
65
66     private QName resolveQName(final NormalizedNode<?, ?> node, final String prefix, final String localName) {
67         final QNameModule module;
68         if (prefix.isEmpty()) {
69             module = node.getNodeType().getModule();
70         } else {
71             module = namespaceContext.convert(prefix);
72         }
73
74         return QName.create(module, localName);
75     }
76
77     @SuppressWarnings("unchecked")
78     private static Entry<QName, String> attribute(final Object attr) {
79         Verify.verify(attr instanceof Entry, "Unhandled attribute %s", attr);
80         return (Entry<QName, String>) attr;
81     }
82
83     @Override
84     public String getElementNamespaceUri(final Object element) {
85         return contextNode(element).getNodeType().getNamespace().toString();
86     }
87
88     @Override
89     public String getElementName(final Object element) {
90         return contextNode(element).getNodeType().getLocalName();
91     }
92
93     @Override
94     public String getElementQName(final Object element) {
95         return namespaceContext.jaxenQName(contextNode(element).getNodeType());
96     }
97
98     @Override
99     public String getAttributeNamespaceUri(final Object attr) {
100         return attribute(attr).getKey().getNamespace().toString();
101     }
102
103     @Override
104     public String getAttributeName(final Object attr) {
105         return attribute(attr).getKey().getLocalName();
106     }
107
108     @Override
109     public String getAttributeQName(final Object attr) {
110         return namespaceContext.jaxenQName(attribute(attr).getKey());
111     }
112
113     @Override
114     public NormalizedNodeContext getDocumentNode(final Object contextNode) {
115         NormalizedNodeContext ctx = cast(contextNode);
116         while (ctx.getParent() != null) {
117             ctx = ctx.getParent();
118         }
119
120         return ctx;
121     }
122
123     @Override
124     public boolean isDocument(final Object object) {
125         return cast(object).getParent() == null;
126     }
127
128     @Override
129     public boolean isElement(final Object object) {
130         return object instanceof NormalizedNodeContext;
131     }
132
133     @Override
134     public boolean isAttribute(final Object object) {
135         return object instanceof Entry;
136     }
137
138     @Override
139     public boolean isNamespace(final Object object) {
140         return false;
141     }
142
143     @Override
144     public boolean isComment(final Object object) {
145         return false;
146     }
147
148     @Override
149     public boolean isText(final Object object) {
150         return object instanceof String;
151     }
152
153     @Override
154     public boolean isProcessingInstruction(final Object object) {
155         return false;
156     }
157
158     @Override
159     public String getCommentStringValue(final Object comment) {
160         throw new UnsupportedOperationException();
161     }
162
163     @Override
164     public String getElementStringValue(final Object element) {
165         final NormalizedNode<?, ?> node = contextNode(element);
166         if (node instanceof LeafNode || node instanceof LeafSetEntryNode) {
167             final Object value = node.getValue();
168
169             // TODO: This is a rather poor approximation of what the codec infrastructure, but it should be sufficient
170             //       to work for now. Tracking SchemaPath will mean we will need to wrap each NormalizedNode with a
171             //       corresponding SchemaPath. That in turn would complicate this class and result in a lot of object
172             //       allocations.
173             if (value instanceof byte[]) {
174                 // Binary
175                 return BaseEncoding.base64().encode((byte[]) value);
176             }
177             if (value instanceof Set) {
178                 // Bits
179                 return JOINER.join((Set<?>)value);
180             }
181             if (value != null) {
182                 // Everything else...
183                 return String.valueOf(value);
184             }
185         }
186
187         return "";
188     }
189
190     @Override
191     public String getAttributeStringValue(final Object attr) {
192         return attribute(attr).getValue();
193     }
194
195     @Override
196     public String getNamespaceStringValue(final Object ns) {
197         throw new UnsupportedOperationException();
198     }
199
200     @Override
201     public String getTextStringValue(final Object text) {
202         return text.toString();
203     }
204
205     @Override
206     public String getNamespacePrefix(final Object ns) {
207         throw new UnsupportedOperationException();
208     }
209
210     @Override
211     public XPath parseXPath(final String xpath) throws SAXPathException {
212         // FIXME: need to bind YangXPath probably
213         throw new UnsupportedOperationException();
214     }
215
216     @Override
217     public Iterator<NormalizedNodeContext> getChildAxisIterator(final Object contextNode) {
218         final NormalizedNodeContext ctx = cast(contextNode);
219         final NormalizedNode<?, ?> node = ctx.getNode();
220         if (node instanceof DataContainerNode) {
221             return Iterators.transform(((DataContainerNode<?>) node).getValue().iterator(), ctx);
222         }
223
224         return null;
225     }
226
227     @Override
228     public Iterator<NormalizedNodeContext> getChildAxisIterator(final Object contextNode, final String localName,
229             final String namespacePrefix, final String namespaceURI) {
230         final NormalizedNodeContext ctx = cast(contextNode);
231         final NormalizedNode<?, ?> node = ctx.getNode();
232         if (!(node instanceof DataContainerNode)) {
233             return null;
234         }
235
236         final QName qname = resolveQName(node, namespacePrefix, localName);
237         @SuppressWarnings({ "unchecked", "rawtypes" })
238         final Optional<NormalizedNode<?, ?>> maybeChild = ((DataContainerNode)node).getChild(new NodeIdentifier(qname));
239         if (!maybeChild.isPresent()) {
240             return null;
241         }
242
243         // The child may be a structural node
244         final NormalizedNode<?, ?> child = maybeChild.get();
245         if (child instanceof MapNode) {
246             return Iterators.transform(((MapNode)child).getValue().iterator(), ctx);
247         }
248         if (child instanceof LeafSetNode) {
249             return Iterators.transform(((LeafSetNode<?>)child).getValue().iterator(), ctx);
250         }
251
252         return Iterators.singletonIterator(ctx.apply(child));
253     }
254
255     @Override
256     public Iterator<? extends Entry<?, ?>> getAttributeAxisIterator(final Object contextNode) {
257         final NormalizedNode<?, ?> node = contextNode(contextNode);
258         if (node instanceof AttributesContainer) {
259             final Map<QName, String> attributes = ((AttributesContainer) node).getAttributes();
260             if (attributes.isEmpty()) {
261                 return null;
262             }
263
264             return attributes.entrySet().iterator();
265         }
266
267         return null;
268     }
269
270     @Override
271     public Iterator<? extends Entry<?, ?>> getAttributeAxisIterator(final Object contextNode, final String localName,
272             final String namespacePrefix, final String namespaceURI) {
273         final NormalizedNode<?, ?> node = contextNode(contextNode);
274         if (node instanceof AttributesContainer) {
275             final Map<QName, String> attributes = ((AttributesContainer) node).getAttributes();
276             if (attributes.isEmpty()) {
277                 return null;
278             }
279
280             final QName qname = resolveQName(node, namespacePrefix, localName);
281             final String value = attributes.get(qname);
282             return value == null ? null : Iterators.singletonIterator(new SimpleImmutableEntry<>(qname, value));
283         }
284
285         return null;
286     }
287
288     @Override
289     public Iterator<NormalizedNodeContext> getParentAxisIterator(final Object contextNode) {
290         final NormalizedNodeContext parent = cast(contextNode).getParent();
291         return parent == null ? null : Iterators.singletonIterator(parent);
292     }
293
294     @Override
295     public Iterator<NormalizedNodeContext> getAncestorAxisIterator(final Object contextNode)
296             throws UnsupportedAxisException {
297         final NormalizedNodeContext parent = cast(contextNode).getParent();
298         return parent == null ? null : new NormalizedNodeContextIterator(parent);
299     }
300
301     @Override
302     public Iterator<NormalizedNodeContext> getSelfAxisIterator(final Object contextNode)
303             throws UnsupportedAxisException {
304         return Iterators.singletonIterator(cast(contextNode));
305     }
306
307     @Override
308     public Iterator<NormalizedNodeContext> getAncestorOrSelfAxisIterator(final Object contextNode)
309             throws UnsupportedAxisException {
310         return new NormalizedNodeContextIterator(cast(contextNode));
311     }
312
313     @Override
314     public NormalizedNodeContext getParentNode(final Object contextNode) throws UnsupportedAxisException {
315         return cast(contextNode).getParent();
316     }
317
318     NormalizedNode<?, ?> getRootNode() {
319         return document.getRootNode();
320     }
321
322     @Nonnull
323     SchemaContext getSchemaContext() {
324         return document.getSchemaContext();
325     }
326
327     private static final class NormalizedNodeContextIterator extends UnmodifiableIterator<NormalizedNodeContext> {
328         private NormalizedNodeContext next;
329
330         NormalizedNodeContextIterator(final NormalizedNodeContext initial) {
331             this.next = requireNonNull(initial);
332         }
333
334         @Override
335         public boolean hasNext() {
336             return next != null;
337         }
338
339         @Override
340         public NormalizedNodeContext next() {
341             if (next == null) {
342                 throw new NoSuchElementException();
343             }
344
345             final NormalizedNodeContext ret = next;
346             next = next.getParent();
347             return ret;
348         }
349     }
350 }