0a23636320ff0dd078c06a2844c5d1e777f83f0c
[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.CaseSchemaNode;
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.TypedDataSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
30
31 final 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     LeafRefContext buildLeafRefContextTree() throws IOException, LeafRefYangSyntaxErrorException {
40         final LeafRefContextBuilder rootBuilder = new LeafRefContextBuilder(schemaContext.getQName(),
41             schemaContext.getPath(), schemaContext);
42
43         final Set<Module> modules = schemaContext.getModules();
44         for (final Module module : modules) {
45             for (final DataSchemaNode childNode : module.getChildNodes()) {
46                 final LeafRefContext childLeafRefContext = buildLeafRefContextReferencingTree(childNode, module);
47                 if (childLeafRefContext.hasReferencingChild() || childLeafRefContext.isReferencing()) {
48                     rootBuilder.addReferencingChild(childLeafRefContext, childLeafRefContext.getNodeName());
49                 }
50             }
51         }
52
53         for (final Module module : modules) {
54             final Collection<DataSchemaNode> childNodes = module.getChildNodes();
55             for (final DataSchemaNode childNode : childNodes) {
56                 final LeafRefContext childLeafRefContext = buildLeafRefContextReferencedByTree(childNode, module);
57
58                 if (childLeafRefContext.hasReferencedChild() || childLeafRefContext.isReferenced()) {
59                     rootBuilder.addReferencedByChild(childLeafRefContext, childLeafRefContext.getNodeName());
60                 }
61             }
62         }
63
64         // FIXME: it might be useful to merge these subtrees (i.e. referencing
65         // and referencedBy subtree)
66
67         return rootBuilder.build();
68     }
69
70     private LeafRefContext buildLeafRefContextReferencingTree(final DataSchemaNode node, final Module currentModule)
71             throws IOException, LeafRefYangSyntaxErrorException {
72         final LeafRefContextBuilder currentLeafRefContextBuilder = new LeafRefContextBuilder(node.getQName(),
73             node.getPath(), schemaContext);
74
75         if (node instanceof DataNodeContainer) {
76             for (final DataSchemaNode childNode : ((DataNodeContainer) node).getChildNodes()) {
77                 final LeafRefContext childLeafRefContext = buildLeafRefContextReferencingTree(childNode, currentModule);
78                 if (childLeafRefContext.hasReferencingChild() || childLeafRefContext.isReferencing()) {
79                     currentLeafRefContextBuilder.addReferencingChild(childLeafRefContext,
80                         childLeafRefContext.getNodeName());
81                 }
82             }
83         } else if (node instanceof ChoiceSchemaNode) {
84             // :FIXME choice without case
85             for (final CaseSchemaNode caseNode : ((ChoiceSchemaNode) node).getCases().values()) {
86                 final LeafRefContext childLeafRefContext = buildLeafRefContextReferencingTree(caseNode, currentModule);
87                 if (childLeafRefContext.hasReferencingChild() || childLeafRefContext.isReferencing()) {
88                     currentLeafRefContextBuilder.addReferencingChild(childLeafRefContext,
89                         childLeafRefContext.getNodeName());
90                 }
91             }
92
93         } else if (node instanceof TypedDataSchemaNode) {
94             final TypeDefinition<?> type = ((TypedDataSchemaNode) node).getType();
95
96             // FIXME: fix case when type is e.g. typedef -> typedef -> leafref
97             if (type instanceof LeafrefTypeDefinition) {
98                 final LeafrefTypeDefinition leafrefType = (LeafrefTypeDefinition) type;
99                 final String leafRefPathString = leafrefType.getPathStatement().toString();
100
101                 currentLeafRefContextBuilder.setLeafRefTargetPathString(leafRefPathString);
102                 currentLeafRefContextBuilder.setReferencing(true);
103
104                 final LeafRefPathParserImpl leafRefPathParser = new LeafRefPathParserImpl(schemaContext,
105                         checkNotNull(getBaseTypeModule(leafrefType), "Unable to find base module for leafref %s", node),
106                         node);
107
108                 final LeafRefPath leafRefPath = leafRefPathParser.parseLeafRefPathSourceToSchemaPath(
109                     new ByteArrayInputStream(leafRefPathString.getBytes(StandardCharsets.UTF_8)));
110
111                 currentLeafRefContextBuilder.setLeafRefTargetPath(leafRefPath);
112
113                 final LeafRefContext currentLeafRefContext = currentLeafRefContextBuilder.build();
114                 leafRefs.add(currentLeafRefContext);
115                 return currentLeafRefContext;
116             }
117         }
118
119         return currentLeafRefContextBuilder.build();
120     }
121
122     private Module getBaseTypeModule(final LeafrefTypeDefinition leafrefType) {
123         /*
124          * Find the first definition of supplied leafref type and return the
125          * module which contains this definition.
126          */
127         LeafrefTypeDefinition baseLeafRefType = leafrefType;
128         while (baseLeafRefType.getBaseType() != null) {
129             baseLeafRefType = baseLeafRefType.getBaseType();
130         }
131         return schemaContext.findModule(baseLeafRefType.getQName().getModule()).orElse(null);
132     }
133
134     private LeafRefContext buildLeafRefContextReferencedByTree(final DataSchemaNode node, final Module currentModule)
135             throws IOException,LeafRefYangSyntaxErrorException {
136         final LeafRefContextBuilder currentLeafRefContextBuilder = new LeafRefContextBuilder(node.getQName(),
137             node.getPath(), schemaContext);
138         if (node instanceof DataNodeContainer) {
139             for (final DataSchemaNode childNode : ((DataNodeContainer) node).getChildNodes()) {
140                 final LeafRefContext childLeafRefContext = buildLeafRefContextReferencedByTree(childNode,
141                     currentModule);
142                 if (childLeafRefContext.hasReferencedChild() || childLeafRefContext.isReferenced()) {
143                     currentLeafRefContextBuilder.addReferencedByChild(childLeafRefContext,
144                         childLeafRefContext.getNodeName());
145                 }
146             }
147         } else if (node instanceof ChoiceSchemaNode) {
148             for (final CaseSchemaNode caseNode : ((ChoiceSchemaNode) node).getCases().values()) {
149                 final LeafRefContext childLeafRefContext = buildLeafRefContextReferencedByTree(caseNode, currentModule);
150                 if (childLeafRefContext.hasReferencedChild() || childLeafRefContext.isReferenced()) {
151                     currentLeafRefContextBuilder.addReferencedByChild(childLeafRefContext,
152                         childLeafRefContext.getNodeName());
153                 }
154             }
155         } else if (node instanceof LeafSchemaNode || node instanceof LeafListSchemaNode) {
156             final List<LeafRefContext> foundLeafRefs = getLeafRefsFor(node, currentModule);
157             if (!foundLeafRefs.isEmpty()) {
158                 currentLeafRefContextBuilder.setReferencedBy(true);
159                 for (final LeafRefContext leafRef : foundLeafRefs) {
160                     currentLeafRefContextBuilder.addReferencedByLeafRefCtx(leafRef.getNodeName(), leafRef);
161                 }
162             }
163         }
164
165         return currentLeafRefContextBuilder.build();
166     }
167
168     private List<LeafRefContext> getLeafRefsFor(final DataSchemaNode node, final Module module) {
169         final LeafRefPath nodeXPath = LeafRefUtils.schemaPathToLeafRefPath(node.getPath(), module);
170         final List<LeafRefContext> foundLeafRefs = new LinkedList<>();
171         for (final LeafRefContext leafref : leafRefs) {
172             final LeafRefPath leafRefTargetPath = leafref.getAbsoluteLeafRefTargetPath();
173             if (leafRefTargetPath.equals(nodeXPath)) {
174                 foundLeafRefs.add(leafref);
175             }
176         }
177
178         return foundLeafRefs;
179     }
180 }