d478818e2abeeef3c9836bb9e31d60ed74512038
[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.base.Throwables;
14 import com.google.common.collect.ImmutableCollection;
15 import com.google.common.collect.ImmutableList;
16 import com.google.common.collect.ImmutableMultimap;
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 javax.annotation.Nonnull;
26 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
27 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
28 import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
29 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
30 import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
31 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory;
32 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
33 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
34 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
35 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
36 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
37 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
38 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
39 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
40 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
41 import org.opendaylight.yangtools.yang.parser.stmt.reactor.NamespaceBehaviourWithListeners.ValueAddedListener;
42
43 public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
44         extends NamespaceStorageSupport implements StmtContext.Mutable<A, D, E> {
45
46     /**
47      * event listener when an item is added to model namespace
48      */
49     interface OnNamespaceItemAdded extends EventListener {
50         /**
51          * @throws SourceException
52          */
53         void namespaceItemAdded(StatementContextBase<?, ?, ?> context, Class<?> namespace, Object key, Object value);
54     }
55
56     /**
57      * event listener when a parsing {@link ModelProcessingPhase} is completed
58      */
59     interface OnPhaseFinished extends EventListener {
60         /**
61          * @throws SourceException
62          */
63         boolean phaseFinished(StatementContextBase<?, ?, ?> context, ModelProcessingPhase phase);
64     }
65
66     /**
67      * interface for all mutations within an {@link ModelActionBuilder.InferenceAction}
68      */
69     interface ContextMutation {
70
71         boolean isFinished();
72     }
73
74     private final StatementDefinitionContext<A, D, E> definition;
75     private final StatementSourceReference statementDeclSource;
76     private final String rawArgument;
77
78     private Multimap<ModelProcessingPhase, OnPhaseFinished> phaseListeners = ImmutableMultimap.of();
79     private Multimap<ModelProcessingPhase, ContextMutation> phaseMutation = ImmutableMultimap.of();
80     private Collection<StatementContextBase<?, ?, ?>> effective = ImmutableList.of();
81     private Collection<StatementContextBase<?, ?, ?>> effectOfStatement = ImmutableList.of();
82     private StatementMap substatements = StatementMap.empty();
83
84     private SupportedByFeatures supportedByFeatures = SupportedByFeatures.UNDEFINED;
85     private CopyHistory copyHistory = CopyHistory.original();
86     private boolean isSupportedToBuildEffective = true;
87     private ModelProcessingPhase completedPhase = null;
88     private StatementContextBase<?, ?, ?> originalCtx;
89     private D declaredInstance;
90     private E effectiveInstance;
91     private int order = 0;
92
93     StatementContextBase(final StatementDefinitionContext<A, D, E> def, final StatementSourceReference ref,
94             final String rawArgument) {
95         this.definition = Preconditions.checkNotNull(def);
96         this.statementDeclSource = Preconditions.checkNotNull(ref);
97         this.rawArgument = rawArgument;
98     }
99
100     StatementContextBase(final StatementContextBase<A, D, E> original) {
101         this.definition = Preconditions.checkNotNull(original.definition,
102                 "Statement context definition cannot be null copying from: %s", original.getStatementSourceReference());
103         this.statementDeclSource = Preconditions.checkNotNull(original.statementDeclSource,
104                 "Statement context statementDeclSource cannot be null copying from: %s",
105                 original.getStatementSourceReference());
106         this.rawArgument = original.rawArgument;
107     }
108
109     @Override
110     public Collection<StatementContextBase<?, ?, ?>> getEffectOfStatement() {
111         return effectOfStatement;
112     }
113
114     @Override
115     public void addAsEffectOfStatement(final StatementContextBase<?, ?, ?> ctx) {
116         if (effectOfStatement.isEmpty()) {
117             effectOfStatement = new ArrayList<>(1);
118         }
119         effectOfStatement.add(ctx);
120     }
121
122     @Override
123     public void addAsEffectOfStatement(final Collection<StatementContextBase<?, ?, ?>> ctxs) {
124         if (ctxs.isEmpty()) {
125             return;
126         }
127
128         if (effectOfStatement.isEmpty()) {
129             effectOfStatement = new ArrayList<>(ctxs.size());
130         }
131         effectOfStatement.addAll(ctxs);
132     }
133
134     @Override
135     public SupportedByFeatures getSupportedByFeatures() {
136         return supportedByFeatures;
137     }
138
139     @Override
140     public void setSupportedByFeatures(final boolean isSupported) {
141         this.supportedByFeatures = isSupported ? SupportedByFeatures.SUPPORTED : SupportedByFeatures.NOT_SUPPORTED;
142     }
143
144     @Override
145     public boolean isSupportedToBuildEffective() {
146         return isSupportedToBuildEffective;
147     }
148
149     @Override
150     public void setIsSupportedToBuildEffective(final boolean isSupportedToBuildEffective) {
151         this.isSupportedToBuildEffective = isSupportedToBuildEffective;
152     }
153
154     @Override
155     public CopyHistory getCopyHistory() {
156         return copyHistory;
157     }
158
159     @Override
160     public void appendCopyHistory(final CopyType typeOfCopy, final CopyHistory toAppend) {
161         copyHistory = copyHistory.append(typeOfCopy, toAppend);
162     }
163
164     @Override
165     public StatementContextBase<?, ?, ?> getOriginalCtx() {
166         return originalCtx;
167     }
168
169     @Override
170     public void setOriginalCtx(final StatementContextBase<?, ?, ?> originalCtx) {
171         this.originalCtx = originalCtx;
172     }
173
174     @Override
175     public void setOrder(final int order) {
176         this.order = order;
177     }
178
179     @Override
180     public int getOrder() {
181         return order;
182     }
183
184     @Override
185     public ModelProcessingPhase getCompletedPhase() {
186         return completedPhase;
187     }
188
189     @Override
190     public void setCompletedPhase(final ModelProcessingPhase completedPhase) {
191         this.completedPhase = completedPhase;
192     }
193
194     @Override
195     public abstract StatementContextBase<?, ?, ?> getParentContext();
196
197     /**
198      * @return root context of statement
199      */
200     @Nonnull
201     @Override
202     public abstract RootStatementContext<?, ?, ?> getRoot();
203
204     /**
205      * @return origin of statement
206      */
207     @Nonnull
208     @Override
209     public StatementSource getStatementSource() {
210         return statementDeclSource.getStatementSource();
211     }
212
213     /**
214      * @return reference of statement source
215      */
216     @Nonnull
217     @Override
218     public StatementSourceReference getStatementSourceReference() {
219         return statementDeclSource;
220     }
221
222     @Override
223     public final String rawStatementArgument() {
224         return rawArgument;
225     }
226
227     @Nonnull
228     @Override
229     public Collection<StatementContextBase<?, ?, ?>> declaredSubstatements() {
230         return substatements.values();
231     }
232
233     @Nonnull
234     @Override
235     public Collection<StatementContextBase<?, ?, ?>> effectiveSubstatements() {
236         if (effective instanceof ImmutableCollection) {
237             return effective;
238         }
239
240         return Collections.unmodifiableCollection(effective);
241     }
242
243     public void removeStatementsFromEffectiveSubstatements(final Collection<StatementContextBase<?, ?, ?>> substatements) {
244         if (!effective.isEmpty()) {
245             effective.removeAll(substatements);
246             shrinkEffective();
247         }
248     }
249
250     private void shrinkEffective() {
251         if (effective.isEmpty()) {
252             effective = ImmutableList.of();
253         }
254     }
255
256     public void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef) {
257         if (effective.isEmpty()) {
258             return;
259         }
260
261         final Iterator<StatementContextBase<?, ?, ?>> iterator = effective.iterator();
262         while (iterator.hasNext()) {
263             final StatementContextBase<?, ?, ?> next = iterator.next();
264             if (statementDef.equals(next.getPublicDefinition())) {
265                 iterator.remove();
266             }
267         }
268
269         shrinkEffective();
270     }
271
272     /**
273      * Removes a statement context from the effective substatements
274      * based on its statement definition (i.e statement keyword) and raw (in String form) statement argument.
275      * The statement context is removed only if both statement definition and statement argument match with
276      * one of the effective substatements' statement definition and argument.
277      *
278      * If the statementArg parameter is null, the statement context is removed based only on its statement definition.
279      *
280      * @param statementDef statement definition of the statement context to remove
281      * @param statementArg statement argument of the statement context to remove
282      */
283     public void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef,
284             final String statementArg) {
285         if (statementArg == null) {
286             removeStatementFromEffectiveSubstatements(statementDef);
287         }
288
289         if (effective.isEmpty()) {
290             return;
291         }
292
293         final Iterator<StatementContextBase<?, ?, ?>> iterator = effective.iterator();
294         while (iterator.hasNext()) {
295             final StatementContextBase<?, ?, ?> next = iterator.next();
296             if (statementDef.equals(next.getPublicDefinition()) && statementArg.equals(next.rawStatementArgument())) {
297                 iterator.remove();
298             }
299         }
300
301         shrinkEffective();
302     }
303
304     /**
305      * adds effective statement to collection of substatements
306      *
307      * @param substatement substatement
308      * @throws IllegalStateException
309      *             if added in declared phase
310      * @throws NullPointerException
311      *             if statement parameter is null
312      */
313     public void addEffectiveSubstatement(final StatementContextBase<?, ?, ?> substatement) {
314         Preconditions.checkNotNull(substatement, "StatementContextBase effective substatement cannot be null at: %s",
315             getStatementSourceReference());
316         beforeAddEffectiveStatement(1);
317         effective.add(substatement);
318     }
319
320     /**
321      * adds effective statement to collection of substatements
322      *
323      * @param substatements substatements
324      * @throws IllegalStateException
325      *             if added in declared phase
326      * @throws NullPointerException
327      *             if statement parameter is null
328      */
329     public void addEffectiveSubstatements(final Collection<StatementContextBase<?, ?, ?>> substatements) {
330         if (substatements.isEmpty()) {
331             return;
332         }
333
334         substatements.forEach(Preconditions::checkNotNull);
335         beforeAddEffectiveStatement(substatements.size());
336         effective.addAll(substatements);
337     }
338
339     private void beforeAddEffectiveStatement(final int toAdd) {
340         final ModelProcessingPhase inProgressPhase = getRoot().getSourceContext().getInProgressPhase();
341         Preconditions.checkState(inProgressPhase == ModelProcessingPhase.FULL_DECLARATION
342                 || inProgressPhase == ModelProcessingPhase.EFFECTIVE_MODEL,
343                 "Effective statement cannot be added in declared phase at: %s", getStatementSourceReference());
344
345         if (effective.isEmpty()) {
346             effective = new ArrayList<>(toAdd);
347         }
348     }
349
350     /**
351      * Create a new substatement at the specified offset.
352      *
353      * @param offset Substatement offset
354      * @param def definition context
355      * @param ref source reference
356      * @param argument statement argument
357      * @return A new substatement
358      */
359     public final <CA, CD extends DeclaredStatement<CA>, CE extends EffectiveStatement<CA, CD>> StatementContextBase<CA, CD, CE>
360             createSubstatement(final int offset, final StatementDefinitionContext<CA, CD, CE> def,
361                     final StatementSourceReference ref, final String argument) {
362         final ModelProcessingPhase inProgressPhase = getRoot().getSourceContext().getInProgressPhase();
363         Preconditions.checkState(inProgressPhase != ModelProcessingPhase.EFFECTIVE_MODEL,
364                 "Declared statement cannot be added in effective phase at: %s", getStatementSourceReference());
365
366         final StatementContextBase<CA, CD, CE> ret = new SubstatementContext<>(this, def, ref, argument);
367         substatements = substatements.put(offset, ret);
368         def.onStatementAdded(ret);
369         return ret;
370     }
371
372     /**
373      * Lookup substatement by its offset in this statement.
374      *
375      * @param offset Substatement offset
376      * @return Substatement, or null if substatement does not exist.
377      */
378     final StatementContextBase<?, ?, ?> lookupSubstatement(final int offset) {
379         return substatements.get(offset);
380     }
381
382     /**
383      * builds {@link DeclaredStatement} for statement context
384      */
385     @Override
386     public D buildDeclared() {
387         Preconditions.checkArgument(completedPhase == ModelProcessingPhase.FULL_DECLARATION
388                 || completedPhase == ModelProcessingPhase.EFFECTIVE_MODEL);
389         if (declaredInstance == null) {
390             declaredInstance = definition().getFactory().createDeclared(this);
391         }
392         return declaredInstance;
393     }
394
395     /**
396      * builds {@link EffectiveStatement} for statement context
397      */
398     @Override
399     public E buildEffective() {
400         if (effectiveInstance == null) {
401             effectiveInstance = definition().getFactory().createEffective(this);
402         }
403         return effectiveInstance;
404     }
405
406     /**
407      * tries to execute current {@link ModelProcessingPhase} of source parsing
408      *
409      * @param phase
410      *            to be executed (completed)
411      * @return if phase was successfully completed
412      * @throws SourceException
413      *             when an error occured in source parsing
414      */
415     boolean tryToCompletePhase(final ModelProcessingPhase phase) {
416
417         boolean finished = true;
418         final Collection<ContextMutation> openMutations = phaseMutation.get(phase);
419         if (!openMutations.isEmpty()) {
420             final Iterator<ContextMutation> it = openMutations.iterator();
421             while (it.hasNext()) {
422                 final ContextMutation current = it.next();
423                 if (current.isFinished()) {
424                     it.remove();
425                 } else {
426                     finished = false;
427                 }
428             }
429
430             if (openMutations.isEmpty()) {
431                 phaseMutation.removeAll(phase);
432                 if (phaseMutation.isEmpty()) {
433                     phaseMutation = ImmutableMultimap.of();
434                 }
435             }
436         }
437
438         for (final StatementContextBase<?, ?, ?> child : substatements.values()) {
439             finished &= child.tryToCompletePhase(phase);
440         }
441         for (final StatementContextBase<?, ?, ?> child : effective) {
442             finished &= child.tryToCompletePhase(phase);
443         }
444
445         if (finished) {
446             onPhaseCompleted(phase);
447             return true;
448         }
449         return false;
450     }
451
452     /**
453      * Occurs on end of {@link ModelProcessingPhase} of source parsing
454      *
455      * @param phase
456      *            that was to be completed (finished)
457      * @throws SourceException
458      *             when an error occurred in source parsing
459      */
460     private void onPhaseCompleted(final ModelProcessingPhase phase) {
461         completedPhase = phase;
462
463         final Collection<OnPhaseFinished> listeners = phaseListeners.get(phase);
464         if (listeners.isEmpty()) {
465             return;
466         }
467
468         final Iterator<OnPhaseFinished> listener = listeners.iterator();
469         while (listener.hasNext()) {
470             final OnPhaseFinished next = listener.next();
471             if (next.phaseFinished(this, phase)) {
472                 listener.remove();
473             }
474         }
475
476         if (listeners.isEmpty()) {
477             phaseListeners.removeAll(phase);
478             if (phaseListeners.isEmpty()) {
479                 phaseListeners = ImmutableMultimap.of();
480             }
481         }
482     }
483
484     /**
485      * Ends declared section of current node.
486      *
487      * @param ref
488      * @throws SourceException
489      */
490     void endDeclared(final StatementSourceReference ref, final ModelProcessingPhase phase) {
491         definition().onDeclarationFinished(this, phase);
492     }
493
494     /**
495      * @return statement definition
496      */
497     protected final StatementDefinitionContext<A, D, E> definition() {
498         return definition;
499     }
500
501     @Override
502     protected void checkLocalNamespaceAllowed(final Class<? extends IdentifierNamespace<?, ?>> type) {
503         definition().checkNamespaceAllowed(type);
504     }
505
506     /**
507      * occurs when an item is added to model namespace
508      *
509      * @throws SourceException instance of SourceException
510      */
511     @Override
512     protected <K, V, N extends IdentifierNamespace<K, V>> void onNamespaceElementAdded(final Class<N> type, final K key, final V value) {
513         // definition().onNamespaceElementAdded(this, type, key, value);
514     }
515
516     <K, V, N extends IdentifierNamespace<K, V>> void onNamespaceItemAddedAction(final Class<N> type, final K key,
517             final OnNamespaceItemAdded listener) throws SourceException {
518         final Object potential = getFromNamespace(type, key);
519         if (potential != null) {
520             listener.namespaceItemAdded(this, type, key, potential);
521             return;
522         }
523         final NamespaceBehaviour<K, V, N> behaviour = getBehaviourRegistry().getNamespaceBehaviour(type);
524         if (behaviour instanceof NamespaceBehaviourWithListeners) {
525             final NamespaceBehaviourWithListeners<K, V, N> casted = (NamespaceBehaviourWithListeners<K, V, N>) behaviour;
526             casted.addValueListener(new ValueAddedListener<K>(this, key) {
527                 @Override
528                 void onValueAdded(final Object key, final Object value) {
529                     try {
530                         listener.namespaceItemAdded(StatementContextBase.this, type, key, value);
531                     } catch (final SourceException e) {
532                         throw Throwables.propagate(e);
533                     }
534                 }
535             });
536         }
537     }
538
539     /**
540      * @see StatementSupport#getPublicView()
541      */
542     @Nonnull
543     @Override
544     public StatementDefinition getPublicDefinition() {
545         return definition().getPublicView();
546     }
547
548     @Override
549     public ModelActionBuilder newInferenceAction(final ModelProcessingPhase phase) {
550         return getRoot().getSourceContext().newInferenceAction(phase);
551     }
552
553     private static <T> Multimap<ModelProcessingPhase, T> newMultimap() {
554         return Multimaps.newListMultimap(new EnumMap<>(ModelProcessingPhase.class), () -> new ArrayList<>(1));
555     }
556
557     /**
558      * adds {@link OnPhaseFinished} listener for a {@link ModelProcessingPhase} end
559      *
560      * @throws SourceException
561      */
562     void addPhaseCompletedListener(final ModelProcessingPhase phase, final OnPhaseFinished listener) {
563
564         Preconditions.checkNotNull(phase, "Statement context processing phase cannot be null at: %s",
565                 getStatementSourceReference());
566         Preconditions.checkNotNull(listener, "Statement context phase listener cannot be null at: %s",
567                 getStatementSourceReference());
568
569         ModelProcessingPhase finishedPhase = completedPhase;
570         while (finishedPhase != null) {
571             if (phase.equals(finishedPhase)) {
572                 listener.phaseFinished(this, finishedPhase);
573                 return;
574             }
575             finishedPhase = finishedPhase.getPreviousPhase();
576         }
577         if (phaseListeners.isEmpty()) {
578             phaseListeners = newMultimap();
579         }
580
581         phaseListeners.put(phase, listener);
582     }
583
584     /**
585      * adds {@link ContextMutation} to {@link ModelProcessingPhase}
586      *
587      * @throws IllegalStateException
588      *             when the mutation was registered after phase was completed
589      */
590     void addMutation(final ModelProcessingPhase phase, final ContextMutation mutation) {
591         ModelProcessingPhase finishedPhase = completedPhase;
592         while (finishedPhase != null) {
593             if (phase.equals(finishedPhase)) {
594                 throw new IllegalStateException("Mutation registered after phase was completed at: "  +
595                         getStatementSourceReference());
596             }
597             finishedPhase = finishedPhase.getPreviousPhase();
598         }
599
600         if (phaseMutation.isEmpty()) {
601             phaseMutation = newMultimap();
602         }
603         phaseMutation.put(phase, mutation);
604     }
605
606     /**
607      * adds statement to namespace map with the key
608      *
609      * @param namespace
610      *            {@link StatementNamespace} child that determines namespace to be added to
611      * @param key
612      *            of type according to namespace class specification
613      * @param stmt
614      *            to be added to namespace map
615      */
616     @Override
617     public <K, KT extends K, N extends StatementNamespace<K, ?, ?>> void addContext(final Class<N> namespace, final KT key,
618             final StmtContext<?, ?, ?> stmt) {
619         addContextToNamespace(namespace, key, stmt);
620     }
621
622     @Override
623     public final String toString() {
624         return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString();
625     }
626
627     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
628         return toStringHelper.add("definition", definition).add("rawArgument", rawArgument);
629     }
630 }