Bug 2366: Initial implementation of Statement Reactor
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / BuildGlobalContext.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.parser.stmt.reactor;
9
10 import com.google.common.base.Preconditions;
11 import com.google.common.base.Throwables;
12 import com.google.common.collect.ImmutableList;
13 import com.google.common.collect.Lists;
14 import java.util.ArrayList;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Objects;
21 import java.util.Set;
22 import javax.annotation.Nonnull;
23 import org.opendaylight.yangtools.yang.common.QName;
24 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
25 import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
26 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
27 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
28 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode;
29 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.StorageNodeType;
30 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceNotAvailableException;
31 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
32 import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
33 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
34 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupportBundle;
35 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
36 import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
37 import org.opendaylight.yangtools.yang.parser.stmt.reactor.SourceSpecificContext.PhaseCompletionProgress;
38
39 class BuildGlobalContext extends NamespaceStorageSupport implements NamespaceBehaviour.Registry {
40
41     private static final List<ModelProcessingPhase> PHASE_EXECUTION_ORDER = ImmutableList.<ModelProcessingPhase>builder()
42             .add(ModelProcessingPhase.SourceLinkage)
43             .add(ModelProcessingPhase.StatementDefinition)
44             .add(ModelProcessingPhase.FullDeclaration)
45             .add(ModelProcessingPhase.EffectiveModel)
46             .build();
47
48     private final Map<QName,StatementDefinitionContext<?,?,?>> definitions = new HashMap<>();
49     private final Map<Class<?>,NamespaceBehaviourWithListeners<?, ?, ?>> namespaces = new HashMap<>();
50
51
52     private final Map<ModelProcessingPhase,StatementSupportBundle> supports;
53     private final Set<SourceSpecificContext> sources = new HashSet<>();
54
55     private ModelProcessingPhase currentPhase;
56     private ModelProcessingPhase finishedPhase;
57
58     public BuildGlobalContext(Map<ModelProcessingPhase, StatementSupportBundle> supports) {
59         super();
60         this.supports = supports;
61     }
62
63     public void addSource(@Nonnull StatementStreamSource source) {
64         sources.add(new SourceSpecificContext(this,source));
65     }
66
67     @Override
68     public StorageNodeType getStorageNodeType() {
69         return StorageNodeType.Global;
70     }
71
72     @Override
73     public NamespaceStorageNode getParentNamespaceStorage() {
74         return null;
75     }
76
77     @Override
78     public NamespaceBehaviour.Registry getBehaviourRegistry() {
79         return this;
80     }
81
82     @Override
83     public <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviourWithListeners<K, V, N> getNamespaceBehaviour(Class<N> type) {
84         NamespaceBehaviourWithListeners<?, ?, ?> potential = namespaces.get(type);
85         if (potential == null) {
86             NamespaceBehaviour<K, V, N> potentialRaw = supports.get(currentPhase).getNamespaceBehaviour(type);
87             if(potentialRaw != null) {
88                 potential = new NamespaceBehaviourWithListeners<>(potentialRaw);
89                 namespaces.put(type, potential);
90             }
91         }
92         if (potential != null) {
93             Preconditions.checkState(type.equals(potential.getIdentifier()));
94
95             /*
96              * Safe cast, previous checkState checks equivalence of key from
97              * which type argument are derived
98              */
99             @SuppressWarnings("unchecked")
100             NamespaceBehaviourWithListeners<K, V, N> casted = (NamespaceBehaviourWithListeners<K, V, N>) potential;
101             return casted;
102         }
103         throw new NamespaceNotAvailableException("Namespace " + type + "is not available in phase " + currentPhase);
104     }
105
106     public StatementDefinitionContext<?, ?, ?> getStatementDefinition(QName name) {
107         StatementDefinitionContext<?, ?, ?> potential = definitions.get(name);
108         if(potential == null) {
109             StatementSupport<?, ?, ?> potentialRaw = supports.get(currentPhase).getStatementDefinition(name);
110             if(potentialRaw != null) {
111                 potential = new StatementDefinitionContext<>(potentialRaw);
112                 definitions.put(name, potential);
113             }
114         }
115         return potential;
116     }
117
118     public EffectiveModelContext build() throws SourceException, ReactorException {
119         for(ModelProcessingPhase phase : PHASE_EXECUTION_ORDER) {
120             startPhase(phase);
121             loadPhaseStatements();
122             completePhaseActions();
123             endPhase(phase);
124         }
125         return transform();
126     }
127
128     private EffectiveModelContext transform() {
129         Preconditions.checkState(finishedPhase == ModelProcessingPhase.EffectiveModel);
130         List<DeclaredStatement<?>> rootStatements = new ArrayList<>();
131         for(SourceSpecificContext source : sources) {
132             DeclaredStatement<?> root = source.getRoot().buildDeclared();
133             rootStatements.add(root);
134         }
135         return new EffectiveModelContext(rootStatements);
136     }
137
138     private void startPhase(ModelProcessingPhase phase) {
139         Preconditions.checkState(Objects.equals(finishedPhase, phase.getPreviousPhase()));
140         for(SourceSpecificContext source : sources) {
141             source.startPhase(phase);
142         }
143         currentPhase = phase;
144     }
145
146     private  void loadPhaseStatements() throws SourceException {
147         Preconditions.checkState(currentPhase != null);
148         for(SourceSpecificContext source : sources) {
149             source.loadStatements();
150         }
151     }
152
153     private  void completePhaseActions() throws ReactorException {
154         Preconditions.checkState(currentPhase != null);
155         ArrayList<SourceSpecificContext> sourcesToProgress = Lists.newArrayList(sources);
156         try {
157             boolean progressing = true;
158             while(progressing) {
159                 // We reset progressing to false.
160                 progressing = false;
161                 Iterator<SourceSpecificContext> currentSource = sourcesToProgress.iterator();
162                 while(currentSource.hasNext()) {
163                     PhaseCompletionProgress sourceProgress = currentSource.next().tryToCompletePhase(currentPhase);
164                     switch (sourceProgress) {
165                         case FINISHED:
166                             currentSource.remove();
167                         case PROGRESS:
168                             progressing = true;
169                         case NO_PROGRESS:
170                             // Noop;
171                     }
172                 }
173             }
174         } catch (SourceException e) {
175             throw Throwables.propagate(e);
176         }
177         if(!sourcesToProgress.isEmpty()) {
178             SomeModifiersUnresolvedException buildFailure = new SomeModifiersUnresolvedException(currentPhase);
179                 for(SourceSpecificContext failedSource : sourcesToProgress) {
180                     SourceException sourceEx = failedSource.failModifiers(currentPhase);
181                     buildFailure.addSuppressed(sourceEx);
182                 }
183                 throw buildFailure;
184         }
185     }
186
187     private  void endPhase(ModelProcessingPhase phase) {
188         Preconditions.checkState(currentPhase == phase);
189         finishedPhase = currentPhase;
190     }
191
192     public Set<SourceSpecificContext> getSources() {
193         return sources;
194     }
195
196 }