Bug 2366 - Effective statments impl merge, retest & bugfix
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / rfc6020 / GroupingUtils.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.parser.stmt.rfc6020;
9
10 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
11
12 import org.opendaylight.yangtools.yang.parser.stmt.reactor.RootStatementContext;
13 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType;
14 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace;
15 import java.util.Iterator;
16 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
17 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.TypeOfCopy;
18 import java.util.Collection;
19 import java.util.HashSet;
20 import java.util.Set;
21 import org.opendaylight.yangtools.yang.common.QName;
22 import org.opendaylight.yangtools.yang.common.QNameModule;
23 import org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping;
24 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
25 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
26 import org.opendaylight.yangtools.yang.model.api.stmt.RefineStatement;
27 import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement;
28 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
29 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
30 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
31 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
32 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
33
34 public final class GroupingUtils {
35
36     private GroupingUtils() {
37     }
38
39     /**
40      * @param sourceGrpStmtCtx
41      * @param targetCtx
42      * @throws SourceException
43      */
44     public static void copyFromSourceToTarget(
45             StatementContextBase<?, ?, ?> sourceGrpStmtCtx,
46             StatementContextBase<?, ?, ?> targetCtx,
47             StmtContext.Mutable<QName, UsesStatement, EffectiveStatement<QName, UsesStatement>> usesNode)
48             throws SourceException {
49
50         QNameModule newQNameModule = getNewQNameModule(targetCtx,
51                 sourceGrpStmtCtx);
52         copyDeclaredStmts(sourceGrpStmtCtx, targetCtx, usesNode, newQNameModule);
53         copyEffectiveStmts(sourceGrpStmtCtx, targetCtx, usesNode,
54                 newQNameModule);
55
56     }
57
58     public static void copyDeclaredStmts(
59             StatementContextBase<?, ?, ?> sourceGrpStmtCtx,
60             StatementContextBase<?, ?, ?> targetCtx,
61             StmtContext.Mutable<QName, UsesStatement, EffectiveStatement<QName, UsesStatement>> usesNode,
62             QNameModule newQNameModule) throws SourceException {
63         Collection<? extends StatementContextBase<?, ?, ?>> declaredSubstatements = sourceGrpStmtCtx
64                 .declaredSubstatements();
65         for (StatementContextBase<?, ?, ?> originalStmtCtx : declaredSubstatements) {
66             if (needToCopyByUses(originalStmtCtx)) {
67                 StatementContextBase<?, ?, ?> copy = originalStmtCtx
68                         .createCopy(newQNameModule, targetCtx,
69                                 TypeOfCopy.ADDED_BY_USES);
70                 targetCtx.addEffectiveSubstatement(copy);
71                 usesNode.addAsEffectOfStatement(copy);
72             } else if (isReusedByUses(originalStmtCtx)) {
73                 targetCtx.addEffectiveSubstatement(originalStmtCtx);
74                 usesNode.addAsEffectOfStatement(originalStmtCtx);
75             }
76         }
77     }
78
79     public static void copyEffectiveStmts(
80             StatementContextBase<?, ?, ?> sourceGrpStmtCtx,
81             StatementContextBase<?, ?, ?> targetCtx,
82             StmtContext.Mutable<QName, UsesStatement, EffectiveStatement<QName, UsesStatement>> usesNode,
83             QNameModule newQNameModule) throws SourceException {
84         Collection<? extends StatementContextBase<?, ?, ?>> effectiveSubstatements = sourceGrpStmtCtx
85                 .effectiveSubstatements();
86         for (StatementContextBase<?, ?, ?> originalStmtCtx : effectiveSubstatements) {
87             if (needToCopyByUses(originalStmtCtx)) {
88                 StatementContextBase<?, ?, ?> copy = originalStmtCtx
89                         .createCopy(newQNameModule, targetCtx,
90                                 TypeOfCopy.ADDED_BY_USES);
91                 targetCtx.addEffectiveSubstatement(copy);
92                 usesNode.addAsEffectOfStatement(copy);
93             } else if (isReusedByUses(originalStmtCtx)) {
94                 targetCtx.addEffectiveSubstatement(originalStmtCtx);
95                 usesNode.addAsEffectOfStatement(originalStmtCtx);
96             }
97         }
98     }
99
100     public static QNameModule getNewQNameModule(
101             StatementContextBase<?, ?, ?> targetCtx,
102             StmtContext<?, ?, ?> stmtContext) {
103         if (needToCreateNewQName(stmtContext.getPublicDefinition())) {
104             if (targetCtx.isRootContext()) {
105                 return targetCtx.getFromNamespace(
106                         ModuleCtxToModuleQName.class,
107                         targetCtx);
108             }
109             if(targetCtx.getPublicDefinition() == Rfc6020Mapping.AUGMENT) {
110                 RootStatementContext<?, ?, ?> root = targetCtx.getRoot();
111                 return targetCtx.getFromNamespace(
112                         ModuleCtxToModuleQName.class,
113                         root);
114             }
115
116             Object targetStmtArgument = targetCtx.getStatementArgument();
117             Object sourceStmtArgument = stmtContext.getStatementArgument();
118             if (targetStmtArgument instanceof QName
119                     && sourceStmtArgument instanceof QName) {
120                 QName targetQName = (QName) targetStmtArgument;
121                 QNameModule targetQNameModule = targetQName.getModule();
122
123                 QName sourceQName = (QName) sourceStmtArgument;
124                 QNameModule sourceQNameModule = sourceQName.getModule();
125
126                 if (targetQNameModule.equals(sourceQNameModule)) {
127                     return null;
128                 } else {
129                     return targetQNameModule;
130                 }
131             } else {
132                 return null;
133             }
134         } else {
135             return null;
136         }
137     }
138
139     public static boolean needToCreateNewQName(
140             StatementDefinition publicDefinition) {
141         return true;
142     }
143
144     public static boolean needToCopyByUses(StmtContext<?, ?, ?> stmtContext) {
145
146         Set<StatementDefinition> noCopyDefSet = new HashSet<>();
147         noCopyDefSet.add(Rfc6020Mapping.USES);
148         noCopyDefSet.add(Rfc6020Mapping.TYPEDEF);
149         noCopyDefSet.add(Rfc6020Mapping.TYPE);
150
151         StatementDefinition def = stmtContext.getPublicDefinition();
152         return !noCopyDefSet.contains(def);
153     }
154
155     public static boolean isReusedByUses(StmtContext<?, ?, ?> stmtContext) {
156
157         Set<StatementDefinition> reusedDefSet = new HashSet<>();
158         reusedDefSet.add(Rfc6020Mapping.TYPEDEF);
159         reusedDefSet.add(Rfc6020Mapping.TYPE);
160
161         StatementDefinition def = stmtContext.getPublicDefinition();
162         return reusedDefSet.contains(def);
163     }
164
165     public static void resolveUsesNode(
166             Mutable<QName, UsesStatement, EffectiveStatement<QName, UsesStatement>> usesNode,
167             StatementContextBase<?, ?, ?> targetNodeStmtCtx)
168             throws SourceException {
169
170         Collection<StatementContextBase<?, ?, ?>> declaredSubstatements = usesNode
171                 .declaredSubstatements();
172         for (StatementContextBase<?, ?, ?> subStmtCtx : declaredSubstatements) {
173             if (StmtContextUtils.producesDeclared(subStmtCtx,
174                     RefineStatement.class)) {
175                 performRefine(subStmtCtx, targetNodeStmtCtx);
176             }
177         }
178     }
179
180     private static void performRefine(StatementContextBase<?, ?, ?> refineCtx,
181             StatementContextBase<?, ?, ?> usesParentCtx) {
182
183         Object refineArgument = refineCtx.getStatementArgument();
184
185         SchemaNodeIdentifier refineTargetNodeIdentifier;
186         if (refineArgument instanceof SchemaNodeIdentifier) {
187             refineTargetNodeIdentifier = (SchemaNodeIdentifier) refineArgument;
188         } else {
189             throw new IllegalArgumentException(
190                     "Invalid refine argument. It must be instance of SchemaNodeIdentifier");
191         }
192
193         StatementContextBase<?, ?, ?> refineTargetNodeCtx = Utils.findNode(
194                 usesParentCtx, refineTargetNodeIdentifier);
195
196         if (refineTargetNodeCtx == null) {
197             throw new IllegalArgumentException(
198                     "Refine target node not found. Path: "
199                             + refineTargetNodeIdentifier);
200         }
201
202         addOrReplaceNodes(refineCtx, refineTargetNodeCtx);
203         refineCtx.addAsEffectOfStatement(refineTargetNodeCtx);
204
205     }
206
207     private static void addOrReplaceNodes(
208             StatementContextBase<?, ?, ?> refineCtx,
209             StatementContextBase<?, ?, ?> refineTargetNodeCtx) {
210
211         Collection<StatementContextBase<?, ?, ?>> declaredSubstatements = refineCtx
212                 .declaredSubstatements();
213         for (StatementContextBase<?, ?, ?> refineSubstatementCtx : declaredSubstatements) {
214             if (isSupportedRefineSubstatement(refineSubstatementCtx)) {
215                 addOrReplaceNode(refineSubstatementCtx, refineTargetNodeCtx);
216             }
217         }
218     }
219
220     private static void addOrReplaceNode(
221             StatementContextBase<?, ?, ?> refineSubstatementCtx,
222             StatementContextBase<?, ?, ?> refineTargetNodeCtx) {
223
224         StatementDefinition refineSubstatementDef = refineSubstatementCtx
225                 .getPublicDefinition();
226         StatementDefinition refineTargetNodeDef = refineTargetNodeCtx
227                 .getPublicDefinition();
228
229         if (!isSupportedRefineTarget(refineSubstatementCtx, refineTargetNodeCtx)) {
230             throw new SourceException("Error in module '"
231                     + refineSubstatementCtx.getRoot().getStatementArgument()
232                     + "' in the refine of uses '"
233                     + refineSubstatementCtx.getParentContext()
234                             .getStatementArgument()
235                     + "': can not perform refine of '"
236                     + refineSubstatementCtx.getPublicDefinition()
237                     + "' for the target '"
238                     + refineTargetNodeCtx.getPublicDefinition() + "'.",
239                     refineSubstatementCtx.getStatementSourceReference());
240         }
241
242         if (isAllowedToAddByRefine(refineSubstatementDef)) {
243             refineTargetNodeCtx.addEffectiveSubstatement(refineSubstatementCtx);
244         } else {
245             refineTargetNodeCtx.removeStatementFromEffectiveSubstatements(refineSubstatementDef);
246             refineTargetNodeCtx.addEffectiveSubstatement(refineSubstatementCtx);
247         }
248     }
249
250     private static boolean isAllowedToAddByRefine(
251             StatementDefinition publicDefinition) {
252         Set<StatementDefinition> allowedToAddByRefineDefSet = new HashSet<>();
253         allowedToAddByRefineDefSet.add(Rfc6020Mapping.MUST);
254
255         return allowedToAddByRefineDefSet.contains(publicDefinition);
256     }
257
258     private static boolean isSupportedRefineSubstatement(
259             StatementContextBase<?, ?, ?> refineSubstatementCtx) {
260
261         Collection<?> supportedRefineSubstatements = refineSubstatementCtx
262                 .getFromNamespace(ValidationBundlesNamespace.class,
263                         ValidationBundleType.SUPPORTED_REFINE_SUBSTATEMENTS);
264
265         return supportedRefineSubstatements == null
266                 || supportedRefineSubstatements.isEmpty()
267                 || supportedRefineSubstatements.contains(refineSubstatementCtx
268                         .getPublicDefinition())
269                 || StmtContextUtils.isUnknownStatement(refineSubstatementCtx);
270     }
271
272     private static boolean isSupportedRefineTarget(
273             StatementContextBase<?, ?, ?> refineSubstatementCtx,
274             StatementContextBase<?, ?, ?> refineTargetNodeCtx) {
275
276         Collection<?> supportedRefineTargets = YangValidationBundles.SUPPORTED_REFINE_TARGETS
277                 .get(refineSubstatementCtx.getPublicDefinition());
278
279         return supportedRefineTargets == null
280                 || supportedRefineTargets.isEmpty()
281                 || supportedRefineTargets.contains(refineTargetNodeCtx
282                         .getPublicDefinition());
283     }
284
285 }