Mass-migrate to java.util.Optional
[mdsal.git] / binding2 / mdsal-binding2-runtime / src / main / java / org / opendaylight / mdsal / binding / javav2 / runtime / context / util / BindingSchemaContextUtils.java
1 /*
2  * Copyright (c) 2017 Pantheon Technologies s.r.o. 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
9 package org.opendaylight.mdsal.binding.javav2.runtime.context.util;
10
11 import com.google.common.annotations.Beta;
12 import com.google.common.base.Preconditions;
13 import java.util.Iterator;
14 import java.util.Optional;
15 import java.util.Set;
16 import org.opendaylight.mdsal.binding.javav2.generator.util.JavaIdentifier;
17 import org.opendaylight.mdsal.binding.javav2.generator.util.JavaIdentifierNormalizer;
18 import org.opendaylight.mdsal.binding.javav2.runtime.reflection.BindingReflections;
19 import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier;
20 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeArgument;
21 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
22 import org.opendaylight.mdsal.binding.javav2.spec.runtime.YangModuleInfo;
23 import org.opendaylight.mdsal.binding.javav2.spec.structural.Augmentation;
24 import org.opendaylight.mdsal.binding.javav2.spec.structural.TreeChildNode;
25 import org.opendaylight.mdsal.binding.javav2.util.BindingMapping;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.common.Revision;
28 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
31 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
33 import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
34 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
35 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
36 import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils;
37
38 @Beta
39 public final class BindingSchemaContextUtils {
40
41     private BindingSchemaContextUtils() {
42         throw new UnsupportedOperationException("Utility class should not be instantiated");
43     }
44
45     /**
46      * Find data node container by binding path in schema context.
47      *
48      * FIXME: This method does not search in case augmentations.
49      *
50      * @param ctx
51      *            - schema context
52      * @param path
53      *            - binding path
54      * @return node container by binding path if exists,.empty otherwise
55      */
56     @SuppressWarnings({ "unchecked", "rawtypes" })
57     public static Optional<DataNodeContainer> findDataNodeContainer(final SchemaContext ctx,
58             final InstanceIdentifier<?> path) {
59         final Iterator<TreeArgument> pathArguments = path.getPathArguments().iterator();
60         TreeArgument currentArg = pathArguments.next();
61         Preconditions.checkArgument(currentArg != null);
62         QName currentQName = BindingReflections.findQName(currentArg.getType());
63
64         Optional<DataNodeContainer> currentContainer;
65         if (BindingReflections.isNotification(currentArg.getType())) {
66             currentContainer = findNotification(ctx, currentQName);
67         } else if (BindingReflections.isOperationType(currentArg.getType())) {
68             currentContainer = findFirstDataNodeContainerInRpcOrAction(ctx, currentArg.getType());
69             if(currentQName == null && currentContainer.isPresent()) {
70                 currentQName = ((DataSchemaNode) currentContainer.get()).getQName();
71             }
72         } else {
73             currentContainer = findDataNodeContainer(ctx, currentQName);
74         }
75
76         while (currentContainer.isPresent() && pathArguments.hasNext()) {
77             currentArg = pathArguments.next();
78             if (Augmentation.class.isAssignableFrom(currentArg.getType())) {
79                 currentQName = BindingReflections.findQName(currentArg.getType());
80                 if(pathArguments.hasNext()) {
81                     currentArg = pathArguments.next();
82                 } else {
83                     return currentContainer;
84                 }
85             }
86             if (TreeChildNode.class.isAssignableFrom(currentArg.getType())
87                     && BindingReflections.isAugmentationChild(currentArg.getType())) {
88                 currentQName = BindingReflections.findQName(currentArg.getType());
89             } else {
90                 currentQName = QName.create(currentQName, BindingReflections.findQName(currentArg.getType()).getLocalName());
91             }
92             final Optional<DataNodeContainer> potential = findDataNodeContainer(currentContainer.get(), currentQName);
93             if (potential.isPresent()) {
94                 currentContainer = potential;
95             } else {
96                 return Optional.empty();
97             }
98         }
99         return currentContainer;
100     }
101
102     private static Optional<DataNodeContainer> findNotification(final SchemaContext ctx, final QName notificationQName) {
103         for (final NotificationDefinition notification : ctx.getNotifications()) {
104             if (notification.getQName().equals(notificationQName)) {
105                 return Optional.of(notification);
106             }
107         }
108         return Optional.empty();
109     }
110
111     private static Optional<DataNodeContainer> findDataNodeContainer(final DataNodeContainer ctx,
112             final QName targetQName) {
113
114         for (final DataSchemaNode child : ctx.getChildNodes()) {
115             if (child instanceof ChoiceSchemaNode) {
116                 final DataNodeContainer potential = findInCases((ChoiceSchemaNode) child, targetQName);
117                 if (potential != null) {
118                     return Optional.of(potential);
119                 }
120             } else if (child instanceof DataNodeContainer && child.getQName().equals(targetQName)) {
121                 return Optional.of((DataNodeContainer) child);
122             } else if (child instanceof DataNodeContainer //
123                     && child.isAddedByUses() //
124                     && child.getQName().getLocalName().equals(targetQName.getLocalName())) {
125                 return Optional.of((DataNodeContainer) child);
126             }
127
128         }
129         return Optional.empty();
130     }
131
132     private static DataNodeContainer findInCases(final ChoiceSchemaNode choiceNode, final QName targetQName) {
133         for (final CaseSchemaNode caze : choiceNode.getCases().values()) {
134             final Optional<DataNodeContainer> potential = findDataNodeContainer(caze, targetQName);
135             if (potential.isPresent()) {
136                 return potential.get();
137             }
138         }
139         return null;
140     }
141
142     private static Optional<DataNodeContainer> findFirstDataNodeContainerInRpcOrAction(final SchemaContext ctx,
143             final Class<? extends TreeNode> targetType) {
144         final YangModuleInfo moduleInfo;
145         try {
146             moduleInfo = BindingReflections.getModuleInfo(targetType);
147         } catch (final Exception e) {
148             throw new IllegalArgumentException(
149                     String.format("Failed to load module information for class %s", targetType), e);
150         }
151         Optional<DataNodeContainer> optional = null;
152         optional = findFirst(ctx.getOperations(), moduleInfo, targetType);
153         if (optional.isPresent()) {
154             return optional;
155         } else {
156             return findFirst(ctx.getActions(), moduleInfo, targetType);
157         }
158     }
159
160     private static Optional<DataNodeContainer> findFirst(final Set<? extends OperationDefinition> operations,
161             final YangModuleInfo moduleInfo, final Class<? extends TreeNode> targetType) {
162         for (final OperationDefinition operation : operations) {
163             final String operationNamespace = operation.getQName().getNamespace().toString();
164             final String operationRevision = operation.getQName().getRevision().map(Revision::toString).orElse(null);
165             if (moduleInfo.getNamespace().equals(operationNamespace)
166                     && moduleInfo.getRevision().equals(operationRevision)) {
167                 final Optional<DataNodeContainer> potential = findInputOutput(operation, targetType.getSimpleName());
168                 if(potential.isPresent()) {
169                     return potential;
170                 }
171             }
172         }
173         return Optional.empty();
174     }
175
176     private static Optional<DataNodeContainer> findInputOutput(final OperationDefinition operation,
177             final String targetType) {
178         final String operationName =
179                 JavaIdentifierNormalizer.normalizeSpecificIdentifier(operation.getQName().getLocalName(),
180                 JavaIdentifier.CLASS);
181         final String actionInputName =
182                 new StringBuilder(operationName).append(BindingMapping.RPC_INPUT_SUFFIX).toString();
183         final String actionOutputName =
184                 new StringBuilder(operationName).append(BindingMapping.RPC_OUTPUT_SUFFIX).toString();
185         if (targetType.equals(actionInputName)) {
186             return Optional.of(operation.getInput());
187         } else if (targetType.equals(actionOutputName)) {
188             return Optional.of(operation.getOutput());
189         }
190        return Optional.empty();
191     }
192
193     /**
194      * Find choice schema node in parent by binding class.
195      *
196      * @param parent
197      *            - choice parent
198      * @param choiceClass
199      *            - choice binding class
200      * @return choice schema node if exists,.empty() otherwise
201      */
202     public static Optional<ChoiceSchemaNode> findInstantiatedChoice(final DataNodeContainer parent, final Class<?> choiceClass) {
203         return findInstantiatedChoice(parent, BindingReflections.findQName(choiceClass));
204     }
205
206     /**
207      * Find choice schema node in parent node by qname of choice.
208      *
209      * @param ctxNode
210      *            - parent node
211      * @param choiceName
212      *            - qname of choice
213      * @return choice schema node if exists,.empty() otherwise
214      */
215     public static Optional<ChoiceSchemaNode> findInstantiatedChoice(final DataNodeContainer ctxNode, final QName choiceName) {
216         final DataSchemaNode potential = ctxNode.getDataChildByName(choiceName);
217         if (potential instanceof ChoiceSchemaNode) {
218             return Optional.of((ChoiceSchemaNode) potential);
219         }
220
221         return Optional.empty();
222     }
223
224     /**
225      * Find choice case node in choice schema node.
226      *
227      * @param instantiatedChoice
228      *            - choice
229      * @param originalDefinition
230      *            - choice case
231      * @return choice case node if exists,.empty() otherwise
232      */
233     public static Optional<CaseSchemaNode> findInstantiatedCase(final ChoiceSchemaNode instantiatedChoice, final CaseSchemaNode originalDefinition) {
234         final QName qname = originalDefinition.getQName();
235         final CaseSchemaNode potential = instantiatedChoice.getCaseNodeByName(qname);
236         if (originalDefinition.equals(potential)) {
237             return Optional.of(potential);
238         }
239         if (potential != null) {
240             final SchemaNode potentialRoot = SchemaNodeUtils.getRootOriginalIfPossible(potential);
241             if (originalDefinition.equals(potentialRoot)) {
242                 return Optional.of(potential);
243             }
244         }
245         // We try to find case by name, then lookup its root definition
246         // and compare it with original definition
247         // This solves case, if choice was inside grouping
248         // which was used in different module and thus namespaces are
249         // different, but local names are still same.
250         //
251         // Still we need to check equality of definition, because local name is not
252         // sufficient to uniquely determine equality of cases
253         for (CaseSchemaNode found : instantiatedChoice.findCaseNodes(qname.getLocalName())) {
254             if (originalDefinition.equals(SchemaNodeUtils.getRootOriginalIfPossible(found))) {
255                 return Optional.of(found);
256             }
257         }
258         return Optional.empty();
259     }
260 }