Further yang-parser-impl checkstyle fixes
[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     @SuppressWarnings("checkstyle:methodTypeParameterName")
409     public final <CA, CD extends DeclaredStatement<CA>, CE extends EffectiveStatement<CA, CD>>
410             StatementContextBase<CA, CD, CE> createSubstatement(final int offset,
411                     final StatementDefinitionContext<CA, CD, CE> def, final StatementSourceReference ref,
412                     final String argument) {
413         final ModelProcessingPhase inProgressPhase = getRoot().getSourceContext().getInProgressPhase();
414         Preconditions.checkState(inProgressPhase != ModelProcessingPhase.EFFECTIVE_MODEL,
415                 "Declared statement cannot be added in effective phase at: %s", getStatementSourceReference());
416
417         final Optional<StatementSupport<?, ?, ?>> implicitParent = definition.getImplicitParentFor(def.getPublicView());
418         if (implicitParent.isPresent()) {
419             return createImplicitParent(offset, implicitParent.get(), ref, argument).createSubstatement(offset, def,
420                     ref, argument);
421         }
422
423         final StatementContextBase<CA, CD, CE> ret = new SubstatementContext<>(this, def, ref, argument);
424         substatements = substatements.put(offset, ret);
425         def.onStatementAdded(ret);
426         return ret;
427     }
428
429     private StatementContextBase<?, ?, ?> createImplicitParent(final int offset,
430             final StatementSupport<?, ?, ?> implicitParent, final StatementSourceReference ref, final String argument) {
431         final StatementDefinitionContext<?, ?, ?> def = new StatementDefinitionContext<>(implicitParent);
432         return createSubstatement(offset, def, ImplicitSubstatement.of(ref), argument);
433     }
434
435     /**
436      * Lookup substatement by its offset in this statement.
437      *
438      * @param offset Substatement offset
439      * @return Substatement, or null if substatement does not exist.
440      */
441     final StatementContextBase<?, ?, ?> lookupSubstatement(final int offset) {
442         return substatements.get(offset);
443     }
444
445     @Override
446     public D buildDeclared() {
447         Preconditions.checkArgument(completedPhase == ModelProcessingPhase.FULL_DECLARATION
448                 || completedPhase == ModelProcessingPhase.EFFECTIVE_MODEL);
449         if (declaredInstance == null) {
450             declaredInstance = definition().getFactory().createDeclared(this);
451         }
452         return declaredInstance;
453     }
454
455     @Override
456     public E buildEffective() {
457         if (effectiveInstance == null) {
458             effectiveInstance = definition().getFactory().createEffective(this);
459         }
460         return effectiveInstance;
461     }
462
463     /**
464      * tries to execute current {@link ModelProcessingPhase} of source parsing.
465      *
466      * @param phase
467      *            to be executed (completed)
468      * @return if phase was successfully completed
469      * @throws SourceException
470      *             when an error occured in source parsing
471      */
472     boolean tryToCompletePhase(final ModelProcessingPhase phase) {
473
474         boolean finished = true;
475         final Collection<ContextMutation> openMutations = phaseMutation.get(phase);
476         if (!openMutations.isEmpty()) {
477             final Iterator<ContextMutation> it = openMutations.iterator();
478             while (it.hasNext()) {
479                 final ContextMutation current = it.next();
480                 if (current.isFinished()) {
481                     it.remove();
482                 } else {
483                     finished = false;
484                 }
485             }
486
487             if (openMutations.isEmpty()) {
488                 phaseMutation.removeAll(phase);
489                 if (phaseMutation.isEmpty()) {
490                     phaseMutation = ImmutableMultimap.of();
491                 }
492             }
493         }
494
495         for (final StatementContextBase<?, ?, ?> child : substatements.values()) {
496             finished &= child.tryToCompletePhase(phase);
497         }
498         for (final Mutable<?, ?, ?> child : effective) {
499             if (child instanceof StatementContextBase) {
500                 finished &= ((StatementContextBase<?, ?, ?>) child).tryToCompletePhase(phase);
501             }
502         }
503
504         if (finished) {
505             onPhaseCompleted(phase);
506             return true;
507         }
508         return false;
509     }
510
511     /**
512      * Occurs on end of {@link ModelProcessingPhase} of source parsing.
513      *
514      * @param phase
515      *            that was to be completed (finished)
516      * @throws SourceException
517      *             when an error occurred in source parsing
518      */
519     private void onPhaseCompleted(final ModelProcessingPhase phase) {
520         completedPhase = phase;
521
522         final Collection<OnPhaseFinished> listeners = phaseListeners.get(phase);
523         if (listeners.isEmpty()) {
524             return;
525         }
526
527         final Iterator<OnPhaseFinished> listener = listeners.iterator();
528         while (listener.hasNext()) {
529             final OnPhaseFinished next = listener.next();
530             if (next.phaseFinished(this, phase)) {
531                 listener.remove();
532             }
533         }
534
535         if (listeners.isEmpty()) {
536             phaseListeners.removeAll(phase);
537             if (phaseListeners.isEmpty()) {
538                 phaseListeners = ImmutableMultimap.of();
539             }
540         }
541     }
542
543     /**
544      * Ends declared section of current node.
545      */
546     void endDeclared(final StatementSourceReference ref, final ModelProcessingPhase phase) {
547         definition().onDeclarationFinished(this, phase);
548     }
549
550     /**
551      * Return the context in which this statement was defined.
552      *
553      * @return statement definition
554      */
555     protected final StatementDefinitionContext<A, D, E> definition() {
556         return definition;
557     }
558
559     @Override
560     protected void checkLocalNamespaceAllowed(final Class<? extends IdentifierNamespace<?, ?>> type) {
561         definition().checkNamespaceAllowed(type);
562     }
563
564     @Override
565     protected <K, V, N extends IdentifierNamespace<K, V>> void onNamespaceElementAdded(final Class<N> type, final K key,
566             final V value) {
567         // definition().onNamespaceElementAdded(this, type, key, value);
568     }
569
570     <K, V, N extends IdentifierNamespace<K, V>> void onNamespaceItemAddedAction(final Class<N> type, final K key,
571             final OnNamespaceItemAdded listener) throws SourceException {
572         final Object potential = getFromNamespace(type, key);
573         if (potential != null) {
574             LOG.trace("Listener on {} key {} satisfied immediately", type, key);
575             listener.namespaceItemAdded(this, type, key, potential);
576             return;
577         }
578
579         final NamespaceBehaviour<K, V, N> behaviour = getBehaviourRegistry().getNamespaceBehaviour(type);
580         Preconditions.checkArgument(behaviour instanceof NamespaceBehaviourWithListeners,
581             "Namespace {} does not support listeners", type);
582
583         final NamespaceBehaviourWithListeners<K, V, N> casted = (NamespaceBehaviourWithListeners<K, V, N>) behaviour;
584         casted.addValueListener(new ValueAddedListener<K>(this, key) {
585             @Override
586             void onValueAdded(final Object key, final Object value) {
587                 listener.namespaceItemAdded(StatementContextBase.this, type, key, value);
588             }
589         });
590     }
591
592     /**
593      * See {@link StatementSupport#getPublicView()}.
594      */
595     @Nonnull
596     @Override
597     public StatementDefinition getPublicDefinition() {
598         return definition().getPublicView();
599     }
600
601     @Override
602     public ModelActionBuilder newInferenceAction(final ModelProcessingPhase phase) {
603         return getRoot().getSourceContext().newInferenceAction(phase);
604     }
605
606     private static <T> Multimap<ModelProcessingPhase, T> newMultimap() {
607         return Multimaps.newListMultimap(new EnumMap<>(ModelProcessingPhase.class), () -> new ArrayList<>(1));
608     }
609
610     /**
611      * Adds {@link OnPhaseFinished} listener for a {@link ModelProcessingPhase} end.
612      */
613     void addPhaseCompletedListener(final ModelProcessingPhase phase, final OnPhaseFinished listener) {
614         Preconditions.checkNotNull(phase, "Statement context processing phase cannot be null at: %s",
615                 getStatementSourceReference());
616         Preconditions.checkNotNull(listener, "Statement context phase listener cannot be null at: %s",
617                 getStatementSourceReference());
618
619         ModelProcessingPhase finishedPhase = completedPhase;
620         while (finishedPhase != null) {
621             if (phase.equals(finishedPhase)) {
622                 listener.phaseFinished(this, finishedPhase);
623                 return;
624             }
625             finishedPhase = finishedPhase.getPreviousPhase();
626         }
627         if (phaseListeners.isEmpty()) {
628             phaseListeners = newMultimap();
629         }
630
631         phaseListeners.put(phase, listener);
632     }
633
634     /**
635      * Adds a {@link ContextMutation} to a {@link ModelProcessingPhase}.
636      *
637      * @throws IllegalStateException
638      *             when the mutation was registered after phase was completed
639      */
640     void addMutation(final ModelProcessingPhase phase, final ContextMutation mutation) {
641         ModelProcessingPhase finishedPhase = completedPhase;
642         while (finishedPhase != null) {
643             Preconditions.checkState(!phase.equals(finishedPhase),
644                 "Mutation registered after phase was completed at: %s", getStatementSourceReference());
645             finishedPhase = finishedPhase.getPreviousPhase();
646         }
647
648         if (phaseMutation.isEmpty()) {
649             phaseMutation = newMultimap();
650         }
651         phaseMutation.put(phase, mutation);
652     }
653
654     @Override
655     public <K, KT extends K, N extends StatementNamespace<K, ?, ?>> void addContext(final Class<N> namespace,
656             final KT key,final StmtContext<?, ?, ?> stmt) {
657         addContextToNamespace(namespace, key, stmt);
658     }
659
660     @Override
661     public <X, Y extends DeclaredStatement<X>, Z extends EffectiveStatement<X, Y>> Mutable<X, Y, Z> childCopyOf(
662             final StmtContext<X, Y, Z> stmt, final CopyType type, final QNameModule targetModule) {
663         Preconditions.checkState(stmt.getCompletedPhase() == ModelProcessingPhase.EFFECTIVE_MODEL,
664                 "Attempted to copy statement %s which has completed phase %s", stmt, stmt.getCompletedPhase());
665
666         Preconditions.checkArgument(stmt instanceof SubstatementContext, "Unsupported statement %s", stmt);
667
668         final SubstatementContext<X, Y, Z> original = (SubstatementContext<X, Y, Z>)stmt;
669         final SubstatementContext<X, Y, Z> copy = new SubstatementContext<>(original, this, type, targetModule);
670
671         original.definition().onStatementAdded(copy);
672         original.copyTo(copy, type, targetModule);
673
674         return copy;
675     }
676
677     final void copyTo(final StatementContextBase<?, ?, ?> target, final CopyType typeOfCopy,
678             @Nullable final QNameModule targetModule) {
679         final Collection<Mutable<?, ?, ?>> buffer = new ArrayList<>(substatements.size() + effective.size());
680
681         for (final Mutable<?, ?, ?> stmtContext : substatements.values()) {
682             if (stmtContext.isSupportedByFeatures()) {
683                 copySubstatement(stmtContext, target, typeOfCopy, targetModule, buffer);
684             }
685         }
686
687         for (final Mutable<?, ?, ?> stmtContext : effective) {
688             copySubstatement(stmtContext, target, typeOfCopy, targetModule, buffer);
689         }
690
691         target.addEffectiveSubstatements(buffer);
692     }
693
694     private void copySubstatement(final Mutable<?, ?, ?> stmtContext,  final Mutable<?, ?, ?> target,
695             final CopyType typeOfCopy, final QNameModule newQNameModule, final Collection<Mutable<?, ?, ?>> buffer) {
696         if (needToCopyByUses(stmtContext)) {
697             final Mutable<?, ?, ?> copy = target.childCopyOf(stmtContext, typeOfCopy, newQNameModule);
698             LOG.debug("Copying substatement {} for {} as", stmtContext, this, copy);
699             buffer.add(copy);
700         } else if (isReusedByUses(stmtContext)) {
701             LOG.debug("Reusing substatement {} for {}", stmtContext, this);
702             buffer.add(stmtContext);
703         } else {
704             LOG.debug("Skipping statement {}", stmtContext);
705         }
706     }
707
708     // FIXME: revise this, as it seems to be wrong
709     private static final Set<YangStmtMapping> NOCOPY_FROM_GROUPING_SET = ImmutableSet.of(
710         YangStmtMapping.DESCRIPTION,
711         YangStmtMapping.REFERENCE,
712         YangStmtMapping.STATUS);
713     private static final Set<YangStmtMapping> REUSED_DEF_SET = ImmutableSet.of(
714         YangStmtMapping.TYPE,
715         YangStmtMapping.TYPEDEF,
716         YangStmtMapping.USES);
717
718     private static boolean needToCopyByUses(final StmtContext<?, ?, ?> stmtContext) {
719         final StatementDefinition def = stmtContext.getPublicDefinition();
720         if (REUSED_DEF_SET.contains(def)) {
721             LOG.debug("Will reuse {} statement {}", def, stmtContext);
722             return false;
723         }
724         if (NOCOPY_FROM_GROUPING_SET.contains(def)) {
725             return !YangStmtMapping.GROUPING.equals(stmtContext.getParentContext().getPublicDefinition());
726         }
727
728         LOG.debug("Will copy {} statement {}", def, stmtContext);
729         return true;
730     }
731
732     private static boolean isReusedByUses(final StmtContext<?, ?, ?> stmtContext) {
733         return REUSED_DEF_SET.contains(stmtContext.getPublicDefinition());
734     }
735
736     @Override
737     public final String toString() {
738         return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString();
739     }
740
741     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
742         return toStringHelper.add("definition", definition).add("rawArgument", rawArgument);
743     }
744 }