*/
package org.opendaylight.yangtools.yang.parser.spi.meta;
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
import com.google.common.annotations.Beta;
+import com.google.common.base.VerifyException;
+import java.util.Collection;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Immutable;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.model.api.meta.ArgumentDefinition;
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.EffectiveStmtCtx.Current;
import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
* @param <D> Declared Statement representation
* @param <E> Effective Statement representation
*/
-public interface StatementSupport<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
- extends StatementDefinition, StatementFactory<A, D, E> {
+public abstract class StatementSupport<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
+ implements StatementDefinition, StatementFactory<A, D, E> {
+ /**
+ * A baseline class for implementing the {@link StatementFactory#canReuseCurrent(Current, Current, Collection)}
+ * contract in a manner which is consistent with a statement's {@link CopyPolicy}.
+ *
+ * @param <A> Argument type
+ * @param <D> Declared Statement representation
+ */
+ public abstract static class StatementPolicy<A, D extends DeclaredStatement<A>> implements Immutable {
+ final @NonNull CopyPolicy copyPolicy;
+
+ StatementPolicy(final CopyPolicy copyPolicy) {
+ this.copyPolicy = requireNonNull(copyPolicy);
+ }
+
+ /**
+ * Return a {@link StatementPolicy} for {@link CopyPolicy#CONTEXT_INDEPENDENT}.
+ *
+ * @param <A> Argument type
+ * @param <D> Declared Statement representation
+ * @return Context-independent policy
+ */
+ @SuppressWarnings("unchecked")
+ public static final <A, D extends DeclaredStatement<A>> @NonNull StatementPolicy<A, D> contextIndependent() {
+ return (StatementPolicy<A, D>) EqualSemantics.CONTEXT_INDEPENDENT;
+ }
+
+ /**
+ * Return a {@link StatementPolicy} for {@link CopyPolicy#EXACT_REPLICA}.
+ *
+ * @param <A> Argument type
+ * @param <D> Declared Statement representation
+ * @return Exact-replica policy
+ */
+ @SuppressWarnings("unchecked")
+ public static final <A, D extends DeclaredStatement<A>> @NonNull StatementPolicy<A, D> exactReplica() {
+ return (StatementPolicy<A, D>) EqualSemantics.EXACT_REPLICA;
+ }
+
+ /**
+ * Return a {@link StatementPolicy} for {@link CopyPolicy#IGNORE}.
+ *
+ * @param <A> Argument type
+ * @param <D> Declared Statement representation
+ * @return Ignoring policy
+ */
+ @SuppressWarnings("unchecked")
+ public static final <A, D extends DeclaredStatement<A>> @NonNull StatementPolicy<A, D> ignore() {
+ return (StatementPolicy<A, D>) AlwaysFail.IGNORE;
+ }
+
+ /**
+ * Return a {@link StatementPolicy} for {@link CopyPolicy#REJECT}.
+ *
+ * @param <A> Argument type
+ * @param <D> Declared Statement representation
+ * @return Rejecting statement policy
+ */
+ @SuppressWarnings("unchecked")
+ public static final <A, D extends DeclaredStatement<A>> @NonNull StatementPolicy<A, D> reject() {
+ return (StatementPolicy<A, D>) AlwaysFail.REJECT;
+ }
+
+ /**
+ * Return a {@link StatementPolicy} for {@link CopyPolicy#DECLARED_COPY}, deferring to a
+ * {@link StatementEquality} for individual decisions.
+ *
+ * @param <A> Argument type
+ * @param <D> Declared Statement representation
+ * @param equality {@link StatementEquality} to apply to effective statements
+ * @return Equality-based statement policy
+ */
+ public static final <A, D extends DeclaredStatement<A>> @NonNull StatementPolicy<A, D> copyDeclared(
+ final @NonNull StatementEquality<A, D> equality) {
+ return new EqualSemantics<>(equality);
+ }
+
+ /**
+ * Return a {@link StatementPolicy} for {@link CopyPolicy#DECLARED_COPY}, always performing a copy operation.
+ *
+ * @param <A> Argument type
+ * @param <D> Declared Statement representation
+ * @return Rejecting statement policy
+ */
+ @SuppressWarnings("unchecked")
+ public static final <A, D extends DeclaredStatement<A>> @NonNull StatementPolicy<A, D> alwaysCopyDeclared() {
+ return (StatementPolicy<A, D>) EqualSemantics.ALWAYS_COPY;
+ }
+
+ abstract boolean canReuseCurrent(@NonNull Current<A, D> copy, @NonNull Current<A, D> current,
+ @NonNull Collection<? extends EffectiveStatement<?, ?>> substatements);
+
+ private static final class AlwaysFail<A, D extends DeclaredStatement<A>> extends StatementPolicy<A, D> {
+ static final @NonNull AlwaysFail<?, ?> IGNORE = new AlwaysFail<>(CopyPolicy.IGNORE);
+ static final @NonNull AlwaysFail<?, ?> REJECT = new AlwaysFail<>(CopyPolicy.REJECT);
+
+ private AlwaysFail(final CopyPolicy copyPolicy) {
+ super(copyPolicy);
+ }
+
+ @Override
+ boolean canReuseCurrent(final Current<A, D> copy, final Current<A, D> current,
+ final Collection<? extends EffectiveStatement<?, ?>> substatements) {
+ throw new VerifyException("This implementation should never be invoked");
+ }
+ }
+
+ private static final class EqualSemantics<A, D extends DeclaredStatement<A>> extends StatementPolicy<A, D> {
+ static final @NonNull EqualSemantics<?, ?> ALWAYS_COPY =
+ new EqualSemantics<>((copy, stmt, substatements) -> false);
+ static final @NonNull EqualSemantics<?, ?> CONTEXT_INDEPENDENT =
+ new EqualSemantics<>(CopyPolicy.CONTEXT_INDEPENDENT, (copy, stmt, substatements) -> true);
+ static final @NonNull EqualSemantics<?, ?> EXACT_REPLICA =
+ new EqualSemantics<>(CopyPolicy.EXACT_REPLICA, (copy, stmt, substatements) -> true);
+
+ private final @NonNull StatementEquality<A, D> equality;
+
+ private EqualSemantics(final CopyPolicy copyPolicy, final StatementEquality<A, D> equality) {
+ super(copyPolicy);
+ this.equality = requireNonNull(equality);
+ }
+
+ EqualSemantics(final StatementEquality<A, D> equality) {
+ this(CopyPolicy.DECLARED_COPY, equality);
+ }
+
+ @Override
+ boolean canReuseCurrent(final Current<A, D> copy, final Current<A, D> current,
+ final Collection<? extends EffectiveStatement<?, ?>> substatements) {
+ return equality.canReuseCurrent(copy, current, substatements);
+ }
+ }
+ }
+
+ /**
+ * Abstract base class for comparators associated with statements with a {@link CopyPolicy#DECLARED_COPY} copy
+ * policy.
+ *
+ * @param <A> Argument type
+ * @param <D> Declared Statement representation
+ */
+ @FunctionalInterface
+ public interface StatementEquality<A, D extends DeclaredStatement<A>> {
+ /**
+ * Determine whether {@code current} statement has the same semantics as the provided copy. See the contract
+ * specification of {@link StatementFactory#canReuseCurrent(Current, Current, Collection)}.
+ *
+ * @param copy Copy of current effective context
+ * @param current Current effective context
+ * @param substatements Current effective substatements
+ * @return True if {@code current} can be reused in place of {@code copy}, false if the copy needs to be used.
+ */
+ boolean canReuseCurrent(@NonNull Current<A, D> copy, @NonNull Current<A, D> current,
+ @NonNull Collection<? extends EffectiveStatement<?, ?>> substatements);
+ }
+
+ private final @NonNull StatementPolicy<A, D> policy;
+ private final @NonNull StatementDefinition def;
+ private final @NonNull CopyPolicy copyPolicy;
+
+ @Beta
+ protected StatementSupport(final StatementSupport<A, D, E> delegate) {
+ checkArgument(delegate != this);
+ this.def = delegate.def;
+ this.policy = delegate.policy;
+ this.copyPolicy = delegate.copyPolicy;
+ }
+
+ @Beta
+ protected StatementSupport(final StatementDefinition publicDefinition, final StatementPolicy<A, D> policy) {
+ checkArgument(publicDefinition != this);
+ this.def = requireNonNull(publicDefinition);
+ this.policy = requireNonNull(policy);
+ this.copyPolicy = policy.copyPolicy;
+ }
+
/**
* Returns public statement definition, which will be present in built statements.
*
*
* @return public statement definition, which will be present in built statements.
*/
- @NonNull StatementDefinition getPublicView();
+ public final @NonNull StatementDefinition getPublicView() {
+ return def;
+ }
+
+ /**
+ * Return this statement's {@link CopyPolicy}. This is a static value, reflecting how this statement reacts to being
+ * replicated to a different context, without reflecting on behaviour of potential substatements, which would come
+ * into play in something like:
+ *
+ * <pre>
+ * <code>
+ * module foo {
+ * namespace foo;
+ * prefix foo;
+ *
+ * extension note {
+ * argument string {
+ * type string {
+ * length 1..max;
+ * }
+ * }
+ * description "Can be used in description/reference statements to attach additional notes";
+ * }
+ *
+ * description "A nice module extending description statement semantics" {
+ * foo:note "We can now attach description/reference a note.";
+ * foo:note "Also another note";
+ * }
+ * }
+ * </code>
+ * </pre>
+ *
+ * <p>
+ * In this scenario, it is the reactor's job to figure out what to do (like talking to substatements).
+ *
+ * @return This statement's copy policy
+ */
+ public final @NonNull CopyPolicy copyPolicy() {
+ return copyPolicy;
+ }
+
+ @Override
+ public final boolean canReuseCurrent(final Current<A, D> copy, final Current<A, D> current,
+ final Collection<? extends EffectiveStatement<?, ?>> substatements) {
+ return policy.canReuseCurrent(copy, current, substatements);
+ }
/**
* Parses textual representation of argument in object representation.
* @return Parsed value
* @throws SourceException when an inconsistency is detected.
*/
- A parseArgumentValue(StmtContext<?, ?, ?> ctx, String value);
+ public abstract A parseArgumentValue(StmtContext<?, ?, ?> ctx, String value);
/**
- * Adapts the argument value to match a new module.
+ * Adapts the argument value to match a new module. Default implementation returns original value stored in context,
+ * which is appropriate for most implementations.
*
* @param ctx Context, which may be used to access source-specific namespaces required for parsing.
* @param targetModule Target module, may not be null.
- * @return Adapted argument value. The default implementation returns original value stored in context.
+ * @return Adapted argument value.
*/
- default A adaptArgumentValue(final StmtContext<A, D, E> ctx, final QNameModule targetModule) {
- return ctx.getStatementArgument();
+ public A adaptArgumentValue(final @NonNull StmtContext<A, D, E> ctx, final @NonNull QNameModule targetModule) {
+ return ctx.argument();
}
/**
*
* @param stmt Context of added statement. No substatements are available.
*/
- void onStatementAdded(StmtContext.Mutable<A, D, E> stmt);
+ public void onStatementAdded(final @NonNull Mutable<A, D, E> stmt) {
+ // NOOP for most implementations
+ }
/**
* Invoked when statement is closed during {@link ModelProcessingPhase#SOURCE_PRE_LINKAGE} phase, only substatements
*
* <p>
* Implementation may use method to perform actions on this event or register modification action using
- * {@link StmtContext.Mutable#newInferenceAction(ModelProcessingPhase)}.
+ * {@link Mutable#newInferenceAction(ModelProcessingPhase)}.
*
* @param stmt Context of added statement.
*/
- void onPreLinkageDeclared(StmtContext.Mutable<A, D, E> stmt);
+ public void onPreLinkageDeclared(final @NonNull Mutable<A, D, E> stmt) {
+ // NOOP for most implementations
+ }
/**
* Invoked when statement is closed during {@link ModelProcessingPhase#SOURCE_LINKAGE} phase, only substatements
*
* <p>
* Implementation may use method to perform actions on this event or register modification action using
- * {@link StmtContext.Mutable#newInferenceAction(ModelProcessingPhase)}.
+ * {@link Mutable#newInferenceAction(ModelProcessingPhase)}.
*
* @param stmt Context of added statement.
* @throws SourceException when an inconsistency is detected.
*/
- void onLinkageDeclared(StmtContext.Mutable<A, D, E> stmt);
+ public void onLinkageDeclared(final @NonNull Mutable<A, D, E> stmt) {
+ // NOOP for most implementations
+ }
/**
* Invoked when statement is closed during {@link ModelProcessingPhase#STATEMENT_DEFINITION} phase,
*
* <p>
* Implementation may use method to perform actions on this event or register modification action using
- * {@link StmtContext.Mutable#newInferenceAction(ModelProcessingPhase)}.
+ * {@link Mutable#newInferenceAction(ModelProcessingPhase)}.
*
* @param stmt Context of added statement. Argument and statement parent is accessible.
* @throws SourceException when an inconsistency is detected.
*/
- void onStatementDefinitionDeclared(StmtContext.Mutable<A, D, E> stmt);
+ public void onStatementDefinitionDeclared(final Mutable<A, D, E> stmt) {
+ // NOOP for most implementations
+ }
/**
* Invoked when statement is closed during {@link ModelProcessingPhase#FULL_DECLARATION} phase,
*
* <p>
* Implementation may use method to perform actions on this event or register modification action using
- * {@link StmtContext.Mutable#newInferenceAction(ModelProcessingPhase)}.
+ * {@link Mutable#newInferenceAction(ModelProcessingPhase)}.
*
* @param stmt Context of added statement. Argument and statement parent is accessible.
* @throws SourceException when an inconsistency is detected.
*/
- void onFullDefinitionDeclared(StmtContext.Mutable<A, D, E> stmt);
+ public void onFullDefinitionDeclared(final Mutable<A, D, E> stmt) {
+ final SubstatementValidator validator = substatementValidator();
+ if (validator != null) {
+ validator.validate(stmt);
+ }
+ }
+
+ /**
+ * Returns corresponding substatement validator of a statement support.
+ *
+ * @return substatement validator or null, if substatement validator is not defined
+ */
+ protected abstract @Nullable SubstatementValidator substatementValidator();
/**
* Returns true if this support has argument specific supports.
+ *
+ * @return true if this support has argument specific supports.
*/
- boolean hasArgumentSpecificSupports();
+ public boolean hasArgumentSpecificSupports() {
+ // Most of statement supports don't have any argument specific supports, so return 'false'.
+ return false;
+ }
/**
* If this support has argument specific supports, the method returns support specific for given argument
* @param argument argument of statement
* @return statement support specific for supplied argument or null
*/
- @Nullable StatementSupport<?, ?, ?> getSupportSpecificForArgument(String argument);
-
- /**
- * Determine reactor copy behavior of a statement instance. Statement support classes are required to determine
- * their operations with regard to their statements being replicated into different contexts, so that
- * {@link Mutable} instances are not created when it is evident they are superfluous.
- *
- * @param stmt Context of statement to be copied statement.
- * @param parent Parent statement context
- * @param copyType Type of copy being performed
- * @param targetModule Target module, if present
- * @return Policy that needs to be applied to the copy operation of this statement.
- */
- // FIXME: YANGTOOLS-694: clarify targetModule semantics (does null mean 'same as declared'?)
- default @NonNull CopyPolicy applyCopyPolicy(final Mutable<?, ?, ?> stmt, final Mutable<?, ?, ?> parent,
- final CopyType copyType, @Nullable final QNameModule targetModule) {
- // Most of statement supports will just want to copy the statement
- // FIXME: YANGTOOLS-694: that is not strictly true. Subclasses of this should indicate if they are themselves
- // copy-sensitive:
- // 1) if they are not and cannot be targeted by inference, and all their current
- // substatements are also non-sensitive, we want to return the same context.
- // 2) if they are not and their current substatements are sensitive, we want to copy
- // as a lazily-instantiated interceptor to let it deal with substatements when needed
- // (YANGTOOLS-1067 prerequisite)
- // 3) otherwise perform this eager copy
- // return Optional.of(parent.childCopyOf(stmt, copyType, targetModule));
- return CopyPolicy.DECLARED_COPY;
+ public @Nullable StatementSupport<?, ?, ?> getSupportSpecificForArgument(final String argument) {
+ // Most of statement supports don't have any argument specific supports, so return null.
+ return null;
}
/**
- * Given a raw string representation of an argument, try to use a shared representation.
+ * Given a raw string representation of an argument, try to use a shared representation. Default implementation
+ * does nothing.
*
* @param rawArgument Argument string
* @return A potentially-shard instance
*/
- default String internArgument(final String rawArgument) {
+ public String internArgument(final String rawArgument) {
return rawArgument;
}
- /**
- * Returns unknown statement form of a regular YANG statement supplied as a parameter to the method.
- *
- * @param yangStmtDef statement definition of a regular YANG statement
- * @return Optional of unknown statement form of a regular YANG statement or empty() if it is not supported by this
- * statement support
- */
- default Optional<StatementSupport<?, ?, ?>> getUnknownStatementDefinitionOf(final StatementDefinition yangStmtDef) {
- return Optional.empty();
- }
-
/**
* Returns true if this statement support and all its substatements ignore if-feature statements (e.g. yang-data
* extension defined in <a href="https://tools.ietf.org/html/rfc8040#section-8">RFC 8040</a>). Default
* implementation returns false.
*
- * @return true if this statement support ignores if-feature statements,
- * otherwise false.
+ * @return true if this statement support ignores if-feature statements, otherwise false.
*/
@Beta
- default boolean isIgnoringIfFeatures() {
+ public boolean isIgnoringIfFeatures() {
return false;
}
* otherwise false.
*/
@Beta
- default boolean isIgnoringConfig() {
+ public boolean isIgnoringConfig() {
return false;
}
@Override
- default QName getStatementName() {
- return getPublicView().getStatementName();
+ public final QName getStatementName() {
+ return def.getStatementName();
}
@Override
- default @NonNull Optional<ArgumentDefinition> getArgumentDefinition() {
- return getPublicView().getArgumentDefinition();
+ public final Optional<ArgumentDefinition> getArgumentDefinition() {
+ return def.getArgumentDefinition();
}
@Override
- default Class<? extends DeclaredStatement<?>> getDeclaredRepresentationClass() {
- return getPublicView().getDeclaredRepresentationClass();
+ // Non-final for compatible extensions
+ public Class<? extends DeclaredStatement<?>> getDeclaredRepresentationClass() {
+ return def.getDeclaredRepresentationClass();
}
@Override
- default Class<? extends EffectiveStatement<?,?>> getEffectiveRepresentationClass() {
- return getPublicView().getEffectiveRepresentationClass();
+ // Non-final for compatible extensions
+ public Class<? extends EffectiveStatement<?,?>> getEffectiveRepresentationClass() {
+ return def.getEffectiveRepresentationClass();
}
/**
* Statement context copy policy, indicating how should reactor handle statement copy operations. Every statement
- * copied by the reactor is subject to policy check done by
- * {@link StatementSupport#applyCopyPolicy(Mutable, Mutable, CopyType, QNameModule)}.
- *
+ * copied by the reactor is subject to this policy.
*/
- enum CopyPolicy {
+ public enum CopyPolicy {
/**
* Reuse the source statement context in the new place, as it cannot be affected by any further operations. This
* implies that the semantics of the effective statement are not affected by any of its substatements. Each
*/
// TODO: does this mean source must have transitioned to ModelProcessingPhase.EFFECTIVE_MODEL?
CONTEXT_INDEPENDENT,
+ /**
+ * Reuse the source statement context in the new place completely. This policy is more stringent than
+ * {@link #CONTEXT_INDEPENDENT} in that the statement is dependent on circumstances of its original definition
+ * and any copy operation must replicate it exactly as is. This implies ignoring the usual policy of its
+ * substatements. A typical example of such a statement is {@code type}.
+ */
+ EXACT_REPLICA,
/**
* Create a copy sharing declared instance, but otherwise having a separate disconnected lifecycle.
*/
// TODO: will the copy transition to ModelProcessingPhase.FULL_DECLARATION or which phase?
DECLARED_COPY,
+ /**
+ * Reject any attempt to copy this statement. This is useful for statements that are defined as top-level
+ * constructs, such as {@code contact}, {@code deviation} and similar.
+ */
+ REJECT,
/**
* Ignore this statement's existence for the purposes of the new place -- it is not impacted. This guidance
* is left here for completeness, as it can have justifiable uses (but I can't think of any). Any substatements