2 * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
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
9 package org.opendaylight.mdsal.binding.javav2.runtime.context.util;
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;
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;
39 public final class BindingSchemaContextUtils {
41 private BindingSchemaContextUtils() {
42 throw new UnsupportedOperationException("Utility class should not be instantiated");
46 * Find data node container by binding path in schema context.
48 * FIXME: This method does not search in case augmentations.
54 * @return node container by binding path if exists, absent otherwise
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());
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();
73 currentContainer = findDataNodeContainer(ctx, currentQName);
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();
83 return currentContainer;
86 if (TreeChildNode.class.isAssignableFrom(currentArg.getType())
87 && BindingReflections.isAugmentationChild(currentArg.getType())) {
88 currentQName = BindingReflections.findQName(currentArg.getType());
90 currentQName = QName.create(currentQName, BindingReflections.findQName(currentArg.getType()).getLocalName());
92 final Optional<DataNodeContainer> potential = findDataNodeContainer(currentContainer.get(), currentQName);
93 if (potential.isPresent()) {
94 currentContainer = potential;
96 return Optional.absent();
99 return currentContainer;
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);
108 return Optional.absent();
111 private static Optional<DataNodeContainer> findDataNodeContainer(final DataNodeContainer ctx,
112 final QName targetQName) {
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);
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);
129 return Optional.absent();
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();
142 private static Optional<DataNodeContainer> findFirstDataNodeContainerInRpcOrAction(final SchemaContext ctx,
143 final Class<? extends TreeNode> targetType) {
144 final YangModuleInfo moduleInfo;
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);
151 Optional<DataNodeContainer> optional = null;
152 optional = findFirst(ctx.getOperations(), moduleInfo, targetType);
153 if (optional.isPresent()) {
156 return findFirst(ctx.getActions(), moduleInfo, targetType);
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()) {
173 return Optional.absent();
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());
190 return Optional.absent();
194 * Find choice schema node in parent by binding class.
199 * - choice binding class
200 * @return choice schema node if exists, absent() otherwise
202 public static Optional<ChoiceSchemaNode> findInstantiatedChoice(final DataNodeContainer parent, final Class<?> choiceClass) {
203 return findInstantiatedChoice(parent, BindingReflections.findQName(choiceClass));
207 * Find choice schema node in parent node by qname of choice.
213 * @return choice schema node if exists, absent() otherwise
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);
221 return Optional.absent();
225 * Find choice case node in choice schema node.
227 * @param instantiatedChoice
229 * @param originalDefinition
231 * @return choice case node if exists, absent() otherwise
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);
239 if (potential != null) {
240 final SchemaNode potentialRoot = SchemaNodeUtils.getRootOriginalIfPossible(potential);
241 if (originalDefinition.equals(potentialRoot)) {
242 return Optional.of(potential);
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.
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);
258 return Optional.absent();