Remove LeafRefYangSyntaxErrorException
[yangtools.git] / data / yang-data-tree-ri / src / main / java / org / opendaylight / yangtools / yang / data / tree / leafref / LeafRefContextTreeBuilder.java
1 /*
2  * Copyright (c) 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.yangtools.yang.data.tree.leafref;
9
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;
25
26 final class LeafRefContextTreeBuilder {
27     private final List<LeafRefContext> leafRefs = new ArrayList<>();
28     private final EffectiveModelContext schemaContext;
29
30     LeafRefContextTreeBuilder(final EffectiveModelContext schemaContext) {
31         this.schemaContext = schemaContext;
32     }
33
34     LeafRefContext buildLeafRefContextTree() {
35         final var stack = SchemaInferenceStack.of(schemaContext);
36         final var rootBuilder = new LeafRefContextBuilder(SchemaContext.NAME, ImmutableList.of(), schemaContext);
37
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);
43                 stack.exit();
44                 if (childLeafRefContext.hasReferencingChild() || childLeafRefContext.isReferencing()) {
45                     rootBuilder.addReferencingChild(childLeafRefContext, childLeafRefContext.getNodeName());
46                 }
47             }
48         }
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);
53                 stack.exit();
54                 if (childLeafRefContext.hasReferencedChild() || childLeafRefContext.isReferenced()) {
55                     rootBuilder.addReferencedByChild(childLeafRefContext, childLeafRefContext.getNodeName());
56                 }
57             }
58         }
59
60         // FIXME: it might be useful to merge these subtrees (i.e. referencing
61         // and referencedBy subtree)
62
63         return rootBuilder.build();
64     }
65
66     private LeafRefContext buildLeafRefContextReferencingTree(final DataSchemaNode node,
67             final SchemaInferenceStack stack) {
68         final LeafRefContextBuilder currentLeafRefContextBuilder = new LeafRefContextBuilder(node.getQName(),
69             extractPath(stack), schemaContext);
70
71         if (node instanceof DataNodeContainer container) {
72             for (var childNode : container.getChildNodes()) {
73                 stack.enterSchemaTree(childNode.getQName());
74                 final LeafRefContext childLeafRefContext = buildLeafRefContextReferencingTree(childNode, stack);
75                 stack.exit();
76                 if (childLeafRefContext.hasReferencingChild() || childLeafRefContext.isReferencing()) {
77                     currentLeafRefContextBuilder.addReferencingChild(childLeafRefContext,
78                         childLeafRefContext.getNodeName());
79                 }
80             }
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);
86                 stack.exit();
87                 if (childLeafRefContext.hasReferencingChild() || childLeafRefContext.isReferencing()) {
88                     currentLeafRefContextBuilder.addReferencingChild(childLeafRefContext,
89                         childLeafRefContext.getNodeName());
90                 }
91             }
92
93         } else if (node instanceof TypedDataSchemaNode typedNode) {
94             final var type = typedNode.getType();
95
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);
101
102                 currentLeafRefContextBuilder.setLeafRefTargetPathString(path.getOriginalString());
103                 currentLeafRefContextBuilder.setReferencing(true);
104                 currentLeafRefContextBuilder.setLeafRefTargetPath(leafRefPath);
105
106                 final var currentLeafRefContext = currentLeafRefContextBuilder.build();
107                 leafRefs.add(currentLeafRefContext);
108                 return currentLeafRefContext;
109             }
110         }
111
112         return currentLeafRefContextBuilder.build();
113     }
114
115     private LeafRefContext buildLeafRefContextReferencedByTree(final DataSchemaNode node, final Module currentModule,
116             final SchemaInferenceStack stack) {
117         final var currentLeafRefContextBuilder = new LeafRefContextBuilder(node.getQName(), extractPath(stack),
118             schemaContext);
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);
123                 stack.exit();
124                 if (childLeafRefContext.hasReferencedChild() || childLeafRefContext.isReferenced()) {
125                     currentLeafRefContextBuilder.addReferencedByChild(childLeafRefContext,
126                         childLeafRefContext.getNodeName());
127                 }
128             }
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);
133                 stack.exit();
134                 if (childLeafRefContext.hasReferencedChild() || childLeafRefContext.isReferenced()) {
135                     currentLeafRefContextBuilder.addReferencedByChild(childLeafRefContext,
136                         childLeafRefContext.getNodeName());
137                 }
138             }
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);
145                 }
146             }
147         }
148
149         return currentLeafRefContextBuilder.build();
150     }
151
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);
159             }
160         }
161
162         return foundLeafRefs;
163     }
164
165     private static ImmutableList<QName> extractPath(final SchemaInferenceStack stack) {
166         return stack.isEmpty() ? ImmutableList.of()
167             : ImmutableList.copyOf(stack.toSchemaNodeIdentifier().getNodeIdentifiers());
168     }
169 }