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