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