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=39f062d8815e892db8b1e9174f8d479c71e92324;hb=f8cf77d783bd1ada1d83a1e6a58237e15c451391;hp=102ad007ff87b58aed7776614bef055331327802;hpb=5c3e87d88694d9f0bbb2ab872b4e68c7d6823dd3;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..39f062d881 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,30 +7,42 @@ */ package org.opendaylight.yangtools.yang.parser.stmt.rfc6020; -import static org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase.FULL_DECLARATION; - +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import java.util.ArrayList; import java.util.Collection; - +import java.util.Set; import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping; 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.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; @@ -38,10 +50,20 @@ import org.slf4j.LoggerFactory; public class UsesStatementImpl extends AbstractDeclaredStatement implements UsesStatement { + private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(Rfc6020Mapping + .USES) + .addAny(Rfc6020Mapping.AUGMENT) + .addOptional(Rfc6020Mapping.DESCRIPTION) + .addAny(Rfc6020Mapping.IF_FEATURE) + .addAny(Rfc6020Mapping.REFINE) + .addOptional(Rfc6020Mapping.REFERENCE) + .addOptional(Rfc6020Mapping.STATUS) + .addOptional(Rfc6020Mapping.WHEN) + .build(); private static final Logger LOG = LoggerFactory.getLogger(UsesStatementImpl.class); - protected UsesStatementImpl(StmtContext context) { + protected UsesStatementImpl(final StmtContext context) { super(context); } @@ -53,57 +75,64 @@ public class UsesStatementImpl extends AbstractDeclaredStatement implemen } @Override - public QName parseArgumentValue(StmtContext ctx, String value) { + public QName parseArgumentValue(final StmtContext ctx, final String value) { return Utils.qNameFromArgument(ctx, value); } @Override public void onFullDefinitionDeclared( - final StmtContext.Mutable> usesNode) - throws SourceException { + final StmtContext.Mutable> usesNode) { + if (!StmtContextUtils.areFeaturesSupported(usesNode)) { + return; + } + + SUBSTATEMENT_VALIDATOR.validate(usesNode); + + if (StmtContextUtils.isInExtensionBody(usesNode)) { + return; + } - ModelActionBuilder usesAction = usesNode.newInferenceAction(FULL_DECLARATION); + 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 { + public void apply() { StatementContextBase targetNodeStmtCtx = (StatementContextBase) targetNodePre.get(); StatementContextBase sourceGrpStmtCtx = (StatementContextBase) sourceGroupingPre.get(); try { - GroupingUtils.copyFromSourceToTarget(sourceGrpStmtCtx, targetNodeStmtCtx); - GroupingUtils.resolveUsesNode(usesNode, targetNodeStmtCtx); + copyFromSourceToTarget(sourceGrpStmtCtx, targetNodeStmtCtx, usesNode); + resolveUsesNode(usesNode, targetNodeStmtCtx); } catch (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); } @@ -148,4 +177,189 @@ public class UsesStatementImpl extends AbstractDeclaredStatement implemen 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 StatementContextBase sourceGrpStmtCtx, + final StatementContextBase targetCtx, + final StmtContext.Mutable> usesNode) { + final Collection> declared = sourceGrpStmtCtx.declaredSubstatements(); + final Collection> effective = sourceGrpStmtCtx.effectiveSubstatements(); + final Collection> buffer = new ArrayList<>(declared.size() + effective.size()); + final QNameModule newQNameModule = getNewQNameModule(targetCtx, sourceGrpStmtCtx); + + for (final StatementContextBase original : declared) { + if (StmtContextUtils.areFeaturesSupported(original)) { + copyStatement(original, targetCtx, newQNameModule, buffer); + } + } + + for (final StatementContextBase original : effective) { + copyStatement(original, targetCtx, newQNameModule, buffer); + } + + targetCtx.addEffectiveSubstatements(buffer); + usesNode.addAsEffectOfStatement(buffer); + } + + private static void copyStatement(final StatementContextBase original, + final StatementContextBase targetCtx, final QNameModule targetModule, + final Collection> buffer) { + if (needToCopyByUses(original)) { + final StatementContextBase 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( + Rfc6020Mapping.TYPE, + Rfc6020Mapping.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( + Rfc6020Mapping.DESCRIPTION, + Rfc6020Mapping.REFERENCE, + Rfc6020Mapping.STATUS); + private static final Set REUSED_DEF_SET = ImmutableSet.of( + Rfc6020Mapping.TYPE, + Rfc6020Mapping.TYPEDEF, + Rfc6020Mapping.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 !Rfc6020Mapping.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 StatementContextBase subStmtCtx : usesNode.declaredSubstatements()) { + if (StmtContextUtils.producesDeclared(subStmtCtx, RefineStatement.class)) { + performRefine(subStmtCtx, targetNodeStmtCtx); + } + } + } + + private static void performRefine(final StatementContextBase refineCtx, + final StatementContextBase usesParentCtx) { + + final Object refineArgument = refineCtx.getStatementArgument(); + Preconditions.checkArgument(refineArgument instanceof SchemaNodeIdentifier, + "Invalid refine argument %s. It must be instance of SchemaNodeIdentifier. At %s", refineArgument, + refineCtx.getStatementSourceReference()); + + final SchemaNodeIdentifier refineTargetNodeIdentifier = (SchemaNodeIdentifier) refineArgument; + final StatementContextBase refineTargetNodeCtx = Utils.findNode(usesParentCtx, + refineTargetNodeIdentifier); + Preconditions.checkArgument(refineTargetNodeCtx != null, "Refine target node %s not found. At %s", + refineTargetNodeIdentifier, refineCtx.getStatementSourceReference()); + if (StmtContextUtils.isUnknownStatement(refineTargetNodeCtx)) { + LOG.debug( + "Refine node '{}' in uses '{}' has target node unknown statement '{}'. Refine has been skipped. At line: {}", + refineCtx.getStatementArgument(), refineCtx.getParentContext().getStatementArgument(), + refineTargetNodeCtx.getStatementArgument(), refineCtx.getStatementSourceReference()); + refineCtx.addAsEffectOfStatement(refineTargetNodeCtx); + return; + } + + addOrReplaceNodes(refineCtx, refineTargetNodeCtx); + refineCtx.addAsEffectOfStatement(refineTargetNodeCtx); + } + + private static void addOrReplaceNodes(final StatementContextBase refineCtx, + final StatementContextBase refineTargetNodeCtx) { + for (final StatementContextBase refineSubstatementCtx : refineCtx.declaredSubstatements()) { + if (isSupportedRefineSubstatement(refineSubstatementCtx)) { + addOrReplaceNode(refineSubstatementCtx, refineTargetNodeCtx); + } + } + } + + private static void addOrReplaceNode(final StatementContextBase 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(Rfc6020Mapping.MUST); + + private static boolean isAllowedToAddByRefine(final StatementDefinition publicDefinition) { + return ALLOWED_TO_ADD_BY_REFINE_DEF_SET.contains(publicDefinition); + } + + private static boolean isSupportedRefineSubstatement(final StatementContextBase 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 StatementContextBase refineSubstatementCtx, + final StatementContextBase 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 StatementContextBase targetCtx, + final StmtContext stmtContext) { + if (targetCtx.isRootContext()) { + return targetCtx.getFromNamespace(ModuleCtxToModuleQName.class, targetCtx); + } + if (targetCtx.getPublicDefinition() == Rfc6020Mapping.AUGMENT) { + return targetCtx.getFromNamespace(ModuleCtxToModuleQName.class, targetCtx.getRoot()); + } + + final Object targetStmtArgument = targetCtx.getStatementArgument(); + final Object sourceStmtArgument = stmtContext.getStatementArgument(); + if (targetStmtArgument instanceof QName && sourceStmtArgument instanceof QName) { + return ((QName) targetStmtArgument).getModule(); + } + + return null; + } + }