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