/* * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.yangtools.yang.parser.rfc7950.stmt; import com.google.common.annotations.Beta; import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement; import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition; import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext; /** * Implementation-internal base class for {@link AbstractStatementSupport} implementations. * * @param Argument type * @param Declared Statement representation * @param Effective Statement representation */ @Beta public abstract class BaseStatementSupport, E extends EffectiveStatement> extends AbstractStatementSupport { protected BaseStatementSupport(final StatementDefinition publicDefinition) { super(publicDefinition); } protected BaseStatementSupport(final StatementDefinition publicDefinition, final CopyPolicy copyPolicy) { super(publicDefinition, copyPolicy); } @Override public final D createDeclared(final StmtContext ctx) { final ImmutableList> substatements = ctx.declaredSubstatements().stream() .map(StmtContext::buildDeclared) .collect(ImmutableList.toImmutableList()); return substatements.isEmpty() ? createEmptyDeclared(ctx) : createDeclared(ctx, substatements); } protected abstract @NonNull D createDeclared(@NonNull StmtContext ctx, @NonNull ImmutableList> substatements); protected abstract @NonNull D createEmptyDeclared(@NonNull StmtContext ctx); @Override public E createEffective(final StmtContext ctx) { final D declared = ctx.buildDeclared(); final ImmutableList> substatements = buildEffectiveSubstatements(ctx, statementsToBuild(ctx, declaredSubstatements(ctx))); return substatements.isEmpty() ? createEmptyEffective(ctx, declared) : createEffective(ctx, declared, substatements); } protected abstract @NonNull E createEffective(@NonNull StmtContext ctx, @NonNull D declared, @NonNull ImmutableList> substatements); protected abstract @NonNull E createEmptyEffective(@NonNull StmtContext ctx, @NonNull D declared); /** * Give statement support a hook to transform statement contexts before they are built. Default implementation * does nothing, but note {@code augment} statement performs a real transformation. * * @param ctx Parent statement context * @param substatements Substatement contexts which have been determined to be built * @return Substatement context which are to be actually built */ protected List> statementsToBuild(final StmtContext ctx, final List> substatements) { return substatements; } protected static final > @Nullable E findFirstStatement( final ImmutableList> statements, final Class type) { for (EffectiveStatement stmt : statements) { if (type.isInstance(stmt)) { return type.cast(stmt); } } return null; } protected static final > A findFirstArgument( final ImmutableList> statements, final Class<@NonNull E> type, final A defValue) { final @Nullable E stmt = findFirstStatement(statements, type); return stmt != null ? stmt.argument() : defValue; } /** * Create a set of substatements. This method is split out so it can be overridden in subclasses adjust the * resulting statements. * * @param ctx Parent statement context * @param substatements proposed substatements * @return Built effective substatements */ protected ImmutableList> buildEffectiveSubstatements( final StmtContext ctx, final List> substatements) { return defaultBuildEffectiveSubstatements(substatements); } private static ImmutableList> defaultBuildEffectiveSubstatements( final List> substatements) { return substatements.stream() .filter(StmtContext::isSupportedToBuildEffective) .map(StmtContext::buildEffective) .collect(ImmutableList.toImmutableList()); } private static @NonNull List> declaredSubstatements(final StmtContext ctx) { /* * This dance is required to ensure that effects of 'uses' nodes are applied in the same order as * the statements were defined -- i.e. if we have something like this: * * container foo { * uses bar; * uses baz; * } * * grouping baz { * leaf baz { * type string; * } * } * * grouping bar { * leaf bar { * type string; * } * } * * The reactor would first inline 'uses baz' as that definition is the first one completely resolved and then * inline 'uses bar'. Here we are iterating in declaration order re-inline the statements. * * FIXME: 7.0.0: this really should be handled by UsesStatementSupport such that 'uses baz' would have a * prerequisite of a resolved 'uses bar'. */ final List> substatementsInit = new ArrayList<>(); Set> filteredStatements = null; for (final StmtContext declaredSubstatement : ctx.declaredSubstatements()) { if (declaredSubstatement.isSupportedByFeatures()) { substatementsInit.add(declaredSubstatement); final Collection> effect = declaredSubstatement.getEffectOfStatement(); if (!effect.isEmpty()) { if (filteredStatements == null) { filteredStatements = new HashSet<>(); } filteredStatements.addAll(effect); substatementsInit.addAll(effect); } } } if (filteredStatements != null) { for (StmtContext stmt : ctx.effectiveSubstatements()) { if (!filteredStatements.contains(stmt)) { substatementsInit.add(stmt); } } } else { substatementsInit.addAll(ctx.effectiveSubstatements()); } return substatementsInit; } }