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.generator.impl.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.spec.base.InstanceIdentifier;
19 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeArgument;
20 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
21 import org.opendaylight.mdsal.binding.javav2.spec.runtime.YangModuleInfo;
22 import org.opendaylight.mdsal.binding.javav2.spec.structural.Augmentation;
23 import org.opendaylight.mdsal.binding.javav2.spec.structural.TreeChildNode;
24 import org.opendaylight.mdsal.binding.javav2.spec.util.BindingReflections;
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;
38 public final class BindingSchemaContextUtils {
40 private BindingSchemaContextUtils() {
41 throw new UnsupportedOperationException("Utility class should not be instantiated");
45 * Find data node container by binding path in schema context.
47 * FIXME: This method does not search in case augmentations.
53 * @return node container by binding path if exists, absent otherwise
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());
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();
72 currentContainer = findDataNodeContainer(ctx, currentQName);
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();
82 return currentContainer;
85 if (TreeChildNode.class.isAssignableFrom(currentArg.getType())
86 && BindingReflections.isAugmentationChild(currentArg.getType())) {
87 currentQName = BindingReflections.findQName(currentArg.getType());
89 currentQName = QName.create(currentQName, BindingReflections.findQName(currentArg.getType()).getLocalName());
91 final Optional<DataNodeContainer> potential = findDataNodeContainer(currentContainer.get(), currentQName);
92 if (potential.isPresent()) {
93 currentContainer = potential;
95 return Optional.absent();
98 return currentContainer;
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);
107 return Optional.absent();
110 private static Optional<DataNodeContainer> findDataNodeContainer(final DataNodeContainer ctx,
111 final QName targetQName) {
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);
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);
128 return Optional.absent();
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();
141 private static Optional<DataNodeContainer> findFirstDataNodeContainerInRpcOrAction(final SchemaContext ctx,
142 final Class<? extends TreeNode> targetType) {
143 final YangModuleInfo moduleInfo;
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);
150 Optional<DataNodeContainer> optional = null;
151 optional = findFirst(ctx.getOperations(), moduleInfo, targetType);
152 if (optional.isPresent()) {
155 return findFirst(ctx.getActions(), moduleInfo, targetType);
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()) {
172 return Optional.absent();
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());
189 return Optional.absent();
193 * Find choice schema node in parent by binding class.
198 * - choice binding class
199 * @return choice schema node if exists, absent() otherwise
201 public static Optional<ChoiceSchemaNode> findInstantiatedChoice(final DataNodeContainer parent, final Class<?> choiceClass) {
202 return findInstantiatedChoice(parent, BindingReflections.findQName(choiceClass));
206 * Find choice schema node in parent node by qname of choice.
212 * @return choice schema node if exists, absent() otherwise
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);
220 return Optional.absent();
224 * Find choice case node in choice schema node.
226 * @param instantiatedChoice
228 * @param originalDefinition
230 * @return choice case node if exists, absent() otherwise
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);
237 if (potential != null) {
238 final SchemaNode potentialRoot = SchemaNodeUtils.getRootOriginalIfPossible(potential);
239 if (originalDefinition.equals(potentialRoot)) {
240 return Optional.of(potential);
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.
249 // Still we need to check equality of definition, because local name is not
250 // sufficient to uniquelly determine equality of cases
252 potential = instantiatedChoice.getCaseNodeByName(originalDefinition.getQName().getLocalName());
253 if(potential != null && (originalDefinition.equals(SchemaNodeUtils.getRootOriginalIfPossible(potential)))) {
254 return Optional.of(potential);
256 return Optional.absent();