Expand reactor documentation a bit
[yangtools.git] / yang / yang-parser-reactor / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / StatementContextBase.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.stmt.reactor;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Preconditions.checkNotNull;
12 import static com.google.common.base.Preconditions.checkState;
13 import static java.util.Objects.requireNonNull;
14
15 import com.google.common.annotations.Beta;
16 import com.google.common.base.MoreObjects;
17 import com.google.common.base.MoreObjects.ToStringHelper;
18 import com.google.common.collect.ImmutableCollection;
19 import com.google.common.collect.ImmutableList;
20 import com.google.common.collect.ImmutableMultimap;
21 import com.google.common.collect.ImmutableSet;
22 import com.google.common.collect.Multimap;
23 import com.google.common.collect.Multimaps;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.EnumMap;
28 import java.util.EventListener;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Map.Entry;
32 import java.util.Objects;
33 import java.util.Optional;
34 import java.util.Set;
35 import org.eclipse.jdt.annotation.NonNull;
36 import org.eclipse.jdt.annotation.Nullable;
37 import org.opendaylight.yangtools.yang.common.QName;
38 import org.opendaylight.yangtools.yang.common.QNameModule;
39 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
40 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
41 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
42 import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
43 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
44 import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
45 import org.opendaylight.yangtools.yang.model.api.stmt.ConfigStatement;
46 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory;
47 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
48 import org.opendaylight.yangtools.yang.parser.spi.meta.ImplicitParentAwareStatementSupport;
49 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
50 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
51 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
52 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
53 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceKeyCriterion;
54 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
55 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
56 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
57 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
58 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
59 import org.opendaylight.yangtools.yang.parser.spi.source.ImplicitSubstatement;
60 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
61 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
62 import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter.ResumedStatement;
63 import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace;
64 import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace.SupportedFeatures;
65 import org.opendaylight.yangtools.yang.parser.stmt.reactor.NamespaceBehaviourWithListeners.KeyedValueAddedListener;
66 import org.opendaylight.yangtools.yang.parser.stmt.reactor.NamespaceBehaviourWithListeners.PredicateValueAddedListener;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
69
70 /**
71  * Core reactor statement implementation of {@link Mutable}.
72  *
73  * @param <A> Argument type
74  * @param <D> Declared Statement representation
75  * @param <E> Effective Statement representation
76  */
77 public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
78         extends NamespaceStorageSupport implements Mutable<A, D, E>, ResumedStatement {
79     /**
80      * Event listener when an item is added to model namespace.
81      */
82     interface OnNamespaceItemAdded extends EventListener {
83         /**
84          * Invoked whenever a new item is added to a namespace.
85          */
86         void namespaceItemAdded(StatementContextBase<?, ?, ?> context, Class<?> namespace, Object key, Object value);
87     }
88
89     /**
90      * Event listener when a parsing {@link ModelProcessingPhase} is completed.
91      */
92     interface OnPhaseFinished extends EventListener {
93         /**
94          * Invoked whenever a processing phase has finished.
95          */
96         boolean phaseFinished(StatementContextBase<?, ?, ?> context, ModelProcessingPhase finishedPhase);
97     }
98
99     /**
100      * Interface for all mutations within an {@link ModelActionBuilder.InferenceAction}.
101      */
102     interface ContextMutation {
103
104         boolean isFinished();
105     }
106
107     private static final Logger LOG = LoggerFactory.getLogger(StatementContextBase.class);
108
109     // Flag bit assignments
110     private static final int IS_SUPPORTED_BY_FEATURES    = 0x01;
111     private static final int HAVE_SUPPORTED_BY_FEATURES  = 0x02;
112     private static final int IS_IGNORE_IF_FEATURE        = 0x04;
113     private static final int HAVE_IGNORE_IF_FEATURE      = 0x08;
114     // Note: these four are related
115     private static final int IS_IGNORE_CONFIG            = 0x10;
116     private static final int HAVE_IGNORE_CONFIG          = 0x20;
117     private static final int IS_CONFIGURATION            = 0x40;
118     private static final int HAVE_CONFIGURATION          = 0x80;
119
120     // Have-and-set flag constants, also used as masks
121     private static final int SET_SUPPORTED_BY_FEATURES = HAVE_SUPPORTED_BY_FEATURES | IS_SUPPORTED_BY_FEATURES;
122     private static final int SET_CONFIGURATION = HAVE_CONFIGURATION | IS_CONFIGURATION;
123     // Note: implies SET_CONFIGURATION, allowing fewer bit operations to be performed
124     private static final int SET_IGNORE_CONFIG = HAVE_IGNORE_CONFIG | IS_IGNORE_CONFIG | SET_CONFIGURATION;
125     private static final int SET_IGNORE_IF_FEATURE = HAVE_IGNORE_IF_FEATURE | IS_IGNORE_IF_FEATURE;
126
127     private final @NonNull StatementDefinitionContext<A, D, E> definition;
128     private final @NonNull StatementSourceReference statementDeclSource;
129     private final StmtContext<?, ?, ?> originalCtx;
130     private final StmtContext<?, ?, ?> prevCopyCtx;
131     private final CopyHistory copyHistory;
132     private final String rawArgument;
133
134     private Multimap<ModelProcessingPhase, OnPhaseFinished> phaseListeners = ImmutableMultimap.of();
135     private Multimap<ModelProcessingPhase, ContextMutation> phaseMutation = ImmutableMultimap.of();
136     private List<Mutable<?, ?, ?>> effective = ImmutableList.of();
137     private List<StmtContext<?, ?, ?>> effectOfStatement = ImmutableList.of();
138     private StatementMap substatements = StatementMap.empty();
139
140     private @Nullable ModelProcessingPhase completedPhase;
141     private @Nullable D declaredInstance;
142     private @Nullable E effectiveInstance;
143
144     // Common state bits
145     private boolean isSupportedToBuildEffective = true;
146     private boolean fullyDefined;
147
148     // Flags for use with SubstatementContext. These are hiding in the alignment shadow created by above booleans and
149     // hence improve memory layout.
150     private byte flags;
151
152     StatementContextBase(final StatementDefinitionContext<A, D, E> def, final StatementSourceReference ref,
153             final String rawArgument) {
154         this.definition = requireNonNull(def);
155         this.statementDeclSource = requireNonNull(ref);
156         this.rawArgument = def.internArgument(rawArgument);
157         this.copyHistory = CopyHistory.original();
158         this.originalCtx = null;
159         this.prevCopyCtx = null;
160     }
161
162     StatementContextBase(final StatementDefinitionContext<A, D, E> def, final StatementSourceReference ref,
163         final String rawArgument, final CopyType copyType) {
164         this.definition = requireNonNull(def);
165         this.statementDeclSource = requireNonNull(ref);
166         this.rawArgument = rawArgument;
167         this.copyHistory = CopyHistory.of(copyType, CopyHistory.original());
168         this.originalCtx = null;
169         this.prevCopyCtx = null;
170     }
171
172     StatementContextBase(final StatementContextBase<A, D, E> original, final CopyType copyType) {
173         this.definition = original.definition;
174         this.statementDeclSource = original.statementDeclSource;
175         this.rawArgument = original.rawArgument;
176         this.copyHistory = CopyHistory.of(copyType, original.getCopyHistory());
177         this.originalCtx = original.getOriginalCtx().orElse(original);
178         this.prevCopyCtx = original;
179     }
180
181     StatementContextBase(final StatementContextBase<A, D, E> original) {
182         this.definition = original.definition;
183         this.statementDeclSource = original.statementDeclSource;
184         this.rawArgument = original.rawArgument;
185         this.copyHistory = original.getCopyHistory();
186         this.originalCtx = original.getOriginalCtx().orElse(original);
187         this.prevCopyCtx = original;
188         this.substatements = original.substatements;
189         this.effective = original.effective;
190     }
191
192     @Override
193     public Collection<? extends StmtContext<?, ?, ?>> getEffectOfStatement() {
194         return effectOfStatement;
195     }
196
197     @Override
198     public void addAsEffectOfStatement(final StmtContext<?, ?, ?> ctx) {
199         if (effectOfStatement.isEmpty()) {
200             effectOfStatement = new ArrayList<>(1);
201         }
202         effectOfStatement.add(ctx);
203     }
204
205     @Override
206     public void addAsEffectOfStatement(final Collection<? extends StmtContext<?, ?, ?>> ctxs) {
207         if (ctxs.isEmpty()) {
208             return;
209         }
210
211         if (effectOfStatement.isEmpty()) {
212             effectOfStatement = new ArrayList<>(ctxs.size());
213         }
214         effectOfStatement.addAll(ctxs);
215     }
216
217     @Override
218     public boolean isSupportedByFeatures() {
219         final int fl = flags & SET_SUPPORTED_BY_FEATURES;
220         if (fl != 0) {
221             return fl == SET_SUPPORTED_BY_FEATURES;
222         }
223         if (isIgnoringIfFeatures()) {
224             flags |= SET_SUPPORTED_BY_FEATURES;
225             return true;
226         }
227
228         /*
229          * If parent is supported, we need to check if-features statements of this context.
230          */
231         if (isParentSupportedByFeatures()) {
232             // If the set of supported features has not been provided, all features are supported by default.
233             final Set<QName> supportedFeatures = getFromNamespace(SupportedFeaturesNamespace.class,
234                     SupportedFeatures.SUPPORTED_FEATURES);
235             if (supportedFeatures == null || StmtContextUtils.checkFeatureSupport(this, supportedFeatures)) {
236                 flags |= SET_SUPPORTED_BY_FEATURES;
237                 return true;
238             }
239         }
240
241         // Either parent is not supported or this statement is not supported
242         flags |= HAVE_SUPPORTED_BY_FEATURES;
243         return false;
244     }
245
246     protected abstract boolean isParentSupportedByFeatures();
247
248     @Override
249     public boolean isSupportedToBuildEffective() {
250         return isSupportedToBuildEffective;
251     }
252
253     @Override
254     public void setIsSupportedToBuildEffective(final boolean isSupportedToBuildEffective) {
255         this.isSupportedToBuildEffective = isSupportedToBuildEffective;
256     }
257
258     @Override
259     public CopyHistory getCopyHistory() {
260         return copyHistory;
261     }
262
263     @Override
264     public Optional<StmtContext<?, ?, ?>> getOriginalCtx() {
265         return Optional.ofNullable(originalCtx);
266     }
267
268     @Override
269     public Optional<? extends StmtContext<?, ?, ?>> getPreviousCopyCtx() {
270         return Optional.ofNullable(prevCopyCtx);
271     }
272
273     @Override
274     public ModelProcessingPhase getCompletedPhase() {
275         return completedPhase;
276     }
277
278     @Override
279     public void setCompletedPhase(final ModelProcessingPhase completedPhase) {
280         this.completedPhase = completedPhase;
281     }
282
283     @Override
284     public abstract StatementContextBase<?, ?, ?> getParentContext();
285
286     /**
287      * Returns the model root for this statement.
288      *
289      * @return root context of statement
290      */
291     @Override
292     public abstract RootStatementContext<?, ?, ?> getRoot();
293
294     @Override
295     public StatementSource getStatementSource() {
296         return statementDeclSource.getStatementSource();
297     }
298
299     @Override
300     public StatementSourceReference getStatementSourceReference() {
301         return statementDeclSource;
302     }
303
304     @Override
305     public final String rawStatementArgument() {
306         return rawArgument;
307     }
308
309     @Override
310     public Collection<? extends StmtContext<?, ?, ?>> declaredSubstatements() {
311         return substatements.values();
312     }
313
314     @Override
315     public Collection<? extends Mutable<?, ?, ?>> mutableDeclaredSubstatements() {
316         return substatements.values();
317     }
318
319     @Override
320     public Collection<? extends StmtContext<?, ?, ?>> effectiveSubstatements() {
321         return mutableEffectiveSubstatements();
322     }
323
324     @Override
325     public Collection<? extends Mutable<?, ?, ?>> mutableEffectiveSubstatements() {
326         if (effective instanceof ImmutableCollection) {
327             return effective;
328         }
329
330         return Collections.unmodifiableCollection(effective);
331     }
332
333     /**
334      * Remove a set of statements from effective statements.
335      *
336      * @param statements statements to be removed
337      * @deprecated This method was used by EffectiveStatementBase to restore proper order of effects of uses statements.
338      *             It is no longer used in that capacity and slated for removal.
339      */
340     // FIXME: 5.0.0: remove this method
341     @Deprecated
342     public void removeStatementsFromEffectiveSubstatements(
343             final Collection<? extends StmtContext<?, ?, ?>> statements) {
344         if (!effective.isEmpty()) {
345             effective.removeAll(statements);
346             shrinkEffective();
347         }
348     }
349
350     private void shrinkEffective() {
351         if (effective.isEmpty()) {
352             effective = ImmutableList.of();
353         }
354     }
355
356     public void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef) {
357         if (effective.isEmpty()) {
358             return;
359         }
360
361         final Iterator<? extends StmtContext<?, ?, ?>> iterator = effective.iterator();
362         while (iterator.hasNext()) {
363             final StmtContext<?, ?, ?> next = iterator.next();
364             if (statementDef.equals(next.getPublicDefinition())) {
365                 iterator.remove();
366             }
367         }
368
369         shrinkEffective();
370     }
371
372     /**
373      * Removes a statement context from the effective substatements based on its statement definition (i.e statement
374      * keyword) and raw (in String form) statement argument. The statement context is removed only if both statement
375      * definition and statement argument match with one of the effective substatements' statement definition
376      * and argument.
377      *
378      * <p>
379      * If the statementArg parameter is null, the statement context is removed based only on its statement definition.
380      *
381      * @param statementDef statement definition of the statement context to remove
382      * @param statementArg statement argument of the statement context to remove
383      */
384     public void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef,
385             final String statementArg) {
386         if (statementArg == null) {
387             removeStatementFromEffectiveSubstatements(statementDef);
388         }
389
390         if (effective.isEmpty()) {
391             return;
392         }
393
394         final Iterator<Mutable<?, ?, ?>> iterator = effective.iterator();
395         while (iterator.hasNext()) {
396             final Mutable<?, ?, ?> next = iterator.next();
397             if (statementDef.equals(next.getPublicDefinition()) && statementArg.equals(next.rawStatementArgument())) {
398                 iterator.remove();
399             }
400         }
401
402         shrinkEffective();
403     }
404
405     /**
406      * Adds an effective statement to collection of substatements.
407      *
408      * @param substatement substatement
409      * @throws IllegalStateException
410      *             if added in declared phase
411      * @throws NullPointerException
412      *             if statement parameter is null
413      */
414     public void addEffectiveSubstatement(final Mutable<?, ?, ?> substatement) {
415         beforeAddEffectiveStatement(1);
416         effective.add(substatement);
417     }
418
419     /**
420      * Adds an effective statement to collection of substatements.
421      *
422      * @param statements substatements
423      * @throws IllegalStateException
424      *             if added in declared phase
425      * @throws NullPointerException
426      *             if statement parameter is null
427      */
428     public void addEffectiveSubstatements(final Collection<? extends Mutable<?, ?, ?>> statements) {
429         if (statements.isEmpty()) {
430             return;
431         }
432
433         statements.forEach(Objects::requireNonNull);
434         beforeAddEffectiveStatement(statements.size());
435         effective.addAll(statements);
436     }
437
438     private void beforeAddEffectiveStatement(final int toAdd) {
439         final ModelProcessingPhase inProgressPhase = getRoot().getSourceContext().getInProgressPhase();
440         checkState(inProgressPhase == ModelProcessingPhase.FULL_DECLARATION
441                 || inProgressPhase == ModelProcessingPhase.EFFECTIVE_MODEL,
442                 "Effective statement cannot be added in declared phase at: %s", getStatementSourceReference());
443
444         if (effective.isEmpty()) {
445             effective = new ArrayList<>(toAdd);
446         }
447     }
448
449     /**
450      * Create a new substatement at the specified offset.
451      *
452      * @param offset Substatement offset
453      * @param def definition context
454      * @param ref source reference
455      * @param argument statement argument
456      * @param <X> new substatement argument type
457      * @param <Y> new substatement declared type
458      * @param <Z> new substatement effective type
459      * @return A new substatement
460      */
461     @SuppressWarnings("checkstyle:methodTypeParameterName")
462     public final <X, Y extends DeclaredStatement<X>, Z extends EffectiveStatement<X, Y>>
463             StatementContextBase<X, Y, Z> createSubstatement(final int offset,
464                     final StatementDefinitionContext<X, Y, Z> def, final StatementSourceReference ref,
465                     final String argument) {
466         final ModelProcessingPhase inProgressPhase = getRoot().getSourceContext().getInProgressPhase();
467         checkState(inProgressPhase != ModelProcessingPhase.EFFECTIVE_MODEL,
468                 "Declared statement cannot be added in effective phase at: %s", getStatementSourceReference());
469
470         final Optional<StatementSupport<?, ?, ?>> implicitParent = definition.getImplicitParentFor(def.getPublicView());
471         if (implicitParent.isPresent()) {
472             return createImplicitParent(offset, implicitParent.get(), ref, argument).createSubstatement(offset, def,
473                     ref, argument);
474         }
475
476         final StatementContextBase<X, Y, Z> ret = new SubstatementContext<>(this, def, ref, argument);
477         substatements = substatements.put(offset, ret);
478         def.onStatementAdded(ret);
479         return ret;
480     }
481
482     private StatementContextBase<?, ?, ?> createImplicitParent(final int offset,
483             final StatementSupport<?, ?, ?> implicitParent, final StatementSourceReference ref, final String argument) {
484         final StatementDefinitionContext<?, ?, ?> def = new StatementDefinitionContext<>(implicitParent);
485         return createSubstatement(offset, def, ImplicitSubstatement.of(ref), argument);
486     }
487
488     public void appendImplicitStatement(final StatementSupport<?, ?, ?> statementToAdd) {
489         createSubstatement(substatements.capacity(), new StatementDefinitionContext<>(statementToAdd),
490                 ImplicitSubstatement.of(getStatementSourceReference()), null);
491     }
492
493     /**
494      * Lookup substatement by its offset in this statement.
495      *
496      * @param offset Substatement offset
497      * @return Substatement, or null if substatement does not exist.
498      */
499     final StatementContextBase<?, ?, ?> lookupSubstatement(final int offset) {
500         return substatements.get(offset);
501     }
502
503     final void setFullyDefined() {
504         this.fullyDefined = true;
505     }
506
507     final void resizeSubstatements(final int expectedSize) {
508         substatements = substatements.ensureCapacity(expectedSize);
509     }
510
511     final void walkChildren(final ModelProcessingPhase phase) {
512         checkState(fullyDefined);
513         substatements.values().forEach(stmt -> {
514             stmt.walkChildren(phase);
515             stmt.endDeclared(phase);
516         });
517     }
518
519     @Override
520     public D buildDeclared() {
521         checkArgument(completedPhase == ModelProcessingPhase.FULL_DECLARATION
522                 || completedPhase == ModelProcessingPhase.EFFECTIVE_MODEL);
523         if (declaredInstance == null) {
524             declaredInstance = definition().getFactory().createDeclared(this);
525         }
526         return declaredInstance;
527     }
528
529     @Override
530     public E buildEffective() {
531         if (effectiveInstance == null) {
532             effectiveInstance = definition().getFactory().createEffective(this);
533         }
534         return effectiveInstance;
535     }
536
537     /**
538      * tries to execute current {@link ModelProcessingPhase} of source parsing.
539      *
540      * @param phase
541      *            to be executed (completed)
542      * @return if phase was successfully completed
543      * @throws SourceException
544      *             when an error occurred in source parsing
545      */
546     boolean tryToCompletePhase(final ModelProcessingPhase phase) {
547
548         boolean finished = true;
549         final Collection<ContextMutation> openMutations = phaseMutation.get(phase);
550         if (!openMutations.isEmpty()) {
551             final Iterator<ContextMutation> it = openMutations.iterator();
552             while (it.hasNext()) {
553                 final ContextMutation current = it.next();
554                 if (current.isFinished()) {
555                     it.remove();
556                 } else {
557                     finished = false;
558                 }
559             }
560
561             if (openMutations.isEmpty()) {
562                 phaseMutation.removeAll(phase);
563                 if (phaseMutation.isEmpty()) {
564                     phaseMutation = ImmutableMultimap.of();
565                 }
566             }
567         }
568
569         for (final StatementContextBase<?, ?, ?> child : substatements.values()) {
570             finished &= child.tryToCompletePhase(phase);
571         }
572         for (final Mutable<?, ?, ?> child : effective) {
573             if (child instanceof StatementContextBase) {
574                 finished &= ((StatementContextBase<?, ?, ?>) child).tryToCompletePhase(phase);
575             }
576         }
577
578         if (finished) {
579             onPhaseCompleted(phase);
580             return true;
581         }
582         return false;
583     }
584
585     /**
586      * Occurs on end of {@link ModelProcessingPhase} of source parsing.
587      *
588      * @param phase
589      *            that was to be completed (finished)
590      * @throws SourceException
591      *             when an error occurred in source parsing
592      */
593     private void onPhaseCompleted(final ModelProcessingPhase phase) {
594         completedPhase = phase;
595
596         final Collection<OnPhaseFinished> listeners = phaseListeners.get(phase);
597         if (listeners.isEmpty()) {
598             return;
599         }
600
601         final Iterator<OnPhaseFinished> listener = listeners.iterator();
602         while (listener.hasNext()) {
603             final OnPhaseFinished next = listener.next();
604             if (next.phaseFinished(this, phase)) {
605                 listener.remove();
606             }
607         }
608
609         if (listeners.isEmpty()) {
610             phaseListeners.removeAll(phase);
611             if (phaseListeners.isEmpty()) {
612                 phaseListeners = ImmutableMultimap.of();
613             }
614         }
615     }
616
617     /**
618      * Ends declared section of current node.
619      */
620     void endDeclared(final ModelProcessingPhase phase) {
621         definition().onDeclarationFinished(this, phase);
622     }
623
624     /**
625      * Return the context in which this statement was defined.
626      *
627      * @return statement definition
628      */
629     protected final @NonNull StatementDefinitionContext<A, D, E> definition() {
630         return definition;
631     }
632
633     @Override
634     protected void checkLocalNamespaceAllowed(final Class<? extends IdentifierNamespace<?, ?>> type) {
635         definition().checkNamespaceAllowed(type);
636     }
637
638     @Override
639     protected <K, V, N extends IdentifierNamespace<K, V>> void onNamespaceElementAdded(final Class<N> type, final K key,
640             final V value) {
641         // definition().onNamespaceElementAdded(this, type, key, value);
642     }
643
644     final <K, V, N extends IdentifierNamespace<K, V>> void onNamespaceItemAddedAction(final Class<N> type, final K key,
645             final OnNamespaceItemAdded listener) {
646         final Object potential = getFromNamespace(type, key);
647         if (potential != null) {
648             LOG.trace("Listener on {} key {} satisfied immediately", type, key);
649             listener.namespaceItemAdded(this, type, key, potential);
650             return;
651         }
652
653         getBehaviour(type).addListener(new KeyedValueAddedListener<K>(this, key) {
654             @Override
655             void onValueAdded(final Object value) {
656                 listener.namespaceItemAdded(StatementContextBase.this, type, key, value);
657             }
658         });
659     }
660
661     final <K, V, N extends IdentifierNamespace<K, V>> void onNamespaceItemAddedAction(final Class<N> type,
662             final ModelProcessingPhase phase, final NamespaceKeyCriterion<K> criterion,
663             final OnNamespaceItemAdded listener) {
664         final Optional<Entry<K, V>> existing = getFromNamespace(type, criterion);
665         if (existing.isPresent()) {
666             final Entry<K, V> entry = existing.get();
667             LOG.debug("Listener on {} criterion {} found a pre-existing match: {}", type, criterion, entry);
668             waitForPhase(entry.getValue(), type, phase, criterion, listener);
669             return;
670         }
671
672         final NamespaceBehaviourWithListeners<K, V, N> behaviour = getBehaviour(type);
673         behaviour.addListener(new PredicateValueAddedListener<K, V>(this) {
674             @Override
675             boolean onValueAdded(final K key, final V value) {
676                 if (criterion.match(key)) {
677                     LOG.debug("Listener on {} criterion {} matched added key {}", type, criterion, key);
678                     waitForPhase(value, type, phase, criterion, listener);
679                     return true;
680                 }
681
682                 return false;
683             }
684         });
685     }
686
687     final <K, V, N extends IdentifierNamespace<K, V>> void selectMatch(final Class<N> type,
688             final NamespaceKeyCriterion<K> criterion, final OnNamespaceItemAdded listener) {
689         final Optional<Entry<K, V>> optMatch = getFromNamespace(type, criterion);
690         checkState(optMatch.isPresent(), "Failed to find a match for criterion %s in namespace %s node %s", criterion,
691             type, this);
692         final Entry<K, V> match = optMatch.get();
693         listener.namespaceItemAdded(StatementContextBase.this, type, match.getKey(), match.getValue());
694     }
695
696     final <K, V, N extends IdentifierNamespace<K, V>> void waitForPhase(final Object value, final Class<N> type,
697             final ModelProcessingPhase phase, final NamespaceKeyCriterion<K> criterion,
698             final OnNamespaceItemAdded listener) {
699         ((StatementContextBase<?, ? ,?>) value).addPhaseCompletedListener(phase,
700             (context, phaseCompleted) -> {
701                 selectMatch(type, criterion, listener);
702                 return true;
703             });
704     }
705
706     private <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviourWithListeners<K, V, N> getBehaviour(
707             final Class<N> type) {
708         final NamespaceBehaviour<K, V, N> behaviour = getBehaviourRegistry().getNamespaceBehaviour(type);
709         checkArgument(behaviour instanceof NamespaceBehaviourWithListeners, "Namespace %s does not support listeners",
710             type);
711
712         return (NamespaceBehaviourWithListeners<K, V, N>) behaviour;
713     }
714
715     @Override
716     public StatementDefinition getPublicDefinition() {
717         return definition().getPublicView();
718     }
719
720     @Override
721     public ModelActionBuilder newInferenceAction(final ModelProcessingPhase phase) {
722         return getRoot().getSourceContext().newInferenceAction(phase);
723     }
724
725     private static <T> Multimap<ModelProcessingPhase, T> newMultimap() {
726         return Multimaps.newListMultimap(new EnumMap<>(ModelProcessingPhase.class), () -> new ArrayList<>(1));
727     }
728
729     /**
730      * Adds {@link OnPhaseFinished} listener for a {@link ModelProcessingPhase} end. If the base has already completed
731      * the listener is notified immediately.
732      *
733      * @param phase requested completion phase
734      * @param listener listener to invoke
735      * @throws NullPointerException if any of the arguments is null
736      */
737     void addPhaseCompletedListener(final ModelProcessingPhase phase, final OnPhaseFinished listener) {
738         checkNotNull(phase, "Statement context processing phase cannot be null at: %s", getStatementSourceReference());
739         checkNotNull(listener, "Statement context phase listener cannot be null at: %s", getStatementSourceReference());
740
741         ModelProcessingPhase finishedPhase = completedPhase;
742         while (finishedPhase != null) {
743             if (phase.equals(finishedPhase)) {
744                 listener.phaseFinished(this, finishedPhase);
745                 return;
746             }
747             finishedPhase = finishedPhase.getPreviousPhase();
748         }
749         if (phaseListeners.isEmpty()) {
750             phaseListeners = newMultimap();
751         }
752
753         phaseListeners.put(phase, listener);
754     }
755
756     /**
757      * Adds a {@link ContextMutation} to a {@link ModelProcessingPhase}.
758      *
759      * @throws IllegalStateException
760      *             when the mutation was registered after phase was completed
761      */
762     void addMutation(final ModelProcessingPhase phase, final ContextMutation mutation) {
763         ModelProcessingPhase finishedPhase = completedPhase;
764         while (finishedPhase != null) {
765             checkState(!phase.equals(finishedPhase), "Mutation registered after phase was completed at: %s",
766                 getStatementSourceReference());
767             finishedPhase = finishedPhase.getPreviousPhase();
768         }
769
770         if (phaseMutation.isEmpty()) {
771             phaseMutation = newMultimap();
772         }
773         phaseMutation.put(phase, mutation);
774     }
775
776     @Override
777     public <K, KT extends K, N extends StatementNamespace<K, ?, ?>> void addContext(final Class<N> namespace,
778             final KT key,final StmtContext<?, ?, ?> stmt) {
779         addContextToNamespace(namespace, key, stmt);
780     }
781
782     @Override
783     public <X, Y extends DeclaredStatement<X>, Z extends EffectiveStatement<X, Y>> Mutable<X, Y, Z> childCopyOf(
784             final StmtContext<X, Y, Z> stmt, final CopyType type, final QNameModule targetModule) {
785         checkState(stmt.getCompletedPhase() == ModelProcessingPhase.EFFECTIVE_MODEL,
786                 "Attempted to copy statement %s which has completed phase %s", stmt, stmt.getCompletedPhase());
787
788         checkArgument(stmt instanceof SubstatementContext, "Unsupported statement %s", stmt);
789
790         final SubstatementContext<X, Y, Z> original = (SubstatementContext<X, Y, Z>)stmt;
791         final Optional<StatementSupport<?, ?, ?>> implicitParent = definition.getImplicitParentFor(
792             original.getPublicDefinition());
793
794         final SubstatementContext<X, Y, Z> result;
795         final SubstatementContext<X, Y, Z> copy;
796
797         if (implicitParent.isPresent()) {
798             final StatementDefinitionContext<?, ?, ?> def = new StatementDefinitionContext<>(implicitParent.get());
799             result = new SubstatementContext(this, def, original.getSourceReference(),
800                 original.rawStatementArgument(), original.getStatementArgument(), type);
801
802             final CopyType childCopyType;
803             switch (type) {
804                 case ADDED_BY_AUGMENTATION:
805                     childCopyType = CopyType.ORIGINAL;
806                     break;
807                 case ADDED_BY_USES_AUGMENTATION:
808                     childCopyType = CopyType.ADDED_BY_USES;
809                     break;
810                 case ADDED_BY_USES:
811                 case ORIGINAL:
812                 default:
813                     childCopyType = type;
814             }
815
816             copy = new SubstatementContext<>(original, result, childCopyType, targetModule);
817             result.addEffectiveSubstatement(copy);
818         } else {
819             result = copy = new SubstatementContext<>(original, this, type, targetModule);
820         }
821
822         original.definition().onStatementAdded(copy);
823         original.copyTo(copy, type, targetModule);
824         return result;
825     }
826
827     @Override
828     public @NonNull StatementDefinition getDefinition() {
829         return getPublicDefinition();
830     }
831
832     @Override
833     public @NonNull StatementSourceReference getSourceReference() {
834         return getStatementSourceReference();
835     }
836
837     @Override
838     public boolean isFullyDefined() {
839         return fullyDefined;
840     }
841
842     @Beta
843     public final boolean hasImplicitParentSupport() {
844         return definition.getFactory() instanceof ImplicitParentAwareStatementSupport;
845     }
846
847     @Beta
848     public final StatementContextBase<?, ?, ?> wrapWithImplicit(final StatementContextBase<?, ?, ?> original) {
849         final Optional<StatementSupport<?, ?, ?>> optImplicit = definition.getImplicitParentFor(
850             original.getPublicDefinition());
851         if (!optImplicit.isPresent()) {
852             return original;
853         }
854
855         final StatementDefinitionContext<?, ?, ?> def = new StatementDefinitionContext<>(optImplicit.get());
856         final CopyType type = original.getCopyHistory().getLastOperation();
857         final SubstatementContext<?, ?, ?> result = new SubstatementContext(original.getParentContext(), def,
858             original.getStatementSourceReference(), original.rawStatementArgument(), original.getStatementArgument(),
859             type);
860
861         result.addEffectiveSubstatement(new SubstatementContext<>(original, result));
862         result.setCompletedPhase(original.getCompletedPhase());
863         return result;
864     }
865
866     /**
867      * Config statements are not all that common which means we are performing a recursive search towards the root
868      * every time {@link #isConfiguration()} is invoked. This is quite expensive because it causes a linear search
869      * for the (usually non-existent) config statement.
870      *
871      * <p>
872      * This method maintains a resolution cache, so once we have returned a result, we will keep on returning the same
873      * result without performing any lookups, solely to support {@link SubstatementContext#isConfiguration()}.
874      *
875      * <p>
876      * Note: use of this method implies that {@link #isIgnoringConfig()} is realized with
877      *       {@link #isIgnoringConfig(StatementContextBase)}.
878      */
879     final boolean isConfiguration(final StatementContextBase<?, ?, ?> parent) {
880         final int fl = flags & SET_CONFIGURATION;
881         if (fl != 0) {
882             return fl == SET_CONFIGURATION;
883         }
884         if (isIgnoringConfig(parent)) {
885             // Note: SET_CONFIGURATION has been stored in flags
886             return true;
887         }
888
889         final StmtContext<Boolean, ?, ?> configStatement = StmtContextUtils.findFirstSubstatement(this,
890             ConfigStatement.class);
891         final boolean isConfig;
892         if (configStatement != null) {
893             isConfig = configStatement.coerceStatementArgument();
894             if (isConfig) {
895                 // Validity check: if parent is config=false this cannot be a config=true
896                 InferenceException.throwIf(!parent.isConfiguration(), getStatementSourceReference(),
897                         "Parent node has config=false, this node must not be specifed as config=true");
898             }
899         } else {
900             // If "config" statement is not specified, the default is the same as the parent's "config" value.
901             isConfig = parent.isConfiguration();
902         }
903
904         // Resolved, make sure we cache this return
905         flags |= isConfig ? SET_CONFIGURATION : HAVE_CONFIGURATION;
906         return isConfig;
907     }
908
909     protected abstract boolean isIgnoringConfig();
910
911     /**
912      * This method maintains a resolution cache for ignore config, so once we have returned a result, we will
913      * keep on returning the same result without performing any lookups. Exists only to support
914      * {@link SubstatementContext#isIgnoringConfig()}.
915      *
916      * <p>
917      * Note: use of this method implies that {@link #isConfiguration()} is realized with
918      *       {@link #isConfiguration(StatementContextBase)}.
919      */
920     final boolean isIgnoringConfig(final StatementContextBase<?, ?, ?> parent) {
921         final int fl = flags & SET_IGNORE_CONFIG;
922         if (fl != 0) {
923             return fl == SET_IGNORE_CONFIG;
924         }
925         if (definition().isIgnoringConfig() || parent.isIgnoringConfig()) {
926             flags |= SET_IGNORE_CONFIG;
927             return true;
928         }
929
930         flags |= HAVE_IGNORE_CONFIG;
931         return false;
932     }
933
934     protected abstract boolean isIgnoringIfFeatures();
935
936     /**
937      * This method maintains a resolution cache for ignore if-feature, so once we have returned a result, we will
938      * keep on returning the same result without performing any lookups. Exists only to support
939      * {@link SubstatementContext#isIgnoringIfFeatures()}.
940      */
941     final boolean isIgnoringIfFeatures(final StatementContextBase<?, ?, ?> parent) {
942         final int fl = flags & SET_IGNORE_IF_FEATURE;
943         if (fl != 0) {
944             return fl == SET_IGNORE_IF_FEATURE;
945         }
946         if (definition().isIgnoringIfFeatures() || parent.isIgnoringIfFeatures()) {
947             flags |= SET_IGNORE_IF_FEATURE;
948             return true;
949         }
950
951         flags |= HAVE_IGNORE_IF_FEATURE;
952         return false;
953     }
954
955     final void copyTo(final StatementContextBase<?, ?, ?> target, final CopyType typeOfCopy,
956             @Nullable final QNameModule targetModule) {
957         final Collection<Mutable<?, ?, ?>> buffer = new ArrayList<>(substatements.size() + effective.size());
958
959         for (final Mutable<?, ?, ?> stmtContext : substatements.values()) {
960             if (stmtContext.isSupportedByFeatures()) {
961                 copySubstatement(stmtContext, target, typeOfCopy, targetModule, buffer);
962             }
963         }
964
965         for (final Mutable<?, ?, ?> stmtContext : effective) {
966             copySubstatement(stmtContext, target, typeOfCopy, targetModule, buffer);
967         }
968
969         target.addEffectiveSubstatements(buffer);
970     }
971
972     private void copySubstatement(final Mutable<?, ?, ?> stmtContext,  final Mutable<?, ?, ?> target,
973             final CopyType typeOfCopy, final QNameModule newQNameModule, final Collection<Mutable<?, ?, ?>> buffer) {
974         if (needToCopyByUses(stmtContext)) {
975             final Mutable<?, ?, ?> copy = target.childCopyOf(stmtContext, typeOfCopy, newQNameModule);
976             LOG.debug("Copying substatement {} for {} as {}", stmtContext, this, copy);
977             buffer.add(copy);
978         } else if (isReusedByUses(stmtContext)) {
979             LOG.debug("Reusing substatement {} for {}", stmtContext, this);
980             buffer.add(stmtContext);
981         } else {
982             LOG.debug("Skipping statement {}", stmtContext);
983         }
984     }
985
986     // FIXME: revise this, as it seems to be wrong
987     private static final ImmutableSet<YangStmtMapping> NOCOPY_FROM_GROUPING_SET = ImmutableSet.of(
988         YangStmtMapping.DESCRIPTION,
989         YangStmtMapping.REFERENCE,
990         YangStmtMapping.STATUS);
991     private static final ImmutableSet<YangStmtMapping> REUSED_DEF_SET = ImmutableSet.of(
992         YangStmtMapping.TYPE,
993         YangStmtMapping.TYPEDEF,
994         YangStmtMapping.USES);
995
996     private static boolean needToCopyByUses(final StmtContext<?, ?, ?> stmtContext) {
997         final StatementDefinition def = stmtContext.getPublicDefinition();
998         if (REUSED_DEF_SET.contains(def)) {
999             LOG.debug("Will reuse {} statement {}", def, stmtContext);
1000             return false;
1001         }
1002         if (NOCOPY_FROM_GROUPING_SET.contains(def)) {
1003             return !YangStmtMapping.GROUPING.equals(stmtContext.coerceParentContext().getPublicDefinition());
1004         }
1005
1006         LOG.debug("Will copy {} statement {}", def, stmtContext);
1007         return true;
1008     }
1009
1010     private static boolean isReusedByUses(final StmtContext<?, ?, ?> stmtContext) {
1011         return REUSED_DEF_SET.contains(stmtContext.getPublicDefinition());
1012     }
1013
1014     @Override
1015     public final String toString() {
1016         return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString();
1017     }
1018
1019     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
1020         return toStringHelper.add("definition", definition).add("rawArgument", rawArgument);
1021     }
1022 }