Optimize BindingSchemaContextUtils.findDataNodeContainer()
[mdsal.git] / binding / mdsal-binding-generator-impl / src / main / java / org / opendaylight / mdsal / binding / generator / impl / BindingSchemaContextUtils.java
1 /*
2  * Copyright (c) 2014, 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.mdsal.binding.generator.impl;
9
10 import com.google.common.base.Preconditions;
11 import java.util.HashSet;
12 import java.util.Iterator;
13 import java.util.Optional;
14 import java.util.Set;
15 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
16 import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
17 import org.opendaylight.yangtools.yang.binding.Augmentation;
18 import org.opendaylight.yangtools.yang.binding.ChildOf;
19 import org.opendaylight.yangtools.yang.binding.DataObject;
20 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
21 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
22 import org.opendaylight.yangtools.yang.common.QName;
23 import org.opendaylight.yangtools.yang.common.QNameModule;
24 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
26 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
29 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
31 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
32 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
33 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
34 import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 public final class BindingSchemaContextUtils {
39     private static final Logger LOG = LoggerFactory.getLogger(BindingSchemaContextUtils.class);
40
41     private BindingSchemaContextUtils() {
42         throw new UnsupportedOperationException("Utility class should not be instantiated");
43     }
44
45     // FIXME: This method does not search in case augmentations.
46     public static Optional<DataNodeContainer> findDataNodeContainer(final SchemaContext ctx,
47             final InstanceIdentifier<?> path) {
48         Iterator<PathArgument> pathArguments = path.getPathArguments().iterator();
49         PathArgument currentArg = pathArguments.next();
50         Preconditions.checkArgument(currentArg != null);
51         QName currentQName = BindingReflections.findQName(currentArg.getType());
52
53         Optional<DataNodeContainer> currentContainer = Optional.empty();
54         if (BindingReflections.isNotification(currentArg.getType())) {
55             currentContainer = findNotification(ctx, currentQName);
56         } else if (BindingReflections.isRpcType(currentArg.getType())) {
57             currentContainer = findFirstDataNodeContainerInRpc(ctx, currentArg.getType());
58             if (currentQName == null && currentContainer.isPresent()) {
59                 currentQName = ((DataSchemaNode) currentContainer.get()).getQName();
60             }
61         } else {
62             currentContainer = findDataNodeContainer(ctx, currentQName);
63         }
64
65         while (currentContainer.isPresent() && pathArguments.hasNext()) {
66             currentArg = pathArguments.next();
67             if (Augmentation.class.isAssignableFrom(currentArg.getType())) {
68                 currentQName = BindingReflections.findQName(currentArg.getType());
69                 if (pathArguments.hasNext()) {
70                     currentArg = pathArguments.next();
71                 } else {
72                     return currentContainer;
73                 }
74             }
75             if (ChildOf.class.isAssignableFrom(currentArg.getType())
76                     && BindingReflections.isAugmentationChild(currentArg.getType())) {
77                 currentQName = BindingReflections.findQName(currentArg.getType());
78             } else {
79                 currentQName = BindingReflections.findQName(currentArg.getType()).withModule(currentQName.getModule());
80             }
81             Optional<DataNodeContainer> potential = findDataNodeContainer(currentContainer.get(), currentQName);
82             if (potential.isPresent()) {
83                 currentContainer = potential;
84             } else {
85                 return Optional.empty();
86             }
87         }
88         return currentContainer;
89     }
90
91     private static Optional<DataNodeContainer> findDataNodeContainer(final DataNodeContainer ctx,
92             final QName targetQName) {
93
94         for (DataSchemaNode child : ctx.getChildNodes()) {
95             if (child instanceof ChoiceSchemaNode) {
96                 DataNodeContainer potential = findInCases((ChoiceSchemaNode) child, targetQName);
97                 if (potential != null) {
98                     return Optional.of(potential);
99                 }
100             } else if (child instanceof DataNodeContainer) {
101                 final QName qname = child.getQName();
102                 if (qname.equals(targetQName)
103                         || child.isAddedByUses() && qname.getLocalName().equals(targetQName.getLocalName())) {
104                     return Optional.of((DataNodeContainer) child);
105                 }
106             }
107
108         }
109         return Optional.empty();
110     }
111
112     private static Optional<DataNodeContainer> findNotification(final SchemaContext ctx,
113             final QName notificationQName) {
114         for (NotificationDefinition notification : ctx.getNotifications()) {
115             if (notification.getQName().equals(notificationQName)) {
116                 return Optional.of(notification);
117             }
118         }
119         return Optional.empty();
120     }
121
122     private static DataNodeContainer findInCases(final ChoiceSchemaNode choiceNode, final QName targetQName) {
123         for (CaseSchemaNode caze : choiceNode.getCases().values()) {
124             Optional<DataNodeContainer> potential = findDataNodeContainer(caze, targetQName);
125             if (potential.isPresent()) {
126                 return potential.get();
127             }
128         }
129         return null;
130     }
131
132     @SuppressWarnings("checkstyle:illegalCatch")
133     private static Optional<DataNodeContainer> findFirstDataNodeContainerInRpc(final SchemaContext ctx,
134             final Class<? extends DataObject> targetType) {
135         final QNameModule targetModule;
136         try {
137             targetModule = BindingReflections.getModuleInfo(targetType).getName().getModule();
138         } catch (Exception e) {
139             throw new IllegalArgumentException(
140                     String.format("Failed to load module information for class %s", targetType), e);
141         }
142
143         for (RpcDefinition rpc : ctx.getOperations()) {
144             if (targetModule.equals(rpc.getQName().getModule())) {
145                 final Optional<DataNodeContainer> potential = findInputOutput(rpc,targetType.getSimpleName());
146                 if (potential.isPresent()) {
147                     return potential;
148                 }
149             }
150         }
151         return Optional.empty();
152     }
153
154     private static Optional<DataNodeContainer> findInputOutput(final RpcDefinition rpc, final String targetType) {
155         final String rpcName = BindingMapping.getClassName(rpc.getQName());
156         final String rpcInputName = rpcName + BindingMapping.RPC_INPUT_SUFFIX;
157         if (targetType.equals(rpcInputName)) {
158             return Optional.of(rpc.getInput());
159         }
160         final String rpcOutputName = rpcName + BindingMapping.RPC_OUTPUT_SUFFIX;
161         if (targetType.equals(rpcOutputName)) {
162             return Optional.of(rpc.getOutput());
163         }
164         return Optional.empty();
165     }
166
167     public static Set<AugmentationSchemaNode> collectAllAugmentationDefinitions(final SchemaContext currentSchema,
168             final AugmentationTarget ctxNode) {
169         HashSet<AugmentationSchemaNode> augmentations = new HashSet<>();
170         augmentations.addAll(ctxNode.getAvailableAugmentations());
171         if (ctxNode instanceof DataSchemaNode && ((DataSchemaNode) ctxNode).isAddedByUses()) {
172             LOG.info("DataSchemaNode target added by uses {}", ctxNode);
173         }
174
175         return augmentations;
176     }
177
178     public static Optional<ChoiceSchemaNode> findInstantiatedChoice(final DataNodeContainer parent,
179             final Class<?> choiceClass) {
180         return findInstantiatedChoice(parent, BindingReflections.findQName(choiceClass));
181     }
182
183     public static Optional<ChoiceSchemaNode> findInstantiatedChoice(final DataNodeContainer ctxNode,
184             final QName choiceName) {
185         DataSchemaNode potential = ctxNode.getDataChildByName(choiceName);
186         if (potential instanceof ChoiceSchemaNode) {
187             return Optional.of((ChoiceSchemaNode) potential);
188         }
189
190         return Optional.empty();
191     }
192
193     public static Optional<CaseSchemaNode> findInstantiatedCase(final ChoiceSchemaNode instantiatedChoice,
194             final CaseSchemaNode originalDefinition) {
195         CaseSchemaNode potential = instantiatedChoice.getCaseNodeByName(originalDefinition.getQName());
196         if (originalDefinition.equals(potential)) {
197             return Optional.of(potential);
198         }
199         if (potential != null) {
200             SchemaNode potentialRoot = SchemaNodeUtils.getRootOriginalIfPossible(potential);
201             if (originalDefinition.equals(potentialRoot)) {
202                 return Optional.of(potential);
203             }
204         }
205
206         // We try to find case by name, then lookup its root definition
207         // and compare it with original definition
208         // This solves case, if choice was inside grouping
209         // which was used in different module and thus namespaces are
210         // different, but local names are still same.
211         //
212         // Still we need to check equality of definition, because local name is not
213         // sufficient to uniquelly determine equality of cases
214         //
215         for (CaseSchemaNode caze : instantiatedChoice.findCaseNodes(originalDefinition.getQName().getLocalName())) {
216             if (originalDefinition.equals(SchemaNodeUtils.getRootOriginalIfPossible(caze))) {
217                 return Optional.of(caze);
218             }
219         }
220         return Optional.empty();
221     }
222
223 }