Revert "Move SchemaNodeIdentifier to yang-common"
[yangtools.git] / data / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / leafref / LeafRefContext.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.checkArgument;
11
12 import com.google.common.annotations.Beta;
13 import com.google.common.collect.ImmutableMap;
14 import java.util.ArrayList;
15 import java.util.Iterator;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Map.Entry;
19 import org.opendaylight.yangtools.yang.common.QName;
20 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
21 import org.opendaylight.yangtools.yang.model.api.Module;
22 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
23 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
24 import org.opendaylight.yangtools.yang.model.spi.AbstractEffectiveModelContextProvider;
25
26 public final class LeafRefContext extends AbstractEffectiveModelContextProvider {
27
28     private final QName currentNodeQName;
29     private final SchemaPath currentNodePath;
30     private final Module module;
31
32     private final LeafRefPath leafRefTargetPath;
33     private final LeafRefPath absoluteLeafRefTargetPath;
34     private final String leafRefTargetPathString;
35
36     private final boolean isReferencedBy;
37     private final boolean isReferencing;
38
39     private final ImmutableMap<QName, LeafRefContext> referencingChilds;
40     private final ImmutableMap<QName, LeafRefContext> referencedByChilds;
41     private final ImmutableMap<QName, LeafRefContext> referencedByLeafRefCtx;
42
43     // FIXME: this looks like it's related to absoluteLeafRefTargetPath, but the original use in LeafRefValidation
44     //        fast path did not make it clear. Analyze the relationship between this field and
45     //        absoluteLeafRefTargetPath.
46     private volatile LeafRefPath leafRefNodePath = null;
47
48     LeafRefContext(final LeafRefContextBuilder leafRefContextBuilder) {
49         super(leafRefContextBuilder.getSchemaContext());
50         this.currentNodeQName = leafRefContextBuilder.getCurrentNodeQName();
51         this.currentNodePath = leafRefContextBuilder.getCurrentNodePath();
52         this.leafRefTargetPath = leafRefContextBuilder.getLeafRefTargetPath();
53         this.absoluteLeafRefTargetPath = leafRefContextBuilder.getAbsoluteLeafRefTargetPath();
54         this.leafRefTargetPathString = leafRefContextBuilder.getLeafRefTargetPathString();
55         this.isReferencedBy = leafRefContextBuilder.isReferencedBy();
56         this.isReferencing = leafRefContextBuilder.isReferencing();
57         this.referencingChilds = ImmutableMap.copyOf(leafRefContextBuilder.getReferencingChilds());
58         this.referencedByChilds = ImmutableMap.copyOf(leafRefContextBuilder.getReferencedByChilds());
59         this.referencedByLeafRefCtx = ImmutableMap.copyOf(leafRefContextBuilder.getAllReferencedByLeafRefCtxs());
60         this.module = leafRefContextBuilder.getLeafRefContextModule();
61     }
62
63     public static LeafRefContext create(final EffectiveModelContext ctx) {
64         try {
65             return new LeafRefContextTreeBuilder(ctx).buildLeafRefContextTree();
66         } catch (LeafRefYangSyntaxErrorException e) {
67             throw new IllegalArgumentException(e);
68         }
69     }
70
71     public boolean hasLeafRefContextChild() {
72         return hasReferencedChild() || hasReferencingChild();
73     }
74
75     public boolean hasReferencedChild() {
76         return !referencedByChilds.isEmpty();
77     }
78
79     public boolean hasReferencingChild() {
80         return !referencingChilds.isEmpty();
81     }
82
83     public boolean isReferenced() {
84         return isReferencedBy;
85     }
86
87     public boolean isReferencing() {
88         return isReferencing;
89     }
90
91     public LeafRefContext getReferencingChildByName(final QName name) {
92         return referencingChilds.get(name);
93     }
94
95     public Map<QName, LeafRefContext> getReferencingChilds() {
96         return referencingChilds;
97     }
98
99     public LeafRefContext getReferencedChildByName(final QName name) {
100         return referencedByChilds.get(name);
101     }
102
103     public Map<QName, LeafRefContext> getReferencedByChilds() {
104         return referencedByChilds;
105     }
106
107     public SchemaPath getCurrentNodePath() {
108         return currentNodePath;
109     }
110
111     public LeafRefPath getLeafRefTargetPath() {
112         return leafRefTargetPath;
113     }
114
115     public String getLeafRefTargetPathString() {
116         return leafRefTargetPathString;
117     }
118
119     public QName getNodeName() {
120         return currentNodeQName;
121     }
122
123     public LeafRefPath getAbsoluteLeafRefTargetPath() {
124         return absoluteLeafRefTargetPath;
125     }
126
127     public Module getLeafRefContextModule() {
128         return module;
129     }
130
131     public LeafRefContext getReferencedByLeafRefCtxByName(final QName qname) {
132         return referencedByLeafRefCtx.get(qname);
133     }
134
135     public Map<QName, LeafRefContext> getAllReferencedByLeafRefCtxs() {
136         return referencedByLeafRefCtx;
137     }
138
139     @Beta
140     public LeafRefContext getLeafRefReferencingContext(final SchemaNodeIdentifier node) {
141         final Iterator<QName> iterator = descendantIterator(node);
142         LeafRefContext leafRefCtx = null;
143         LeafRefContext current = this;
144         while (iterator.hasNext() && current != null) {
145             final QName qname = iterator.next();
146             leafRefCtx = current.getReferencingChildByName(qname);
147             if (iterator.hasNext()) {
148                 current = leafRefCtx;
149             }
150         }
151
152         return leafRefCtx;
153     }
154
155     @Beta
156     public LeafRefContext getLeafRefReferencedByContext(final SchemaNodeIdentifier node) {
157         final Iterator<QName> iterator = descendantIterator(node);
158         LeafRefContext leafRefCtx = null;
159         LeafRefContext current = this;
160         while (iterator.hasNext() && current != null) {
161             final QName qname = iterator.next();
162             leafRefCtx = current.getReferencedChildByName(qname);
163             if (iterator.hasNext()) {
164                 current = leafRefCtx;
165             }
166         }
167
168         return leafRefCtx;
169     }
170
171     private Iterator<QName> descendantIterator(final SchemaNodeIdentifier node) {
172         final Iterator<QName> nodeSteps = node.getNodeIdentifiers().iterator();
173         if (node instanceof SchemaNodeIdentifier.Absolute) {
174             final Iterator<QName> mySteps = currentNodePath.getPathFromRoot().iterator();
175             while (mySteps.hasNext()) {
176                 final QName myNext = mySteps.next();
177                 checkArgument(nodeSteps.hasNext(), "Node %s is an ancestor of %s", node, currentNodePath);
178                 final QName nodeNext = nodeSteps.next();
179                 checkArgument(myNext.equals(nodeNext), "Node %s is not a descendant of %s", node, currentNodePath);
180             }
181         }
182         return nodeSteps;
183     }
184
185     @Beta
186     public boolean isLeafRef(final SchemaNodeIdentifier node) {
187         final LeafRefContext leafRefReferencingContext = getLeafRefReferencingContext(node);
188         return leafRefReferencingContext != null && leafRefReferencingContext.isReferencing();
189     }
190
191     @Beta
192     public boolean hasLeafRefChild(final SchemaNodeIdentifier node) {
193         final LeafRefContext leafRefReferencingContext = getLeafRefReferencingContext(node);
194         return leafRefReferencingContext != null && leafRefReferencingContext.hasReferencingChild();
195     }
196
197     @Beta
198     public boolean isReferencedByLeafRef(final SchemaNodeIdentifier node) {
199         final LeafRefContext leafRefReferencedByContext = getLeafRefReferencedByContext(node);
200         return leafRefReferencedByContext != null && leafRefReferencedByContext.isReferenced();
201     }
202
203     @Beta
204     public boolean hasChildReferencedByLeafRef(final SchemaNodeIdentifier node) {
205         final LeafRefContext leafRefReferencedByContext = getLeafRefReferencedByContext(node);
206         return leafRefReferencedByContext != null && leafRefReferencedByContext.hasReferencedChild();
207     }
208
209     @Beta
210     public List<LeafRefContext> findAllLeafRefChilds(final SchemaNodeIdentifier node) {
211         final LeafRefContext ctx = getLeafRefReferencingContext(node);
212         return ctx == null ? List.of() : ctx.findAllLeafRefChilds();
213     }
214
215     private List<LeafRefContext> findAllLeafRefChilds() {
216         if (isReferencing()) {
217             return List.of(this);
218         }
219
220         final List<LeafRefContext> leafRefChilds = new ArrayList<>();
221         for (final Entry<QName, LeafRefContext> child : getReferencingChilds().entrySet()) {
222             leafRefChilds.addAll(child.getValue().findAllLeafRefChilds());
223         }
224         return leafRefChilds;
225     }
226
227     @Beta
228     public List<LeafRefContext> findAllChildsReferencedByLeafRef(final SchemaNodeIdentifier node) {
229         final LeafRefContext ctx = getLeafRefReferencedByContext(node);
230         return ctx == null ? List.of() : ctx.findAllChildsReferencedByLeafRef();
231     }
232
233     private List<LeafRefContext> findAllChildsReferencedByLeafRef() {
234         if (isReferenced()) {
235             return List.of(this);
236         }
237
238         final List<LeafRefContext> childsReferencedByLeafRef = new ArrayList<>();
239         for (final Entry<QName, LeafRefContext> child : getReferencedByChilds().entrySet()) {
240             childsReferencedByLeafRef.addAll(child.getValue().findAllChildsReferencedByLeafRef());
241         }
242         return childsReferencedByLeafRef;
243     }
244
245     @Beta
246     public Map<QName, LeafRefContext> getAllLeafRefsReferencingThisNode(final SchemaNodeIdentifier node) {
247         final LeafRefContext referencedByContext = getLeafRefReferencedByContext(node);
248         return referencedByContext == null ? Map.of() : referencedByContext.getAllReferencedByLeafRefCtxs();
249     }
250
251     LeafRefPath getLeafRefNodePath() {
252         LeafRefPath ret = leafRefNodePath;
253         if (ret == null) {
254             synchronized (this) {
255                 ret = leafRefNodePath;
256                 if (ret == null) {
257                     ret = leafRefNodePath = LeafRefUtils.schemaPathToLeafRefPath(currentNodePath, module);
258                 }
259             }
260         }
261         return ret;
262     }
263 }