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