2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.parser.stmt.rfc6020;
10 import com.google.common.base.Verify;
11 import com.google.common.collect.ImmutableSet;
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.Objects;
16 import java.util.regex.Pattern;
17 import javax.annotation.Nonnull;
18 import org.opendaylight.yangtools.yang.common.QName;
19 import org.opendaylight.yangtools.yang.common.YangVersion;
20 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
21 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
22 import org.opendaylight.yangtools.yang.model.api.stmt.ActionStatement;
23 import org.opendaylight.yangtools.yang.model.api.stmt.AugmentStatement;
24 import org.opendaylight.yangtools.yang.model.api.stmt.DataDefinitionStatement;
25 import org.opendaylight.yangtools.yang.model.api.stmt.NotificationStatement;
26 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
27 import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement;
28 import org.opendaylight.yangtools.yang.model.api.stmt.WhenStatement;
29 import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
30 import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
31 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
32 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
33 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
34 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prerequisite;
35 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
36 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
37 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
38 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
39 import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
40 import org.opendaylight.yangtools.yang.parser.spi.source.AugmentToChoiceNamespace;
41 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
42 import org.opendaylight.yangtools.yang.parser.spi.source.StmtOrderingNamespace;
43 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace;
44 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType;
45 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
46 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.AugmentEffectiveStatementImpl;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
50 public class AugmentStatementImpl extends AbstractDeclaredStatement<SchemaNodeIdentifier> implements AugmentStatement {
51 private static final Logger LOG = LoggerFactory.getLogger(AugmentStatementImpl.class);
52 private static final Pattern PATH_REL_PATTERN1 = Pattern.compile("\\.\\.?\\s*/(.+)");
53 private static final Pattern PATH_REL_PATTERN2 = Pattern.compile("//.*");
54 private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator
55 .builder(YangStmtMapping.AUGMENT)
56 .addAny(YangStmtMapping.ANYXML)
57 .addAny(YangStmtMapping.CASE)
58 .addAny(YangStmtMapping.CHOICE)
59 .addAny(YangStmtMapping.CONTAINER)
60 .addOptional(YangStmtMapping.DESCRIPTION)
61 .addAny(YangStmtMapping.IF_FEATURE)
62 .addAny(YangStmtMapping.LEAF)
63 .addAny(YangStmtMapping.LEAF_LIST)
64 .addAny(YangStmtMapping.LIST)
65 .addOptional(YangStmtMapping.REFERENCE)
66 .addOptional(YangStmtMapping.STATUS)
67 .addAny(YangStmtMapping.USES)
68 .addOptional(YangStmtMapping.WHEN)
71 protected AugmentStatementImpl(final StmtContext<SchemaNodeIdentifier, AugmentStatement, ?> context) {
75 public static class Definition extends AbstractStatementSupport<SchemaNodeIdentifier, AugmentStatement,
76 EffectiveStatement<SchemaNodeIdentifier, AugmentStatement>> {
79 super(YangStmtMapping.AUGMENT);
83 public SchemaNodeIdentifier parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
84 SourceException.throwIf(PATH_REL_PATTERN1.matcher(value).matches()
85 || PATH_REL_PATTERN2.matcher(value).matches(), ctx.getStatementSourceReference(),
86 "Augment argument \'%s\' is not valid, it can be only absolute path; or descendant if used in uses",
89 return Utils.nodeIdentifierFromPath(ctx, value);
93 public AugmentStatement createDeclared(
94 final StmtContext<SchemaNodeIdentifier, AugmentStatement, ?> ctx) {
95 return new AugmentStatementImpl(ctx);
99 public EffectiveStatement<SchemaNodeIdentifier, AugmentStatement> createEffective(
100 final StmtContext<SchemaNodeIdentifier, AugmentStatement,
101 EffectiveStatement<SchemaNodeIdentifier, AugmentStatement>> ctx) {
102 return new AugmentEffectiveStatementImpl(ctx);
106 public void onFullDefinitionDeclared(final Mutable<SchemaNodeIdentifier, AugmentStatement,
107 EffectiveStatement<SchemaNodeIdentifier, AugmentStatement>> augmentNode) {
108 if (!augmentNode.isSupportedByFeatures()) {
112 super.onFullDefinitionDeclared(augmentNode);
114 if (StmtContextUtils.isInExtensionBody(augmentNode)) {
118 final ModelActionBuilder augmentAction = augmentNode.newInferenceAction(
119 ModelProcessingPhase.EFFECTIVE_MODEL);
120 final Prerequisite<StmtContext<SchemaNodeIdentifier, AugmentStatement,
121 EffectiveStatement<SchemaNodeIdentifier, AugmentStatement>>> sourceCtxPrereq =
122 augmentAction.requiresCtx(augmentNode, ModelProcessingPhase.EFFECTIVE_MODEL);
123 final Prerequisite<Mutable<?, ?, EffectiveStatement<?, ?>>> target =
124 augmentAction.mutatesEffectiveCtx(getSearchRoot(augmentNode),
125 SchemaNodeIdentifierBuildNamespace.class, augmentNode.getStatementArgument());
127 augmentAction.apply(new ModelActionBuilder.InferenceAction() {
129 public void apply(final ModelActionBuilder.InferenceContext ctx) {
130 final StatementContextBase<?, ?, ?> augmentTargetCtx =
131 (StatementContextBase<?, ?, ?>) target.resolve(ctx);
132 if (!isSupportedAugmentTarget(augmentTargetCtx)
133 || StmtContextUtils.isInExtensionBody(augmentTargetCtx)) {
134 augmentNode.setIsSupportedToBuildEffective(false);
138 * Marks case short hand in augment
140 if (augmentTargetCtx.getPublicDefinition() == YangStmtMapping.CHOICE) {
141 augmentNode.addToNs(AugmentToChoiceNamespace.class, augmentNode, Boolean.TRUE);
144 // FIXME: this is a workaround for models which augment a node which is added via an extension
145 // which we do not handle. This needs to be reworked in terms of unknown schema nodes.
146 final StatementContextBase<?, ?, ?> augmentSourceCtx = (StatementContextBase<?, ?, ?>) augmentNode;
148 copyFromSourceToTarget(augmentSourceCtx, augmentTargetCtx);
149 augmentTargetCtx.addEffectiveSubstatement(augmentSourceCtx);
150 updateAugmentOrder(augmentSourceCtx);
151 } catch (final SourceException e) {
152 LOG.warn("Failed to add augmentation {} defined at {}",
153 augmentTargetCtx.getStatementSourceReference(),
154 augmentSourceCtx.getStatementSourceReference(), e);
158 private void updateAugmentOrder(final StatementContextBase<?, ?, ?> augmentSourceCtx) {
159 Integer currentOrder = augmentSourceCtx.getFromNamespace(StmtOrderingNamespace.class,
160 YangStmtMapping.AUGMENT);
161 if (currentOrder == null) {
167 augmentSourceCtx.addToNs(StmtOrderingNamespace.class, YangStmtMapping.AUGMENT, currentOrder);
171 public void prerequisiteFailed(final Collection<? extends ModelActionBuilder.Prerequisite<?>> failed) {
173 * Do not fail, if it is an uses-augment to an unknown node.
175 if (YangStmtMapping.USES == augmentNode.getParentContext().getPublicDefinition()) {
176 final StatementContextBase<?, ?, ?> targetNode = Utils.findNode(getSearchRoot(augmentNode),
177 augmentNode.getStatementArgument());
178 if (targetNode != null && StmtContextUtils.isUnknownStatement(targetNode)) {
179 augmentNode.setIsSupportedToBuildEffective(false);
181 "Uses-augment to unknown node {}. Augmentation has not been performed. At line: {}",
182 augmentNode.getStatementArgument(), augmentNode.getStatementSourceReference());
187 throw new InferenceException(augmentNode.getStatementSourceReference(),
188 "Augment target '%s' not found", augmentNode.getStatementArgument());
193 private static Mutable<?, ?, ?> getSearchRoot(final Mutable<?, ?, ?> augmentContext) {
194 final Mutable<?, ?, ?> parent = augmentContext.getParentContext();
195 // Augment is in uses - we need to augment instantiated nodes in parent.
196 if (YangStmtMapping.USES == parent.getPublicDefinition()) {
197 return parent.getParentContext();
202 public static void copyFromSourceToTarget(final StatementContextBase<?, ?, ?> sourceCtx,
203 final StatementContextBase<?, ?, ?> targetCtx) {
204 final CopyType typeOfCopy = UsesStatement.class.equals(sourceCtx.getParentContext().getPublicDefinition()
205 .getDeclaredRepresentationClass()) ? CopyType.ADDED_BY_USES_AUGMENTATION
206 : CopyType.ADDED_BY_AUGMENTATION;
208 * Since Yang 1.1, if an augmentation is made conditional with a
209 * "when" statement, it is allowed to add mandatory nodes.
211 final boolean skipCheckOfMandatoryNodes = YangVersion.VERSION_1_1.equals(sourceCtx.getRootVersion())
212 && isConditionalAugmentStmt(sourceCtx);
214 final Collection<? extends Mutable<?, ?, ?>> declared = sourceCtx.mutableDeclaredSubstatements();
215 final Collection<? extends Mutable<?, ?, ?>> effective = sourceCtx.mutableEffectiveSubstatements();
216 final Collection<Mutable<?, ?, ?>> buffer = new ArrayList<>(declared.size() + effective.size());
218 for (final Mutable<?, ?, ?> originalStmtCtx : declared) {
219 if (originalStmtCtx.isSupportedByFeatures()) {
220 copyStatement(originalStmtCtx, targetCtx, typeOfCopy, buffer, skipCheckOfMandatoryNodes);
223 for (final Mutable<?, ?, ?> originalStmtCtx : effective) {
224 copyStatement(originalStmtCtx, targetCtx, typeOfCopy, buffer, skipCheckOfMandatoryNodes);
227 targetCtx.addEffectiveSubstatements(buffer);
231 * Checks whether supplied statement context is conditional augment
235 * statement context to be checked
237 * @return true if supplied statement context is conditional augment
238 * statement, otherwise false
240 private static boolean isConditionalAugmentStmt(final StmtContext<?, ?, ?> ctx) {
241 return ctx.getPublicDefinition() == YangStmtMapping.AUGMENT
242 && StmtContextUtils.findFirstSubstatement(ctx, WhenStatement.class) != null;
245 private static void copyStatement(final Mutable<?, ?, ?> original, final StatementContextBase<?, ?, ?> target,
246 final CopyType typeOfCopy, final Collection<Mutable<?, ?, ?>> buffer,
247 final boolean skipCheckOfMandatoryNodes) {
248 if (needToCopyByAugment(original)) {
249 validateNodeCanBeCopiedByAugment(original, target, typeOfCopy, skipCheckOfMandatoryNodes);
251 buffer.add(target.childCopyOf(original, typeOfCopy));
252 } else if (isReusedByAugment(original)) {
253 buffer.add(original);
257 private static void validateNodeCanBeCopiedByAugment(final StmtContext<?, ?, ?> sourceCtx,
258 final StatementContextBase<?, ?, ?> targetCtx, final CopyType typeOfCopy,
259 final boolean skipCheckOfMandatoryNodes) {
261 if (WhenStatement.class.equals(sourceCtx.getPublicDefinition().getDeclaredRepresentationClass())) {
265 if (!skipCheckOfMandatoryNodes && typeOfCopy == CopyType.ADDED_BY_AUGMENTATION
266 && reguiredCheckOfMandatoryNodes(sourceCtx, targetCtx)) {
267 checkForMandatoryNodes(sourceCtx);
270 for (final StmtContext<?, ?, ?> subStatement : targetCtx.allSubstatements()) {
271 final boolean sourceIsDataNode = DataDefinitionStatement.class.isAssignableFrom(sourceCtx
272 .getPublicDefinition().getDeclaredRepresentationClass());
273 final boolean targetIsDataNode = DataDefinitionStatement.class.isAssignableFrom(subStatement
274 .getPublicDefinition().getDeclaredRepresentationClass());
275 final boolean qNamesEqual = sourceIsDataNode && targetIsDataNode
276 && Objects.equals(sourceCtx.getStatementArgument(), subStatement.getStatementArgument());
278 InferenceException.throwIf(qNamesEqual, sourceCtx.getStatementSourceReference(),
279 "An augment cannot add node named '%s' because this name is already used in target",
280 sourceCtx.rawStatementArgument());
284 private static void checkForMandatoryNodes(final StmtContext<?, ?, ?> sourceCtx) {
285 if (StmtContextUtils.isNonPresenceContainer(sourceCtx)) {
287 * We need to iterate over both declared and effective sub-statements,
288 * because a mandatory node can be:
289 * a) declared in augment body
290 * b) added to augment body also via uses of a grouping and
291 * such sub-statements are stored in effective sub-statements collection.
293 sourceCtx.allSubstatementsStream().forEach(Definition::checkForMandatoryNodes);
296 InferenceException.throwIf(StmtContextUtils.isMandatoryNode(sourceCtx),
297 sourceCtx.getStatementSourceReference(),
298 "An augment cannot add node '%s' because it is mandatory and in module different than target",
299 sourceCtx.rawStatementArgument());
302 private static boolean reguiredCheckOfMandatoryNodes(final StmtContext<?, ?, ?> sourceCtx,
303 Mutable<?, ?, ?> targetCtx) {
305 * If the statement argument is not QName, it cannot be mandatory
306 * statement, therefore return false and skip mandatory nodes validation
308 if (!(sourceCtx.getStatementArgument() instanceof QName)) {
311 final QName sourceStmtQName = (QName) sourceCtx.getStatementArgument();
313 // RootStatementContext, for example
314 final Mutable<?, ?, ?> root = targetCtx.getRoot();
316 Verify.verify(targetCtx.getStatementArgument() instanceof QName,
317 "Argument of augment target statement must be QName.");
318 final QName targetStmtQName = (QName) targetCtx.getStatementArgument();
320 * If target is from another module, return true and perform
321 * mandatory nodes validation
323 if (!Utils.belongsToTheSameModule(targetStmtQName, sourceStmtQName)) {
328 * If target or one of the target's ancestors from the same namespace
329 * is a presence container
330 * or is non-mandatory choice
331 * or is non-mandatory list
332 * return false and skip mandatory nodes validation, because these nodes
333 * are not mandatory node containers according to RFC 6020 section 3.1.
335 if (StmtContextUtils.isPresenceContainer(targetCtx)
336 || StmtContextUtils.isNotMandatoryNodeOfType(targetCtx, YangStmtMapping.CHOICE)
337 || StmtContextUtils.isNotMandatoryNodeOfType(targetCtx, YangStmtMapping.LIST)) {
340 } while ((targetCtx = targetCtx.getParentContext()) != root);
343 * All target node's parents belong to the same module as source node,
344 * therefore return false and skip mandatory nodes validation.
349 private static final Set<YangStmtMapping> NOCOPY_DEF_SET = ImmutableSet.of(YangStmtMapping.USES,
350 YangStmtMapping.WHEN, YangStmtMapping.DESCRIPTION, YangStmtMapping.REFERENCE, YangStmtMapping.STATUS);
352 public static boolean needToCopyByAugment(final StmtContext<?, ?, ?> stmtContext) {
353 return !NOCOPY_DEF_SET.contains(stmtContext.getPublicDefinition());
356 private static final Set<YangStmtMapping> REUSED_DEF_SET = ImmutableSet.of(YangStmtMapping.TYPEDEF);
358 public static boolean isReusedByAugment(final StmtContext<?, ?, ?> stmtContext) {
359 return REUSED_DEF_SET.contains(stmtContext.getPublicDefinition());
362 static boolean isSupportedAugmentTarget(final StmtContext<?, ?, ?> substatementCtx) {
364 * :TODO Substatement must be allowed augment target type e.g.
365 * Container, etc... and must not be for example grouping, identity etc.
366 * It is problem in case when more than one substatements have the same
367 * QName, for example Grouping and Container are siblings and they have
368 * the same QName. We must find the Container and the Grouping must be
369 * ignored as disallowed augment target.
371 final Collection<?> allowedAugmentTargets = substatementCtx.getFromNamespace(
372 ValidationBundlesNamespace.class, ValidationBundleType.SUPPORTED_AUGMENT_TARGETS);
374 // if no allowed target is returned we consider all targets allowed
375 return allowedAugmentTargets == null || allowedAugmentTargets.isEmpty()
376 || allowedAugmentTargets.contains(substatementCtx.getPublicDefinition());
380 protected SubstatementValidator getSubstatementValidator() {
381 return SUBSTATEMENT_VALIDATOR;
387 public SchemaNodeIdentifier getTargetNode() {
393 public Collection<? extends DataDefinitionStatement> getDataDefinitions() {
394 return allDeclared(DataDefinitionStatement.class);
399 public Collection<? extends ActionStatement> getActions() {
400 return allDeclared(ActionStatement.class);
404 public final Collection<? extends NotificationStatement> getNotifications() {
405 return allDeclared(NotificationStatement.class);