X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=yang%2Fyang-parser-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Fyang%2Fparser%2Fstmt%2Frfc6020%2FAugmentStatementImpl.java;h=bf01b2cb9e7d57d55d21e57df482c446a1bcc7f7;hb=refs%2Fchanges%2F70%2F61570%2F1;hp=86b7533357b8ef01a04167fc23f2ff967f75f52c;hpb=f45c04d93c92101cb5938b72b8f6d6312e680abb;p=yangtools.git diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/AugmentStatementImpl.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/AugmentStatementImpl.java index 86b7533357..bf01b2cb9e 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/AugmentStatementImpl.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/AugmentStatementImpl.java @@ -7,19 +7,30 @@ */ package org.opendaylight.yangtools.yang.parser.stmt.rfc6020; -import static org.opendaylight.yangtools.yang.parser.spi.SubstatementValidator.MAX; -import com.google.common.base.Preconditions; +import com.google.common.base.Verify; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.Set; import java.util.regex.Pattern; import javax.annotation.Nonnull; -import org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.YangVersion; +import org.opendaylight.yangtools.yang.model.api.YangStmtMapping; import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.ActionStatement; import org.opendaylight.yangtools.yang.model.api.stmt.AugmentStatement; import org.opendaylight.yangtools.yang.model.api.stmt.DataDefinitionStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.NotificationStatement; import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier; -import org.opendaylight.yangtools.yang.parser.spi.SubstatementValidator; +import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.WhenStatement; import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement; import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport; +import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType; import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException; import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder; import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prerequisite; @@ -27,8 +38,12 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils; +import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator; +import org.opendaylight.yangtools.yang.parser.spi.source.AugmentToChoiceNamespace; import org.opendaylight.yangtools.yang.parser.spi.source.SourceException; import org.opendaylight.yangtools.yang.parser.spi.source.StmtOrderingNamespace; +import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace; +import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType; import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase; import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.AugmentEffectiveStatementImpl; import org.slf4j.Logger; @@ -39,20 +54,20 @@ public class AugmentStatementImpl extends AbstractDeclaredStatement context) { @@ -63,14 +78,15 @@ public class AugmentStatementImpl extends AbstractDeclaredStatement> { public Definition() { - super(Rfc6020Mapping.AUGMENT); + super(YangStmtMapping.AUGMENT); } @Override public SchemaNodeIdentifier parseArgumentValue(final StmtContext ctx, final String value) { - Preconditions.checkArgument(!PATH_REL_PATTERN1.matcher(value).matches() - && !PATH_REL_PATTERN2.matcher(value).matches(), - "An argument for augment can be only absolute path; or descendant if used in uses"); + SourceException.throwIf(PATH_REL_PATTERN1.matcher(value).matches() + || PATH_REL_PATTERN2.matcher(value).matches(), ctx.getStatementSourceReference(), + "Augment argument \'%s\' is not valid, it can be only absolute path; or descendant if used in uses", + value); return Utils.nodeIdentifierFromPath(ctx, value); } @@ -89,9 +105,12 @@ public class AugmentStatementImpl extends AbstractDeclaredStatement> augmentNode) - throws SourceException { - SUBSTATEMENT_VALIDATOR.validate(augmentNode); + final Mutable> augmentNode) { + if (!augmentNode.isSupportedByFeatures()) { + return; + } + + super.onFullDefinitionDeclared(augmentNode); if (StmtContextUtils.isInExtensionBody(augmentNode)) { return; @@ -106,55 +125,266 @@ public class AugmentStatementImpl extends AbstractDeclaredStatement augmentTargetCtx = (StatementContextBase) target.get(); - if (!AugmentUtils.isSupportedAugmentTarget(augmentTargetCtx) || StmtContextUtils.isInExtensionBody(augmentTargetCtx)) { + public void apply(final ModelActionBuilder.InferenceContext ctx) { + final StatementContextBase augmentTargetCtx = + (StatementContextBase) target.resolve(ctx); + if (!isSupportedAugmentTarget(augmentTargetCtx) + || StmtContextUtils.isInExtensionBody(augmentTargetCtx)) { augmentNode.setIsSupportedToBuildEffective(false); return; } + /** + * Marks case short hand in augment + */ + if (augmentTargetCtx.getPublicDefinition() == YangStmtMapping.CHOICE) { + augmentNode.addToNs(AugmentToChoiceNamespace.class, augmentNode, Boolean.TRUE); + } // FIXME: this is a workaround for models which augment a node which is added via an extension // which we do not handle. This needs to be reworked in terms of unknown schema nodes. final StatementContextBase augmentSourceCtx = (StatementContextBase) augmentNode; try { - AugmentUtils.copyFromSourceToTarget(augmentSourceCtx, augmentTargetCtx); + copyFromSourceToTarget(augmentSourceCtx, augmentTargetCtx); augmentTargetCtx.addEffectiveSubstatement(augmentSourceCtx); updateAugmentOrder(augmentSourceCtx); - } catch (SourceException e) { - LOG.debug("Failed to add augmentation %s defined at {}", - augmentSourceCtx.getStatementSourceReference(), e); + } catch (final SourceException e) { + LOG.warn("Failed to add augmentation {} defined at {}", + augmentTargetCtx.getStatementSourceReference(), + augmentSourceCtx.getStatementSourceReference(), e); } } private void updateAugmentOrder(final StatementContextBase augmentSourceCtx) { Integer currentOrder = augmentSourceCtx.getFromNamespace(StmtOrderingNamespace.class, - Rfc6020Mapping.AUGMENT); + YangStmtMapping.AUGMENT); if (currentOrder == null) { currentOrder = 1; } else { currentOrder++; } - augmentSourceCtx.setOrder(currentOrder); - augmentSourceCtx.addToNs(StmtOrderingNamespace.class, Rfc6020Mapping.AUGMENT, currentOrder); + augmentSourceCtx.addToNs(StmtOrderingNamespace.class, YangStmtMapping.AUGMENT, currentOrder); } @Override public void prerequisiteFailed(final Collection> failed) { + /* + * Do not fail, if it is an uses-augment to an unknown node. + */ + if (YangStmtMapping.USES == augmentNode.getParentContext().getPublicDefinition()) { + final StatementContextBase targetNode = Utils.findNode(getSearchRoot(augmentNode), + augmentNode.getStatementArgument()); + if (StmtContextUtils.isUnknownNode(targetNode)) { + augmentNode.setIsSupportedToBuildEffective(false); + LOG.warn( + "Uses-augment to unknown node {}. Augmentation has not been performed. At line: {}", + augmentNode.getStatementArgument(), augmentNode.getStatementSourceReference()); + return; + } + } + throw new InferenceException(augmentNode.getStatementSourceReference(), - "Augment target '%s' not found", augmentNode.getStatementArgument()); + "Augment target '%s' not found", augmentNode.getStatementArgument()); } }); } private static Mutable getSearchRoot(final Mutable augmentContext) { - Mutable parent = augmentContext.getParentContext(); + final Mutable parent = augmentContext.getParentContext(); // Augment is in uses - we need to augment instantiated nodes in parent. - if (Rfc6020Mapping.USES.equals(parent.getPublicDefinition())) { + if (YangStmtMapping.USES == parent.getPublicDefinition()) { return parent.getParentContext(); } return parent; } + + public static void copyFromSourceToTarget(final StatementContextBase sourceCtx, + final StatementContextBase targetCtx) { + final CopyType typeOfCopy = UsesStatement.class.equals(sourceCtx.getParentContext().getPublicDefinition() + .getDeclaredRepresentationClass()) ? CopyType.ADDED_BY_USES_AUGMENTATION + : CopyType.ADDED_BY_AUGMENTATION; + /* + * Since Yang 1.1, if an augmentation is made conditional with a + * "when" statement, it is allowed to add mandatory nodes. + */ + final boolean skipCheckOfMandatoryNodes = YangVersion.VERSION_1_1.equals(sourceCtx.getRootVersion()) + && isConditionalAugmentStmt(sourceCtx); + + final Collection> declared = sourceCtx.mutableDeclaredSubstatements(); + final Collection> effective = sourceCtx.mutableEffectiveSubstatements(); + final Collection> buffer = new ArrayList<>(declared.size() + effective.size()); + + for (final Mutable originalStmtCtx : declared) { + if (originalStmtCtx.isSupportedByFeatures()) { + copyStatement(originalStmtCtx, targetCtx, typeOfCopy, buffer, skipCheckOfMandatoryNodes); + } + } + for (final Mutable originalStmtCtx : effective) { + copyStatement(originalStmtCtx, targetCtx, typeOfCopy, buffer, skipCheckOfMandatoryNodes); + } + + targetCtx.addEffectiveSubstatements(buffer); + } + + /** + * Checks whether supplied statement context is conditional augment + * statement. + * + * @param ctx + * statement context to be checked + * + * @return true if supplied statement context is conditional augment + * statement, otherwise false + */ + private static boolean isConditionalAugmentStmt(final StmtContext ctx) { + return ctx.getPublicDefinition() == YangStmtMapping.AUGMENT + && StmtContextUtils.findFirstSubstatement(ctx, WhenStatement.class) != null; + } + + private static void copyStatement(final Mutable original, final StatementContextBase target, + final CopyType typeOfCopy, final Collection> buffer, + final boolean skipCheckOfMandatoryNodes) { + if (needToCopyByAugment(original)) { + validateNodeCanBeCopiedByAugment(original, target, typeOfCopy, skipCheckOfMandatoryNodes); + + final Mutable copy = original.createCopy(target, typeOfCopy); + buffer.add(copy); + } else if (isReusedByAugment(original)) { + buffer.add(original); + } + } + + private static void validateNodeCanBeCopiedByAugment(final StmtContext sourceCtx, + final StatementContextBase targetCtx, final CopyType typeOfCopy, + final boolean skipCheckOfMandatoryNodes) { + + if (WhenStatement.class.equals(sourceCtx.getPublicDefinition().getDeclaredRepresentationClass())) { + return; + } + + if (!skipCheckOfMandatoryNodes && typeOfCopy == CopyType.ADDED_BY_AUGMENTATION + && reguiredCheckOfMandatoryNodes(sourceCtx, targetCtx)) { + checkForMandatoryNodes(sourceCtx); + } + + final List> targetSubStatements = ImmutableList.>builder() + .addAll(targetCtx.mutableDeclaredSubstatements()).addAll(targetCtx.mutableEffectiveSubstatements()) + .build(); + + for (final Mutable subStatement : targetSubStatements) { + final boolean sourceIsDataNode = DataDefinitionStatement.class.isAssignableFrom(sourceCtx + .getPublicDefinition().getDeclaredRepresentationClass()); + final boolean targetIsDataNode = DataDefinitionStatement.class.isAssignableFrom(subStatement + .getPublicDefinition().getDeclaredRepresentationClass()); + final boolean qNamesEqual = sourceIsDataNode && targetIsDataNode + && Objects.equals(sourceCtx.getStatementArgument(), subStatement.getStatementArgument()); + + InferenceException.throwIf(qNamesEqual, sourceCtx.getStatementSourceReference(), + "An augment cannot add node named '%s' because this name is already used in target", + sourceCtx.rawStatementArgument()); + } + } + + private static void checkForMandatoryNodes(final StmtContext sourceCtx) { + if (StmtContextUtils.isNonPresenceContainer(sourceCtx)) { + /* + * We need to iterate over both declared and effective sub-statements, + * because a mandatory node can be: + * a) declared in augment body + * b) added to augment body also via uses of a grouping and + * such sub-statements are stored in effective sub-statements collection. + */ + sourceCtx.declaredSubstatements().forEach(Definition::checkForMandatoryNodes); + sourceCtx.effectiveSubstatements().forEach(Definition::checkForMandatoryNodes); + } + + InferenceException.throwIf(StmtContextUtils.isMandatoryNode(sourceCtx), + sourceCtx.getStatementSourceReference(), + "An augment cannot add node '%s' because it is mandatory and in module different than target", + sourceCtx.rawStatementArgument()); + } + + private static boolean reguiredCheckOfMandatoryNodes(final StmtContext sourceCtx, + Mutable targetCtx) { + /* + * If the statement argument is not QName, it cannot be mandatory + * statement, therefore return false and skip mandatory nodes validation + */ + if (!(sourceCtx.getStatementArgument() instanceof QName)) { + return false; + } + final QName sourceStmtQName = (QName) sourceCtx.getStatementArgument(); + + // RootStatementContext, for example + final Mutable root = targetCtx.getRoot(); + do { + Verify.verify(targetCtx.getStatementArgument() instanceof QName, + "Argument of augment target statement must be QName."); + final QName targetStmtQName = (QName) targetCtx.getStatementArgument(); + /* + * If target is from another module, return true and perform + * mandatory nodes validation + */ + if (!Utils.belongsToTheSameModule(targetStmtQName, sourceStmtQName)) { + return true; + } + + /* + * If target or one of the target's ancestors from the same namespace + * is a presence container + * or is non-mandatory choice + * or is non-mandatory list + * return false and skip mandatory nodes validation, because these nodes + * are not mandatory node containers according to RFC 6020 section 3.1. + */ + if (StmtContextUtils.isPresenceContainer(targetCtx) + || StmtContextUtils.isNotMandatoryNodeOfType(targetCtx, YangStmtMapping.CHOICE) + || StmtContextUtils.isNotMandatoryNodeOfType(targetCtx, YangStmtMapping.LIST)) { + return false; + } + } while ((targetCtx = targetCtx.getParentContext()) != root); + + /* + * All target node's parents belong to the same module as source node, + * therefore return false and skip mandatory nodes validation. + */ + return false; + } + + private static final Set NOCOPY_DEF_SET = ImmutableSet.of(YangStmtMapping.USES, YangStmtMapping.WHEN, + YangStmtMapping.DESCRIPTION, YangStmtMapping.REFERENCE, YangStmtMapping.STATUS); + + public static boolean needToCopyByAugment(final StmtContext stmtContext) { + return !NOCOPY_DEF_SET.contains(stmtContext.getPublicDefinition()); + } + + private static final Set REUSED_DEF_SET = ImmutableSet.of(YangStmtMapping.TYPEDEF); + + public static boolean isReusedByAugment(final StmtContext stmtContext) { + return REUSED_DEF_SET.contains(stmtContext.getPublicDefinition()); + } + + static boolean isSupportedAugmentTarget(final StmtContext substatementCtx) { + /* + * :TODO Substatement must be allowed augment target type e.g. + * Container, etc... and must not be for example grouping, identity etc. + * It is problem in case when more than one substatements have the same + * QName, for example Grouping and Container are siblings and they have + * the same QName. We must find the Container and the Grouping must be + * ignored as disallowed augment target. + */ + final Collection allowedAugmentTargets = substatementCtx.getFromNamespace( + ValidationBundlesNamespace.class, ValidationBundleType.SUPPORTED_AUGMENT_TARGETS); + + // if no allowed target is returned we consider all targets allowed + return allowedAugmentTargets == null || allowedAugmentTargets.isEmpty() + || allowedAugmentTargets.contains(substatementCtx.getPublicDefinition()); + } + + @Override + protected SubstatementValidator getSubstatementValidator() { + return SUBSTATEMENT_VALIDATOR; + } } @Nonnull @@ -163,8 +393,20 @@ public class AugmentStatementImpl extends AbstractDeclaredStatement getDataDefinitions() { return allDeclared(DataDefinitionStatement.class); } + + @Nonnull + @Override + public Collection getActions() { + return allDeclared(ActionStatement.class); + } + + @Override + public final Collection getNotifications() { + return allDeclared(NotificationStatement.class); + } }