BUG-4456: add RecursiveExtensionResolver
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / BuildGlobalContext.java
index a4c0b4446b40dbc596e114a3ce6329b983133310..62822474d7042f4659c9bde7afcaced862b000ef 100644 (file)
@@ -8,7 +8,6 @@
 package org.opendaylight.yangtools.yang.parser.stmt.reactor;
 
 import com.google.common.base.Preconditions;
-import com.google.common.base.Throwables;
 import com.google.common.base.Verify;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
@@ -28,6 +27,8 @@ 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.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
 import org.opendaylight.yangtools.yang.parser.spi.meta.DerivedNamespaceBehaviour;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
@@ -45,6 +46,8 @@ import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamesp
 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace;
 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType;
 import org.opendaylight.yangtools.yang.parser.stmt.reactor.SourceSpecificContext.PhaseCompletionProgress;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.RecursiveObjectLeaker;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Utils;
 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.EffectiveSchemaContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -52,42 +55,51 @@ import org.slf4j.LoggerFactory;
 class BuildGlobalContext extends NamespaceStorageSupport implements NamespaceBehaviour.Registry {
     private static final Logger LOG = LoggerFactory.getLogger(BuildGlobalContext.class);
 
-    private static final List<ModelProcessingPhase> PHASE_EXECUTION_ORDER = ImmutableList.<ModelProcessingPhase>builder()
-            .add(ModelProcessingPhase.SOURCE_PRE_LINKAGE)
-            .add(ModelProcessingPhase.SOURCE_LINKAGE)
-            .add(ModelProcessingPhase.STATEMENT_DEFINITION)
-            .add(ModelProcessingPhase.FULL_DECLARATION)
-            .add(ModelProcessingPhase.EFFECTIVE_MODEL)
-            .build();
+    private static final List<ModelProcessingPhase> PHASE_EXECUTION_ORDER = ImmutableList
+            .<ModelProcessingPhase> builder().add(ModelProcessingPhase.SOURCE_PRE_LINKAGE)
+            .add(ModelProcessingPhase.SOURCE_LINKAGE).add(ModelProcessingPhase.STATEMENT_DEFINITION)
+            .add(ModelProcessingPhase.FULL_DECLARATION).add(ModelProcessingPhase.EFFECTIVE_MODEL).build();
 
-    private final Map<QName,StatementDefinitionContext<?,?,?>> definitions = new HashMap<>();
-    private final Map<Class<?>,NamespaceBehaviourWithListeners<?, ?, ?>> supportedNamespaces = new HashMap<>();
+    private final Map<QName, StatementDefinitionContext<?, ?, ?>> definitions = new HashMap<>();
+    private final Map<Class<?>, NamespaceBehaviourWithListeners<?, ?, ?>> supportedNamespaces = new HashMap<>();
 
-    private final Map<ModelProcessingPhase,StatementSupportBundle> supports;
+    private final Map<ModelProcessingPhase, StatementSupportBundle> supports;
     private final Set<SourceSpecificContext> sources = new HashSet<>();
 
     private ModelProcessingPhase currentPhase = ModelProcessingPhase.INIT;
     private ModelProcessingPhase finishedPhase = ModelProcessingPhase.INIT;
 
+    private final boolean enabledSemanticVersions;
+
     public BuildGlobalContext(final Map<ModelProcessingPhase, StatementSupportBundle> supports,
-                              final Predicate<QName> isFeatureSupported) {
+            final StatementParserMode statementParserMode, final Predicate<QName> isFeatureSupported) {
         super();
         this.supports = Preconditions.checkNotNull(supports, "BuildGlobalContext#supports cannot be null");
+        Preconditions.checkNotNull(statementParserMode, "Statement parser mode must not be null.");
+        this.enabledSemanticVersions = statementParserMode == StatementParserMode.SEMVER_MODE;
 
-        addToNs(SupportedFeaturesNamespace.class, SupportedFeatures.SUPPORTED_FEATURES, isFeatureSupported);
+        addToNs(SupportedFeaturesNamespace.class, SupportedFeatures.SUPPORTED_FEATURES,
+                Preconditions.checkNotNull(isFeatureSupported, "Supported feature predicate must not be null."));
     }
 
     public BuildGlobalContext(final Map<ModelProcessingPhase, StatementSupportBundle> supports,
-                              final Map<ValidationBundleType,Collection<?>> supportedValidation,
-                              final Predicate<QName> isFeatureSupported) {
+            final Map<ValidationBundleType, Collection<?>> supportedValidation,
+            final StatementParserMode statementParserMode, final Predicate<QName> isFeatureSupported) {
         super();
         this.supports = Preconditions.checkNotNull(supports, "BuildGlobalContext#supports cannot be null");
+        Preconditions.checkNotNull(statementParserMode, "Statement parser mode must not be null.");
+        this.enabledSemanticVersions = statementParserMode == StatementParserMode.SEMVER_MODE;
 
         for (Entry<ValidationBundleType, Collection<?>> validationBundle : supportedValidation.entrySet()) {
             addToNs(ValidationBundlesNamespace.class, validationBundle.getKey(), validationBundle.getValue());
         }
 
-        addToNs(SupportedFeaturesNamespace.class, SupportedFeatures.SUPPORTED_FEATURES, isFeatureSupported);
+        addToNs(SupportedFeaturesNamespace.class, SupportedFeatures.SUPPORTED_FEATURES,
+                Preconditions.checkNotNull(isFeatureSupported, "Supported feature predicate must not be null."));
+    }
+
+    public boolean isEnabledSemanticVersioning() {
+        return enabledSemanticVersions;
     }
 
     public StatementSupportBundle getSupportsForPhase(final ModelProcessingPhase currentPhase) {
@@ -95,7 +107,7 @@ class BuildGlobalContext extends NamespaceStorageSupport implements NamespaceBeh
     }
 
     public void addSource(@Nonnull final StatementStreamSource source) {
-        sources.add(new SourceSpecificContext(this,source));
+        sources.add(new SourceSpecificContext(this, source));
     }
 
     @Override
@@ -114,7 +126,8 @@ class BuildGlobalContext extends NamespaceStorageSupport implements NamespaceBeh
     }
 
     @Override
