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