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%2FUsesStatementImpl.java;h=860fd2d1033537dc4b90dca5c111ef404de9f7be;hb=refs%2Fchanges%2F06%2F58706%2F2;hp=102ad007ff87b58aed7776614bef055331327802;hpb=3d283ec6184505ad5e7eefb173044ff383222e9f;p=yangtools.git diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/UsesStatementImpl.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/UsesStatementImpl.java index 102ad007ff..860fd2d103 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/UsesStatementImpl.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/UsesStatementImpl.java @@ -7,41 +7,64 @@ */ package org.opendaylight.yangtools.yang.parser.stmt.rfc6020; -import static org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase.FULL_DECLARATION; - +import com.google.common.collect.ImmutableSet; +import java.util.ArrayList; import java.util.Collection; - +import java.util.Set; +import javax.annotation.Nonnull; import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping; +import org.opendaylight.yangtools.yang.common.QNameModule; +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.meta.StatementDefinition; import org.opendaylight.yangtools.yang.model.api.stmt.AugmentStatement; import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionStatement; import org.opendaylight.yangtools.yang.model.api.stmt.IfFeatureStatement; import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceStatement; import org.opendaylight.yangtools.yang.model.api.stmt.RefineStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier; import org.opendaylight.yangtools.yang.model.api.stmt.StatusStatement; 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.GroupingNamespace; +import org.opendaylight.yangtools.yang.parser.spi.SubstatementValidator; 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.InferenceAction; +import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceContext; import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prerequisite; +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.source.ModuleCtxToModuleQName; import org.opendaylight.yangtools.yang.parser.spi.source.SourceException; +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.UsesEffectiveStatementImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - public class UsesStatementImpl extends AbstractDeclaredStatement implements UsesStatement { + private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping + .USES) + .addAny(YangStmtMapping.AUGMENT) + .addOptional(YangStmtMapping.DESCRIPTION) + .addAny(YangStmtMapping.IF_FEATURE) + .addAny(YangStmtMapping.REFINE) + .addOptional(YangStmtMapping.REFERENCE) + .addOptional(YangStmtMapping.STATUS) + .addOptional(YangStmtMapping.WHEN) + .build(); private static final Logger LOG = LoggerFactory.getLogger(UsesStatementImpl.class); - protected UsesStatementImpl(StmtContext context) { + protected UsesStatementImpl(final StmtContext context) { super(context); } @@ -49,66 +72,79 @@ public class UsesStatementImpl extends AbstractDeclaredStatement implemen AbstractStatementSupport> { public Definition() { - super(Rfc6020Mapping.USES); + super(YangStmtMapping.USES); } @Override - public QName parseArgumentValue(StmtContext ctx, String value) { - return Utils.qNameFromArgument(ctx, value); + public QName parseArgumentValue(final StmtContext ctx, final String value) { + return StmtContextUtils.qnameFromArgument(ctx, value); } @Override public void onFullDefinitionDeclared( - final StmtContext.Mutable> usesNode) - throws SourceException { + final Mutable> usesNode) { + if (!usesNode.isSupportedByFeatures()) { + return; + } + super.onFullDefinitionDeclared(usesNode); - ModelActionBuilder usesAction = usesNode.newInferenceAction(FULL_DECLARATION); + if (StmtContextUtils.isInExtensionBody(usesNode)) { + return; + } + + final ModelActionBuilder usesAction = usesNode.newInferenceAction(ModelProcessingPhase.EFFECTIVE_MODEL); final QName groupingName = usesNode.getStatementArgument(); final Prerequisite> sourceGroupingPre = usesAction.requiresCtx(usesNode, - GroupingNamespace.class, groupingName, FULL_DECLARATION); - final Prerequisite> targetNodePre = usesAction.mutatesCtx( - usesNode.getParentContext(), FULL_DECLARATION); + GroupingNamespace.class, groupingName, ModelProcessingPhase.EFFECTIVE_MODEL); + final Prerequisite> targetNodePre = usesAction.mutatesEffectiveCtx( + usesNode.getParentContext()); usesAction.apply(new InferenceAction() { @Override - public void apply() throws InferenceException { - StatementContextBase targetNodeStmtCtx = (StatementContextBase) targetNodePre.get(); - StatementContextBase sourceGrpStmtCtx = (StatementContextBase) sourceGroupingPre.get(); + public void apply(final InferenceContext ctx) { + final StatementContextBase targetNodeStmtCtx = (StatementContextBase) targetNodePre.resolve(ctx); + final StatementContextBase sourceGrpStmtCtx = (StatementContextBase) sourceGroupingPre.resolve(ctx); try { - GroupingUtils.copyFromSourceToTarget(sourceGrpStmtCtx, targetNodeStmtCtx); - GroupingUtils.resolveUsesNode(usesNode, targetNodeStmtCtx); - } catch (SourceException e) { + copyFromSourceToTarget(sourceGrpStmtCtx, targetNodeStmtCtx, usesNode); + resolveUsesNode(usesNode, targetNodeStmtCtx); + StmtContextUtils.validateIfFeatureAndWhenOnListKeys(usesNode); + } catch (final SourceException e) { LOG.warn(e.getMessage(), e); + throw e; } } @Override - public void prerequisiteFailed(Collection> failed) throws InferenceException { - if (failed.contains(sourceGroupingPre)) { - throw new InferenceException("Grouping " + groupingName + " was not resolved.", usesNode - .getStatementSourceReference()); - } + public void prerequisiteFailed(final Collection> failed) { + InferenceException.throwIf(failed.contains(sourceGroupingPre), + usesNode.getStatementSourceReference(), "Grouping '%s' was not resolved.", groupingName); throw new InferenceException("Unknown error occurred.", usesNode.getStatementSourceReference()); } }); } @Override - public UsesStatement createDeclared(StmtContext ctx) { + public UsesStatement createDeclared(final StmtContext ctx) { return new UsesStatementImpl(ctx); } @Override public EffectiveStatement createEffective( - StmtContext> ctx) { + final StmtContext> ctx) { return new UsesEffectiveStatementImpl(ctx); } + @Override + protected SubstatementValidator getSubstatementValidator() { + return SUBSTATEMENT_VALIDATOR; + } + } + @Nonnull @Override public QName getName() { return argument(); @@ -119,6 +155,7 @@ public class UsesStatementImpl extends AbstractDeclaredStatement implemen return firstDeclared(WhenStatement.class); } + @Nonnull @Override public Collection getIfFeatures() { return allDeclared(IfFeatureStatement.class); @@ -139,13 +176,208 @@ public class UsesStatementImpl extends AbstractDeclaredStatement implemen return firstDeclared(ReferenceStatement.class); } + @Nonnull @Override public Collection getAugments() { return allDeclared(AugmentStatement.class); } + @Nonnull @Override public Collection getRefines() { return allDeclared(RefineStatement.class); } + + /** + * @param sourceGrpStmtCtx + * source grouping statement context + * @param targetCtx + * target context + * @param usesNode + * uses node + * @throws SourceException + * instance of SourceException + */ + private static void copyFromSourceToTarget(final Mutable sourceGrpStmtCtx, + final StatementContextBase targetCtx, + final StmtContext.Mutable> usesNode) { + final Collection> declared = sourceGrpStmtCtx.mutableDeclaredSubstatements(); + final Collection> effective = sourceGrpStmtCtx.mutableEffectiveSubstatements(); + final Collection> buffer = new ArrayList<>(declared.size() + effective.size()); + final QNameModule newQNameModule = getNewQNameModule(targetCtx, sourceGrpStmtCtx); + + for (final Mutable original : declared) { + if (original.isSupportedByFeatures()) { + copyStatement(original, targetCtx, newQNameModule, buffer); + } + } + + for (final Mutable original : effective) { + copyStatement(original, targetCtx, newQNameModule, buffer); + } + + targetCtx.addEffectiveSubstatements(buffer); + usesNode.addAsEffectOfStatement(buffer); + } + + private static void copyStatement(final Mutable original, + final StatementContextBase targetCtx, final QNameModule targetModule, + final Collection> buffer) { + if (needToCopyByUses(original)) { + final Mutable copy = original.createCopy(targetModule, targetCtx, CopyType.ADDED_BY_USES); + buffer.add(copy); + } else if (isReusedByUsesOnTop(original)) { + buffer.add(original); + } + } + + private static final Set TOP_REUSED_DEF_SET = ImmutableSet.of( + YangStmtMapping.TYPE, + YangStmtMapping.TYPEDEF); + + private static boolean isReusedByUsesOnTop(final StmtContext stmtContext) { + return TOP_REUSED_DEF_SET.contains(stmtContext.getPublicDefinition()); + } + + private static final Set NOCOPY_FROM_GROUPING_SET = ImmutableSet.of( + YangStmtMapping.DESCRIPTION, + YangStmtMapping.REFERENCE, + YangStmtMapping.STATUS); + private static final Set REUSED_DEF_SET = ImmutableSet.of( + YangStmtMapping.TYPE, + YangStmtMapping.TYPEDEF, + YangStmtMapping.USES); + + public static boolean needToCopyByUses(final StmtContext stmtContext) { + final StatementDefinition def = stmtContext.getPublicDefinition(); + if (REUSED_DEF_SET.contains(def)) { + LOG.debug("Will reuse {} statement {}", def, stmtContext); + return false; + } + if (NOCOPY_FROM_GROUPING_SET.contains(def)) { + return !YangStmtMapping.GROUPING.equals(stmtContext.getParentContext().getPublicDefinition()); + } + + LOG.debug("Will copy {} statement {}", def, stmtContext); + return true; + } + + public static void resolveUsesNode( + final Mutable> usesNode, + final StatementContextBase targetNodeStmtCtx) { + for (final Mutable subStmtCtx : usesNode.mutableDeclaredSubstatements()) { + if (StmtContextUtils.producesDeclared(subStmtCtx, RefineStatement.class) + && areFeaturesSupported(subStmtCtx)) { + performRefine(subStmtCtx, targetNodeStmtCtx); + } + } + } + + private static boolean areFeaturesSupported(final Mutable subStmtCtx) { + /* + * In case of Yang 1.1, checks whether features are supported. + */ + return !YangVersion.VERSION_1_1.equals(subStmtCtx.getRootVersion()) || subStmtCtx.isSupportedByFeatures(); + } + + private static void performRefine(final Mutable subStmtCtx, + final StatementContextBase usesParentCtx) { + + final Object refineArgument = subStmtCtx.getStatementArgument(); + InferenceException.throwIf(!(refineArgument instanceof SchemaNodeIdentifier), + subStmtCtx.getStatementSourceReference(), + "Invalid refine argument %s. It must be instance of SchemaNodeIdentifier.", refineArgument); + + final SchemaNodeIdentifier refineTargetNodeIdentifier = (SchemaNodeIdentifier) refineArgument; + final StatementContextBase refineTargetNodeCtx = Utils.findNode(usesParentCtx, + refineTargetNodeIdentifier); + + InferenceException.throwIfNull(refineTargetNodeCtx, subStmtCtx.getStatementSourceReference(), + "Refine target node %s not found.", refineTargetNodeIdentifier); + + if (StmtContextUtils.isUnknownStatement(refineTargetNodeCtx)) { + LOG.debug( + "Refine node '{}' in uses '{}' has target node unknown statement '{}'. Refine has been skipped. At line: {}", + subStmtCtx.getStatementArgument(), subStmtCtx.getParentContext().getStatementArgument(), + refineTargetNodeCtx.getStatementArgument(), subStmtCtx.getStatementSourceReference()); + subStmtCtx.addAsEffectOfStatement(refineTargetNodeCtx); + return; + } + + addOrReplaceNodes(subStmtCtx, refineTargetNodeCtx); + subStmtCtx.addAsEffectOfStatement(refineTargetNodeCtx); + } + + private static void addOrReplaceNodes(final Mutable subStmtCtx, + final StatementContextBase refineTargetNodeCtx) { + for (final Mutable refineSubstatementCtx : subStmtCtx.mutableDeclaredSubstatements()) { + if (isSupportedRefineSubstatement(refineSubstatementCtx)) { + addOrReplaceNode(refineSubstatementCtx, refineTargetNodeCtx); + } + } + } + + private static void addOrReplaceNode(final Mutable refineSubstatementCtx, + final StatementContextBase refineTargetNodeCtx) { + + final StatementDefinition refineSubstatementDef = refineSubstatementCtx.getPublicDefinition(); + + SourceException.throwIf(!isSupportedRefineTarget(refineSubstatementCtx, refineTargetNodeCtx), + refineSubstatementCtx.getStatementSourceReference(), + "Error in module '%s' in the refine of uses '%s': can not perform refine of '%s' for the target '%s'.", + refineSubstatementCtx.getRoot().getStatementArgument(), refineSubstatementCtx.getParentContext() + .getStatementArgument(), refineSubstatementCtx.getPublicDefinition(), refineTargetNodeCtx + .getPublicDefinition()); + + if (isAllowedToAddByRefine(refineSubstatementDef)) { + refineTargetNodeCtx.addEffectiveSubstatement(refineSubstatementCtx); + } else { + refineTargetNodeCtx.removeStatementFromEffectiveSubstatements(refineSubstatementDef); + refineTargetNodeCtx.addEffectiveSubstatement(refineSubstatementCtx); + } + } + + private static final Set ALLOWED_TO_ADD_BY_REFINE_DEF_SET = ImmutableSet.of(YangStmtMapping.MUST); + + private static boolean isAllowedToAddByRefine(final StatementDefinition publicDefinition) { + return ALLOWED_TO_ADD_BY_REFINE_DEF_SET.contains(publicDefinition); + } + + private static boolean isSupportedRefineSubstatement(final StmtContext refineSubstatementCtx) { + final Collection supportedRefineSubstatements = refineSubstatementCtx.getFromNamespace( + ValidationBundlesNamespace.class, ValidationBundleType.SUPPORTED_REFINE_SUBSTATEMENTS); + + return supportedRefineSubstatements == null || supportedRefineSubstatements.isEmpty() + || supportedRefineSubstatements.contains(refineSubstatementCtx.getPublicDefinition()) + || StmtContextUtils.isUnknownStatement(refineSubstatementCtx); + } + + private static boolean isSupportedRefineTarget(final StmtContext refineSubstatementCtx, + final StmtContext refineTargetNodeCtx) { + final Collection supportedRefineTargets = YangValidationBundles.SUPPORTED_REFINE_TARGETS + .get(refineSubstatementCtx.getPublicDefinition()); + + return supportedRefineTargets == null || supportedRefineTargets.isEmpty() + || supportedRefineTargets.contains(refineTargetNodeCtx.getPublicDefinition()); + } + + + private static QNameModule getNewQNameModule(final StmtContext targetCtx, + final StmtContext stmtContext) { + if (targetCtx.isRootContext()) { + return targetCtx.getFromNamespace(ModuleCtxToModuleQName.class, targetCtx); + } + if (targetCtx.getPublicDefinition() == YangStmtMapping.AUGMENT) { + return StmtContextUtils.getRootModuleQName(targetCtx); + } + + final Object targetStmtArgument = targetCtx.getStatementArgument(); + final Object sourceStmtArgument = stmtContext.getStatementArgument(); + if (targetStmtArgument instanceof QName && sourceStmtArgument instanceof QName) { + return ((QName) targetStmtArgument).getModule(); + } + + return null; + } + }