-    public <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviourWithListeners<K, V, N> getNamespaceBehaviour(final Class<N> type) {
+    public <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviourWithListeners<K, V, N> getNamespaceBehaviour(
+            final Class<N> type) {
         NamespaceBehaviourWithListeners<?, ?, ?> potential = supportedNamespaces.get(type);
         if (potential == null) {
             NamespaceBehaviour<K, V, N> potentialRaw = supports.get(currentPhase).getNamespaceBehaviour(type);
@@ -122,27 +135,27 @@ class BuildGlobalContext extends NamespaceStorageSupport implements NamespaceBeh
                 potential = createNamespaceContext(potentialRaw);
                 supportedNamespaces.put(type, potential);
             } else {
-                throw new NamespaceNotAvailableException(
-                        "Namespace " + type + " is not available in phase " + currentPhase);
+                throw new NamespaceNotAvailableException("Namespace " + type + " is not available in phase "
+                        + currentPhase);
             }
         }
 
         Verify.verify(type.equals(potential.getIdentifier()));
         /*
-         * Safe cast, previous checkState checks equivalence of key from which type argument are
-         * derived
+         * Safe cast, previous checkState checks equivalence of key from which
+         * type argument are derived
          */
         return (NamespaceBehaviourWithListeners<K, V, N>) potential;
     }
 
