Clean up more Sonar warnings
[yangtools.git] / data / yang-data-util / src / main / java / org / opendaylight / yangtools / yang / data / util / DataSchemaContext.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.util;
9
10 import static java.util.Objects.requireNonNull;
11
12 import org.eclipse.jdt.annotation.NonNull;
13 import org.eclipse.jdt.annotation.Nullable;
14 import org.opendaylight.yangtools.yang.common.QName;
15 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
16 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
17 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
18 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
19 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
20 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
21 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
22 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
23 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
24 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
25 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
26 import org.opendaylight.yangtools.yang.data.api.schema.ValueNode;
27 import org.opendaylight.yangtools.yang.data.util.DataSchemaContext.Composite;
28 import org.opendaylight.yangtools.yang.data.util.DataSchemaContext.SimpleValue;
29 import org.opendaylight.yangtools.yang.data.util.impl.context.AbstractCompositeContext;
30 import org.opendaylight.yangtools.yang.data.util.impl.context.AbstractContext;
31 import org.opendaylight.yangtools.yang.data.util.impl.context.AbstractPathMixinContext;
32 import org.opendaylight.yangtools.yang.data.util.impl.context.AbstractValueContext;
33 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
39 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
40
41 /**
42  * Schema derived data providing necessary information for mapping between
43  * {@link org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode} and serialization format defined in RFC6020,
44  * since the mapping is not one-to-one.
45  */
46 public sealed interface DataSchemaContext permits AbstractContext, Composite, SimpleValue {
47     /**
48      * A {@link DataSchemaContext} containing other {@link DataSchemaContext}s.
49      */
50     sealed interface Composite extends DataSchemaContext permits PathMixin, AbstractCompositeContext {
51         /**
52          * Find a child node identifier by its {@link PathArgument}.
53          *
54          * @param arg Child path argument
55          * @return A child node, or {@code null} if not found
56          * @throws NullPointerException if {@code arg} is {@code null}
57          */
58         @Nullable DataSchemaContext childByArg(PathArgument arg);
59
60         /**
61          * Find a child node identifier by its {code data tree} {@link QName}. This method returns intermediate nodes
62          * significant from {@link YangInstanceIdentifier} hierarchy of {@link PathArgument}s. If the returned node
63          * indicates is also a {@link PathMixin}, it represents a {@link NormalizedNode} encapsulation which is not
64          * visible in RFC7950 XML encoding, and a further call to this method with the same {@code child} argument will
65          * provide the next step.
66          *
67          * @param qname Child data tree QName
68          * @return A child node, or {@code null} if not found
69          * @throws NullPointerException if {@code arg} is {@code null}
70          */
71         @Nullable DataSchemaContext childByQName(QName qname);
72
73         /**
74          * Find a child node as identified by a {@link YangInstanceIdentifier} relative to this node.
75          *
76          * @param path Path towards the child node
77          * @return Child node if present, or empty when corresponding child is not found.
78          * @throws NullPointerException if {@code path} is {@code null}
79          */
80         default @Nullable DataSchemaContext childByPath(final @NonNull YangInstanceIdentifier path) {
81             final var it = path.getPathArguments().iterator();
82             if (!it.hasNext()) {
83                 return this;
84             }
85
86             var current = this;
87             while (true) {
88                 final var child = current.childByArg(it.next());
89                 if (child == null || !it.hasNext()) {
90                     return child;
91                 }
92                 if (!(child instanceof Composite compositeChild)) {
93                     return null;
94                 }
95                 current = compositeChild;
96             }
97         }
98
99         /**
100          * Attempt to enter a child {@link DataSchemaContext} towards the {@link DataSchemaNode} child identified by
101          * specified {@code data tree} {@link QName}, adjusting provided {@code stack} with inference steps
102          * corresponding to the transition to the returned node. The stack is expected to be correctly pointing at this
103          * node's schema, otherwise the results of this method are undefined.
104          *
105          * @param stack {@link SchemaInferenceStack} to update
106          * @param child Child QName
107          * @return A DataSchemaContextNode on the path towards the specified child
108          * @throws NullPointerException if any argument is {@code null}
109          */
110         @Nullable DataSchemaContext enterChild(SchemaInferenceStack stack, QName child);
111
112         /**
113          * Attempt to enter a child {@link DataSchemaContext} towards the {@link DataSchemaNode} child identified by
114          * specified {@link PathArgument}, adjusting provided {@code stack} with inference steps corresponding to
115          * the transition to the returned node. The stack is expected to be correctly pointing at this node's schema,
116          * otherwise the results of this method are undefined.
117          *
118          * @param stack {@link SchemaInferenceStack} to update
119          * @param child Child path argument
120          * @return A DataSchemaContextNode for the specified child
121          * @throws NullPointerException if any argument is {@code null}
122          */
123         @Nullable DataSchemaContext enterChild(SchemaInferenceStack stack, PathArgument child);
124     }
125
126     /**
127     * This node is a {@link NormalizedNode} intermediate, not represented in RFC7950 XML encoding. This is typically
128     * one of
129     * <ul>
130     *   <li>{@link ChoiceNode} backed by a {@link ChoiceSchemaNode}, or</li>
131     *   <li>{@link LeafSetNode} backed by a {@link LeafListSchemaNode}, or</li>
132     *   <li>{@link MapNode} backed by a {@link ListSchemaNode} with a non-empty
133     *       {@link ListSchemaNode#getKeyDefinition()}, or</li>
134     *   <li>{@link UnkeyedListNode} backed by a {@link ListSchemaNode} with an empty
135     *       {@link ListSchemaNode#getKeyDefinition()}</li>
136     * </ul>
137     *
138     * <p>
139     * This trait is important for XML codec, but also for JSON encoding of {@link YangInstanceIdentifier}.
140     */
141     sealed interface PathMixin extends Composite permits AbstractPathMixinContext {
142         /**
143          * The mixed-in {@link NodeIdentifier}.
144          *
145          * @return Mixed-in NodeIdentifier
146          */
147         default @NonNull NodeIdentifier mixinPathStep() {
148             return getPathStep();
149         }
150     }
151
152     /**
153      * Marker interface for contexts which boil down to a simple, not-structured {@link ValueNode}. This can be one of
154      * <ul>
155     *   <li>{@link LeafNode} backed by a {@link LeafSchemaNode}, or</li>
156     *   <li>{@link LeafSetNode} backed by a {@link LeafListSchemaNode}</li>
157      * </ul>
158      *
159      * <p>
160      * This trait interface is exposed for determining that the corresponding {@link TypeDefinition} of the normalized
161      * body.
162      */
163     sealed interface SimpleValue extends DataSchemaContext permits AbstractValueContext {
164         /**
165          * Return the {@link TypeDefinition} of the type backing this context.
166          *
167          * @return A {@link TypeDefinition}
168          */
169         // FIXME: YANGTOOLS-1528: return yang.data.api.type.NormalizedType
170         @NonNull TypeDefinition<?> type();
171     }
172
173     /**
174      * Get a {@link DataSchemaContext} for a particular {@link DataSchemaNode}.
175      *
176      * @param schema Backing DataSchemaNode
177      * @return A {@link DataSchemaContext}
178      * @throws NullPointerException if {@code schema} is {@code null}
179      * @throws IllegalStateException if {@code schema} is not handled
180      */
181     static @NonNull DataSchemaContext of(final DataSchemaNode schema) {
182         return AbstractContext.of(requireNonNull(schema));
183     }
184
185     @NonNull DataSchemaNode dataSchemaNode();
186
187     /**
188      * Return the fixed {@link YangInstanceIdentifier} step, if available. This method returns {@code null} for contexts
189      * like {@link MapEntryNode} and {@link LeafSetEntryNode}, where the step depends on the actual node value.
190      *
191      * @return A {@link NodeIdentifier}, or {@code null}
192      */
193     @Nullable NodeIdentifier pathStep();
194
195     /**
196      * Return the fixed {@link YangInstanceIdentifier} step.
197      *
198      * @return A {@link NodeIdentifier}
199      * @throws UnsupportedOperationException if this node does not have fixed step
200      */
201     default @NonNull NodeIdentifier getPathStep() {
202         final var arg = pathStep();
203         if (arg != null) {
204             return arg;
205         }
206         throw new UnsupportedOperationException(this + " does not have a fixed path step");
207     }
208 }