90b9a6e6533297fbbf44384b616127bf5358e120
[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.base.Verify;
13 import com.google.common.collect.ImmutableList;
14 import com.google.common.collect.Lists;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.HashMap;
18 import java.util.HashSet;
19 import java.util.Iterator;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Map.Entry;
23 import java.util.Objects;
24 import java.util.Set;
25 import javax.annotation.Nonnull;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
28 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
29 import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
30 import org.opendaylight.yangtools.yang.parser.spi.meta.DerivedNamespaceBehaviour;
31 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
32 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
33 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode;
34 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.StorageNodeType;
35 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceNotAvailableException;
36 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
37 import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
38 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
39 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupportBundle;
40 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
41 import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
42 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace;
43 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType;
44 import org.opendaylight.yangtools.yang.parser.stmt.reactor.SourceSpecificContext.PhaseCompletionProgress;
45 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.EffectiveSchemaContext;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 class BuildGlobalContext extends NamespaceStorageSupport implements NamespaceBehaviour.Registry {
50     private static final Logger LOG = LoggerFactory.getLogger(BuildGlobalContext.class);
51
52     private static final List<ModelProcessingPhase> PHASE_EXECUTION_ORDER = ImmutableList.<ModelProcessingPhase>builder()
53             .add(ModelProcessingPhase.SOURCE_LINKAGE)
54             .add(ModelProcessingPhase.STATEMENT_DEFINITION)
55             .add(ModelProcessingPhase.FULL_DECLARATION)
56             .add(ModelProcessingPhase.EFFECTIVE_MODEL)
57             .build();
58
59     private final Map<QName,StatementDefinitionContext<?,?,?>> definitions = new HashMap<>();
60     private final Map<Class<?>,NamespaceBehaviourWithListeners<?, ?, ?>> supportedNamespaces = new HashMap<>();
61
62     private final Map<ModelProcessingPhase,StatementSupportBundle> supports;
63     private final Set<SourceSpecificContext> sources = new HashSet<>();
64
65     private ModelProcessingPhase currentPhase = ModelProcessingPhase.INIT;
66     private ModelProcessingPhase finishedPhase = ModelProcessingPhase.INIT;
67
68     public BuildGlobalContext(final Map<ModelProcessingPhase, StatementSupportBundle> supports) {
69         super();
70         this.supports = Preconditions.checkNotNull(supports, "BuildGlobalContext#supports cannot be null");
71     }
72
73     public BuildGlobalContext(final Map<ModelProcessingPhase, StatementSupportBundle> supports, final Map<ValidationBundleType,Collection<?>> supportedValidation) {
74         super();
75         this.supports = Preconditions.checkNotNull(supports, "BuildGlobalContext#supports cannot be null");
76
77         for (Entry<ValidationBundleType, Collection<?>> validationBundle : supportedValidation.entrySet()) {
78             addToNs(ValidationBundlesNamespace.class, validationBundle.getKey(), validationBundle.getValue());
79         }
80     }
81
82     public StatementSupportBundle getSupportsForPhase(final ModelProcessingPhase currentPhase) {
83         return supports.get(currentPhase);
84     }
85
86     public void addSource(@Nonnull final StatementStreamSource source) {
87         sources.add(new SourceSpecificContext(this,source));
88     }
89
90     @Override
91     public StorageNodeType getStorageNodeType() {
92         return StorageNodeType.GLOBAL;
93     }
94
95     @Override
96     public NamespaceStorageNode getParentNamespaceStorage() {
97         return null;
98     }
99
100     @Override
101     public NamespaceBehaviour.Registry getBehaviourRegistry() {
102         return this;
103     }
104
105     @Override
106     public <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviourWithListeners<K, V, N> getNamespaceBehaviour(final Class<N> type) {
107         NamespaceBehaviourWithListeners<?, ?, ?> potential = supportedNamespaces.get(type);
108         if (potential == null) {
109             NamespaceBehaviour<K, V, N> potentialRaw = supports.get(currentPhase).getNamespaceBehaviour(type);
110             if (potentialRaw != null) {
111                 potential = createNamespaceContext(potentialRaw);
112                 supportedNamespaces.put(type, potential);
113             } else {
114                 throw new NamespaceNotAvailableException(
115                         "Namespace " + type + " is not available in phase " + currentPhase);
116             }
117         }
118
119         Verify.verify(type.equals(potential.getIdentifier()));
120         /*
121          * Safe cast, previous checkState checks equivalence of key from which type argument are
122          * derived
123          */
124         return (NamespaceBehaviourWithListeners<K, V, N>) potential;
125     }
126
127     @SuppressWarnings({"unchecked", "rawtypes"})
128     private <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviourWithListeners<K, V, N> createNamespaceContext(
129             final NamespaceBehaviour<K, V, N> potentialRaw) {
130         if (potentialRaw instanceof DerivedNamespaceBehaviour) {
131             VirtualNamespaceContext derivedContext =
132                     new VirtualNamespaceContext((DerivedNamespaceBehaviour) potentialRaw);
133             getNamespaceBehaviour(((DerivedNamespaceBehaviour) potentialRaw).getDerivedFrom())
134                     .addDerivedNamespace(derivedContext);
135             return derivedContext;
136         }
137         return new SimpleNamespaceContext<>(potentialRaw);
138     }
139
140     public StatementDefinitionContext<?, ?, ?> getStatementDefinition(final QName name) {
141         StatementDefinitionContext<?, ?, ?> potential = definitions.get(name);
142         if (potential == null) {
143             StatementSupport<?, ?, ?> potentialRaw = supports.get(currentPhase).getStatementDefinition(name);
144             if (potentialRaw != null) {
145                 potential = new StatementDefinitionContext<>(potentialRaw);
146                 definitions.put(name, potential);
147             }
148         }
149         return potential;
150     }
151
152     public EffectiveModelContext build() throws SourceException, ReactorException {
153         for (ModelProcessingPhase phase : PHASE_EXECUTION_ORDER) {
154             startPhase(phase);
155             loadPhaseStatements();
156             completePhaseActions();
157             endPhase(phase);
158         }
159         return transform();
160     }
161
162     private EffectiveModelContext transform() {
163         Preconditions.checkState(finishedPhase == ModelProcessingPhase.EFFECTIVE_MODEL);
164         List<DeclaredStatement<?>> rootStatements = new ArrayList<>(sources.size());
165         for (SourceSpecificContext source : sources) {
166             rootStatements.add(source.getRoot().buildDeclared());
167         }
168         return new EffectiveModelContext(rootStatements);
169     }
170
171     public EffectiveSchemaContext buildEffective() throws SourceException, ReactorException {
172         for (ModelProcessingPhase phase : PHASE_EXECUTION_ORDER) {
173             startPhase(phase);
174             loadPhaseStatements();
175             completePhaseActions();
176             endPhase(phase);
177         }
178         return transformEffective();
179     }
180
181     private EffectiveSchemaContext transformEffective() {
182         Preconditions.checkState(finishedPhase == ModelProcessingPhase.EFFECTIVE_MODEL);
183         List<DeclaredStatement<?>> rootStatements = new ArrayList<>(sources.size());
184         List<EffectiveStatement<?,?>> rootEffectiveStatements = new ArrayList<>(sources.size());
185
186         for (SourceSpecificContext source : sources) {
187             final RootStatementContext<?, ?, ?> root = source.getRoot();
188             rootStatements.add(root.buildDeclared());
189             rootEffectiveStatements.add(root.buildEffective());
190         }
191
192         return new EffectiveSchemaContext(rootStatements, rootEffectiveStatements);
193     }
194
195     private void startPhase(final ModelProcessingPhase phase) {
196         Preconditions.checkState(Objects.equals(finishedPhase, phase.getPreviousPhase()));
197         for (SourceSpecificContext source : sources) {
198             source.startPhase(phase);
199         }
200         currentPhase = phase;
201     }
202
203     private  void loadPhaseStatements() throws SourceException {
204         Preconditions.checkState(currentPhase != null);
205         for (SourceSpecificContext source : sources) {
206             source.loadStatements();
207         }
208     }
209
210     private SomeModifiersUnresolvedException addSourceExceptions(final SomeModifiersUnresolvedException buildFailure,
211             final List<SourceSpecificContext> sourcesToProgress) {
212         boolean addedCause = false;
213         for (SourceSpecificContext failedSource : sourcesToProgress) {
214             final SourceException sourceEx = failedSource.failModifiers(currentPhase);
215
216             // Workaround for broken logging implementations which ignore suppressed exceptions
217             Throwable cause = sourceEx.getCause() != null ? sourceEx.getCause() : sourceEx;
218             if (LOG.isDebugEnabled()) {
219                 LOG.error("Failed to parse YANG from source {}", failedSource, sourceEx);
220             } else {
221                 LOG.error("Failed to parse YANG from source {}: {}", failedSource, cause.getMessage());
222             }
223
224             final Throwable[] suppressed = sourceEx.getSuppressed();
225             if (suppressed.length > 0) {
226                 LOG.error("{} additional errors reported:", suppressed.length);
227
228                 int i = 1;
229                 for (Throwable t : suppressed) {
230                     // FIXME: this should be configured in the appender, really
231                     if (LOG.isDebugEnabled()) {
232                         LOG.error("Error {}: {}", i, t.getMessage(), t);
233                     } else {
234                         LOG.error("Error {}: {}", i, t.getMessage());
235                     }
236
237                     i++;
238                 }
239             }
240
241             if (!addedCause) {
242                 addedCause = true;
243                 buildFailure.initCause(sourceEx);
244             } else {
245                 buildFailure.addSuppressed(sourceEx);
246             }
247         }
248         return buildFailure;
249     }
250
251     private  void completePhaseActions() throws ReactorException {
252         Preconditions.checkState(currentPhase != null);
253         List<SourceSpecificContext> sourcesToProgress = Lists.newArrayList(sources);
254         try {
255             boolean progressing = true;
256             while (progressing) {
257                 // We reset progressing to false.
258                 progressing = false;
259                 Iterator<SourceSpecificContext> currentSource = sourcesToProgress.iterator();
260                 while(currentSource.hasNext()) {
261                     SourceSpecificContext nextSourceCtx = currentSource.next();
262                     PhaseCompletionProgress sourceProgress = nextSourceCtx.tryToCompletePhase(currentPhase);
263                     switch (sourceProgress) {
264                         case FINISHED:
265                             currentSource.remove();
266                             // Fallback to progress, since we were able to make progress in computation
267                         case PROGRESS:
268                             progressing = true;
269                             break;
270                         case NO_PROGRESS:
271                             // Noop
272                             break;
273                         default:
274                            throw new IllegalStateException("Unsupported phase progress " + sourceProgress);
275                     }
276                 }
277             }
278         } catch (SourceException e) {
279             throw Throwables.propagate(e);
280         }
281         if (!sourcesToProgress.isEmpty()) {
282             SomeModifiersUnresolvedException buildFailure = new SomeModifiersUnresolvedException(currentPhase);
283             buildFailure = addSourceExceptions(buildFailure, sourcesToProgress);
284             throw buildFailure;
285         }
286     }
287
288     private  void endPhase(final ModelProcessingPhase phase) {
289         Preconditions.checkState(currentPhase == phase);
290         finishedPhase = currentPhase;
291     }
292
293     public Set<SourceSpecificContext> getSources() {
294         return sources;
295     }
296 }