-    @SuppressWarnings({"unchecked", "rawtypes"})
+    @SuppressWarnings({ "unchecked", "rawtypes" })
     private <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviourWithListeners<K, V, N> createNamespaceContext(
             final NamespaceBehaviour<K, V, N> potentialRaw) {
         if (potentialRaw instanceof DerivedNamespaceBehaviour) {
-            VirtualNamespaceContext derivedContext =
-                    new VirtualNamespaceContext((DerivedNamespaceBehaviour) potentialRaw);
-            getNamespaceBehaviour(((DerivedNamespaceBehaviour) potentialRaw).getDerivedFrom())
-                    .addDerivedNamespace(derivedContext);
+            VirtualNamespaceContext derivedContext = new VirtualNamespaceContext(
+                    (DerivedNamespaceBehaviour) potentialRaw);
+            getNamespaceBehaviour(((DerivedNamespaceBehaviour) potentialRaw).getDerivedFrom()).addDerivedNamespace(
+                    derivedContext);
             return derivedContext;
         }
         return new SimpleNamespaceContext<>(potentialRaw);
@@ -179,7 +192,7 @@ class BuildGlobalContext extends NamespaceStorageSupport implements NamespaceBeh
         return new EffectiveModelContext(rootStatements);
     }
 
-    public EffectiveSchemaContext buildEffective() throws SourceException, ReactorException {
+    public EffectiveSchemaContext buildEffective() throws ReactorException {
         for (ModelProcessingPhase phase : PHASE_EXECUTION_ORDER) {
             startPhase(phase);
             loadPhaseStatements();
@@ -189,15 +202,23 @@ class BuildGlobalContext extends NamespaceStorageSupport implements NamespaceBeh
         return transformEffective();
     }
 
-    private EffectiveSchemaContext transformEffective() {
+    private EffectiveSchemaContext transformEffective() throws ReactorException {
         Preconditions.checkState(finishedPhase == ModelProcessingPhase.EFFECTIVE_MODEL);
         List<DeclaredStatement<?>> rootStatements = new ArrayList<>(sources.size());
-        List<EffectiveStatement<?,?>> rootEffectiveStatements = new ArrayList<>(sources.size());
+        List<EffectiveStatement<?, ?>> rootEffectiveStatements = new ArrayList<>(sources.size());
+        SourceIdentifier sourceId = null;
 
-        for (SourceSpecificContext source : sources) {
-            final RootStatementContext<?, ?, ?> root = source.getRoot();
-            rootStatements.add(root.buildDeclared());
-            rootEffectiveStatements.add(root.buildEffective());
+        try {
+            for (SourceSpecificContext source : sources) {
+                final RootStatementContext<?, ?, ?> root = source.getRoot();
+                sourceId = Utils.createSourceIdentifier(root);
+                rootStatements.add(root.buildDeclared());
+                rootEffectiveStatements.add(root.buildEffective());
+            }
+        } catch (SourceException ex) {
+            throw new SomeModifiersUnresolvedException(currentPhase, sourceId, ex);
+        } finally {
+            RecursiveObjectLeaker.cleanup();
         }
 
         return new EffectiveSchemaContext(rootStatements, rootEffectiveStatements);
@@ -211,20 +232,26 @@ class BuildGlobalContext extends NamespaceStorageSupport implements NamespaceBeh
         currentPhase = phase;
     }
 
-    private  void loadPhaseStatements() throws SourceException {
+    private void loadPhaseStatements() throws ReactorException {
         Preconditions.checkState(currentPhase != null);
         for (SourceSpecificContext source : sources) {
-            source.loadStatements();
+            try {
+                source.loadStatements();
+            } catch (SourceException ex) {
+                final SourceIdentifier sourceId = Utils.createSourceIdentifier(source.getRoot());
+                throw new SomeModifiersUnresolvedException(currentPhase, sourceId, ex);
+            }
         }
     }
 
-    private SomeModifiersUnresolvedException addSourceExceptions(final SomeModifiersUnresolvedException buildFailure,
-            final List<SourceSpecificContext> sourcesToProgress) {
+    private SomeModifiersUnresolvedException addSourceExceptions(final List<SourceSpecificContext> sourcesToProgress) {
         boolean addedCause = false;
+        SomeModifiersUnresolvedException buildFailure = null;
         for (SourceSpecificContext failedSource : sourcesToProgress) {
             final SourceException sourceEx = failedSource.failModifiers(currentPhase);
 
-            // Workaround for broken logging implementations which ignore suppressed exceptions
+            // Workaround for broken logging implementations which ignore
+            // suppressed exceptions
             Throwable cause = sourceEx.getCause() != null ? sourceEx.getCause() : sourceEx;
             if (LOG.isDebugEnabled()) {
                 LOG.error("Failed to parse YANG from source {}", failedSource, sourceEx);
@@ -251,7 +278,8 @@ class BuildGlobalContext extends NamespaceStorageSupport implements NamespaceBeh
 
             if (!addedCause) {
                 addedCause = true;
-                buildFailure.initCause(sourceEx);
+                final SourceIdentifier sourceId = Utils.createSourceIdentifier(failedSource.getRoot());
+                buildFailure = new SomeModifiersUnresolvedException(currentPhase, sourceId, sourceEx);
             } else {
                 buildFailure.addSuppressed(sourceEx);
             }
@@ -259,9 +287,10 @@ class BuildGlobalContext extends NamespaceStorageSupport implements NamespaceBeh
         return buildFailure;
     }
 
-    private  void completePhaseActions() throws ReactorException {
+    private void completePhaseActions() throws ReactorException {
         Preconditions.checkState(currentPhase != null);
         List<SourceSpecificContext> sourcesToProgress = Lists.newArrayList(sources);
+        SourceIdentifier sourceId = null;
         try {
             boolean progressing = true;
             while (progressing) {
@@ -270,33 +299,34 @@ class BuildGlobalContext extends NamespaceStorageSupport implements NamespaceBeh
                 Iterator<SourceSpecificContext> currentSource = sourcesToProgress.iterator();
                 while (currentSource.hasNext()) {
                     SourceSpecificContext nextSourceCtx = currentSource.next();
+                    sourceId = Utils.createSourceIdentifier(nextSourceCtx.getRoot());
                     PhaseCompletionProgress sourceProgress = nextSourceCtx.tryToCompletePhase(currentPhase);
                     switch (sourceProgress) {
-                        case FINISHED:
-                            currentSource.remove();
-                            // Fallback to progress, since we were able to make progress in computation
-                        case PROGRESS:
-                            progressing = true;
-                            break;
-                        case NO_PROGRESS:
-                            // Noop
-                            break;
-                        default:
-                           throw new IllegalStateException("Unsupported phase progress " + sourceProgress);
+                    case FINISHED:
+                        currentSource.remove();
+                        // Fallback to progress, since we were able to make
+                        // progress in computation
+                    case PROGRESS:
+                        progressing = true;
+                        break;
+                    case NO_PROGRESS:
+                        // Noop
+                        break;
+                    default:
+                        throw new IllegalStateException("Unsupported phase progress " + sourceProgress);
                     }
                 }
             }
         } catch (SourceException e) {
-            throw Throwables.propagate(e);
+            throw new SomeModifiersUnresolvedException(currentPhase, sourceId, e);
         }
         if (!sourcesToProgress.isEmpty()) {
-            SomeModifiersUnresolvedException buildFailure = new SomeModifiersUnresolvedException(currentPhase);
-            buildFailure = addSourceExceptions(buildFailure, sourcesToProgress);
+            final SomeModifiersUnresolvedException buildFailure = addSourceExceptions(sourcesToProgress);
             throw buildFailure;
         }
     }
 
-    private  void endPhase(final ModelProcessingPhase phase) {
+    private void endPhase(final ModelProcessingPhase phase) {
         Preconditions.checkState(currentPhase == phase);
         finishedPhase = currentPhase;
     }