Promote BaseStatementSupport to parser.spi.meta
[yangtools.git] / yang / yang-parser-spi / src / main / java / org / opendaylight / yangtools / yang / parser / spi / meta / StatementSupport.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.parser.spi.meta;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.annotations.Beta;
14 import com.google.common.base.VerifyException;
15 import java.util.Collection;
16 import java.util.Optional;
17 import org.eclipse.jdt.annotation.NonNull;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.opendaylight.yangtools.concepts.Immutable;
20 import org.opendaylight.yangtools.yang.common.QName;
21 import org.opendaylight.yangtools.yang.common.QNameModule;
22 import org.opendaylight.yangtools.yang.model.api.meta.ArgumentDefinition;
23 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
24 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
25 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
26 import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx.Current;
27 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
28 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
29
30 /**
31  * Support for processing concrete YANG statement.
32  *
33  * <p>
34  * This interface is intended to be implemented by developers, which want to introduce support of statement to parser.
35  * Consider subclassing {@link AbstractStatementSupport} for easier implementation of this interface.
36  *
37  * @param <A> Argument type
38  * @param <D> Declared Statement representation
39  * @param <E> Effective Statement representation
40  */
41 public abstract class StatementSupport<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
42         implements StatementDefinition, StatementFactory<A, D, E> {
43     /**
44      * A baseline class for implementing the {@link StatementFactory#canReuseCurrent(Current, Current, Collection)}
45      * contract in a manner which is consistent with a statement's {@link CopyPolicy}.
46      *
47      * @param <A> Argument type
48      * @param <D> Declared Statement representation
49      */
50     public abstract static class StatementPolicy<A, D extends DeclaredStatement<A>> implements Immutable {
51         final @NonNull CopyPolicy copyPolicy;
52
53         StatementPolicy(final CopyPolicy copyPolicy) {
54             this.copyPolicy = requireNonNull(copyPolicy);
55         }
56
57         /**
58          * Return an {@link StatementPolicy} for {@link CopyPolicy#CONTEXT_INDEPENDENT}.
59          *
60          * @param <A> Argument type
61          * @param <D> Declared Statement representation
62          * @return Context-independent policy
63          */
64         @SuppressWarnings("unchecked")
65         public static final <A, D extends DeclaredStatement<A>> @NonNull StatementPolicy<A, D> contextIndependent() {
66             return (StatementPolicy<A, D>) AlwaysReuse.CONTEXT_INDEPENDENT;
67         }
68
69         /**
70          * Return an {@link StatementPolicy} for {@link CopyPolicy#IGNORE}.
71          *
72          * @param <A> Argument type
73          * @param <D> Declared Statement representation
74          * @return Ignoring policy
75          */
76         @SuppressWarnings("unchecked")
77         public static final <A, D extends DeclaredStatement<A>> @NonNull StatementPolicy<A, D> ignore() {
78             return (StatementPolicy<A, D>) AlwaysFail.IGNORE;
79         }
80
81         /**
82          * Return an {@link StatementPolicy} for {@link CopyPolicy#REJECT}.
83          *
84          * @param <A> Argument type
85          * @param <D> Declared Statement representation
86          * @return Rejecting statement policy
87          */
88         @SuppressWarnings("unchecked")
89         public static final <A, D extends DeclaredStatement<A>> @NonNull StatementPolicy<A, D> reject() {
90             return (StatementPolicy<A, D>) AlwaysFail.REJECT;
91         }
92
93         /**
94          * Return an {@link StatementPolicy} for {@link CopyPolicy#DECLARED_COPY}, deferring to a
95          * {@link StatementEquality} for individual decisions.
96          *
97          * @param <A> Argument type
98          * @param <D> Declared Statement representation
99          * @param equality {@link StatementEquality} to apply to effective statements
100          * @return Rejecting statement policy
101          */
102         public static final <A, D extends DeclaredStatement<A>> @NonNull StatementPolicy<A, D> copyDeclared(
103                 final @NonNull StatementEquality<A, D> equality) {
104             return new EqualSemantics<>(equality);
105         }
106
107         abstract boolean canReuseCurrent(@NonNull Current<A, D> copy, @NonNull Current<A, D> current,
108             @NonNull Collection<? extends EffectiveStatement<?, ?>> substatements);
109
110         @Deprecated
111         @SuppressWarnings("unchecked")
112         static <A, D extends DeclaredStatement<A>> StatementPolicy<A, D> compat(final CopyPolicy copyPolicy) {
113             switch (copyPolicy) {
114                 case CONTEXT_INDEPENDENT:
115                     return contextIndependent();
116                 case DECLARED_COPY:
117                     return (StatementPolicy<A, D>) AlwaysCopy.DECLARED_COPY;
118                 case IGNORE:
119                     return ignore();
120                 case REJECT:
121                     return reject();
122                 default:
123                     throw new IllegalStateException("Unsupported policy " + copyPolicy);
124             }
125         }
126
127         private static final class AlwaysCopy<A, D extends DeclaredStatement<A>> extends StatementPolicy<A, D> {
128             @Deprecated
129             static final @NonNull AlwaysCopy<?, ?> DECLARED_COPY = new AlwaysCopy<>(CopyPolicy.DECLARED_COPY);
130
131             AlwaysCopy(final CopyPolicy copyPolicy) {
132                 super(copyPolicy);
133             }
134
135             @Override
136             boolean canReuseCurrent(final Current<A, D> copy, final Current<A, D> current,
137                     final Collection<? extends EffectiveStatement<?, ?>> substatements) {
138                 return false;
139             }
140         }
141
142         private static final class AlwaysReuse<A, D extends DeclaredStatement<A>> extends StatementPolicy<A, D> {
143             static final @NonNull AlwaysReuse<?, ?> CONTEXT_INDEPENDENT =
144                 new AlwaysReuse<>(CopyPolicy.CONTEXT_INDEPENDENT);
145
146             private AlwaysReuse(final CopyPolicy copyPolicy) {
147                 super(copyPolicy);
148             }
149
150             @Override
151             boolean canReuseCurrent(final Current<A, D> copy, final Current<A, D> current,
152                     final Collection<? extends EffectiveStatement<?, ?>> substatements) {
153                 return true;
154             }
155         }
156
157         private static final class AlwaysFail<A, D extends DeclaredStatement<A>> extends StatementPolicy<A, D> {
158             static final @NonNull AlwaysFail<?, ?> IGNORE = new AlwaysFail<>(CopyPolicy.IGNORE);
159             static final @NonNull AlwaysFail<?, ?> REJECT = new AlwaysFail<>(CopyPolicy.REJECT);
160
161             private AlwaysFail(final CopyPolicy copyPolicy) {
162                 super(copyPolicy);
163             }
164
165             @Override
166             boolean canReuseCurrent(final Current<A, D> copy, final Current<A, D> current,
167                     final Collection<? extends EffectiveStatement<?, ?>> substatements) {
168                 throw new VerifyException("This implementation should never be invoked");
169             }
170         }
171
172         private static final class EqualSemantics<A, D extends DeclaredStatement<A>> extends StatementPolicy<A, D> {
173             private final @NonNull StatementEquality<A, D> equality;
174
175             EqualSemantics(final @NonNull StatementEquality<A, D> equality) {
176                 super(CopyPolicy.DECLARED_COPY);
177                 this.equality = requireNonNull(equality);
178             }
179
180             @Override
181             boolean canReuseCurrent(final Current<A, D> copy, final Current<A, D> current,
182                     final Collection<? extends EffectiveStatement<?, ?>> substatements) {
183                 return equality.canReuseCurrent(copy, current, substatements);
184             }
185         }
186     }
187
188     /**
189      * Abstract base class for comparators associated with statements with a {@link CopyPolicy#DECLARED_COPY} copy
190      * policy.
191      *
192      * @param <A> Argument type
193      * @param <D> Declared Statement representation
194      */
195     @FunctionalInterface
196     public interface StatementEquality<A, D extends DeclaredStatement<A>> {
197         /**
198          * Determine whether {@code current} statement has the same semantics as the provided copy. See the contract
199          * specification of {@link StatementFactory#canReuseCurrent(Current, Current, Collection)}.
200          *
201          * @param copy Copy of current effective context
202          * @param current Current effective context
203          * @param substatements Current effective substatements
204          * @return True if {@code current} can be reused in place of {@code copy}, false if the copy needs to be used.
205          */
206         boolean canReuseCurrent(@NonNull Current<A, D> copy, @NonNull Current<A, D> current,
207             @NonNull Collection<? extends EffectiveStatement<?, ?>> substatements);
208     }
209
210     private final @NonNull StatementPolicy<A, D> policy;
211     private final @NonNull StatementDefinition def;
212     private final @NonNull CopyPolicy copyPolicy;
213
214     @Beta
215     protected StatementSupport(final StatementDefinition publicDefinition, final StatementPolicy<A, D> policy) {
216         checkArgument(publicDefinition != this);
217         this.def = requireNonNull(publicDefinition);
218         this.policy = requireNonNull(policy);
219         this.copyPolicy = policy.copyPolicy;
220     }
221
222     @Beta
223     @Deprecated
224     // FIXME: remove this constructor
225     protected StatementSupport(final StatementDefinition publicDefinition, final CopyPolicy copyPolicy) {
226         this(publicDefinition, StatementPolicy.compat(copyPolicy));
227     }
228
229     /**
230      * Returns public statement definition, which will be present in built statements.
231      *
232      * <p>
233      * Public statement definition may be used to provide different implementation of statement definition,
234      * which will not retain any build specific data or context.
235      *
236      * @return public statement definition, which will be present in built statements.
237      */
238     public final @NonNull StatementDefinition getPublicView() {
239         return def;
240     }
241
242     /**
243      * Return this statement's {@link CopyPolicy}. This is a static value, reflecting how this statement reacts to being
244      * replicated to a different context, without reflecting on behaviour of potential substatements, which would come
245      * into play in something like:
246      *
247      * <pre>
248      *   <code>
249      *     module foo {
250      *       namespace foo;
251      *       prefix foo;
252      *
253      *       extension note {
254      *         argument string {
255      *           type string {
256      *             length 1..max;
257      *           }
258      *         }
259      *         description "Can be used in description/reference statements to attach additional notes";
260      *       }
261      *
262      *       description "A nice module extending description statement semantics" {
263      *         foo:note "We can now attach description/reference a note.";
264      *         foo:note "Also another note";
265      *       }
266      *     }
267      *   </code>
268      * </pre>
269      *
270      * <p>
271      * In this scenario, it is the reactor's job to figure out what to do (like talking to substatements).
272      *
273      * @return This statement's copy policy
274      */
275     public final @NonNull CopyPolicy copyPolicy() {
276         return copyPolicy;
277     }
278
279     @Override
280     public final boolean canReuseCurrent(final Current<A, D> copy, final Current<A, D> current,
281             final Collection<? extends EffectiveStatement<?, ?>> substatements) {
282         return policy.canReuseCurrent(copy, current, substatements);
283     }
284
285     /**
286      * Parses textual representation of argument in object representation.
287      *
288      * @param ctx Context, which may be used to access source-specific namespaces required for parsing.
289      * @param value String representation of value, as was present in text source.
290      * @return Parsed value
291      * @throws SourceException when an inconsistency is detected.
292      */
293     public abstract A parseArgumentValue(StmtContext<?, ?, ?> ctx, String value);
294
295     /**
296      * Adapts the argument value to match a new module. Default implementation returns original value stored in context,
297      * which is appropriate for most implementations.
298      *
299      * @param ctx Context, which may be used to access source-specific namespaces required for parsing.
300      * @param targetModule Target module, may not be null.
301      * @return Adapted argument value.
302      */
303     public A adaptArgumentValue(final @NonNull StmtContext<A, D, E> ctx, final @NonNull QNameModule targetModule) {
304         return ctx.argument();
305     }
306
307     /**
308      * Invoked when a statement supported by this instance is added to build context. This allows implementations
309      * of this interface to start tracking the statement and perform any modifications to the build context hierarchy,
310      * accessible via {@link StmtContext#getParentContext()}. One such use is populating the parent's namespaces to
311      * allow it to locate this child statement.
312      *
313      * @param stmt Context of added statement. No substatements are available.
314      */
315     public void onStatementAdded(final @NonNull Mutable<A, D, E> stmt) {
316         // NOOP for most implementations
317     }
318
319     /**
320      * Invoked when statement is closed during {@link ModelProcessingPhase#SOURCE_PRE_LINKAGE} phase, only substatements
321      * from this and previous phase are available.
322      *
323      * <p>
324      * Implementation may use method to perform actions on this event or register modification action using
325      * {@link Mutable#newInferenceAction(ModelProcessingPhase)}.
326      *
327      * @param stmt Context of added statement.
328      */
329     public void onPreLinkageDeclared(final @NonNull Mutable<A, D, E> stmt) {
330         // NOOP for most implementations
331     }
332
333     /**
334      * Invoked when statement is closed during {@link ModelProcessingPhase#SOURCE_LINKAGE} phase, only substatements
335      * from this and previous phase are available.
336      *
337      * <p>
338      * Implementation may use method to perform actions on this event or register modification action using
339      * {@link StmtContext.Mutable#newInferenceAction(ModelProcessingPhase)}.
340      *
341      * @param stmt Context of added statement.
342      * @throws SourceException when an inconsistency is detected.
343      */
344     public void onLinkageDeclared(final @NonNull Mutable<A, D, E> stmt) {
345         // NOOP for most implementations
346     }
347
348     /**
349      * Invoked when statement is closed during {@link ModelProcessingPhase#STATEMENT_DEFINITION} phase,
350      * only substatements from this phase are available.
351      *
352      * <p>
353      * Implementation may use method to perform actions on this event or register modification action using
354      * {@link StmtContext.Mutable#newInferenceAction(ModelProcessingPhase)}.
355      *
356      * @param stmt Context of added statement. Argument and statement parent is accessible.
357      * @throws SourceException when an inconsistency is detected.
358      */
359     public void onStatementDefinitionDeclared(final Mutable<A, D, E> stmt) {
360         // NOOP for most implementations
361     }
362
363     /**
364      * Invoked when statement is closed during {@link ModelProcessingPhase#FULL_DECLARATION} phase,
365      * only substatements from this phase are available.
366      *
367      * <p>
368      * Implementation may use method to perform actions on this event or register modification action using
369      * {@link StmtContext.Mutable#newInferenceAction(ModelProcessingPhase)}.
370      *
371      * @param stmt Context of added statement. Argument and statement parent is accessible.
372      * @throws SourceException when an inconsistency is detected.
373      */
374     public void onFullDefinitionDeclared(final StmtContext.Mutable<A, D, E> stmt) {
375         final SubstatementValidator validator = getSubstatementValidator();
376         if (validator != null) {
377             validator.validate(stmt);
378         }
379     }
380
381     /**
382      * Returns corresponding substatement validator of a statement support.
383      *
384      * @return substatement validator or null, if substatement validator is not defined
385      */
386     // FIXME: rename to 'substatementValidator' and perhaps let it be passed in?
387     protected abstract @Nullable SubstatementValidator getSubstatementValidator();
388
389     /**
390      * Returns true if this support has argument specific supports.
391      */
392     public boolean hasArgumentSpecificSupports() {
393         // Most of statement supports don't have any argument specific supports, so return 'false'.
394         return false;
395     }
396
397     /**
398      * If this support has argument specific supports, the method returns support specific for given argument
399      * (e.g. type statement support need to be specialized based on its argument), otherwise returns null.
400      *
401      * @param argument argument of statement
402      * @return statement support specific for supplied argument or null
403      */
404     public @Nullable StatementSupport<?, ?, ?> getSupportSpecificForArgument(final String argument) {
405         // Most of statement supports don't have any argument specific supports, so return null.
406         return null;
407     }
408
409     /**
410      * Given a raw string representation of an argument, try to use a shared representation. Default implementation
411      * does nothing.
412      *
413      * @param rawArgument Argument string
414      * @return A potentially-shard instance
415      */
416     public String internArgument(final String rawArgument) {
417         return rawArgument;
418     }
419
420     /**
421      * Returns unknown statement form of a regular YANG statement supplied as a parameter to the method. Default
422      * implementation does nothing.
423      *
424      * @param yangStmtDef statement definition of a regular YANG statement
425      * @return Optional of unknown statement form of a regular YANG statement or empty() if it is not supported by this
426      *         statement support
427      */
428     public Optional<StatementSupport<?, ?, ?>> getUnknownStatementDefinitionOf(final StatementDefinition yangStmtDef) {
429         return Optional.empty();
430     }
431
432     /**
433      * Returns true if this statement support and all its substatements ignore if-feature statements (e.g. yang-data
434      * extension defined in <a href="https://tools.ietf.org/html/rfc8040#section-8">RFC 8040</a>). Default
435      * implementation returns false.
436      *
437      * @return true if this statement support ignores if-feature statements, otherwise false.
438      */
439     @Beta
440     public boolean isIgnoringIfFeatures() {
441         return false;
442     }
443
444     /**
445      * Returns true if this statement support and all its substatements ignore config statements (e.g. yang-data
446      * extension defined in <a href="https://tools.ietf.org/html/rfc8040#section-8">RFC 8040</a>). Default
447      * implementation returns false.
448      *
449      * @return true if this statement support ignores config statements,
450      *         otherwise false.
451      */
452     @Beta
453     public boolean isIgnoringConfig() {
454         return false;
455     }
456
457     @Override
458     public final QName getStatementName() {
459         return def.getStatementName();
460     }
461
462     @Override
463     public final Optional<ArgumentDefinition> getArgumentDefinition() {
464         return def.getArgumentDefinition();
465     }
466
467     @Override
468     // Non-final for compatible extensions
469     public Class<? extends DeclaredStatement<?>> getDeclaredRepresentationClass() {
470         return def.getDeclaredRepresentationClass();
471     }
472
473     @Override
474     // Non-final for compatible extensions
475     public Class<? extends EffectiveStatement<?,?>> getEffectiveRepresentationClass() {
476         return def.getEffectiveRepresentationClass();
477     }
478
479     /**
480      * Statement context copy policy, indicating how should reactor handle statement copy operations. Every statement
481      * copied by the reactor is subject to this policy.
482      */
483     public enum CopyPolicy {
484         /**
485          * Reuse the source statement context in the new place, as it cannot be affected by any further operations. This
486          * implies that the semantics of the effective statement are not affected by any of its substatements. Each
487          * of the substatements is free to make its own policy.
488          *
489          * <p>
490          * This policy is typically used by static constant statements such as {@code description} or {@code length},
491          * where the baseline RFC7950 does not allow any impact. A {@code description} could hold an extension statement
492          * in which case this interaction would come into play. Normal YANG will see empty substatements, so the reactor
493          * will be free to complete reuse the context.
494          *
495          * <p>
496          * In case any substatement is of stronger policy, it is up to the reactor to handle correct handling of
497          * resulting subobjects.
498          */
499         // TODO: does this mean source must have transitioned to ModelProcessingPhase.EFFECTIVE_MODEL?
500         CONTEXT_INDEPENDENT,
501         /**
502          * Create a copy sharing declared instance, but otherwise having a separate disconnected lifecycle.
503          */
504         // TODO: will the copy transition to ModelProcessingPhase.FULL_DECLARATION or which phase?
505         DECLARED_COPY,
506         /**
507          * Reject any attempt to copy this statement. This is useful for statements that are defined as top-level
508          * constructs, such as {@code contact}, {@code deviation} and similar.
509          */
510         REJECT,
511         /**
512          * Ignore this statement's existence for the purposes of the new place -- it is not impacted. This guidance
513          * is left here for completeness, as it can have justifiable uses (but I can't think of any). Any substatements
514          * need to be ignored, too.
515          */
516         IGNORE;
517     }
518 }