Do not compile augment/refine/deviate paths
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / rfc6020 / AugmentUtils.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 com.google.common.base.Preconditions;
11 import com.google.common.collect.ImmutableList.Builder;
12 import com.google.common.collect.ImmutableSet;
13 import com.google.common.collect.Iterables;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.Iterator;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Objects;
20 import java.util.Set;
21 import javax.annotation.Nullable;
22 import org.opendaylight.yangtools.yang.common.QName;
23 import org.opendaylight.yangtools.yang.common.QNameModule;
24 import org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping;
25 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
26 import org.opendaylight.yangtools.yang.model.api.stmt.AugmentStatement;
27 import org.opendaylight.yangtools.yang.model.api.stmt.DataDefinitionStatement;
28 import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryStatement;
29 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
30 import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement;
31 import org.opendaylight.yangtools.yang.model.api.stmt.WhenStatement;
32 import org.opendaylight.yangtools.yang.parser.spi.NamespaceToModule;
33 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
34 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
35 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.TypeOfCopy;
36 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
37 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
38 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
39 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace;
40 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType;
41 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 public final class AugmentUtils {
46
47     private static final Logger LOG = LoggerFactory.getLogger(AugmentUtils.class);
48
49     private AugmentUtils() {
50         throw new UnsupportedOperationException();
51     }
52
53     public static void copyFromSourceToTarget(final StatementContextBase<?, ?, ?> sourceCtx,
54             final StatementContextBase<?, ?, ?> targetCtx) throws SourceException {
55
56         copyDeclaredStmts(sourceCtx, targetCtx);
57         copyEffectiveStmts(sourceCtx, targetCtx);
58     }
59
60     public static void copyDeclaredStmts(final StatementContextBase<?, ?, ?> sourceCtx,
61             final StatementContextBase<?, ?, ?> targetCtx) throws SourceException {
62
63         final List<StatementContextBase<?, ?, ?>> subStatements = new Builder<StatementContextBase<?, ?, ?>>()
64                 .addAll(targetCtx.declaredSubstatements()).addAll(targetCtx.effectiveSubstatements()).build();
65         boolean sourceAndTargetInSameModule = Utils.getRootModuleQName(sourceCtx).equals(
66                 Utils.getRootModuleQName(targetCtx));
67
68         TypeOfCopy typeOfCopy = sourceCtx.getParentContext().getPublicDefinition().getDeclaredRepresentationClass()
69                 .equals(UsesStatement.class) ? TypeOfCopy.ADDED_BY_USES_AUGMENTATION : TypeOfCopy.ADDED_BY_AUGMENTATION;
70
71         for (StatementContextBase<?, ?, ?> originalStmtCtx : sourceCtx.declaredSubstatements()) {
72             if (needToCopyByAugment(originalStmtCtx)) {
73                 validateNodeCanBeCopiedByAugment(originalStmtCtx, subStatements, sourceAndTargetInSameModule);
74
75                 StatementContextBase<?, ?, ?> copy = originalStmtCtx.createCopy(targetCtx, typeOfCopy);
76                 targetCtx.addEffectiveSubstatement(copy);
77             } else if (isReusedByAugment(originalStmtCtx)) {
78                 targetCtx.addEffectiveSubstatement(originalStmtCtx);
79             }
80         }
81     }
82
83     public static void copyEffectiveStmts(final StatementContextBase<?, ?, ?> sourceCtx,
84             final StatementContextBase<?, ?, ?> targetCtx) throws SourceException {
85
86         final List<StatementContextBase<?, ?, ?>> subStatements = new Builder<StatementContextBase<?, ?, ?>>()
87                 .addAll(targetCtx.declaredSubstatements()).addAll(targetCtx.effectiveSubstatements()).build();
88         boolean sourceAndTargetInSameModule = Utils.getRootModuleQName(sourceCtx).equals(
89                 Utils.getRootModuleQName(targetCtx));
90
91         TypeOfCopy typeOfCopy = sourceCtx.getParentContext().getPublicDefinition().getDeclaredRepresentationClass()
92                 .equals(UsesStatement.class) ? TypeOfCopy.ADDED_BY_USES_AUGMENTATION : TypeOfCopy.ADDED_BY_AUGMENTATION;
93
94         for (StatementContextBase<?, ?, ?> originalStmtCtx : sourceCtx.effectiveSubstatements()) {
95             if (needToCopyByAugment(originalStmtCtx)) {
96                 validateNodeCanBeCopiedByAugment(originalStmtCtx, subStatements, sourceAndTargetInSameModule);
97
98                 StatementContextBase<?, ?, ?> copy = originalStmtCtx.createCopy(targetCtx, typeOfCopy);
99                 targetCtx.addEffectiveSubstatement(copy);
100             } else if (isReusedByAugment(originalStmtCtx)) {
101                 targetCtx.addEffectiveSubstatement(originalStmtCtx);
102             }
103         }
104     }
105
106     private static void validateNodeCanBeCopiedByAugment(final StatementContextBase<?, ?, ?> sourceCtx,
107             final Iterable<StatementContextBase<?, ?, ?>> targetSubStatements,
108             final boolean sourceAndTargetInSameModule) {
109
110         if (WhenStatement.class.equals(sourceCtx.getPublicDefinition().getDeclaredRepresentationClass())) {
111             return;
112         }
113
114         if (!sourceAndTargetInSameModule) {
115             for (final StatementContextBase<?, ?, ?> sourceSubStatement :
116                 Iterables.concat(sourceCtx.declaredSubstatements(), sourceCtx.effectiveSubstatements())) {
117                 Preconditions.checkArgument(!MandatoryStatement.class.equals(
118                     sourceSubStatement.getPublicDefinition().getDeclaredRepresentationClass()),
119                     "An augment cannot add node '%s' because it is mandatory and in module different from target",
120                     sourceCtx.rawStatementArgument());
121             }
122         }
123
124         for (final StatementContextBase<?, ?, ?> subStatement : targetSubStatements) {
125
126             final boolean sourceIsDataNode = DataDefinitionStatement.class.isAssignableFrom(
127                 sourceCtx.getPublicDefinition().getDeclaredRepresentationClass());
128             final boolean targetIsDataNode = DataDefinitionStatement.class.isAssignableFrom(
129                 subStatement.getPublicDefinition().getDeclaredRepresentationClass());
130             Preconditions.checkState(!sourceIsDataNode || !targetIsDataNode
131                     || !Objects.equals(sourceCtx.getStatementArgument(), subStatement.getStatementArgument()),
132                 "An augment cannot add node named '%s' because this name is already used in target",
133                 sourceCtx.rawStatementArgument());
134         }
135     }
136
137     public static QNameModule getNewQNameModule(final StatementContextBase<?, ?, ?> targetCtx,
138             final StatementContextBase<?, ?, ?> sourceCtx) {
139         Object targetStmtArgument = targetCtx.getStatementArgument();
140
141         final StatementContextBase<?, ?, ?> root = sourceCtx.getRoot();
142         final QNameModule sourceQNameModule = root.getFromNamespace(ModuleCtxToModuleQName.class, root);
143
144         if (targetStmtArgument instanceof QName) {
145             QName targetQName = (QName) targetStmtArgument;
146             QNameModule targetQNameModule = targetQName.getModule();
147
148             if (targetQNameModule.equals(sourceQNameModule)) {
149                 return null;
150             } else {
151                 return targetQNameModule;
152             }
153         } else {
154             return null;
155         }
156     }
157
158     private static final Set<Rfc6020Mapping> NOCOPY_DEV_SET = ImmutableSet.of(Rfc6020Mapping.USES);
159
160     public static boolean needToCopyByAugment(final StmtContext<?, ?, ?> stmtContext) {
161         return !NOCOPY_DEV_SET.contains(stmtContext.getPublicDefinition());
162     }
163
164     private static final Set<Rfc6020Mapping> REUSED_DEF_SET = ImmutableSet.of(Rfc6020Mapping.TYPEDEF);
165
166     public static boolean isReusedByAugment(final StmtContext<?, ?, ?> stmtContext) {
167         return REUSED_DEF_SET.contains(stmtContext.getPublicDefinition());
168     }
169
170     public static StatementContextBase<?, ?, ?> getAugmentTargetCtx(
171             final Mutable<SchemaNodeIdentifier, AugmentStatement, EffectiveStatement<SchemaNodeIdentifier, AugmentStatement>> augmentNode) {
172
173         final SchemaNodeIdentifier augmentTargetNode = augmentNode.getStatementArgument();
174         Preconditions.checkArgument(augmentTargetNode != null,
175                 "Augment argument null, something bad happened in some of previous parsing phases");
176
177         List<StatementContextBase<?, ?, ?>> rootStatementCtxList = new ArrayList<>();
178         if (augmentTargetNode.isAbsolute()) {
179
180             QNameModule module = augmentTargetNode.getPathFromRoot().iterator().next().getModule();
181
182             StatementContextBase<?, ?, ?> rootStatementCtx =
183                     (StatementContextBase<?, ?, ?>) augmentNode.getFromNamespace(NamespaceToModule.class, module);
184             rootStatementCtxList.add(rootStatementCtx);
185
186             final Map<?, ?> subModules = rootStatementCtx.getAllFromNamespace(IncludedModuleContext.class);
187             if (subModules != null) {
188                 rootStatementCtxList.addAll((Collection<? extends StatementContextBase<?, ?, ?>>) subModules.values());
189             }
190
191         } else {
192             StatementContextBase<?, ?, ?> parent = (StatementContextBase<?, ?, ?>) augmentNode.getParentContext();
193             if (StmtContextUtils.producesDeclared(parent, UsesStatement.class)) {
194                 rootStatementCtxList.add(parent.getParentContext());
195             } else {
196                 // error
197             }
198         }
199
200         StatementContextBase<?, ?, ?> augmentTargetCtx = null;
201         for (final StatementContextBase<?, ?, ?> rootStatementCtx : rootStatementCtxList) {
202             augmentTargetCtx = findCtxOfNodeInRoot(rootStatementCtx, augmentTargetNode);
203             if (augmentTargetCtx != null) {
204                 break;
205             }
206         }
207
208         return augmentTargetCtx;
209     }
210
211     @Nullable
212     public static StatementContextBase<?, ?, ?> findCtxOfNodeInSubstatements(final StatementContextBase<?, ?, ?> rootStmtCtx,
213             final Iterable<QName> path) {
214
215         StatementContextBase<?, ?, ?> parent = rootStmtCtx;
216
217         Iterator<QName> pathIter = path.iterator();
218         while (pathIter.hasNext()) {
219             QName nextPathQName = pathIter.next();
220             StatementContextBase<?, ?, ?> foundSubstatement = getSubstatementByQName(parent, nextPathQName);
221
222             if (foundSubstatement == null) {
223                 return null;
224             }
225             if (!pathIter.hasNext()) {
226                 return foundSubstatement;
227             }
228
229             parent = foundSubstatement;
230         }
231
232         return null;
233     }
234
235     public static StatementContextBase<?, ?, ?> getSubstatementByQName(final StatementContextBase<?, ?, ?> parent,
236             final QName nextPathQName) {
237
238         Collection<StatementContextBase<?, ?, ?>> declaredSubstatement = parent.declaredSubstatements();
239         Collection<StatementContextBase<?, ?, ?>> effectiveSubstatement = parent.effectiveSubstatements();
240
241         for (StatementContextBase<?, ?, ?> substatement : Iterables.concat(declaredSubstatement, effectiveSubstatement)) {
242             Object substatementArgument = substatement.getStatementArgument();
243             QName substatementQName;
244             if (substatementArgument instanceof QName) {
245                 substatementQName = (QName) substatementArgument;
246
247                 if (nextPathQName.getLocalName().equals(
248                         substatementQName.getLocalName())) {
249                     if (isSupportedAugmentTarget(substatement)) {
250                         return substatement;
251                     } else if (Utils.isUnknownNode(substatement)) {
252                         LOG.warn("Module '{}': augment into unknown node '{}'.",
253                                 substatement.getRoot().getStatementArgument(), substatementArgument);
254                         return substatement;
255                     }
256                 }
257             }
258         }
259
260         return null;
261     }
262
263     public static boolean isSupportedAugmentTarget(final StatementContextBase<?, ?, ?> substatementCtx) {
264
265         /*
266          * :TODO Substatement must be allowed augment target type e.g. Container, etc... and must be not for example
267          * grouping, identity etc. It is problem in case when more than one substatements have the same QName, for
268          * example Grouping and Container are siblings and they have the same QName. We must find the Container and the
269          * Grouping must be ignored as disallowed augment target.
270          */
271
272         Collection<?> allowedAugmentTargets = substatementCtx.getFromNamespace(ValidationBundlesNamespace.class,
273                 ValidationBundleType.SUPPORTED_AUGMENT_TARGETS);
274
275         // if no allowed target is returned we consider all targets allowed
276         return allowedAugmentTargets == null || allowedAugmentTargets.isEmpty()
277                 || allowedAugmentTargets.contains(substatementCtx.getPublicDefinition());
278     }
279
280     @Nullable
281     public static StatementContextBase<?, ?, ?> findCtxOfNodeInRoot(final StatementContextBase<?, ?, ?> rootStmtCtx,
282             final SchemaNodeIdentifier node) {
283         return findCtxOfNodeInSubstatements(rootStmtCtx, node.getPathFromRoot());
284     }
285 }