2 * Copyright (c) 2015 Cisco Systems, Inc. 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
8 package org.opendaylight.yangtools.yang.data.tree.leafref;
10 import com.google.common.collect.ImmutableList;
11 import java.util.ArrayList;
12 import java.util.List;
13 import org.opendaylight.yangtools.yang.common.QName;
14 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
15 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
16 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
17 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
18 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
19 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
20 import org.opendaylight.yangtools.yang.model.api.Module;
21 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
22 import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
23 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
24 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
26 final class LeafRefContextTreeBuilder {
27 private final List<LeafRefContext> leafRefs = new ArrayList<>();
28 private final EffectiveModelContext schemaContext;
30 LeafRefContextTreeBuilder(final EffectiveModelContext schemaContext) {
31 this.schemaContext = schemaContext;
34 LeafRefContext buildLeafRefContextTree() {
35 final var stack = SchemaInferenceStack.of(schemaContext);
36 final var rootBuilder = new LeafRefContextBuilder(SchemaContext.NAME, ImmutableList.of(), schemaContext);
38 final var modules = schemaContext.getModules();
39 for (final var module : modules) {
40 for (var childNode : module.getChildNodes()) {
41 stack.enterSchemaTree(childNode.getQName());
42 final var childLeafRefContext = buildLeafRefContextReferencingTree(childNode, stack);
44 if (childLeafRefContext.hasReferencingChild() || childLeafRefContext.isReferencing()) {
45 rootBuilder.addReferencingChild(childLeafRefContext, childLeafRefContext.getNodeName());
49 for (final var module : modules) {
50 for (var childNode : module.getChildNodes()) {
51 stack.enterSchemaTree(childNode.getQName());
52 final var childLeafRefContext = buildLeafRefContextReferencedByTree(childNode, module, stack);
54 if (childLeafRefContext.hasReferencedChild() || childLeafRefContext.isReferenced()) {
55 rootBuilder.addReferencedByChild(childLeafRefContext, childLeafRefContext.getNodeName());
60 // FIXME: it might be useful to merge these subtrees (i.e. referencing
61 // and referencedBy subtree)
63 return rootBuilder.build();
66 private LeafRefContext buildLeafRefContextReferencingTree(final DataSchemaNode node,
67 final SchemaInferenceStack stack) {
68 final LeafRefContextBuilder currentLeafRefContextBuilder = new LeafRefContextBuilder(node.getQName(),
69 extractPath(stack), schemaContext);
71 if (node instanceof DataNodeContainer container) {
72 for (var childNode : container.getChildNodes()) {
73 stack.enterSchemaTree(childNode.getQName());
74 final LeafRefContext childLeafRefContext = buildLeafRefContextReferencingTree(childNode, stack);
76 if (childLeafRefContext.hasReferencingChild() || childLeafRefContext.isReferencing()) {
77 currentLeafRefContextBuilder.addReferencingChild(childLeafRefContext,
78 childLeafRefContext.getNodeName());
81 } else if (node instanceof ChoiceSchemaNode choice) {
82 // :FIXME choice without case
83 for (var caseNode : choice.getCases()) {
84 stack.enterSchemaTree(caseNode.getQName());
85 final LeafRefContext childLeafRefContext = buildLeafRefContextReferencingTree(caseNode, stack);
87 if (childLeafRefContext.hasReferencingChild() || childLeafRefContext.isReferencing()) {
88 currentLeafRefContextBuilder.addReferencingChild(childLeafRefContext,
89 childLeafRefContext.getNodeName());
93 } else if (node instanceof TypedDataSchemaNode typedNode) {
94 final var type = typedNode.getType();
96 // FIXME: fix case when type is e.g. typedef -> typedef -> leafref
97 if (type instanceof LeafrefTypeDefinition leafrefType) {
98 final var path = leafrefType.getPathStatement();
99 final var leafRefPathParser = new LeafRefPathParserImpl(leafrefType, typedNode);
100 final var leafRefPath = leafRefPathParser.parseLeafRefPath(path);
102 currentLeafRefContextBuilder.setLeafRefTargetPathString(path.getOriginalString());
103 currentLeafRefContextBuilder.setReferencing(true);
104 currentLeafRefContextBuilder.setLeafRefTargetPath(leafRefPath);
106 final var currentLeafRefContext = currentLeafRefContextBuilder.build();
107 leafRefs.add(currentLeafRefContext);
108 return currentLeafRefContext;
112 return currentLeafRefContextBuilder.build();
115 private LeafRefContext buildLeafRefContextReferencedByTree(final DataSchemaNode node, final Module currentModule,
116 final SchemaInferenceStack stack) {
117 final var currentLeafRefContextBuilder = new LeafRefContextBuilder(node.getQName(), extractPath(stack),
119 if (node instanceof DataNodeContainer container) {
120 for (var childNode : container.getChildNodes()) {
121 stack.enterSchemaTree(childNode.getQName());
122 final var childLeafRefContext = buildLeafRefContextReferencedByTree(childNode, currentModule, stack);
124 if (childLeafRefContext.hasReferencedChild() || childLeafRefContext.isReferenced()) {
125 currentLeafRefContextBuilder.addReferencedByChild(childLeafRefContext,
126 childLeafRefContext.getNodeName());
129 } else if (node instanceof ChoiceSchemaNode choice) {
130 for (var caseNode : choice.getCases()) {
131 stack.enterSchemaTree(caseNode.getQName());
132 final var childLeafRefContext = buildLeafRefContextReferencedByTree(caseNode, currentModule, stack);
134 if (childLeafRefContext.hasReferencedChild() || childLeafRefContext.isReferenced()) {
135 currentLeafRefContextBuilder.addReferencedByChild(childLeafRefContext,
136 childLeafRefContext.getNodeName());
139 } else if (node instanceof LeafSchemaNode || node instanceof LeafListSchemaNode) {
140 final var foundLeafRefs = getLeafRefsFor(currentModule, stack);
141 if (!foundLeafRefs.isEmpty()) {
142 currentLeafRefContextBuilder.setReferencedBy(true);
143 for (var leafRef : foundLeafRefs) {
144 currentLeafRefContextBuilder.addReferencedByLeafRefCtx(leafRef.getNodeName(), leafRef);
149 return currentLeafRefContextBuilder.build();
152 private List<LeafRefContext> getLeafRefsFor(final Module module, final SchemaInferenceStack stack) {
153 final var nodeXPath = LeafRefUtils.schemaPathToLeafRefPath(extractPath(stack), module);
154 final var foundLeafRefs = new ArrayList<LeafRefContext>();
155 for (var leafref: leafRefs) {
156 final var leafRefTargetPath = leafref.getAbsoluteLeafRefTargetPath();
157 if (leafRefTargetPath.equals(nodeXPath)) {
158 foundLeafRefs.add(leafref);
162 return foundLeafRefs;
165 private static ImmutableList<QName> extractPath(final SchemaInferenceStack stack) {
166 return stack.isEmpty() ? ImmutableList.of()
167 : ImmutableList.copyOf(stack.toSchemaNodeIdentifier().getNodeIdentifiers());