Rework identifier path subscription
[yangtools.git] / yang / yang-parser-reactor / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / ModifierImpl.java
1 /*
2  * Copyright (c) 2015, 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.parser.stmt.reactor;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Preconditions.checkState;
12 import static java.util.Objects.requireNonNull;
13 import static org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase.EFFECTIVE_MODEL;
14 import static org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase.FULL_DECLARATION;
15
16 import com.google.common.base.MoreObjects;
17 import com.google.common.base.MoreObjects.ToStringHelper;
18 import java.util.HashSet;
19 import java.util.Iterator;
20 import java.util.Set;
21 import java.util.function.Function;
22 import javax.annotation.Nonnull;
23 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
24 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
25 import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
26 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
27 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
28 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceKeyCriterion;
29 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
30 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
31 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
32 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase.ContextMutation;
33 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase.OnNamespaceItemAdded;
34 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase.OnPhaseFinished;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 final class ModifierImpl implements ModelActionBuilder {
39     private static final Logger LOG = LoggerFactory.getLogger(ModifierImpl.class);
40
41     private final InferenceContext ctx = new InferenceContext() { };
42
43     private final Set<AbstractPrerequisite<?>> unsatisfied = new HashSet<>(1);
44     private final Set<AbstractPrerequisite<?>> mutations = new HashSet<>(1);
45
46     private InferenceAction action;
47     private boolean actionApplied = false;
48
49     private <D> AbstractPrerequisite<D> addReq(final AbstractPrerequisite<D> prereq) {
50         LOG.trace("Modifier {} adding prerequisite {}", this, prereq);
51         unsatisfied.add(prereq);
52         return prereq;
53     }
54
55     private <T> AbstractPrerequisite<T> addMutation(final AbstractPrerequisite<T> mutation) {
56         LOG.trace("Modifier {} adding mutation {}", this, mutation);
57         mutations.add(mutation);
58         return mutation;
59     }
60
61     private void checkNotRegistered() {
62         checkState(action == null, "Action was already registered.");
63     }
64
65     private boolean removeSatisfied() {
66         final Iterator<AbstractPrerequisite<?>> it = unsatisfied.iterator();
67         while (it.hasNext()) {
68             final AbstractPrerequisite<?> prereq = it.next();
69             if (prereq.isDone()) {
70                 // We are removing current prerequisite from list.
71                 LOG.trace("Modifier {} prerequisite {} satisfied", this, prereq);
72                 it.remove();
73             }
74         }
75         return unsatisfied.isEmpty();
76     }
77
78     boolean isApplied() {
79         return actionApplied;
80     }
81
82     void failModifier() {
83         removeSatisfied();
84         action.prerequisiteFailed(unsatisfied);
85         action = null;
86     }
87
88     private void applyAction() {
89         checkState(!actionApplied);
90         action.apply(ctx);
91         actionApplied = true;
92     }
93
94     private <K, C extends StmtContext<?, ?, ?>, N extends StatementNamespace<K, ?, ?>> AbstractPrerequisite<C>
95             requiresCtxImpl(final StmtContext<?, ?, ?> context, final Class<N> namespace, final K key,
96                     final ModelProcessingPhase phase)  {
97         checkNotRegistered();
98
99         AddedToNamespace<C> addedToNs = new AddedToNamespace<>(phase);
100         addReq(addedToNs);
101         contextImpl(context).onNamespaceItemAddedAction(namespace, key, addedToNs);
102         return addedToNs;
103     }
104
105     private <K, C extends StmtContext<?, ?, ?>, N extends StatementNamespace<K, ?, ?>> AbstractPrerequisite<C>
106             requiresCtxImpl(final StmtContext<?, ?, ?> context, final Class<N> namespace,
107                     final NamespaceKeyCriterion<K> criterion, final ModelProcessingPhase phase)  {
108         checkNotRegistered();
109
110         AddedToNamespace<C> addedToNs = new AddedToNamespace<>(phase);
111         addReq(addedToNs);
112         contextImpl(context).onNamespaceItemAddedAction(namespace, phase, criterion, addedToNs);
113         return addedToNs;
114     }
115
116     private <C extends StmtContext<?, ?, ?>> AbstractPrerequisite<C> requiresCtxImpl(final C context,
117             final ModelProcessingPhase phase) {
118         checkNotRegistered();
119
120         PhaseFinished<C> phaseFin = new PhaseFinished<>();
121         addReq(phaseFin);
122         contextImpl(context).addPhaseCompletedListener(phase, phaseFin);
123         return phaseFin;
124     }
125
126     @SuppressWarnings({ "rawtypes", "unchecked" })
127     private <K, C extends Mutable<?, ?, ?>, N extends IdentifierNamespace<K, ? extends StmtContext<?, ?, ?>>>
128             AbstractPrerequisite<C> mutatesCtxImpl(final StmtContext<?, ?, ?> context, final Class<N> namespace,
129                     final K key, final ModelProcessingPhase phase) {
130         checkNotRegistered();
131
132         final PhaseModificationInNamespace<C> mod = new PhaseModificationInNamespace<>(EFFECTIVE_MODEL);
133         addReq(mod);
134         addMutation(mod);
135         contextImpl(context).onNamespaceItemAddedAction((Class) namespace, key, mod);
136         return mod;
137     }
138
139     private static StatementContextBase<?, ?, ?> contextImpl(final Object value) {
140         checkArgument(value instanceof StatementContextBase, "Supplied context %s is not provided by this reactor.",
141             value);
142         return StatementContextBase.class.cast(value);
143     }
144
145     boolean tryApply() {
146         checkState(action != null, "Action was not defined yet.");
147
148         if (removeSatisfied()) {
149             applyAction();
150             return true;
151         }
152         return false;
153     }
154
155     @Nonnull
156     @Override
157     public <C extends Mutable<?, ?, ?>, T extends C> Prerequisite<C> mutatesCtx(final T context,
158             final ModelProcessingPhase phase) {
159         return addMutation(new PhaseMutation<>(contextImpl(context), phase));
160     }
161
162     @Nonnull
163     @Override
164     public <A,D extends DeclaredStatement<A>,E extends EffectiveStatement<A, D>>
165             AbstractPrerequisite<StmtContext<A, D, E>> requiresCtx(final StmtContext<A, D, E> context,
166                     final ModelProcessingPhase phase) {
167         return requiresCtxImpl(context, phase);
168     }
169
170
171     @Nonnull
172     @Override
173     public <K, N extends StatementNamespace<K, ?, ?>> Prerequisite<StmtContext<?, ?, ?>> requiresCtx(
174             final StmtContext<?, ?, ?> context, final Class<N> namespace, final K key,
175             final ModelProcessingPhase phase) {
176         return requiresCtxImpl(context, namespace, key, phase);
177     }
178
179     @Nonnull
180     @Override
181     public <K, N extends StatementNamespace<K, ?, ?>> Prerequisite<StmtContext<?, ?, ?>> requiresCtx(
182             final StmtContext<?, ?, ?> context, final Class<N> namespace, final NamespaceKeyCriterion<K> criterion,
183             final ModelProcessingPhase phase) {
184         return requiresCtxImpl(context, namespace, criterion, phase);
185     }
186
187     @Nonnull
188     @Override
189     public <D extends DeclaredStatement<?>> Prerequisite<D> requiresDeclared(
190             final StmtContext<?, ? extends D, ?> context) {
191         return requiresCtxImpl(context, FULL_DECLARATION).transform(StmtContext::buildDeclared);
192     }
193
194     @Nonnull
195     @Override
196     public <K, D extends DeclaredStatement<?>, N extends StatementNamespace<K, ? extends D, ?>> Prerequisite<D>
197             requiresDeclared(final StmtContext<?, ?, ?> context, final Class<N> namespace, final K key) {
198         final AbstractPrerequisite<StmtContext<?, D, ?>> rawContext = requiresCtxImpl(context, namespace, key,
199             FULL_DECLARATION);
200         return rawContext.transform(StmtContext::buildDeclared);
201     }
202
203     @Nonnull
204     @Override
205     public <K, D extends DeclaredStatement<?>, N extends StatementNamespace<K, ? extends D, ?>>
206             AbstractPrerequisite<StmtContext<?, D, ?>> requiresDeclaredCtx(final StmtContext<?, ?, ?> context,
207                     final Class<N> namespace, final K key) {
208         return requiresCtxImpl(context, namespace, key, FULL_DECLARATION);
209     }
210
211     @Nonnull
212     @Override
213     public <E extends EffectiveStatement<?, ?>> Prerequisite<E> requiresEffective(
214             final StmtContext<?, ?, ? extends E> stmt) {
215         return requiresCtxImpl(stmt, EFFECTIVE_MODEL).transform(StmtContext::buildEffective);
216     }
217
218     @Nonnull
219     @Override
220     public <K, E extends EffectiveStatement<?, ?>, N extends StatementNamespace<K, ?, ? extends E>> Prerequisite<E>
221             requiresEffective(final StmtContext<?, ?, ?> context, final Class<N> namespace, final K key) {
222         final AbstractPrerequisite<StmtContext<?, ?, E>> rawContext = requiresCtxImpl(context, namespace, key,
223             EFFECTIVE_MODEL);
224         return rawContext.transform(StmtContext::buildEffective);
225     }
226
227     @Nonnull
228     @Override
229     public <K, E extends EffectiveStatement<?, ?>, N extends StatementNamespace<K, ?, ? extends E>>
230             AbstractPrerequisite<StmtContext<?, ?, E>> requiresEffectiveCtx(final StmtContext<?, ?, ?> context,
231                     final Class<N> namespace, final K key) {
232         return requiresCtxImpl(contextImpl(context), namespace, key, EFFECTIVE_MODEL);
233     }
234
235     @Nonnull
236     @Override
237     public <N extends IdentifierNamespace<?, ?>> Prerequisite<Mutable<?, ?, ?>> mutatesNs(
238             final Mutable<?, ?, ?> context, final Class<N> namespace) {
239         return addMutation(new NamespaceMutation<>(contextImpl(context), namespace));
240     }
241
242     @Nonnull
243     @Override
244     public <K, E extends EffectiveStatement<?, ?>, N extends IdentifierNamespace<K, ? extends StmtContext<?, ?, ?>>>
245             AbstractPrerequisite<Mutable<?, ?, E>> mutatesEffectiveCtx(final StmtContext<?, ?, ?> context,
246                     final Class<N> namespace, final K key) {
247         return mutatesCtxImpl(context, namespace, key, EFFECTIVE_MODEL);
248     }
249
250     @Nonnull
251     @Override
252     @SuppressWarnings({ "rawtypes", "unchecked" })
253     public <K, E extends EffectiveStatement<?, ?>, N extends IdentifierNamespace<K, ? extends StmtContext<?, ?, ?>>>
254             AbstractPrerequisite<Mutable<?, ?, E>> mutatesEffectiveCtxPath(final StmtContext<?, ?, ?> context,
255                     final Class<N> namespace, final Iterable<K> keys) {
256         checkNotRegistered();
257
258         final PhaseModificationInNamespacePath<Mutable<?, ?, E>, K, N> ret = new PhaseModificationInNamespacePath<>(
259                 EFFECTIVE_MODEL, namespace, keys);
260         addReq(ret);
261         addMutation(ret);
262
263         contextImpl(context).onNamespaceItemAddedAction((Class) namespace, ret.nextKey(), ret);
264         return ret;
265     }
266
267     @Override
268     @SuppressWarnings("checkstyle:hiddenField")
269     public void apply(final InferenceAction action) {
270         checkState(this.action == null, "Action already defined to %s", this.action);
271         this.action = requireNonNull(action);
272     }
273
274     private abstract class AbstractPrerequisite<T> implements Prerequisite<T> {
275         private boolean done = false;
276         private T value;
277
278         @Override
279         @SuppressWarnings("checkstyle:hiddenField")
280         public final T resolve(final InferenceContext ctx) {
281             checkState(done);
282             checkArgument(ctx == ModifierImpl.this.ctx);
283             return value;
284         }
285
286         final boolean isDone() {
287             return done;
288         }
289
290         @SuppressWarnings("checkstyle:hiddenField")
291         final boolean resolvePrereq(final T value) {
292             this.value = value;
293             this.done = true;
294             return isApplied();
295         }
296
297         final <O> Prerequisite<O> transform(final Function<? super T, O> transformation) {
298             return context -> transformation.apply(resolve(context));
299         }
300
301         @Override
302         public final String toString() {
303             return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString();
304         }
305
306         ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
307             return toStringHelper.add("value", value);
308         }
309     }
310
311     private final class PhaseMutation<C> extends AbstractPrerequisite<C> implements ContextMutation {
312         @SuppressWarnings("unchecked")
313         PhaseMutation(final StatementContextBase<?, ?, ?> context, final ModelProcessingPhase phase) {
314             context.addMutation(phase, this);
315             resolvePrereq((C) context);
316         }
317
318         @Override
319         public boolean isFinished() {
320             return isApplied();
321         }
322     }
323
324     private final class PhaseFinished<C extends StmtContext<?, ?, ?>> extends AbstractPrerequisite<C>
325             implements OnPhaseFinished {
326         @SuppressWarnings("unchecked")
327         @Override
328         public boolean phaseFinished(final StatementContextBase<?, ?, ?> context,
329                 final ModelProcessingPhase finishedPhase) {
330             return resolvePrereq((C) context);
331         }
332     }
333
334     private final class NamespaceMutation<N extends IdentifierNamespace<?, ?>>
335             extends AbstractPrerequisite<Mutable<?, ?, ?>>  {
336         NamespaceMutation(final StatementContextBase<?, ?, ?> ctx, final Class<N> namespace) {
337             resolvePrereq(ctx);
338         }
339     }
340
341     private final class AddedToNamespace<C extends StmtContext<?, ?, ?>> extends AbstractPrerequisite<C>
342             implements OnNamespaceItemAdded, OnPhaseFinished {
343         private final ModelProcessingPhase phase;
344
345         AddedToNamespace(final ModelProcessingPhase phase) {
346             this.phase = requireNonNull(phase);
347         }
348
349         @Override
350         public void namespaceItemAdded(final StatementContextBase<?, ?, ?> context, final Class<?> namespace,
351                 final Object key, final Object value) {
352             ((StatementContextBase<?, ?, ?>) value).addPhaseCompletedListener(phase, this);
353         }
354
355         @SuppressWarnings("unchecked")
356         @Override
357         public boolean phaseFinished(final StatementContextBase<?, ?, ?> context,
358                 final ModelProcessingPhase finishedPhase) {
359             return resolvePrereq((C) context);
360         }
361
362         @Override
363         ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
364             return super.addToStringAttributes(toStringHelper).add("phase", phase);
365         }
366     }
367
368     private final class PhaseModificationInNamespace<C extends Mutable<?, ?, ?>> extends AbstractPrerequisite<C>
369             implements OnNamespaceItemAdded, ContextMutation {
370         private final ModelProcessingPhase modPhase;
371
372         PhaseModificationInNamespace(final ModelProcessingPhase phase) {
373             checkArgument(phase != null, "Model processing phase must not be null");
374             this.modPhase = phase;
375         }
376
377         @SuppressWarnings("unchecked")
378         @Override
379         public void namespaceItemAdded(final StatementContextBase<?, ?, ?> context, final Class<?> namespace,
380                 final Object key, final Object value) {
381             StatementContextBase<?, ?, ?> targetCtx = contextImpl(value);
382             targetCtx.addMutation(modPhase, this);
383             resolvePrereq((C) targetCtx);
384         }
385
386         @Override
387         public boolean isFinished() {
388             return isApplied();
389         }
390     }
391
392     private final class PhaseModificationInNamespacePath<C extends Mutable<?, ?, ?>, K,
393             N extends IdentifierNamespace<K, ? extends StmtContext<?, ?, ?>>> extends AbstractPrerequisite<C>
394             implements OnNamespaceItemAdded, ContextMutation {
395         private final ModelProcessingPhase modPhase;
396         private final Iterable<K> keys;
397         private final Iterator<K> it;
398
399         PhaseModificationInNamespacePath(final ModelProcessingPhase phase, final Class<N> namespace,
400             final Iterable<K> keys) {
401             this.modPhase = requireNonNull(phase);
402             this.keys = requireNonNull(keys);
403             it = keys.iterator();
404             checkArgument(it.hasNext(), "Namespace %s keys may not be empty", namespace);
405         }
406
407         K nextKey() {
408             return it.next();
409         }
410
411         @Override
412         public boolean isFinished() {
413             return isApplied();
414         }
415
416         @Override
417         public void namespaceItemAdded(final StatementContextBase<?, ?, ?> context, final Class<?> namespace,
418                 final Object key, final Object value) {
419             LOG.debug("Action for {} got key {}", keys, key);
420             if (!it.hasNext()) {
421                 resolvePrereq((C) value);
422                 return;
423             }
424
425             final K next = nextKey();
426             contextImpl(value).onNamespaceItemAddedAction((Class) namespace, next, this);
427         }
428     }
429 }