Allow StatementWriter to be stateful
[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 com.google.common.base.MoreObjects;
11 import com.google.common.base.MoreObjects.ToStringHelper;
12 import com.google.common.base.Preconditions;
13 import com.google.common.collect.ImmutableCollection;
14 import com.google.common.collect.ImmutableList;
15 import com.google.common.collect.ImmutableMultimap;
16 import com.google.common.collect.ImmutableSet;
17 import com.google.common.collect.Multimap;
18 import com.google.common.collect.Multimaps;
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.EnumMap;
23 import java.util.EventListener;
24 import java.util.Iterator;
25 import java.util.Map.Entry;
26 import java.util.Optional;
27 import java.util.Set;
28 import javax.annotation.Nonnull;
29 import javax.annotation.Nullable;
30 import org.eclipse.jdt.annotation.NonNull;
31 import org.opendaylight.yangtools.util.OptionalBoolean;
32 import org.opendaylight.yangtools.yang.common.QName;
33 import org.opendaylight.yangtools.yang.common.QNameModule;
34 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
35 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
36 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
37 import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
38 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
39 import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
40 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory;
41 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
42 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
43 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
44 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
45 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceKeyCriterion;
46 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
47 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
48 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
49 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
50 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
51 import org.opendaylight.yangtools.yang.parser.spi.source.ImplicitSubstatement;
52 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
53 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
54 import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter.ResumedStatement;
55 import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace;
56 import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace.SupportedFeatures;
57 import org.opendaylight.yangtools.yang.parser.stmt.reactor.NamespaceBehaviourWithListeners.KeyedValueAddedListener;
58 import org.opendaylight.yangtools.yang.parser.stmt.reactor.NamespaceBehaviourWithListeners.PredicateValueAddedListener;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62 public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
63         extends NamespaceStorageSupport implements Mutable<A, D, E>, ResumedStatement {
64     /**
65      * Event listener when an item is added to model namespace.
66      */
67     interface OnNamespaceItemAdded extends EventListener {
68         /**
69          * Invoked whenever a new item is added to a namespace.
70          */
71         void namespaceItemAdded(StatementContextBase<?, ?, ?> context, Class<?> namespace, Object key, Object value);
72     }
73
74     /**
75      * Event listener when a parsing {@link ModelProcessingPhase} is completed.
76      */
77     interface OnPhaseFinished extends EventListener {
78         /**
79          * Invoked whenever a processing phase has finished.
80          */
81         boolean phaseFinished(StatementContextBase<?, ?, ?> context, ModelProcessingPhase finishedPhase);
82     }
83
84     /**
85      * Interface for all mutations within an {@link ModelActionBuilder.InferenceAction}.
86      */
87     interface ContextMutation {
88
89         boolean isFinished();
90     }
91
92     private static final Logger LOG = LoggerFactory.getLogger(StatementContextBase.class);
93
94     private final StatementDefinitionContext<A, D, E> definition;
95     private final StatementSourceReference statementDeclSource;
96     private final StmtContext<?, ?, ?> originalCtx;
97     private final CopyHistory copyHistory;
98     private final String rawArgument;
99
100     private Multimap<ModelProcessingPhase, OnPhaseFinished> phaseListeners = ImmutableMultimap.of();
101     private Multimap<ModelProcessingPhase, ContextMutation> phaseMutation = ImmutableMultimap.of();
102     private Collection<Mutable<?, ?, ?>> effective = ImmutableList.of();
103     private Collection<StmtContext<?, ?, ?>> effectOfStatement = ImmutableList.of();
104     private StatementMap substatements = StatementMap.empty();
105
106     private boolean isSupportedToBuildEffective = true;
107     private ModelProcessingPhase completedPhase = null;
108     private D declaredInstance;
109     private E effectiveInstance;
110
111     // BooleanFields value
112     private byte supportedByFeatures;
113
114     private boolean fullyDefined;
115
116     StatementContextBase(final StatementDefinitionContext<A, D, E> def, final StatementSourceReference ref,
117             final String rawArgument) {
118         this.definition = Preconditions.checkNotNull(def);
119         this.statementDeclSource = Preconditions.checkNotNull(ref);
120         this.rawArgument = def.internArgument(rawArgument);
121         this.copyHistory = CopyHistory.original();
122         this.originalCtx = null;
123     }
124
125     StatementContextBase(final StatementContextBase<A, D, E> original, final CopyType copyType) {
126         this.definition = Preconditions.checkNotNull(original.definition,
127                 "Statement context definition cannot be null copying from: %s", original.getStatementSourceReference());
128         this.statementDeclSource = Preconditions.checkNotNull(original.statementDeclSource,
129                 "Statement context statementDeclSource cannot be null copying from: %s",
130                 original.getStatementSourceReference());
131         this.rawArgument = original.rawArgument;
132         this.copyHistory = CopyHistory.of(copyType, original.getCopyHistory());
133         this.originalCtx = original.getOriginalCtx().orElse(original);
134     }
135
136     @Override
137     public Collection<? extends StmtContext<?, ?, ?>> getEffectOfStatement() {
138         return effectOfStatement;
139     }
140
141     @Override
142     public void addAsEffectOfStatement(final StmtContext<?, ?, ?> ctx) {
143         if (effectOfStatement.isEmpty()) {
144             effectOfStatement = new ArrayList<>(1);
145         }
146         effectOfStatement.add(ctx);
147     }
148
149     @Override
150     public void addAsEffectOfStatement(final Collection<? extends StmtContext<?, ?, ?>> ctxs) {
151         if (ctxs.isEmpty()) {
152             return;
153         }
154
155         if (effectOfStatement.isEmpty()) {
156             effectOfStatement = new ArrayList<>(ctxs.size());
157         }
158         effectOfStatement.addAll(ctxs);
159     }
160
161     @Override
162     public boolean isSupportedByFeatures() {
163         if (OptionalBoolean.isPresent(supportedByFeatures)) {
164             return OptionalBoolean.get(supportedByFeatures);
165         }
166
167         if (isIgnoringIfFeatures()) {
168             supportedByFeatures = OptionalBoolean.of(true);
169             return true;
170         }
171
172         final boolean isParentSupported = isParentSupportedByFeatures();
173         /*
174          * If parent is not supported, then this context is also not supported.
175          * So we do not need to check if-features statements of this context and
176          * we can return false immediately.
177          */
178         if (!isParentSupported) {
179             supportedByFeatures = OptionalBoolean.of(false);
180             return false;
181         }
182
183         /*
184          * If parent is supported, we need to check if-features statements of
185          * this context.
186          */
187         // If the set of supported features has not been provided, all features are supported by default.
188         final Set<QName> supportedFeatures = getFromNamespace(SupportedFeaturesNamespace.class,
189                 SupportedFeatures.SUPPORTED_FEATURES);
190         final boolean ret = supportedFeatures == null ? true
191                 : StmtContextUtils.checkFeatureSupport(this, supportedFeatures);
192
193         supportedByFeatures = OptionalBoolean.of(ret);
194         return ret;
195     }
196
197     protected abstract boolean isParentSupportedByFeatures();
198
199     protected abstract boolean isIgnoringIfFeatures();
200
201     protected abstract boolean isIgnoringConfig();
202
203     @Override
204     public boolean isSupportedToBuildEffective() {
205         return isSupportedToBuildEffective;
206     }
207
208     @Override
209     public void setIsSupportedToBuildEffective(final boolean isSupportedToBuildEffective) {
210         this.isSupportedToBuildEffective = isSupportedToBuildEffective;
211     }
212
213     @Override
214     public CopyHistory getCopyHistory() {
215         return copyHistory;
216     }
217
218     @Override
219     public Optional<StmtContext<?, ?, ?>> getOriginalCtx() {
220         return Optional.ofNullable(originalCtx);
221     }
222
223     @Override
224     public ModelProcessingPhase getCompletedPhase() {
225         return completedPhase;
226     }
227
228     @Override
229     public void setCompletedPhase(final ModelProcessingPhase completedPhase) {
230         this.completedPhase = completedPhase;
231     }
232
233     @Override
234     public abstract StatementContextBase<?, ?, ?> getParentContext();
235
236     /**
237      * Returns the model root for this statement.
238      *
239      * @return root context of statement
240      */
241     @Nonnull
242     @Override
243     public abstract RootStatementContext<?, ?, ?> getRoot();
244
245     /**
246      * Returns the origin of the statement.
247      *
248      * @return origin of statement
249      */
250     @Nonnull
251     @Override
252     public StatementSource getStatementSource() {
253         return statementDeclSource.getStatementSource();
254     }
255
256     /**
257      * Returns a reference to statement source.
258      *
259      * @return reference of statement source
260      */
261     @Nonnull
262     @Override
263     public StatementSourceReference getStatementSourceReference() {
264         return statementDeclSource;
265     }
266
267     @Override
268     public final String rawStatementArgument() {
269         return rawArgument;
270     }
271
272     @Nonnull
273     @Override
274     public Collection<? extends StmtContext<?, ?, ?>> declaredSubstatements() {
275         return substatements.values();
276     }
277
278     @Nonnull
279     @Override
280     public Collection<? extends Mutable<?, ?, ?>> mutableDeclaredSubstatements() {
281         return substatements.values();
282     }
283
284     @Override
285     public Collection<? extends StmtContext<?, ?, ?>> effectiveSubstatements() {
286         return mutableEffectiveSubstatements();
287     }
288
289     @Nonnull
290     @Override
291     public Collection<? extends Mutable<?, ?, ?>> mutableEffectiveSubstatements() {
292         if (effective instanceof ImmutableCollection) {
293             return effective;
294         }
295
296         return Collections.unmodifiableCollection(effective);
297     }
298
299     public void removeStatementsFromEffectiveSubstatements(
300             final Collection<? extends StmtContext<?, ?, ?>> statements) {
301         if (!effective.isEmpty()) {
302             effective.removeAll(statements);
303             shrinkEffective();
304         }
305     }
306
307     private void shrinkEffective() {
308         if (effective.isEmpty()) {
309             effective = ImmutableList.of();
310         }
311     }
312
313     public void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef) {
314         if (effective.isEmpty()) {
315             return;
316         }
317
318         final Iterator<? extends StmtContext<?, ?, ?>> iterator = effective.iterator();
319         while (iterator.hasNext()) {
320             final StmtContext<?, ?, ?> next = iterator.next();
321             if (statementDef.equals(next.getPublicDefinition())) {
322                 iterator.remove();
323             }
324         }
325
326         shrinkEffective();
327     }
328
329     /**
330      * Removes a statement context from the effective substatements based on its statement definition (i.e statement
331      * keyword) and raw (in String form) statement argument. The statement context is removed only if both statement
332      * definition and statement argument match with one of the effective substatements' statement definition
333      * and argument.
334      *
335      * <p>
336      * If the statementArg parameter is null, the statement context is removed based only on its statement definition.
337      *
338      * @param statementDef statement definition of the statement context to remove
339      * @param statementArg statement argument of the statement context to remove
340      */
341     public void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef,
342             final String statementArg) {
343         if (statementArg == null) {
344             removeStatementFromEffectiveSubstatements(statementDef);
345         }
346
347         if (effective.isEmpty()) {
348             return;
349         }
350
351         final Iterator<Mutable<?, ?, ?>> iterator = effective.iterator();
352         while (iterator.hasNext()) {
353             final Mutable<?, ?, ?> next = iterator.next();
354             if (statementDef.equals(next.getPublicDefinition()) && statementArg.equals(next.rawStatementArgument())) {
355                 iterator.remove();
356             }
357         }
358
359         shrinkEffective();
360     }
361
362     /**
363      * Adds an effective statement to collection of substatements.
364      *
365      * @param substatement substatement
366      * @throws IllegalStateException
367      *             if added in declared phase
368      * @throws NullPointerException
369      *             if statement parameter is null
370      */
371     public void addEffectiveSubstatement(final Mutable<?, ?, ?> substatement) {
372         beforeAddEffectiveStatement(1);
373         effective.add(substatement);
374     }
375
376     /**
377      * Adds an effective statement to collection of substatements.
378      *
379      * @param statements substatements
380      * @throws IllegalStateException
381      *             if added in declared phase
382      * @throws NullPointerException
383      *             if statement parameter is null
384      */
385     public void addEffectiveSubstatements(final Collection<? extends Mutable<?, ?, ?>> statements) {
386         if (statements.isEmpty()) {
387             return;
388         }
389
390         statements.forEach(Preconditions::checkNotNull);
391         beforeAddEffectiveStatement(statements.size());
392         effective.addAll(statements);
393     }
394
395     private void beforeAddEffectiveStatement(final int toAdd) {
396         final ModelProcessingPhase inProgressPhase = getRoot().getSourceContext().getInProgressPhase();
397         Preconditions.checkState(inProgressPhase == ModelProcessingPhase.FULL_DECLARATION
398                 || inProgressPhase == ModelProcessingPhase.EFFECTIVE_MODEL,
399                 "Effective statement cannot be added in declared phase at: %s", getStatementSourceReference());
400
401         if (effective.isEmpty()) {
402             effective = new ArrayList<>(toAdd);
403         }
404     }
405
406     /**
407      * Create a new substatement at the specified offset.
408      *
409      * @param offset Substatement offset
410      * @param def definition context
411      * @param ref source reference
412      * @param argument statement argument
413      * @return A new substatement
414      */
415     @SuppressWarnings("checkstyle:methodTypeParameterName")
416     public final <CA, CD extends DeclaredStatement<CA>, CE extends EffectiveStatement<CA, CD>>
417             StatementContextBase<CA, CD, CE> createSubstatement(final int offset,
418                     final StatementDefinitionContext<CA, CD, CE> def, final StatementSourceReference ref,
419                     final String argument) {
420         final ModelProcessingPhase inProgressPhase = getRoot().getSourceContext().getInProgressPhase();
421         Preconditions.checkState(inProgressPhase != ModelProcessingPhase.EFFECTIVE_MODEL,
422                 "Declared statement cannot be added in effective phase at: %s", getStatementSourceReference());
423
424         final Optional<StatementSupport<?, ?, ?>> implicitParent = definition.getImplicitParentFor(def.getPublicView());
425         if (implicitParent.isPresent()) {
426             return createImplicitParent(offset, implicitParent.get(), ref, argument).createSubstatement(offset, def,
427                     ref, argument);
428         }
429
430         final StatementContextBase<CA, CD, CE> ret = new SubstatementContext<>(this, def, ref, argument);
431         substatements = substatements.put(offset, ret);
432         def.onStatementAdded(ret);
433         return ret;
434     }
435
436     private StatementContextBase<?, ?, ?> createImplicitParent(final int offset,
437             final StatementSupport<?, ?, ?> implicitParent, final StatementSourceReference ref, final String argument) {
438         final StatementDefinitionContext<?, ?, ?> def = new StatementDefinitionContext<>(implicitParent);
439         return createSubstatement(offset, def, ImplicitSubstatement.of(ref), argument);
440     }
441
442     /**
443      * Lookup substatement by its offset in this statement.
444      *
445      * @param offset Substatement offset
446      * @return Substatement, or null if substatement does not exist.
447      */
448     final StatementContextBase<?, ?, ?> lookupSubstatement(final int offset) {
449         return substatements.get(offset);
450     }
451
452     final void setFullyDefined() {
453         this.fullyDefined = true;
454     }
455
456     final void walkChildren(final ModelProcessingPhase phase) {
457         Preconditions.checkState(fullyDefined);
458         substatements.values().forEach(stmt -> {
459             stmt.walkChildren(phase);
460             stmt.endDeclared(phase);
461         });
462     }
463
464     @Override
465     public D buildDeclared() {
466         Preconditions.checkArgument(completedPhase == ModelProcessingPhase.FULL_DECLARATION
467                 || completedPhase == ModelProcessingPhase.EFFECTIVE_MODEL);
468         if (declaredInstance == null) {
469             declaredInstance = definition().getFactory().createDeclared(this);
470         }
471         return declaredInstance;
472     }
473
474     @Override
475     public E buildEffective() {
476         if (effectiveInstance == null) {
477             effectiveInstance = definition().getFactory().createEffective(this);
478         }
479         return effectiveInstance;
480     }
481
482     /**
483      * tries to execute current {@link ModelProcessingPhase} of source parsing.
484      *
485      * @param phase
486      *            to be executed (completed)
487      * @return if phase was successfully completed
488      * @throws SourceException
489      *             when an error occured in source parsing
490      */
491     boolean tryToCompletePhase(final ModelProcessingPhase phase) {
492
493         boolean finished = true;
494         final Collection<ContextMutation> openMutations = phaseMutation.get(phase);
495         if (!openMutations.isEmpty()) {
496             final Iterator<ContextMutation> it = openMutations.iterator();
497             while (it.hasNext()) {
498                 final ContextMutation current = it.next();
499                 if (current.isFinished()) {
500                     it.remove();
501                 } else {
502                     finished = false;
503                 }
504             }
505
506             if (openMutations.isEmpty()) {
507                 phaseMutation.removeAll(phase);
508                 if (phaseMutation.isEmpty()) {
509                     phaseMutation = ImmutableMultimap.of();
510                 }
511             }
512         }
513
514         for (final StatementContextBase<?, ?, ?> child : substatements.values()) {
515             finished &= child.tryToCompletePhase(phase);
516         }
517         for (final Mutable<?, ?, ?> child : effective) {
518             if (child instanceof StatementContextBase) {
519                 finished &= ((StatementContextBase<?, ?, ?>) child).tryToCompletePhase(phase);
520             }
521         }
522
523         if (finished) {
524             onPhaseCompleted(phase);
525             return true;
526         }
527         return false;
528     }
529
530     /**
531      * Occurs on end of {@link ModelProcessingPhase} of source parsing.
532      *
533      * @param phase
534      *            that was to be completed (finished)
535      * @throws SourceException
536      *             when an error occurred in source parsing
537      */
538     private void onPhaseCompleted(final ModelProcessingPhase phase) {
539         completedPhase = phase;
540
541         final Collection<OnPhaseFinished> listeners = phaseListeners.get(phase);
542         if (listeners.isEmpty()) {
543             return;
544         }
545
546         final Iterator<OnPhaseFinished> listener = listeners.iterator();
547         while (listener.hasNext()) {
548             final OnPhaseFinished next = listener.next();
549             if (next.phaseFinished(this, phase)) {
550                 listener.remove();
551             }
552         }
553
554         if (listeners.isEmpty()) {
555             phaseListeners.removeAll(phase);
556             if (phaseListeners.isEmpty()) {
557                 phaseListeners = ImmutableMultimap.of();
558             }
559         }
560     }
561
562     /**
563      * Ends declared section of current node.
564      */
565     void endDeclared(final ModelProcessingPhase phase) {
566         definition().onDeclarationFinished(this, phase);
567     }
568
569     /**
570      * Return the context in which this statement was defined.
571      *
572      * @return statement definition
573      */
574     protected final StatementDefinitionContext<A, D, E> definition() {
575         return definition;
576     }
577
578     @Override
579     protected void checkLocalNamespaceAllowed(final Class<? extends IdentifierNamespace<?, ?>> type) {
580         definition().checkNamespaceAllowed(type);
581     }
582
583     @Override
584     protected <K, V, N extends IdentifierNamespace<K, V>> void onNamespaceElementAdded(final Class<N> type, final K key,
585             final V value) {
586         // definition().onNamespaceElementAdded(this, type, key, value);
587     }
588
589     final <K, V, N extends IdentifierNamespace<K, V>> void onNamespaceItemAddedAction(final Class<N> type, final K key,
590             final OnNamespaceItemAdded listener) {
591         final Object potential = getFromNamespace(type, key);
592         if (potential != null) {
593             LOG.trace("Listener on {} key {} satisfied immediately", type, key);
594             listener.namespaceItemAdded(this, type, key, potential);
595             return;
596         }
597
598         getBehaviour(type).addListener(new KeyedValueAddedListener<K>(this, key) {
599             @Override
600             void onValueAdded(final Object value) {
601                 listener.namespaceItemAdded(StatementContextBase.this, type, key, value);
602             }
603         });
604     }
605
606     final <K, V, N extends IdentifierNamespace<K, V>> void onNamespaceItemAddedAction(final Class<N> type,
607             final ModelProcessingPhase phase, final NamespaceKeyCriterion<K> criterion,
608             final OnNamespaceItemAdded listener) {
609         final Optional<Entry<K, V>> existing = getFromNamespace(type, criterion);
610         if (existing.isPresent()) {
611             final Entry<K, V> entry = existing.get();
612             LOG.debug("Listener on {} criterion {} found a pre-existing match: {}", type, criterion, entry);
613             waitForPhase(entry.getValue(), type, phase, criterion, listener);
614             return;
615         }
616
617         final NamespaceBehaviourWithListeners<K, V, N> behaviour = getBehaviour(type);
618         behaviour.addListener(new PredicateValueAddedListener<K, V>(this) {
619             @Override
620             boolean onValueAdded(final K key, final V value) {
621                 if (criterion.match(key)) {
622                     LOG.debug("Listener on {} criterion {} matched added key {}", type, criterion, key);
623                     waitForPhase(value, type, phase, criterion, listener);
624                     return true;
625                 }
626
627                 return false;
628             }
629         });
630     }
631
632     final <K, V, N extends IdentifierNamespace<K, V>> void selectMatch(final Class<N> type,
633             final NamespaceKeyCriterion<K> criterion, final OnNamespaceItemAdded listener) {
634         final Optional<Entry<K, V>> optMatch = getFromNamespace(type, criterion);
635         Preconditions.checkState(optMatch.isPresent(),
636             "Failed to find a match for criterion %s in namespace %s node %s", criterion, type, this);
637         final Entry<K, V> match = optMatch.get();
638         listener.namespaceItemAdded(StatementContextBase.this, type, match.getKey(), match.getValue());
639     }
640
641     final <K, V, N extends IdentifierNamespace<K, V>> void waitForPhase(final Object value, final Class<N> type,
642             final ModelProcessingPhase phase, final NamespaceKeyCriterion<K> criterion,
643             final OnNamespaceItemAdded listener) {
644         ((StatementContextBase<?, ? ,?>) value).addPhaseCompletedListener(phase,
645             (context, phaseCompleted) -> {
646                 selectMatch(type, criterion, listener);
647                 return true;
648             });
649     }
650
651     private <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviourWithListeners<K, V, N> getBehaviour(
652             final Class<N> type) {
653         final NamespaceBehaviour<K, V, N> behaviour = getBehaviourRegistry().getNamespaceBehaviour(type);
654         Preconditions.checkArgument(behaviour instanceof NamespaceBehaviourWithListeners,
655             "Namespace %s does not support listeners", type);
656
657         return (NamespaceBehaviourWithListeners<K, V, N>) behaviour;
658     }
659
660     /**
661      * See {@link StatementSupport#getPublicView()}.
662      */
663     @Nonnull
664     @Override
665     public StatementDefinition getPublicDefinition() {
666         return definition().getPublicView();
667     }
668
669     @Override
670     public ModelActionBuilder newInferenceAction(final ModelProcessingPhase phase) {
671         return getRoot().getSourceContext().newInferenceAction(phase);
672     }
673
674     private static <T> Multimap<ModelProcessingPhase, T> newMultimap() {
675         return Multimaps.newListMultimap(new EnumMap<>(ModelProcessingPhase.class), () -> new ArrayList<>(1));
676     }
677
678     /**
679      * Adds {@link OnPhaseFinished} listener for a {@link ModelProcessingPhase} end. If the base has already completed
680      * the listener is notified immediately.
681      *
682      * @param phase requested completion phase
683      * @param listener listener to invoke
684      * @throws NullPointerException if any of the arguments is null
685      */
686     void addPhaseCompletedListener(final ModelProcessingPhase phase, final OnPhaseFinished listener) {
687         Preconditions.checkNotNull(phase, "Statement context processing phase cannot be null at: %s",
688                 getStatementSourceReference());
689         Preconditions.checkNotNull(listener, "Statement context phase listener cannot be null at: %s",
690                 getStatementSourceReference());
691
692         ModelProcessingPhase finishedPhase = completedPhase;
693         while (finishedPhase != null) {
694             if (phase.equals(finishedPhase)) {
695                 listener.phaseFinished(this, finishedPhase);
696                 return;
697             }
698             finishedPhase = finishedPhase.getPreviousPhase();
699         }
700         if (phaseListeners.isEmpty()) {
701             phaseListeners = newMultimap();
702         }
703
704         phaseListeners.put(phase, listener);
705     }
706
707     /**
708      * Adds a {@link ContextMutation} to a {@link ModelProcessingPhase}.
709      *
710      * @throws IllegalStateException
711      *             when the mutation was registered after phase was completed
712      */
713     void addMutation(final ModelProcessingPhase phase, final ContextMutation mutation) {
714         ModelProcessingPhase finishedPhase = completedPhase;
715         while (finishedPhase != null) {
716             Preconditions.checkState(!phase.equals(finishedPhase),
717                 "Mutation registered after phase was completed at: %s", getStatementSourceReference());
718             finishedPhase = finishedPhase.getPreviousPhase();
719         }
720
721         if (phaseMutation.isEmpty()) {
722             phaseMutation = newMultimap();
723         }
724         phaseMutation.put(phase, mutation);
725     }
726
727     @Override
728     public <K, KT extends K, N extends StatementNamespace<K, ?, ?>> void addContext(final Class<N> namespace,
729             final KT key,final StmtContext<?, ?, ?> stmt) {
730         addContextToNamespace(namespace, key, stmt);
731     }
732
733     @Override
734     public <X, Y extends DeclaredStatement<X>, Z extends EffectiveStatement<X, Y>> Mutable<X, Y, Z> childCopyOf(
735             final StmtContext<X, Y, Z> stmt, final CopyType type, final QNameModule targetModule) {
736         Preconditions.checkState(stmt.getCompletedPhase() == ModelProcessingPhase.EFFECTIVE_MODEL,
737                 "Attempted to copy statement %s which has completed phase %s", stmt, stmt.getCompletedPhase());
738
739         Preconditions.checkArgument(stmt instanceof SubstatementContext, "Unsupported statement %s", stmt);
740
741         final SubstatementContext<X, Y, Z> original = (SubstatementContext<X, Y, Z>)stmt;
742         final SubstatementContext<X, Y, Z> copy = new SubstatementContext<>(original, this, type, targetModule);
743
744         original.definition().onStatementAdded(copy);
745         original.copyTo(copy, type, targetModule);
746
747         return copy;
748     }
749
750
751     @Override
752     public @NonNull StatementDefinition getDefinition() {
753         return getPublicDefinition();
754     }
755
756     @Override
757     public @NonNull StatementSourceReference getSourceReference() {
758         return getStatementSourceReference();
759     }
760
761     @Override
762     public boolean isFullyDefined() {
763         return fullyDefined;
764     }
765
766     final void copyTo(final StatementContextBase<?, ?, ?> target, final CopyType typeOfCopy,
767             @Nullable final QNameModule targetModule) {
768         final Collection<Mutable<?, ?, ?>> buffer = new ArrayList<>(substatements.size() + effective.size());
769
770         for (final Mutable<?, ?, ?> stmtContext : substatements.values()) {
771             if (stmtContext.isSupportedByFeatures()) {
772                 copySubstatement(stmtContext, target, typeOfCopy, targetModule, buffer);
773             }
774         }
775
776         for (final Mutable<?, ?, ?> stmtContext : effective) {
777             copySubstatement(stmtContext, target, typeOfCopy, targetModule, buffer);
778         }
779
780         target.addEffectiveSubstatements(buffer);
781     }
782
783     private void copySubstatement(final Mutable<?, ?, ?> stmtContext,  final Mutable<?, ?, ?> target,
784             final CopyType typeOfCopy, final QNameModule newQNameModule, final Collection<Mutable<?, ?, ?>> buffer) {
785         if (needToCopyByUses(stmtContext)) {
786             final Mutable<?, ?, ?> copy = target.childCopyOf(stmtContext, typeOfCopy, newQNameModule);
787             LOG.debug("Copying substatement {} for {} as", stmtContext, this, copy);
788             buffer.add(copy);
789         } else if (isReusedByUses(stmtContext)) {
790             LOG.debug("Reusing substatement {} for {}", stmtContext, this);
791             buffer.add(stmtContext);
792         } else {
793             LOG.debug("Skipping statement {}", stmtContext);
794         }
795     }
796
797     // FIXME: revise this, as it seems to be wrong
798     private static final Set<YangStmtMapping> NOCOPY_FROM_GROUPING_SET = ImmutableSet.of(
799         YangStmtMapping.DESCRIPTION,
800         YangStmtMapping.REFERENCE,
801         YangStmtMapping.STATUS);
802     private static final Set<YangStmtMapping> REUSED_DEF_SET = ImmutableSet.of(
803         YangStmtMapping.TYPE,
804         YangStmtMapping.TYPEDEF,
805         YangStmtMapping.USES);
806
807     private static boolean needToCopyByUses(final StmtContext<?, ?, ?> stmtContext) {
808         final StatementDefinition def = stmtContext.getPublicDefinition();
809         if (REUSED_DEF_SET.contains(def)) {
810             LOG.debug("Will reuse {} statement {}", def, stmtContext);
811             return false;
812         }
813         if (NOCOPY_FROM_GROUPING_SET.contains(def)) {
814             return !YangStmtMapping.GROUPING.equals(stmtContext.getParentContext().getPublicDefinition());
815         }
816
817         LOG.debug("Will copy {} statement {}", def, stmtContext);
818         return true;
819     }
820
821     private static boolean isReusedByUses(final StmtContext<?, ?, ?> stmtContext) {
822         return REUSED_DEF_SET.contains(stmtContext.getPublicDefinition());
823     }
824
825     @Override
826     public final String toString() {
827         return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString();
828     }
829
830     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
831         return toStringHelper.add("definition", definition).add("rawArgument", rawArgument);
832     }
833 }