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