BUG-4688: Rework SchemaContext module lookups
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / 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.impl.leafref;
9
10 import static com.google.common.base.Preconditions.checkNotNull;
11
12 import java.io.ByteArrayInputStream;
13 import java.io.IOException;
14 import java.nio.charset.StandardCharsets;
15 import java.util.Collection;
16 import java.util.LinkedList;
17 import java.util.List;
18 import java.util.Set;
19 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
20 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
21 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
22 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
23 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.Module;
26 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
27 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
28 import org.opendaylight.yangtools.yang.model.api.TypedSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
30
31 class LeafRefContextTreeBuilder {
32     private final List<LeafRefContext> leafRefs = new LinkedList<>();
33     private final SchemaContext schemaContext;
34
35     LeafRefContextTreeBuilder(final SchemaContext schemaContext) {
36         this.schemaContext = schemaContext;
37     }
38
39     public LeafRefContext buildLeafRefContextTree() throws IOException,
40             LeafRefYangSyntaxErrorException {
41         final LeafRefContextBuilder rootBuilder = new LeafRefContextBuilder(schemaContext.getQName(),
42             schemaContext.getPath(), schemaContext);
43
44         final Set<Module> modules = schemaContext.getModules();
45         for (final Module module : modules) {
46             final Collection<DataSchemaNode> childNodes = module.getChildNodes();
47             for (final DataSchemaNode childNode : childNodes) {
48                 final LeafRefContext childLeafRefContext = buildLeafRefContextReferencingTree(
49                         childNode, module);
50
51                 if (childLeafRefContext.hasReferencingChild()
52                         || childLeafRefContext.isReferencing()) {
53                     rootBuilder.addReferencingChild(childLeafRefContext,
54                             childLeafRefContext.getNodeName());
55                 }
56             }
57         }
58
59         for (final Module module : modules) {
60             final Collection<DataSchemaNode> childNodes = module.getChildNodes();
61             for (final DataSchemaNode childNode : childNodes) {
62                 final LeafRefContext childLeafRefContext = buildLeafRefContextReferencedByTree(
63                         childNode, module);
64
65                 if (childLeafRefContext.hasReferencedChild()
66                         || childLeafRefContext.isReferenced()) {
67                     rootBuilder.addReferencedByChild(childLeafRefContext,
68                             childLeafRefContext.getNodeName());
69                 }
70             }
71         }
72
73         // FIXME: it might be useful to merge these subtrees (i.e. referencing
74         // and referencedBy subtree)
75
76         return rootBuilder.build();
77     }
78
79     private LeafRefContext buildLeafRefContextReferencingTree(
80             final DataSchemaNode node, final Module currentModule) throws IOException,
81             LeafRefYangSyntaxErrorException {
82
83         final LeafRefContextBuilder currentLeafRefContextBuilder = new LeafRefContextBuilder(
84                 node.getQName(), node.getPath(), schemaContext);
85
86         if (node instanceof DataNodeContainer) {
87             final DataNodeContainer dataNodeContainer = (DataNodeContainer) node;
88             final Collection<DataSchemaNode> childNodes = dataNodeContainer
89                     .getChildNodes();
90
91             for (final DataSchemaNode childNode : childNodes) {
92                 final LeafRefContext childLeafRefContext = buildLeafRefContextReferencingTree(
93                         childNode, currentModule);
94
95                 if (childLeafRefContext.hasReferencingChild()
96                         || childLeafRefContext.isReferencing()) {
97                     currentLeafRefContextBuilder.addReferencingChild(
98                             childLeafRefContext,
99                             childLeafRefContext.getNodeName());
100                 }
101             }
102         } else if (node instanceof ChoiceSchemaNode) {
103
104             final ChoiceSchemaNode choice = (ChoiceSchemaNode) node;
105             final Set<ChoiceCaseNode> cases = choice.getCases();
106             // :FIXME choice without case
107
108             for (final ChoiceCaseNode caseNode : cases) {
109                 final LeafRefContext childLeafRefContext = buildLeafRefContextReferencingTree(
110                         caseNode, currentModule);
111
112                 if (childLeafRefContext.hasReferencingChild()
113                         || childLeafRefContext.isReferencing()) {
114                     currentLeafRefContextBuilder.addReferencingChild(
115                             childLeafRefContext,
116                             childLeafRefContext.getNodeName());
117                 }
118             }
119
120         } else if (node instanceof TypedSchemaNode) {
121             final TypeDefinition<?> type = ((TypedSchemaNode) node).getType();
122
123             // FIXME: fix case when type is e.g. typedef -> typedef -> leafref
124             if (type instanceof LeafrefTypeDefinition) {
125                 final LeafrefTypeDefinition leafrefType = (LeafrefTypeDefinition) type;
126                 final String leafRefPathString = leafrefType.getPathStatement().toString();
127
128                 currentLeafRefContextBuilder.setLeafRefTargetPathString(leafRefPathString);
129                 currentLeafRefContextBuilder.setReferencing(true);
130
131                 final LeafRefPathParserImpl leafRefPathParser = new LeafRefPathParserImpl(schemaContext,
132                         checkNotNull(getBaseTypeModule(leafrefType), "Unable to find base module for leafref %s", node),
133                         node);
134
135                 final LeafRefPath leafRefPath = leafRefPathParser.parseLeafRefPathSourceToSchemaPath(
136                     new ByteArrayInputStream(leafRefPathString.getBytes(StandardCharsets.UTF_8)));
137
138                 currentLeafRefContextBuilder.setLeafRefTargetPath(leafRefPath);
139
140                 final LeafRefContext currentLeafRefContext = currentLeafRefContextBuilder.build();
141                 leafRefs.add(currentLeafRefContext);
142                 return currentLeafRefContext;
143             }
144         }
145
146         return currentLeafRefContextBuilder.build();
147     }
148
149     private Module getBaseTypeModule(final LeafrefTypeDefinition leafrefType) {
150         /*
151          * Find the first definition of supplied leafref type and return the
152          * module which contains this definition.
153          */
154         LeafrefTypeDefinition baseLeafRefType = leafrefType;
155         while (baseLeafRefType.getBaseType() != null) {
156             baseLeafRefType = baseLeafRefType.getBaseType();
157         }
158         return schemaContext.findModule(baseLeafRefType.getQName().getModule()).orElse(null);
159     }
160
161     private LeafRefContext buildLeafRefContextReferencedByTree(
162             final DataSchemaNode node, final Module currentModule) throws IOException,
163             LeafRefYangSyntaxErrorException {
164
165         final LeafRefContextBuilder currentLeafRefContextBuilder = new LeafRefContextBuilder(
166                 node.getQName(), node.getPath(), schemaContext);
167
168         if (node instanceof DataNodeContainer) {
169             final DataNodeContainer dataNodeContainer = (DataNodeContainer) node;
170             final Collection<DataSchemaNode> childNodes = dataNodeContainer
171                     .getChildNodes();
172
173             for (final DataSchemaNode childNode : childNodes) {
174                 final LeafRefContext childLeafRefContext = buildLeafRefContextReferencedByTree(
175                         childNode, currentModule);
176
177                 if (childLeafRefContext.hasReferencedChild()
178                         || childLeafRefContext.isReferenced()) {
179                     currentLeafRefContextBuilder.addReferencedByChild(
180                             childLeafRefContext,
181                             childLeafRefContext.getNodeName());
182                 }
183             }
184         } else if (node instanceof ChoiceSchemaNode) {
185
186             final ChoiceSchemaNode choice = (ChoiceSchemaNode) node;
187             final Set<ChoiceCaseNode> cases = choice.getCases();
188
189             for (final ChoiceCaseNode caseNode : cases) {
190                 final LeafRefContext childLeafRefContext = buildLeafRefContextReferencedByTree(
191                         caseNode, currentModule);
192
193                 if (childLeafRefContext.hasReferencedChild()
194                         || childLeafRefContext.isReferenced()) {
195                     currentLeafRefContextBuilder.addReferencedByChild(
196                             childLeafRefContext,
197                             childLeafRefContext.getNodeName());
198                 }
199             }
200
201         } else if (node instanceof LeafSchemaNode
202                 || node instanceof LeafListSchemaNode) {
203
204             final List<LeafRefContext> foundLeafRefs = getLeafRefsFor(node,
205                     currentModule);
206             if (!foundLeafRefs.isEmpty()) {
207                 currentLeafRefContextBuilder.setReferencedBy(true);
208                 for (final LeafRefContext leafRef : foundLeafRefs) {
209                     currentLeafRefContextBuilder.addReferencedByLeafRefCtx(
210                             leafRef.getNodeName(), leafRef);
211                 }
212             }
213         }
214
215         return currentLeafRefContextBuilder.build();
216     }
217
218     private List<LeafRefContext> getLeafRefsFor(final DataSchemaNode node,
219             final Module module) {
220         final LeafRefPath nodeXPath = LeafRefUtils.schemaPathToLeafRefPath(
221                 node.getPath(), module);
222
223         final List<LeafRefContext> foundLeafRefs = new LinkedList<>();
224
225         for (final LeafRefContext leafref : leafRefs) {
226             final LeafRefPath leafRefTargetPath = leafref
227                     .getAbsoluteLeafRefTargetPath();
228             if (leafRefTargetPath.equals(nodeXPath)) {
229                 foundLeafRefs.add(leafref);
230             }
231         }
232
233         return foundLeafRefs;
234     }
235
236 }