Bug 2366: Initial implementation of Statement Reactor 51/15351/9
authorMartin Ciglan <mciglan@cisco.com>
Fri, 20 Mar 2015 12:36:18 +0000 (13:36 +0100)
committerRobert Varga <nite@hq.sk>
Sat, 21 Mar 2015 17:23:11 +0000 (17:23 +0000)
Initial implementation of statement reactor,
which supports:
   - loading linkage statements from models
   - modifiers
   - resolving of forward references
   - building final statments

Implementation currently does not support:

- export of PrefixToQNameModule and QNameToStatementDefinition
  namespaces during loading statement streams.
- copying statement subtrees (useful for uses, augmentations)

Added initial set of tests - which deals with
RFC6020 import resolution.

Change-Id: Idde3ce496228a157ba11c07bccf6f177c52a8fe9
Signed-off-by: Tony Tkacik <ttkacik@cisco.com>
Signed-off-by: Martin Ciglan <mciglan@cisco.com>
16 files changed:
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/BuildGlobalContext.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/CrossSourceStatementReactor.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/EffectiveModelContext.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ModifierImpl.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/NamespaceBehaviourWithListeners.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/NamespaceStorageSupport.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/RootStatementContext.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SourceSpecificContext.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextWriter.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementDefinitionContext.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementIdentifier.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SubstatementContext.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/YangInferencePipeline.java [new file with mode: 0644]
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/test/ImportResolutionTest.java [new file with mode: 0644]
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/test/TestStatementSource.java [new file with mode: 0644]

diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/BuildGlobalContext.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/BuildGlobalContext.java
new file mode 100644 (file)
index 0000000..b538022
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.stmt.reactor;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.StorageNodeType;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceNotAvailableException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupportBundle;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.SourceSpecificContext.PhaseCompletionProgress;
+
+class BuildGlobalContext extends NamespaceStorageSupport implements NamespaceBehaviour.Registry {
+
+    private static final List<ModelProcessingPhase> PHASE_EXECUTION_ORDER = ImmutableList.<ModelProcessingPhase>builder()
+            .add(ModelProcessingPhase.SourceLinkage)
+            .add(ModelProcessingPhase.StatementDefinition)
+            .add(ModelProcessingPhase.FullDeclaration)
+            .add(ModelProcessingPhase.EffectiveModel)
+            .build();
+
+    private final Map<QName,StatementDefinitionContext<?,?,?>> definitions = new HashMap<>();
+    private final Map<Class<?>,NamespaceBehaviourWithListeners<?, ?, ?>> namespaces = new HashMap<>();
+
+
+    private final Map<ModelProcessingPhase,StatementSupportBundle> supports;
+    private final Set<SourceSpecificContext> sources = new HashSet<>();
+
+    private ModelProcessingPhase currentPhase;
+    private ModelProcessingPhase finishedPhase;
+
+    public BuildGlobalContext(Map<ModelProcessingPhase, StatementSupportBundle> supports) {
+        super();
+        this.supports = supports;
+    }
+
+    public void addSource(@Nonnull StatementStreamSource source) {
+        sources.add(new SourceSpecificContext(this,source));
+    }
+
+    @Override
+    public StorageNodeType getStorageNodeType() {
+        return StorageNodeType.Global;
+    }
+
+    @Override
+    public NamespaceStorageNode getParentNamespaceStorage() {
+        return null;
+    }
+
+    @Override
+    public NamespaceBehaviour.Registry getBehaviourRegistry() {
+        return this;
+    }
+
+    @Override
+    public <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviourWithListeners<K, V, N> getNamespaceBehaviour(Class<N> type) {
+        NamespaceBehaviourWithListeners<?, ?, ?> potential = namespaces.get(type);
+        if (potential == null) {
+            NamespaceBehaviour<K, V, N> potentialRaw = supports.get(currentPhase).getNamespaceBehaviour(type);
+            if(potentialRaw != null) {
+                potential = new NamespaceBehaviourWithListeners<>(potentialRaw);
+                namespaces.put(type, potential);
+            }
+        }
+        if (potential != null) {
+            Preconditions.checkState(type.equals(potential.getIdentifier()));
+
+            /*
+             * Safe cast, previous checkState checks equivalence of key from
+             * which type argument are derived
+             */
+            @SuppressWarnings("unchecked")
+            NamespaceBehaviourWithListeners<K, V, N> casted = (NamespaceBehaviourWithListeners<K, V, N>) potential;
+            return casted;
+        }
+        throw new NamespaceNotAvailableException("Namespace " + type + "is not available in phase " + currentPhase);
+    }
+
+    public StatementDefinitionContext<?, ?, ?> getStatementDefinition(QName name) {
+        StatementDefinitionContext<?, ?, ?> potential = definitions.get(name);
+        if(potential == null) {
+            StatementSupport<?, ?, ?> potentialRaw = supports.get(currentPhase).getStatementDefinition(name);
+            if(potentialRaw != null) {
+                potential = new StatementDefinitionContext<>(potentialRaw);
+                definitions.put(name, potential);
+            }
+        }
+        return potential;
+    }
+
+    public EffectiveModelContext build() throws SourceException, ReactorException {
+        for(ModelProcessingPhase phase : PHASE_EXECUTION_ORDER) {
+            startPhase(phase);
+            loadPhaseStatements();
+            completePhaseActions();
+            endPhase(phase);
+        }
+        return transform();
+    }
+
+    private EffectiveModelContext transform() {
+        Preconditions.checkState(finishedPhase == ModelProcessingPhase.EffectiveModel);
+        List<DeclaredStatement<?>> rootStatements = new ArrayList<>();
+        for(SourceSpecificContext source : sources) {
+            DeclaredStatement<?> root = source.getRoot().buildDeclared();
+            rootStatements.add(root);
+        }
+        return new EffectiveModelContext(rootStatements);
+    }
+
+    private void startPhase(ModelProcessingPhase phase) {
+        Preconditions.checkState(Objects.equals(finishedPhase, phase.getPreviousPhase()));
+        for(SourceSpecificContext source : sources) {
+            source.startPhase(phase);
+        }
+        currentPhase = phase;
+    }
+
+    private  void loadPhaseStatements() throws SourceException {
+        Preconditions.checkState(currentPhase != null);
+        for(SourceSpecificContext source : sources) {
+            source.loadStatements();
+        }
+    }
+
+    private  void completePhaseActions() throws ReactorException {
+        Preconditions.checkState(currentPhase != null);
+        ArrayList<SourceSpecificContext> sourcesToProgress = Lists.newArrayList(sources);
+        try {
+            boolean progressing = true;
+            while(progressing) {
+                // We reset progressing to false.
+                progressing = false;
+                Iterator<SourceSpecificContext> currentSource = sourcesToProgress.iterator();
+                while(currentSource.hasNext()) {
+                    PhaseCompletionProgress sourceProgress = currentSource.next().tryToCompletePhase(currentPhase);
+                    switch (sourceProgress) {
+                        case FINISHED:
+                            currentSource.remove();
+                        case PROGRESS:
+                            progressing = true;
+                        case NO_PROGRESS:
+                            // Noop;
+                    }
+                }
+            }
+        } catch (SourceException e) {
+            throw Throwables.propagate(e);
+        }
+        if(!sourcesToProgress.isEmpty()) {
+            SomeModifiersUnresolvedException buildFailure = new SomeModifiersUnresolvedException(currentPhase);
+                for(SourceSpecificContext failedSource : sourcesToProgress) {
+                    SourceException sourceEx = failedSource.failModifiers(currentPhase);
+                    buildFailure.addSuppressed(sourceEx);
+                }
+                throw buildFailure;
+        }
+    }
+
+    private  void endPhase(ModelProcessingPhase phase) {
+        Preconditions.checkState(currentPhase == phase);
+        finishedPhase = currentPhase;
+    }
+
+    public Set<SourceSpecificContext> getSources() {
+        return sources;
+    }
+
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/CrossSourceStatementReactor.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/CrossSourceStatementReactor.java
new file mode 100644 (file)
index 0000000..819dfff
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.stmt.reactor;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.EnumMap;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupportBundle;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+
+public class CrossSourceStatementReactor {
+
+    private final Map<ModelProcessingPhase,StatementSupportBundle> supportedTerminology;
+
+    CrossSourceStatementReactor(Map<ModelProcessingPhase, StatementSupportBundle> supportedTerminology) {
+        this.supportedTerminology = ImmutableMap.copyOf(supportedTerminology);
+    }
+
+    public static final Builder builder() {
+        return new Builder();
+    }
+
+    public final BuildAction newBuild() {
+        return new BuildAction();
+    }
+
+    public static class Builder implements org.opendaylight.yangtools.concepts.Builder<CrossSourceStatementReactor>{
+
+        final Map<ModelProcessingPhase,StatementSupportBundle> bundles = new EnumMap<>(ModelProcessingPhase.class);
+
+        public Builder setBundle(ModelProcessingPhase phase,StatementSupportBundle bundle) {
+            bundles.put(phase, bundle);
+            return this;
+        }
+
+        @Override
+        public CrossSourceStatementReactor build() {
+            return new CrossSourceStatementReactor(bundles);
+        }
+
+    }
+
+    public class BuildAction {
+
+        private final BuildGlobalContext context;
+
+        public BuildAction() {
+            this.context = new BuildGlobalContext(supportedTerminology);
+        }
+
+        public void addSource(StatementStreamSource source) {
+            context.addSource(source);
+        }
+
+        public EffectiveModelContext build() throws SourceException, ReactorException {
+            return context.build();
+        }
+
+
+
+    }
+
+
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/EffectiveModelContext.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/EffectiveModelContext.java
new file mode 100644 (file)
index 0000000..0b1e823
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.stmt.reactor;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public class EffectiveModelContext implements Immutable {
+
+    private final ImmutableList<DeclaredStatement<?>> rootStatements;
+
+    public EffectiveModelContext(List<DeclaredStatement<?>> rootStatements) {
+        this.rootStatements = ImmutableList.copyOf(rootStatements);
+    }
+
+    public ImmutableList<DeclaredStatement<?>> getRootStatements() {
+        return rootStatements;
+    }
+
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ModifierImpl.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ModifierImpl.java
new file mode 100644 (file)
index 0000000..fba7bb9
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.stmt.reactor;
+
+import static org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase.EffectiveModel;
+import static org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase.FullDeclaration;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase.ContextMutation;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase.OnNamespaceItemAdded;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase.OnPhaseFinished;
+
+class ModifierImpl implements ModelActionBuilder {
+
+    private final ModelProcessingPhase phase;
+    private final Set<AbstractPrerequisite<?>> unsatisfied = new HashSet<>();
+    private final Set<AbstractPrerequisite<?>> mutations = new HashSet<>();
+
+    private InferenceAction action;
+    private boolean actionApplied = false;
+
+    ModifierImpl(ModelProcessingPhase phase) {
+        this.phase = Preconditions.checkNotNull(phase);
+    }
+
+    private <D> AbstractPrerequisite<D> addReq(AbstractPrerequisite<D> prereq) {
+        unsatisfied.add(prereq);
+        return prereq;
+    }
+
+    private <T> AbstractPrerequisite<T> addMutation(AbstractPrerequisite<T> mutation) {
+        mutations.add(mutation);
+        return mutation;
+    }
+
+
+
+    private void checkNotRegistered() {
+        Preconditions.checkState(action == null, "Action was already registered.");
+    }
+
+    private IllegalStateException shouldNotHappenProbablyBug(SourceException e) {
+        return new IllegalStateException("Source exception during registering prerequisite. This is probably bug.",e);
+    }
+
+    private void tryToResolve() throws InferenceException {
+        if(action == null) {
+            return; // Action was not yet defined
+        }
+        if(removeSatisfied()) {
+            applyAction();
+        }
+    }
+
+    private boolean removeSatisfied() {
+        Iterator<AbstractPrerequisite<?>> prereq = unsatisfied.iterator();
+        boolean allSatisfied = true;
+        while(prereq.hasNext()) {
+            if(prereq.next().isDone()) {
+                prereq.remove(); // We are removing current prerequisite from list.
+            } else {
+                allSatisfied  = false;
+            }
+        }
+        return allSatisfied;
+    }
+
+    ModelProcessingPhase getPhase() {
+        return phase;
+    }
+
+    boolean isApplied() {
+
+        return actionApplied;
+    }
+
+    void failModifier() throws InferenceException {
+        removeSatisfied();
+        action.prerequisiteFailed(unsatisfied);
+        action = null;
+    }
+
+    private void applyAction() throws InferenceException {
+
+        action.apply();
+        // Mark all mutations as performed, so context node could move to next.
+        actionApplied = true;
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    private <K, C extends StmtContext<?,?,?>, N extends StatementNamespace<K, ?, ?>> AbstractPrerequisite<C> requiresCtxImpl(StmtContext<?, ?, ?> context, Class<N> namespace, K key,ModelProcessingPhase phase)  {
+        checkNotRegistered();
+        try {
+            AddedToNamespace<C> addedToNs = new AddedToNamespace<C>(phase);
+            addReq(addedToNs);
+            contextImpl(context).onNamespaceItemAddedAction((Class) namespace,key,addedToNs);
+            return addedToNs;
+        } catch (SourceException e) {
+            throw shouldNotHappenProbablyBug(e);
+        }
+    }
+
+    private <C extends StmtContext<?, ?, ?>> AbstractPrerequisite<C> requiresCtxImpl(C context, ModelProcessingPhase phase) {
+        Preconditions.checkState(action == null, "Action was already registered.");
+        try {
+            PhaseFinished<C> phaseFin = new PhaseFinished<C>();
+            addReq(phaseFin);
+            contextImpl(context).addPhaseCompletedListener(FullDeclaration,phaseFin);
+            return phaseFin;
+        } catch (SourceException e) {
+            throw shouldNotHappenProbablyBug(e);
+        }
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    private <K, C extends StmtContext.Mutable<?, ?, ?> , N extends StatementNamespace<K, ?, ? >> AbstractPrerequisite<C> mutatesCtxImpl(
+                StmtContext<?, ?, ?> context, Class<N> namespace, K key, ModelProcessingPhase phase) {
+            try {
+                PhaseModificationInNamespace<C> mod = new PhaseModificationInNamespace<C>(phase);
+                addMutation(mod);
+                contextImpl(context).onNamespaceItemAddedAction((Class) namespace,key,mod);
+                return mod;
+            } catch (SourceException e) {
+                throw shouldNotHappenProbablyBug(e);
+            }
+        }
+
+    private static StatementContextBase<?,?,?> contextImpl(StmtContext<?,?,?> context) {
+        Preconditions.checkArgument(context instanceof StatementContextBase,"Supplied context was not provided by this reactor.");
+        return StatementContextBase.class.cast(context);
+    }
+
+    @Override
+    public <C extends Mutable<?, ?, ?>, CT extends C> Prerequisite<C> mutatesCtx(CT context, ModelProcessingPhase phase) {
+        try {
+            return addMutation(new PhaseMutation<C>(contextImpl(context),phase));
+        } catch (InferenceException e) {
+            throw shouldNotHappenProbablyBug(e);
+        }
+    }
+
+    @Override
+    public  <A,D extends DeclaredStatement<A>,E extends EffectiveStatement<A, D>> AbstractPrerequisite<StmtContext<A, D, E>> requiresCtx(StmtContext<A, D, E> context, ModelProcessingPhase phase) {
+        return requiresCtxImpl(context, phase);
+    }
+
+
+    @Override
+    public <K, N extends StatementNamespace<K, ?, ? >> Prerequisite<StmtContext<?,?,?>> requiresCtx(StmtContext<?, ?, ?> context, Class<N> namespace, K key, ModelProcessingPhase phase) {
+        return requiresCtxImpl(context, namespace, key, phase);
+    }
+
+    @Override
+    public <D extends DeclaredStatement<?>> Prerequisite<D> requiresDeclared(StmtContext<?, ? extends D, ?> context) {
+        return requiresCtxImpl(context,FullDeclaration).transform(StmtContextUtils.<D>buildDeclared());
+    }
+
+    @Override
+    public <K, D extends DeclaredStatement<?>, N extends StatementNamespace<K, ? extends D, ?>> AbstractPrerequisite<StmtContext<?, D, ?>> requiresDeclaredCtx(
+            StmtContext<?, ?, ?> context, Class<N> namespace, K key) {
+        return requiresCtxImpl(context, namespace, key,FullDeclaration);
+    }
+
+    @Override
+    public <K, D extends DeclaredStatement<?>, N extends StatementNamespace<K, ? extends D, ?>> Prerequisite<D> requiresDeclared(
+            StmtContext<?, ?, ?> context, Class<N> namespace, K key) {
+        final AbstractPrerequisite<StmtContext<?,D,?>> rawContext = requiresCtxImpl(context, namespace, key,FullDeclaration);
+        return rawContext.transform(StmtContextUtils.<D>buildDeclared());
+    }
+
+    @Override
+    public <E extends EffectiveStatement<?, ?>> Prerequisite<E> requiresEffective(StmtContext<?, ?, ? extends E> stmt) {
+        return requiresCtxImpl(stmt,EffectiveModel).transform(StmtContextUtils.<E>buildEffective());
+    }
+
+    @Override
+    public <K, E extends EffectiveStatement<?, ?>, N extends StatementNamespace<K, ?, ? extends E>> AbstractPrerequisite<StmtContext<?, ?, E>> requiresEffectiveCtx(
+            StmtContext<?, ?, ?> context, Class<N> namespace, K key) {
+        return requiresCtxImpl(contextImpl(context),namespace,key,EffectiveModel);
+    }
+
+    @Override
+    public <K, E extends EffectiveStatement<?, ?>, N extends StatementNamespace<K, ?, ? extends E>> Prerequisite<E> requiresEffective(
+            StmtContext<?, ?, ?> context, Class<N> namespace, K key) {
+        final AbstractPrerequisite<StmtContext<?,?,E>> rawContext = requiresCtxImpl(context, namespace, key,EffectiveModel);
+        return rawContext.transform(StmtContextUtils.<E>buildEffective());
+    }
+
+
+    @Override
+    public <N extends IdentifierNamespace<?, ?>> Prerequisite<Mutable<?, ?, ?>> mutatesNs(Mutable<?, ?, ?> context,
+            Class<N> namespace) {
+        try {
+            return addMutation(new NamespaceMutation<N>(contextImpl(context),namespace));
+        } catch (SourceException e) {
+            throw shouldNotHappenProbablyBug(e);
+        }
+    }
+
+    @Override
+    public <T extends Mutable<?, ?, ?>> Prerequisite<T> mutatesEffectiveCtx(T stmt) {
+        return mutatesCtx(stmt, EffectiveModel);
+    }
+
+
+   @Override
+    public <K, E extends EffectiveStatement<?, ?>, N extends StatementNamespace<K, ?, ? extends E>> AbstractPrerequisite<Mutable<?, ?, E>> mutatesEffectiveCtx(
+            StmtContext<?, ?, ?> context, Class<N> namespace, K key) {
+        return mutatesCtxImpl(context, namespace, key, EffectiveModel);
+    }
+
+
+
+    @Override
+    public void apply(InferenceAction action) throws InferenceException {
+        this.action = Preconditions.checkNotNull(action);
+        tryToResolve();
+    }
+
+    private abstract class AbstractPrerequisite<T> implements Prerequisite<T> {
+
+        private T value;
+        private boolean done = false;
+
+        @Override
+        public T get() {
+            Preconditions.checkState(isDone());
+            return value;
+        }
+
+        @Override
+        public boolean isDone() {
+            return done;
+        }
+
+        protected void resolvePrereq(T value) throws InferenceException {
+            Preconditions.checkState(!isDone());
+            this.value = value;
+            this.done = true;
+            tryToResolve();
+        }
+
+        protected <O> Prerequisite<O> transform(final Function<? super T,O> transformation) {
+
+            return new Prerequisite<O>() {
+
+                @Override
+                public O get() {
+                    return transformation.apply(AbstractPrerequisite.this.get());
+                }
+
+                @Override
+                public boolean isDone() {
+                    return AbstractPrerequisite.this.isDone();
+                }
+
+            };
+        }
+
+    }
+
+    private class PhaseMutation<C> extends AbstractPrerequisite<C> implements ContextMutation {
+
+        @SuppressWarnings("unchecked")
+        public PhaseMutation(StatementContextBase<?, ?, ?> context, ModelProcessingPhase phase) throws InferenceException {
+            context.addMutation(phase, this);
+            resolvePrereq((C) context);
+        }
+
+        @Override
+        public boolean isFinished() {
+            return isApplied();
+        }
+
+
+    }
+    private class PhaseFinished<C extends StmtContext<?, ?, ?>> extends AbstractPrerequisite<C> implements OnPhaseFinished {
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public void phaseFinished(StatementContextBase<?, ?, ?> context, ModelProcessingPhase phase) throws SourceException {
+            resolvePrereq((C) (context));
+        }
+    }
+
+    private class NamespaceMutation<N extends IdentifierNamespace<?,?>> extends  AbstractPrerequisite<StmtContext.Mutable<?, ?, ?>>  {
+
+        public NamespaceMutation(StatementContextBase<?, ?, ?> ctx, Class<N> namespace) throws InferenceException {
+            resolvePrereq(ctx);
+        }
+
+    }
+
+    private class AddedToNamespace<C extends StmtContext<?,?,?>> extends  AbstractPrerequisite<C> implements OnNamespaceItemAdded,OnPhaseFinished {
+
+        private final ModelProcessingPhase phase;
+
+        public <K, N extends StatementNamespace<K, ?, ?>> AddedToNamespace(ModelProcessingPhase phase) {
+            this.phase = phase;
+        }
+
+        @Override
+        public void namespaceItemAdded(StatementContextBase<?, ?, ?> context, Class<?> namespace, Object key,
+                Object value) throws SourceException {
+            StatementContextBase<?, ?, ?> targetContext = (StatementContextBase<?, ?, ?>) value;
+            targetContext.addPhaseCompletedListener(phase, this);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public void phaseFinished(StatementContextBase<?, ?, ?> context, ModelProcessingPhase phase) throws SourceException {
+            resolvePrereq((C) context);
+        }
+
+    }
+
+    private class PhaseModificationInNamespace<C extends Mutable<?,?,?>> extends AbstractPrerequisite<C> implements OnNamespaceItemAdded, ContextMutation {
+
+        private final ModelProcessingPhase modPhase;
+
+        public <K, N extends StatementNamespace<K, ?, ?>> PhaseModificationInNamespace(ModelProcessingPhase phase) throws SourceException {
+            Preconditions.checkArgument(phase != null, "Model processing phase must not be null");
+            this.modPhase = phase;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public void namespaceItemAdded(StatementContextBase<?, ?, ?> context, Class<?> namespace, Object key,
+                Object value) throws SourceException {
+            context.addMutation(modPhase,this);
+            resolvePrereq((C) context);
+        }
+
+        @Override
+        public boolean isFinished() {
+            return isApplied();
+        }
+    }
+
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/NamespaceBehaviourWithListeners.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/NamespaceBehaviourWithListeners.java
new file mode 100644 (file)
index 0000000..2a99e6e
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.stmt.reactor;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import java.util.Iterator;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+
+final class NamespaceBehaviourWithListeners<K,V, N extends IdentifierNamespace<K, V>> extends NamespaceBehaviour<K, V, N> {
+
+    static abstract class ValueAddedListener {
+
+        private org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode ctxNode;
+
+        public ValueAddedListener(NamespaceStorageNode contextNode) {
+            this.ctxNode = contextNode;
+        }
+
+        abstract void onValueAdded(Object key, Object value);
+
+    }
+
+    private final NamespaceBehaviour<K, V, N> delegate;
+    private final Multimap<K, ValueAddedListener> listeners = HashMultimap.create();
+
+    protected NamespaceBehaviourWithListeners(NamespaceBehaviour<K, V, N> delegate) {
+        super(delegate.getIdentifier());
+        this.delegate = delegate;
+    }
+
+    @Override
+    public void addTo(NamespaceBehaviour.NamespaceStorageNode storage,
+            K key, V value) {
+        delegate.addTo(storage, key, value);
+
+        Iterator<ValueAddedListener> keyListeners = listeners.get(key).iterator();
+        while(keyListeners.hasNext()) {
+            ValueAddedListener listener = keyListeners.next();
+            if(listener.ctxNode == storage || hasIdentiticalValue(listener.ctxNode,key,value)) {
+                keyListeners.remove();
+                listener.onValueAdded(key, value);
+            }
+        }
+    }
+
+    private boolean hasIdentiticalValue(NamespaceBehaviour.NamespaceStorageNode ctxNode, K key, V value) {
+        return getFrom(ctxNode, key) == value;
+    }
+
+    void addValueListener(K key, ValueAddedListener listener) {
+        listeners.put(key, listener);
+    }
+
+    @Override
+    public V getFrom(NamespaceBehaviour.NamespaceStorageNode storage,
+            K key) {
+        return delegate.getFrom(storage, key);
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/NamespaceStorageSupport.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/NamespaceStorageSupport.java
new file mode 100644 (file)
index 0000000..f79a062
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.stmt.reactor;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceNotAvailableException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+abstract class NamespaceStorageSupport implements NamespaceStorageNode {
+
+    private final Map<Class<?>,Map<?,?>> namespaces = new HashMap<>();
+
+
+    @Override
+    public abstract NamespaceStorageNode getParentNamespaceStorage();
+
+    public abstract NamespaceBehaviour.Registry getBehaviourRegistry();
+
+    protected void checkLocalNamespaceAllowed(Class<? extends IdentifierNamespace<?, ?>> type) {
+        // NOOP
+    }
+
+    protected <K, V, N extends IdentifierNamespace<K, V>> void onNamespaceElementAdded(Class<N> type, K key, V value) {
+        // NOOP
+    }
+
+    public final <K, VT, V extends VT ,N extends IdentifierNamespace<K, V>> VT getFromNamespace(Class<N> type, K key)
+            throws NamespaceNotAvailableException {
+        return getBehaviourRegistry().getNamespaceBehaviour(type).getFrom(this,key);
+    }
+
+    public final <K,V,VT extends V,N extends IdentifierNamespace<K, V>> void addToNs(Class<N> type, K key, VT value)
+            throws NamespaceNotAvailableException {
+        getBehaviourRegistry().getNamespaceBehaviour(type).addTo(this,key,value);
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public final <K, N extends StatementNamespace<K, ?,?>> void addContextToNamespace(Class<N> type, K key, StmtContext<?, ?, ?> value)
+            throws NamespaceNotAvailableException {
+        getBehaviourRegistry().getNamespaceBehaviour((Class)type).addTo(this, key, value);
+    }
+
+    @Override
+    public <K, V, N extends IdentifierNamespace<K, V>> V getFromLocalStorage(Class<N> type, K key) {
+        @SuppressWarnings("unchecked")
+        Map<K, V> localNamespace = (Map<K,V>) namespaces.get(type);
+        if(localNamespace != null) {
+            return localNamespace.get(key);
+        }
+        return null;
+    }
+
+
+
+    @Override
+    public <K, V, N extends IdentifierNamespace<K, V>> void addToLocalStorage(Class<N> type, K key, V value) {
+        @SuppressWarnings("unchecked")
+        Map<K, V> localNamespace = (Map<K,V>) namespaces.get(type);
+        if(localNamespace == null) {
+            checkLocalNamespaceAllowed(type);
+            localNamespace = new HashMap<>();
+            namespaces.put(type, localNamespace);
+        }
+        localNamespace.put(key,value);
+        onNamespaceElementAdded(type,key,value);
+    }
+
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/RootStatementContext.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/RootStatementContext.java
new file mode 100644 (file)
index 0000000..295d2cc
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.stmt.reactor;
+
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.Registry;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+
+class RootStatementContext<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
+    extends StatementContextBase<A, D,E> {
+
+
+    private final SourceSpecificContext sourceContext;
+
+    RootStatementContext(ContextBuilder<A, D,E> builder, SourceSpecificContext sourceContext) throws SourceException {
+        super(builder);
+        this.sourceContext = sourceContext;
+    }
+
+    @Override
+    public StatementContextBase<?,?, ?> getParentContext() {
+        return null;
+    }
+
+    @Override
+    public NamespaceStorageNode getParentNamespaceStorage() {
+        return sourceContext;
+    }
+
+    @Override
+    public Registry getBehaviourRegistry() {
+        return sourceContext;
+    }
+
+    @Override
+    public RootStatementContext<?,?,?> getRoot() {
+        return this;
+    }
+
+    SourceSpecificContext getSourceContext() {
+        return sourceContext;
+    }
+
+
+
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SourceSpecificContext.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SourceSpecificContext.java
new file mode 100644 (file)
index 0000000..7cdcfb0
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.stmt.reactor;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Objects;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Mutable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ImportedNamespaceContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.StorageNodeType;
+import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
+import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase.ContextBuilder;
+
+public class SourceSpecificContext implements NamespaceStorageNode, NamespaceBehaviour.Registry, Mutable {
+
+    public enum PhaseCompletionProgress {
+        NO_PROGRESS,
+        PROGRESS,
+        FINISHED
+    }
+
+    private final StatementStreamSource source;
+    private final BuildGlobalContext currentContext;
+    private final Collection<NamespaceStorageNode> importedNamespaces = new ArrayList<>();
+    private final Multimap<ModelProcessingPhase, ModifierImpl> modifiers = HashMultimap.create();
+
+    private RootStatementContext<?,?, ?> root;
+
+    private ModelProcessingPhase inProgressPhase;
+    private ModelProcessingPhase finishedPhase;
+
+
+    SourceSpecificContext(BuildGlobalContext currentContext,StatementStreamSource source) {
+        this.source = source;
+        this.currentContext = currentContext;
+    }
+
+    StatementDefinitionContext<?,?,?> getDefinition(QName name) {
+        return currentContext.getStatementDefinition(name);
+    }
+
+    ContextBuilder<?, ?, ?> createDeclaredChild(StatementContextBase<?, ?, ?> current, QName name, StatementSourceReference ref) {
+        StatementDefinitionContext<?,?,?> def = getDefinition(name);
+        Preconditions.checkArgument(def != null, "Statement %s does not have type mapping defined.",name);
+        if(current == null) {
+            return createDeclaredRoot(def,ref);
+        }
+        return current.substatementBuilder(def,ref);
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    private ContextBuilder<?,?, ?> createDeclaredRoot(StatementDefinitionContext<?,?,?> def, StatementSourceReference ref) {
+        return new ContextBuilder(def,ref) {
+
+            @Override
+            public StatementContextBase build() throws SourceException {
+                if(root == null) {
+                    root = new RootStatementContext(this, SourceSpecificContext.this);
+                } else {
+                    Preconditions.checkState(root.getIdentifier().equals(getIdentifier()), "Root statement was already defined.");
+                }
+                root.resetLists();
+                return root;
+            }
+
+        };
+    }
+
+    RootStatementContext<?,?,?> getRoot() {
+        return root;
+    }
+
+    DeclaredStatement<?> buildDeclared() {
+        return root.buildDeclared();
+    }
+
+    EffectiveStatement<?,?> build() {
+        return root.buildEffective();
+    }
+
+    void startPhase(ModelProcessingPhase phase) {
+        @Nullable ModelProcessingPhase previousPhase = phase.getPreviousPhase();
+        Preconditions.checkState(Objects.equals(previousPhase, finishedPhase));
+        Preconditions.checkState(modifiers.get(previousPhase).isEmpty());
+        inProgressPhase = phase;
+    }
+
+    @Override
+    public <K, V, N extends IdentifierNamespace<K, V>> void addToLocalStorage(Class<N> type, K key, V value) {
+        if(ImportedNamespaceContext.class.isAssignableFrom(type)) {
+            importedNamespaces.add((NamespaceStorageNode) value);
+        }
+        getRoot().addToLocalStorage(type, key, value);
+    }
+
+    @Override
+    public StorageNodeType getStorageNodeType() {
+        return StorageNodeType.SourceLocalSpecial;
+    }
+
+    @Override
+    public <K, V, N extends IdentifierNamespace<K, V>> V getFromLocalStorage(Class<N> type, K key) {
+        final V potentialLocal = getRoot().getFromLocalStorage(type, key);
+        if(potentialLocal != null) {
+            return potentialLocal;
+        }
+        for(NamespaceStorageNode importedSource : importedNamespaces) {
+            V potential = importedSource.getFromLocalStorage(type, key);
+            if(potential != null) {
+                return potential;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviour<K, V, N> getNamespaceBehaviour(Class<N> type) {
+        return currentContext.getNamespaceBehaviour(type);
+    }
+
+    @Override
+    public NamespaceStorageNode getParentNamespaceStorage() {
+        return currentContext;
+    }
+
+    PhaseCompletionProgress tryToCompletePhase(ModelProcessingPhase phase) throws SourceException {
+        Collection<ModifierImpl> currentPhaseModifiers = modifiers.get(phase);
+        Iterator<ModifierImpl> modifier = currentPhaseModifiers.iterator();
+        boolean hasProgressed = false;
+        while(modifier.hasNext()) {
+            if(modifier.next().isApplied()) {
+                modifier.remove();
+                hasProgressed = true;
+            }
+        }
+        if(root.tryToCompletePhase(phase) && currentPhaseModifiers.isEmpty()) {
+            finishedPhase = phase;
+            return PhaseCompletionProgress.FINISHED;
+
+        }
+        if(hasProgressed) {
+            return PhaseCompletionProgress.PROGRESS;
+        }
+        return PhaseCompletionProgress.NO_PROGRESS;
+    }
+
+    ModelActionBuilder newInferenceAction(ModelProcessingPhase phase) {
+        ModifierImpl action = new ModifierImpl(phase);
+        modifiers.put(phase, action);
+        return action;
+    }
+
+    @Override
+    public String toString() {
+        return "SourceSpecificContext [source=" + source + ", current=" + inProgressPhase + ", finished="
+                + finishedPhase + "]";
+    }
+
+    SourceException failModifiers(ModelProcessingPhase identifier) {
+        InferenceException sourceEx = new InferenceException("Fail to infer source relationships", root.getStatementSourceReference());
+
+
+        for(ModifierImpl mod : modifiers.get(identifier)) {
+            try {
+                mod.failModifier();
+            } catch (SourceException e) {
+                sourceEx.addSuppressed(e);
+            }
+        }
+        return sourceEx;
+    }
+
+    void loadStatements() throws SourceException {
+        switch (inProgressPhase) {
+        case SourceLinkage:
+            source.writeLinkage(new StatementContextWriter(this, inProgressPhase),stmtDef());
+            break;
+        case StatementDefinition:
+            source.writeLinkageAndStatementDefinitions(new StatementContextWriter(this, inProgressPhase), stmtDef(), prefixes());
+        case FullDeclaration:
+            source.writeFull(new StatementContextWriter(this, inProgressPhase), stmtDef(), prefixes());
+
+        default:
+            break;
+        }
+    }
+
+    private PrefixToModule prefixes() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    private QNameToStatementDefinition stmtDef() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java
new file mode 100644 (file)
index 0000000..035e192
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.stmt.reactor;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EventListener;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.StorageNodeType;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.NamespaceBehaviourWithListeners.ValueAddedListener;
+
+abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>> extends
+        NamespaceStorageSupport implements StmtContext.Mutable<A, D, E>, Identifiable<StatementIdentifier> {
+
+    interface OnNamespaceItemAdded extends EventListener{
+
+        void namespaceItemAdded(StatementContextBase<?,?,?> context, Class<?> namespace, Object key, Object value) throws SourceException;
+
+    }
+
+    interface OnPhaseFinished extends EventListener{
+
+        void phaseFinished(StatementContextBase<?,?,?> context, ModelProcessingPhase phase) throws SourceException;
+
+    }
+
+    interface ContextMutation {
+
+        boolean isFinished();
+
+    }
+
+    abstract static class ContextBuilder<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>> {
+
+        private final StatementDefinitionContext<A, D, E> definition;
+        private final StatementSourceReference stmtRef;
+        private String rawArg;
+        private StatementSourceReference argRef;
+
+        public ContextBuilder(StatementDefinitionContext<A, D, E> def, StatementSourceReference sourceRef) {
+            this.definition = def;
+            this.stmtRef = sourceRef;
+        }
+
+        public void setArgument(@Nonnull String argument, @Nonnull StatementSourceReference argumentSource) {
+            Preconditions.checkArgument(definition.hasArgument(), "Statement does not take argument.");
+            this.rawArg = Preconditions.checkNotNull(argument);
+            this.argRef = Preconditions.checkNotNull(argumentSource);
+        }
+
+        public String getRawArgument() {
+            return rawArg;
+        }
+
+        public StatementSourceReference getStamementSource() {
+            return stmtRef;
+        }
+
+        public StatementSourceReference getArgumentSource() {
+            return argRef;
+        }
+
+        public StatementDefinitionContext<A, D, E> getDefinition() {
+            return definition;
+        }
+
+        public StatementIdentifier getIdentifier() {
+            return new StatementIdentifier(definition.getStatementName(), rawArg);
+        }
+
+        public abstract StatementContextBase<A, D, E> build() throws SourceException;
+
+    }
+
+    private final StatementDefinitionContext<A, D, E> definition;
+    private final StatementIdentifier identifier;
+    private final StatementSourceReference statementDeclSource;
+    private final A argument;
+
+    private LinkedHashMap<StatementIdentifier, StatementContextBase<?, ?, ?> > substatements = new LinkedHashMap<>();
+
+    private Collection<StatementContextBase<?, ?, ?>> declared = new ArrayList<>();
+    private Collection<StatementContextBase<?, ?, ?>> effective = new ArrayList<>();
+
+    private ModelProcessingPhase completedPhase;
+
+    private Multimap<ModelProcessingPhase,OnPhaseFinished> phaseListeners = HashMultimap.create();
+    private Multimap<ModelProcessingPhase, ContextMutation> phaseMutation = HashMultimap.create();
+
+    private D declaredInstance;
+    private E effectiveInstance;
+
+
+    StatementContextBase(@Nonnull ContextBuilder<A, D, E> builder) throws SourceException {
+        this.definition = builder.getDefinition();
+        this.identifier = builder.getIdentifier();
+        this.statementDeclSource = builder.getStamementSource();
+        this.argument = definition.parseArgumentValue(this, this.rawStatementArgument());
+        this.completedPhase = null;
+    }
+
+    @Override
+    public abstract StatementContextBase<?, ?, ?> getParentContext();
+
+    @Override
+    public abstract RootStatementContext<?, ?, ?> getRoot();
+
+
+    @Override
+    public StatementIdentifier getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public StatementSource getStatementSource() {
+        return statementDeclSource.getStatementSource();
+    }
+
+    @Override
+    public StatementSourceReference getStatementSourceReference() {
+        return statementDeclSource;
+    }
+
+    @Override
+    public String rawStatementArgument() {
+        return identifier.getArgument();
+    }
+
+    @Override
+    public A getStatementArgument() {
+        return argument;
+    }
+
+    @Override
+    public Collection<? extends StmtContext<?, ?, ?>> declaredSubstatements() {
+        return Collections.unmodifiableCollection(declared);
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public ContextBuilder<?, ?, ?> substatementBuilder(StatementDefinitionContext<?, ?, ?> def,
+            StatementSourceReference ref) {
+        return new ContextBuilder(def, ref) {
+
+            @Override
+            public StatementContextBase build() throws SourceException {
+                StatementContextBase<?, ?, ?> potential = substatements.get(getIdentifier());
+                if(potential == null) {
+                    potential = new SubstatementContext(StatementContextBase.this, this);
+                    substatements.put(getIdentifier(), potential);
+                }
+                potential.resetLists();
+                switch (this.getStamementSource().getStatementSource()) {
+                    case DECLARATION:
+                        declared.add(potential);
+                        break;
+                    case CONTEXT:
+                        effective.add(potential);
+                        break;
+                }
+                return potential;
+            }
+        };
+    }
+
+    @Override
+    public StorageNodeType getStorageNodeType() {
+        return StorageNodeType.StatementLocal;
+    }
+
+    @Override
+    public D buildDeclared() {
+        Preconditions.checkArgument(completedPhase == ModelProcessingPhase.FullDeclaration || completedPhase == ModelProcessingPhase.EffectiveModel);
+        if (declaredInstance == null) {
+            declaredInstance = definition().getFactory().createDeclared(this);
+        }
+        return declaredInstance;
+    }
+
+    @Override
+    public E buildEffective() {
+        Preconditions.checkArgument(completedPhase == ModelProcessingPhase.EffectiveModel);
+        if (effectiveInstance == null) {
+            effectiveInstance = definition().getFactory().createEffective(this);
+        }
+        return effectiveInstance;
+    }
+
+
+    void resetLists() {
+        declared.clear();
+    }
+
+    boolean tryToCompletePhase(ModelProcessingPhase phase) throws SourceException {
+        if(phase.equals(completedPhase)) {
+            return true;
+        }
+        Iterator<ContextMutation> openMutations = phaseMutation.get(phase).iterator();
+        boolean finished = true;
+        while(openMutations.hasNext()) {
+            ContextMutation current = openMutations.next();
+            if(current.isFinished()) {
+                openMutations.remove();
+            } else {
+                finished = false;
+            }
+        }
+        for(StatementContextBase<?, ?, ?> child: declared) {
+            finished &= child.tryToCompletePhase(phase);
+        }
+        if(finished) {
+            onPhaseCompleted(phase);
+            return true;
+        }
+        return false;
+    }
+
+
+    private void onPhaseCompleted(ModelProcessingPhase phase) throws SourceException {
+        completedPhase = phase;
+        Iterator<OnPhaseFinished> listener = phaseListeners.get(completedPhase).iterator();
+        while(listener.hasNext()) {
+            listener.next().phaseFinished(this, phase);
+            listener.remove();
+        }
+    }
+
+    /**
+     *
+     * Ends declared section of current node.
+     *
+     * @param ref
+     * @throws SourceException
+     *
+     */
+    void endDeclared(StatementSourceReference ref,ModelProcessingPhase phase) throws SourceException {
+        definition().onDeclarationFinished(this,phase);
+    }
+
+    protected final StatementDefinitionContext<A, D, E> definition() {
+        return definition;
+    }
+
+    @Override
+    protected void checkLocalNamespaceAllowed(Class<? extends IdentifierNamespace<?, ?>> type) {
+        definition().checkNamespaceAllowed(type);
+    }
+
+    @Override
+    protected <K, V, N extends IdentifierNamespace<K, V>> void onNamespaceElementAdded(Class<N> type, K key, V value) {
+        //definition().onNamespaceElementAdded(this, type, key, value);
+    }
+
+    <K, V, N extends IdentifierNamespace<K, V>> void onNamespaceItemAddedAction(final Class<N> type, K key, final OnNamespaceItemAdded listener) throws SourceException {
+        Object potential = getFromNamespace(type, key);
+        if(potential != null) {
+            listener.namespaceItemAdded(this, type, key, potential);
+            return;
+        }
+        NamespaceBehaviour<K,V,N> behaviour = getBehaviourRegistry().getNamespaceBehaviour(type);
+        if(behaviour instanceof NamespaceBehaviourWithListeners) {
+            NamespaceBehaviourWithListeners<K, V, N> casted = (NamespaceBehaviourWithListeners<K,V,N>) behaviour;
+            casted.addValueListener(key, new ValueAddedListener(this) {
+                @Override
+                void onValueAdded(Object key, Object value) {
+                    try {
+                        listener.namespaceItemAdded(StatementContextBase.this, type, key, value);
+                    } catch (SourceException e) {
+                        throw Throwables.propagate(e);
+                    }
+                }
+            });
+        }
+    }
+
+    @Override
+    public StatementDefinition getPublicDefinition() {
+        return definition().getPublicView();
+    }
+
+    @Override
+    public ModelActionBuilder newInferenceAction(ModelProcessingPhase phase) {
+        return getRoot().getSourceContext().newInferenceAction(phase);
+    }
+
+    void addPhaseCompletedListener(ModelProcessingPhase phase, OnPhaseFinished listener) throws SourceException {
+        ModelProcessingPhase finishedPhase = completedPhase;
+        while (finishedPhase != null) {
+            if(phase.equals(finishedPhase)) {
+                listener.phaseFinished(this, finishedPhase);
+                return;
+            }
+            finishedPhase = finishedPhase.getPreviousPhase();
+        }
+        phaseListeners.put(phase, listener);
+    }
+
+    void addMutation(ModelProcessingPhase phase, ContextMutation mutation) {
+        ModelProcessingPhase finishedPhase = completedPhase;
+        while (finishedPhase != null) {
+            if(phase.equals(finishedPhase)) {
+                throw new IllegalStateException("Mutation registered after phase was completed.");
+            }
+            finishedPhase = finishedPhase.getPreviousPhase();
+        }
+        phaseMutation.put(phase, mutation);
+    }
+
+    @Override
+    public <K,KT extends K, N extends StatementNamespace<K, ?, ?>> void addContext(
+            Class<N> namepsace, KT key, StmtContext<?, ?, ?> stmt) {
+        addContextToNamespace(namepsace,(K) key, stmt);
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextWriter.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextWriter.java
new file mode 100644 (file)
index 0000000..3a1a8b7
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.stmt.reactor;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase.ContextBuilder;
+
+class StatementContextWriter implements StatementWriter {
+
+    private final SourceSpecificContext ctx;
+    private StatementContextBase<?, ?, ?> parent;
+    private ContextBuilder<?, ?, ?> current;
+    private ModelProcessingPhase phase;
+
+    public StatementContextWriter(SourceSpecificContext ctx, ModelProcessingPhase phase) {
+        this.ctx = Preconditions.checkNotNull(ctx);
+        this.phase = Preconditions.checkNotNull(phase);
+    }
+
+    @Override
+    public void startStatement(QName name, StatementSourceReference ref) throws SourceException {
+        defferedCreate();
+        current = ctx.createDeclaredChild(parent, name, ref);
+
+    }
+
+    @Override
+    public void argumentValue(String value, StatementSourceReference ref) {
+        Preconditions.checkState(current != null, "Could not two arguments for one statement.");
+        current.setArgument(value, ref);
+    }
+
+    void defferedCreate() throws SourceException {
+        if(current != null) {
+            parent = current.build();
+            current = null;
+        }
+    }
+
+    @Override
+    public void endStatement(StatementSourceReference ref) throws SourceException {
+        defferedCreate();
+        Preconditions.checkState(parent != null);
+        parent.endDeclared(ref,phase);
+        parent = parent.getParentContext();
+    }
+
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementDefinitionContext.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementDefinitionContext.java
new file mode 100644 (file)
index 0000000..bf99398
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.stmt.reactor;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceNotAvailableException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementFactory;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+class StatementDefinitionContext<A,D extends DeclaredStatement<A>,E extends EffectiveStatement<A,D>> {
+    private final StatementSupport<A,D,E> support;
+    public StatementDefinitionContext(StatementSupport<A,D,E> support) {
+        this.support= support;
+    }
+
+
+    public StatementFactory<A,D,E> getFactory() {
+        return support;
+    }
+
+    public A parseArgumentValue(StmtContext<A,D,E> context, String value) throws SourceException {
+        return support.parseArgumentValue(context,value);
+    }
+
+
+    public void checkNamespaceAllowed(Class<? extends IdentifierNamespace<?,?>> namespace) throws NamespaceNotAvailableException {
+        // Noop
+    }
+
+    public StatementDefinition getPublicView() {
+        return support.getPublicView();
+    }
+
+    public boolean onStatementAdded(Mutable<A,D,E> stmt) {
+        return false;
+    }
+
+
+    public void onDeclarationFinished(Mutable<A,D,E> statement, ModelProcessingPhase phase) throws SourceException {
+        switch (phase) {
+        case SourceLinkage:
+            support.onLinkageDeclared(statement);
+            break;
+        case StatementDefinition:
+            support.onStatementDefinitionDeclared(statement);
+        case FullDeclaration:
+            support.onFullDefinitionDeclared(statement);
+        default:
+            break;
+        }
+    }
+
+
+
+    public Class<?> getRepresentingClass() {
+        return support.getDeclaredRepresentationClass();
+    }
+
+
+    public boolean hasArgument() {
+        return support.getArgumentName() != null;
+    }
+
+
+    public QName getStatementName() {
+        return support.getStatementName();
+    }
+
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementIdentifier.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementIdentifier.java
new file mode 100644 (file)
index 0000000..4ebc2c2
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.stmt.reactor;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+
+class StatementIdentifier {
+
+    private final @Nonnull QName name;
+    private final @Nullable String argument;
+
+    StatementIdentifier(QName name, String argument) {
+        this.name = Preconditions.checkNotNull(name);
+        this.argument = argument;
+    }
+
+    QName getName() {
+        return name;
+    }
+
+    String getArgument() {
+        return argument;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result +  name.hashCode();
+        result = prime * result + ((argument == null) ? 0 : argument.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        StatementIdentifier other = (StatementIdentifier) obj;
+        if (!name.equals(other.name)) {
+            return false;
+        }
+        if (!Objects.equal(argument, other.argument)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "StatementIdentifier [name=" + name + ", argument=" + argument + "]";
+    }
+
+
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SubstatementContext.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SubstatementContext.java
new file mode 100644 (file)
index 0000000..aafdfca
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.stmt.reactor;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.Registry;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+class SubstatementContext<A,D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
+    extends StatementContextBase<A,D,E> {
+
+    private final StatementContextBase<?,?,?> parent;
+
+    SubstatementContext(StatementContextBase<?,?,?> parent,ContextBuilder<A,D,E> builder) throws SourceException {
+        super(builder);
+        this.parent = Preconditions.checkNotNull(parent, "Parent must not be null");
+    }
+
+    @Override
+    public StatementContextBase<?,?,?> getParentContext() {
+        return parent;
+    }
+
+    @Override
+    public NamespaceStorageNode getParentNamespaceStorage() {
+        return parent;
+    }
+
+    @Override
+    public Registry getBehaviourRegistry() {
+        return parent.getBehaviourRegistry();
+    }
+
+    @Override
+    public RootStatementContext<?, ?, ?> getRoot() {
+        return parent.getRoot();
+    }
+
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/YangInferencePipeline.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/YangInferencePipeline.java
new file mode 100644 (file)
index 0000000..c16cd19
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.stmt.rfc6020;
+
+import static org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.global;
+import static org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.sourceLocal;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.parser.spi.ModuleNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.NamespaceToModule;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupportBundle;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+
+public final class YangInferencePipeline {
+
+    public static final StatementSupportBundle LINKAGE_BUNDLE = StatementSupportBundle.builder()
+            .addSupport(new ModuleStatementSupport())
+            .addSupport(new NamespaceStatementImpl.Definition())
+            .addSupport(new ImportStatementDefinition())
+            .addSupport(new PrefixStatementImpl.Definition())
+            .addSupport(global(ModuleNamespace.class))
+            .addSupport(global(NamespaceToModule.class))
+            .addSupport(sourceLocal(ImportedModuleContext.class))
+            .build();
+
+    private static final StatementSupportBundle STMT_DEF_BUNDLE = StatementSupportBundle.derivedFrom(LINKAGE_BUNDLE).build();
+
+    private static final StatementSupportBundle FULL_DECL_BUNDLE = StatementSupportBundle.derivedFrom(STMT_DEF_BUNDLE).build();
+
+    public static final Map<ModelProcessingPhase, StatementSupportBundle> RFC6020_BUNDLES = ImmutableMap
+            .<ModelProcessingPhase, StatementSupportBundle> builder()
+            .put(ModelProcessingPhase.SourceLinkage, LINKAGE_BUNDLE)
+            .put(ModelProcessingPhase.StatementDefinition,STMT_DEF_BUNDLE)
+            .put(ModelProcessingPhase.FullDeclaration,FULL_DECL_BUNDLE)
+            .put(ModelProcessingPhase.EffectiveModel,FULL_DECL_BUNDLE)
+            .build();
+
+    public static final CrossSourceStatementReactor RFC6020_REACTOR = CrossSourceStatementReactor.builder()
+            .setBundle(ModelProcessingPhase.SourceLinkage, LINKAGE_BUNDLE)
+            .setBundle(ModelProcessingPhase.StatementDefinition,STMT_DEF_BUNDLE)
+            .setBundle(ModelProcessingPhase.FullDeclaration,FULL_DECL_BUNDLE)
+            .setBundle(ModelProcessingPhase.EffectiveModel,FULL_DECL_BUNDLE)
+            .build();
+
+    private YangInferencePipeline() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/test/ImportResolutionTest.java b/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/test/ImportResolutionTest.java
new file mode 100644 (file)
index 0000000..afcd486
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yangtools.yang.stmt.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor.BuildAction;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
+
+public class ImportResolutionTest {
+
+    private static final TestStatementSource ROOT_WITHOUT_IMPORT = new TestStatementSource("nature");
+    private static final TestStatementSource IMPORT_ROOT = new TestStatementSource("mammal","nature");
+    private static final TestStatementSource IMPORT_DERIVED = new TestStatementSource("human", "mammal");
+    private static final TestStatementSource IMPORT_SELF = new TestStatementSource("egocentric", "egocentric");
+    private static final TestStatementSource CICLE_YIN = new TestStatementSource("cycle-yin", "cycle-yang");
+    private static final TestStatementSource CICLE_YANG = new TestStatementSource("cycle-yang", "cycle-yin");
+
+
+    @Test
+    public void inImportOrderTest() throws SourceException, ReactorException {
+        BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
+        addSources(reactor,ROOT_WITHOUT_IMPORT,IMPORT_ROOT,IMPORT_DERIVED);
+        EffectiveModelContext result = reactor.build();
+        assertNotNull(result);
+    }
+
+    @Test
+    public void inInverseOfImportOrderTest() throws SourceException, ReactorException {
+        BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
+        addSources(reactor,IMPORT_DERIVED,IMPORT_ROOT,ROOT_WITHOUT_IMPORT);
+        EffectiveModelContext result = reactor.build();
+        assertNotNull(result);
+    }
+
+    @Test
+    public void missingImportedSourceTest() throws SourceException {
+        BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
+        addSources(reactor,IMPORT_DERIVED,ROOT_WITHOUT_IMPORT);
+        try {
+            reactor.build();
+            fail("reactor.process should fail doe to misssing imported source");
+        } catch (ReactorException e) {
+            assertTrue(e instanceof SomeModifiersUnresolvedException);
+            assertEquals(ModelProcessingPhase.SourceLinkage,e.getPhase());
+        }
+
+    }
+
+    @Test
+    public void circularImportsTest() throws SourceException {
+        BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
+        addSources(reactor,CICLE_YIN,CICLE_YANG);
+        try {
+            reactor.build();
+            fail("reactor.process should fail doe to circular import");
+        } catch (ReactorException e) {
+            assertTrue(e instanceof SomeModifiersUnresolvedException);
+            assertEquals(ModelProcessingPhase.SourceLinkage,e.getPhase());
+        }
+    }
+
+    @Test
+    public void selfImportTest() throws SourceException {
+        BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
+        addSources(reactor,IMPORT_SELF,IMPORT_ROOT,ROOT_WITHOUT_IMPORT);
+        try {
+            reactor.build();
+            fail("reactor.process should fail doe to self import");
+        } catch (ReactorException e) {
+            assertTrue(e instanceof SomeModifiersUnresolvedException);
+            assertEquals(ModelProcessingPhase.SourceLinkage,e.getPhase());
+        }
+    }
+
+
+    private void addSources(BuildAction reactor, TestStatementSource... sources) {
+        for(TestStatementSource source : sources) {
+            reactor.addSource(source);
+        }
+    }
+
+}
diff --git a/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/test/TestStatementSource.java b/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/test/TestStatementSource.java
new file mode 100644 (file)
index 0000000..6da6f06
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.stmt.test;
+
+import static org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping.Import;
+import static org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping.Module;
+import static org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping.Namespace;
+import static org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping.Prefix;
+
+import java.util.Arrays;
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
+import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter;
+
+class TestStatementSource implements StatementStreamSource {
+
+    private static final String NS_PREFIX = "urn:org:opendaylight:yangtools:test:";
+
+    private final String name;
+    private final List<String> imports;
+    private StatementWriter writer;
+    private StatementSourceReference REF = new StatementSourceReference() {
+
+        @Override
+        public StatementSource getStatementSource() {
+            return StatementSource.DECLARATION;
+        }
+    };
+
+
+    public TestStatementSource(String name, String... imports) {
+        this.name = name;
+        this.imports = Arrays.asList(imports);
+    }
+
+    @Override
+    public void writeFull(StatementWriter writer, QNameToStatementDefinition stmtDef, PrefixToModule prefixes)
+            throws SourceException {
+        this.writer = writer;
+        header();
+        extensions();
+        body();
+        end();
+
+    }
+
+
+
+    @Override
+    public void writeLinkage(StatementWriter writer, QNameToStatementDefinition stmtDef) throws SourceException {
+        this.writer = writer;
+        header().end();
+    }
+
+    @Override
+    public void writeLinkageAndStatementDefinitions(StatementWriter writer, QNameToStatementDefinition stmtDef,
+            PrefixToModule prefixes) throws SourceException {
+        this.writer = writer;
+        header();
+        extensions();
+        end();
+
+    }
+
+    protected void extensions() {
+        // TODO Auto-generated method stub
+
+    }
+
+    protected void body() {
+
+    }
+
+    TestStatementSource header() throws SourceException {
+        stmt(Module).arg(name); {
+            stmt(Namespace).arg(getNamespace()).end();
+            stmt(Prefix).arg(name).end();
+            for(String imp : imports)  {
+                stmt(Import).arg(imp);
+                    stmt(Prefix).arg(imp).end();
+                end();
+            }
+        }
+        return this;
+    }
+
+    private String getNamespace() {
+        return NS_PREFIX + name;
+    }
+
+    protected TestStatementSource arg(String arg) throws SourceException {
+        writer.argumentValue(arg, REF);
+        return this;
+    }
+
+    protected TestStatementSource stmt(Rfc6020Mapping stmt) throws SourceException {
+        writer.startStatement(stmt.getStatementName(), REF);
+        return this;
+    }
+
+    protected TestStatementSource end() throws SourceException {
+        writer.endStatement(REF);
+        return this;
+    }
+
+
+}