Bug 6244: Add context to exceptions thrown by yang statement parser 13/43113/2
authorIgor Foltin <ifoltin@cisco.com>
Thu, 4 Aug 2016 12:30:34 +0000 (14:30 +0200)
committerTony Tkacik <ttkacik@cisco.com>
Mon, 8 Aug 2016 08:09:12 +0000 (08:09 +0000)
Yang statement parser exceptions now contain information
about the failed module and its revision. This information is
represented by SourceIdentifier.

Change-Id: I4a48b5a1e38e0b91a76ac660b131511aefce14a5
Signed-off-by: Igor Foltin <ifoltin@cisco.com>
19 files changed:
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaResolutionException.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactory.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/ReactorException.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/SomeModifiersUnresolvedException.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/BuildGlobalContext.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/ModuleStatementSupport.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/Utils.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/YangInferencePipeline.java
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5946Test.java
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6131Test.java
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/CaseStmtTest.java
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/ListKeysTest.java
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/YangParserNegativeTest.java
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/semver/SemanticVersionBorderCaseTest.java
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/semver/SemanticVersionDefaultsTest.java
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/semver/SemanticVersionImportTest.java
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/semver/SemanticVersionMultipleImportTest.java
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/semver/SemanticVersionTest.java
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/semver/yin/YinSemanticVersionTest.java

index b864611bb3bc0a9265a71cc522dc2199df56c163..eaca037a51fd0201f0f8c742d09abffbd1191250 100644 (file)
@@ -24,9 +24,8 @@ import org.opendaylight.yangtools.yang.model.api.ModuleImport;
 @Beta
 public class SchemaResolutionException extends SchemaSourceException {
 
-    private static final String MESSAGE_BLUEPRINT = "%s, resolved sources: %s, unsatisfied imports: %s";
-
     private static final long serialVersionUID = 1L;
+    private final SourceIdentifier failedSource;
     private final Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports;
     private final Collection<SourceIdentifier> resolvedSources;
 
@@ -35,24 +34,42 @@ public class SchemaResolutionException extends SchemaSourceException {
     }
 
     public SchemaResolutionException(@Nonnull final String message, final Throwable cause) {
-        this(message, cause, Collections.emptySet(), ImmutableMultimap.of());
+        this(message, null, cause, Collections.emptySet(), ImmutableMultimap.of());
+    }
+
+    public SchemaResolutionException(@Nonnull final String message, final SourceIdentifier failedSource,
+            final Throwable cause) {
+        this(message, failedSource, cause, Collections.emptySet(), ImmutableMultimap.of());
     }
 
     public SchemaResolutionException(@Nonnull final String message, final Collection<SourceIdentifier> resolvedSources,
             final @Nonnull Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports) {
-        this(message, null, resolvedSources, unsatisfiedImports);
+        this(message, null, null, resolvedSources, unsatisfiedImports);
     }
 
-    public SchemaResolutionException(@Nonnull final String message, final Throwable cause,
-            @Nonnull final Collection<SourceIdentifier> resolvedSources,
+    public SchemaResolutionException(@Nonnull final String message, final SourceIdentifier failedSource,
+            final Throwable cause, @Nonnull final Collection<SourceIdentifier> resolvedSources,
             @Nonnull final Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports) {
-        super(formatMessage(message, resolvedSources, unsatisfiedImports), cause);
+        super(formatMessage(message, failedSource, resolvedSources, unsatisfiedImports), cause);
+        this.failedSource = failedSource;
         this.unsatisfiedImports = ImmutableMultimap.copyOf(unsatisfiedImports);
         this.resolvedSources = ImmutableList.copyOf(resolvedSources);
     }
 
-    private static String formatMessage(final String message, final Collection<SourceIdentifier> resolvedSources, final Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports) {
-        return String.format(MESSAGE_BLUEPRINT, message, resolvedSources, unsatisfiedImports);
+    private static String formatMessage(final String message, final SourceIdentifier failedSource,
+            final Collection<SourceIdentifier> resolvedSources,
+            final Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports) {
+        return String.format("%s, failed source: %s, resolved sources: %s, unsatisfied imports: %s", message,
+                failedSource, resolvedSources, unsatisfiedImports);
+    }
+
+    /**
+     * Return YANG schema source identifier consisting of name and revision of the module which caused this exception
+     *
+     * @return YANG schema source identifier
+     */
+    public final SourceIdentifier getFailedSource() {
+        return this.failedSource;
     }
 
     /**
index 7b40342003c575eca5692f0f88d4ca6ad827a441..fb83f5db5a6231ff57383c938e3487ac1777d5f2 100644 (file)
@@ -219,7 +219,12 @@ final class SharedSchemaContextFactory implements SchemaContextFactory {
                 reactor.addSource(new YangStatementSourceImpl(e.getKey(), (StatementContext) parserRuleCtx));
             }
 
-            final SchemaContext schemaContext = reactor.buildEffective();
+            final SchemaContext schemaContext;
+            try {
+                schemaContext = reactor.buildEffective();
+            } catch (ReactorException ex) {
+                throw new SchemaResolutionException("Failed to resolve required models", ex.getSourceIdentifier(), ex);
+            }
 
             return Futures.immediateCheckedFuture(schemaContext);
         }
index 1ea15e28f30f77bbe8950a936626eb02816c90ad..9ab46d9f21c6edeee03a55974c4fa54b097480ca 100644 (file)
@@ -8,25 +8,32 @@
 package org.opendaylight.yangtools.yang.parser.spi.meta;
 
 import com.google.common.base.Preconditions;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 
 
 public class ReactorException extends Exception {
 
     private static final long serialVersionUID = 1L;
     private final ModelProcessingPhase phase;
+    private final SourceIdentifier sourceIdentifier;
 
-    public ReactorException(ModelProcessingPhase phase, String message, Throwable cause) {
+    public ReactorException(ModelProcessingPhase phase, String message, SourceIdentifier sourceId, Throwable cause) {
         super(message, cause);
         this.phase = Preconditions.checkNotNull(phase);
+        this.sourceIdentifier = sourceId;
     }
 
-    public ReactorException(ModelProcessingPhase phase, String message) {
+    public ReactorException(ModelProcessingPhase phase, String message, SourceIdentifier sourceId) {
         super(message);
         this.phase = Preconditions.checkNotNull(phase);
+        this.sourceIdentifier = sourceId;
     }
 
     public final ModelProcessingPhase getPhase() {
         return phase;
     }
 
+    public final SourceIdentifier getSourceIdentifier() {
+        return sourceIdentifier;
+    }
 }
index 169a75afd6a8e2e562155929727ea96382cb552e..ef3b151d96a1146c2f6ff601e3ac5667cf32e454 100644 (file)
@@ -7,12 +7,14 @@
  */
 package org.opendaylight.yangtools.yang.parser.spi.meta;
 
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
 public class SomeModifiersUnresolvedException extends ReactorException {
 
     private static final long serialVersionUID = 1L;
 
-    public SomeModifiersUnresolvedException(ModelProcessingPhase phase) {
-        super(phase,"Some of " + phase + " modifiers for statements were not resolved.");
+    public SomeModifiersUnresolvedException(ModelProcessingPhase phase, SourceIdentifier sourceId, Throwable cause) {
+        super(phase, "Some of " + phase + " modifiers for statements were not resolved.", sourceId, cause);
     }
 
 }
index 9752834623a7fed2fe4490db67ea79a552c6e83d..8bc0936327441acc94cae0622ae5e33033d96e2c 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,7 @@ 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;
@@ -46,6 +46,7 @@ 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.Utils;
 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.EffectiveSchemaContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -190,7 +191,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();
@@ -200,15 +201,21 @@ 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());
+        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);
         }
 
         return new EffectiveSchemaContext(rootStatements, rootEffectiveStatements);
@@ -222,16 +229,21 @@ 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);
 
@@ -263,7 +275,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);
             }
@@ -274,6 +287,7 @@ class BuildGlobalContext extends NamespaceStorageSupport implements NamespaceBeh
     private void completePhaseActions() throws ReactorException {
         Preconditions.checkState(currentPhase != null);
         List<SourceSpecificContext> sourcesToProgress = Lists.newArrayList(sources);
+        SourceIdentifier sourceId = null;
         try {
             boolean progressing = true;
             while (progressing) {
@@ -282,6 +296,7 @@ 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:
@@ -300,11 +315,10 @@ class BuildGlobalContext extends NamespaceStorageSupport implements NamespaceBeh
                 }
             }
         } 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;
         }
     }
index 2f0e06ba1da14889f8fd41f99a21469dbdaa0244..255fdd337eb744383af7136bf97258d707c0d010 100644 (file)
@@ -117,6 +117,16 @@ public class ModuleStatementSupport extends
         stmt.addToNs(ImpPrefixToNamespace.class, modulePrefix, moduleNs);
 
         stmt.addContext(PreLinkageModuleNamespace.class, moduleName, stmt);
+
+        Optional<Date> revisionDate = Optional.fromNullable(Utils.getLatestRevision(stmt.declaredSubstatements
+                ()));
+        if (!revisionDate.isPresent()) {
+            revisionDate = Optional.of(SimpleDateFormatUtil.DEFAULT_DATE_REV);
+        }
+
+        QNameModule qNameModule = QNameModule.create(moduleNs, revisionDate.orNull()).intern();
+
+        stmt.addToNs(ModuleCtxToModuleQName.class, stmt, qNameModule);
     };
 
     @Override
@@ -146,7 +156,7 @@ public class ModuleStatementSupport extends
 
         stmt.addToNs(PrefixToModule.class, modulePrefix, qNameModule);
         stmt.addToNs(ModuleNameToModuleQName.class, stmt.getStatementArgument(), qNameModule);
-        stmt.addToNs(ModuleCtxToModuleQName.class, stmt, qNameModule);
+        stmt.addToNs(ModuleCtxToModuleQName.class, stmt, qNameModule); // tu
         stmt.addToNs(ModuleCtxToModuleIdentifier.class, stmt, moduleIdentifier);
         stmt.addToNs(ModuleQNameToModuleName.class, qNameModule, stmt.getStatementArgument());
         stmt.addToNs(ModuleIdentifierToModuleQName.class, moduleIdentifier, qNameModule);
index 103736d63fdd44262c33be91b584528d11323137..abd4ce7c749e533dded6d2ccc1168c8a41a92d53 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.yangtools.yang.parser.stmt.rfc6020;
 import static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.firstAttributeOf;
 
 import com.google.common.base.CharMatcher;
+import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Splitter;
 import com.google.common.base.Strings;
@@ -50,6 +51,8 @@ import org.opendaylight.yangtools.yang.model.api.stmt.RevisionStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Relative;
 import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleStatement;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 import org.opendaylight.yangtools.yang.model.util.RevisionAwareXPathImpl;
 import org.opendaylight.yangtools.yang.parser.spi.meta.QNameCacheNamespace;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
@@ -62,6 +65,7 @@ import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToModuleQName
 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.stmt.reactor.RootStatementContext;
 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -692,4 +696,20 @@ public final class Utils {
 
         return false;
     }
+
+    public static SourceIdentifier createSourceIdentifier(RootStatementContext<?, ?, ?> root) {
+        final QNameModule qNameModule = root.getFromNamespace(ModuleCtxToModuleQName.class, root);
+        if (qNameModule != null) {
+            // creates SourceIdentifier for a module
+            return RevisionSourceIdentifier.create((String) root.getStatementArgument(),
+                qNameModule.getFormattedRevision());
+        } else {
+            // creates SourceIdentifier for a submodule
+            final Date revision = Optional.fromNullable(Utils.getLatestRevision(root.declaredSubstatements()))
+                    .or(SimpleDateFormatUtil.DEFAULT_DATE_REV);
+            final String formattedRevision = SimpleDateFormatUtil.getRevisionFormat().format(revision);
+            return RevisionSourceIdentifier.create((String) root.getStatementArgument(),
+                    formattedRevision);
+        }
+    }
 }
index 8382e00bb67a65cc1e05d0d82901f090b3ba8a0f..cfb9cd0ea781105649d9a098afb587b900e61fa7 100644 (file)
@@ -67,15 +67,16 @@ public final class YangInferencePipeline {
             .addSupport(new IncludeStatementImpl.Definition())
             .addSupport(new PrefixStatementImpl.Definition())
             .addSupport(new YangVersionStatementImpl.Definition())
+            .addSupport(new RevisionStatementImpl.Definition())
             .addSupport(global(ModuleNameToNamespace.class))
             .addSupport(global(PreLinkageModuleNamespace.class))
             .addSupport(sourceLocal(ImpPrefixToNamespace.class))
+            .addSupport(global(ModuleCtxToModuleQName.class))
             .build();
 
     public static final StatementSupportBundle LINKAGE_BUNDLE = StatementSupportBundle
             .derivedFrom(PRE_LINKAGE_BUNDLE)
             .addSupport(new DescriptionStatementImpl.Definition())
-            .addSupport(new RevisionStatementImpl.Definition())
             .addSupport(new RevisionDateStatementImpl.Definition())
             .addSupport(new ReferenceStatementImpl.Definition())
             .addSupport(new ContactStatementImpl.Definition())
@@ -86,7 +87,6 @@ public final class YangInferencePipeline {
             .addSupport(global(SubmoduleNamespace.class))
             .addSupport(global(NamespaceToModule.class))
             .addSupport(global(ModuleNameToModuleQName.class))
-            .addSupport(global(ModuleCtxToModuleQName.class))
             .addSupport(global(ModuleCtxToModuleIdentifier.class))
             .addSupport(global(ModuleQNameToModuleName.class))
             .addSupport(global(PrefixToModule.class))
index 235d207174f5375c8eb8c90f44c9f74deb18f115..827c0a52d6027e3ab7a324e43e376a21db247551 100644 (file)
@@ -82,8 +82,8 @@ public class Bug5946Test {
         try {
             StmtTestUtils.parseYangSources(new File(getClass().getResource("/bugs/bug5946/foo-invalid.yang").toURI()));
             fail("Should fail due to invalid argument of unique constraint");
-        } catch (SourceException e) {
-            assertTrue(e.getMessage().startsWith(
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getMessage().startsWith(
                     "Unique statement argument '/simple-unique/l1' contains schema node identifier '/simple-unique/l1'"
                             + " which is not in the descendant node identifier form."));
         }
index e8d1f752a01dade8f944221ab7556eee398c4049..ebab29e3b19dc420ecf01d3161004f244fa97a2b 100644 (file)
@@ -8,7 +8,8 @@
 
 package org.opendaylight.yangtools.yang.stmt;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import java.io.FileNotFoundException;
 import java.net.URISyntaxException;
@@ -21,8 +22,9 @@ public class Bug6131Test {
     public void test() throws ReactorException, SourceException, FileNotFoundException, URISyntaxException {
         try {
             StmtTestUtils.parseYangSources("/bugs/bug6131");
+            fail("A NullPointerException should have been thrown.");
         } catch (final Exception e) {
-            assertEquals("Malformed source. Valid root element is missing.", e.getMessage());
+            assertTrue(e instanceof NullPointerException);
         }
     }
 }
\ No newline at end of file
index 0aa86e76a78dfd25a866efaef689c7c67c1d5623..be6c02b0c79c37dfa527cba96430e16df3348923 100644 (file)
@@ -28,7 +28,6 @@ import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.ChoiceEffectiveStatementImpl;
 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.ContainerEffectiveStatementImpl;
@@ -535,13 +534,13 @@ public class CaseStmtTest {
 
     @Test
     public void InferenceExceptionChoiceTest() throws ReactorException, FileNotFoundException, URISyntaxException {
-        expectedEx.expect(InferenceException.class);
+        expectedEx.expect(ReactorException.class);
         schema = StmtTestUtils.parseYangSources("/case-test/case-test-exceptions/choice");
     }
 
     @Test
     public void InferenceExceptionCaseTest() throws ReactorException, FileNotFoundException, URISyntaxException {
-        expectedEx.expect(InferenceException.class);
+        expectedEx.expect(ReactorException.class);
         schema = StmtTestUtils.parseYangSources("/case-test/case-test-exceptions/case");
     }
 }
\ No newline at end of file
index d772e493c183ebcdf56b43702081b0b8b97134fd..e1fab4e0995f4c4f58b2df58f230b99796dfb9fc 100644 (file)
@@ -7,15 +7,14 @@
  */
 package org.opendaylight.yangtools.yang.stmt;
 
-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 java.io.IOException;
 import java.net.URISyntaxException;
 import org.junit.Test;
 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
-import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
 import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
@@ -50,8 +49,8 @@ public class ListKeysTest {
             reactor.buildEffective();
             fail("effective build should fail due to list instead of leaf referenced in list key");
         } catch (Exception e) {
-            assertEquals(InferenceException.class, e.getClass());
-            assertTrue(e.getMessage().startsWith("Key 'test1_key1 test1_key2' misses node 'test1_key2'"));
+            assertTrue(e instanceof ReactorException);
+            assertTrue(e.getCause().getMessage().startsWith("Key 'test1_key1 test1_key2' misses node 'test1_key2'"));
         }
     }
 
@@ -68,8 +67,8 @@ public class ListKeysTest {
             reactor.buildEffective();
             fail("effective build should fail due to missing leaf referenced in list key");
         } catch (Exception e) {
-            assertEquals(InferenceException.class, e.getClass());
-            assertTrue(e.getMessage().startsWith("Key 'test1_key1 test1_key2' misses node 'test1_key2'"));
+            assertTrue(e instanceof ReactorException);
+            assertTrue(e.getCause().getMessage().startsWith("Key 'test1_key1 test1_key2' misses node 'test1_key2'"));
         }
     }
 
@@ -86,8 +85,8 @@ public class ListKeysTest {
             reactor.buildEffective();
             fail("effective build should fail due to list instead of leaf in grouping referenced in list key");
         } catch (Exception e) {
-            assertEquals(InferenceException.class, e.getClass());
-            assertTrue(e.getMessage().startsWith("Key 'grp_list' misses node 'grp_list'"));
+            assertTrue(e instanceof ReactorException);
+            assertTrue(e.getCause().getMessage().startsWith("Key 'grp_list' misses node 'grp_list'"));
         }
     }
 
@@ -105,8 +104,8 @@ public class ListKeysTest {
             fail("effective build should fail due to list instead of leaf in grouping augmented to list referenced " +
                     "in list key");
         } catch (Exception e) {
-            assertEquals(InferenceException.class, e.getClass());
-            assertTrue(e.getMessage().startsWith("Key 'grp_leaf' misses node 'grp_leaf'"));
+            assertTrue(e instanceof ReactorException);
+            assertTrue(e.getCause().getMessage().startsWith("Key 'grp_leaf' misses node 'grp_leaf'"));
         }
     }
 
index 29543ee044925e853727b1cb2bd8885f6d62f491..72243324808b45bd74cb8adb009ad72152bf800b 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.yangtools.yang.stmt;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+
 import com.google.common.base.Throwables;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -23,8 +24,8 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+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.util.NamedFileInputStream;
 
 public class YangParserNegativeTest {
@@ -106,9 +107,9 @@ public class YangParserNegativeTest {
                 TestUtils.loadModule(stream);
                 fail("SourceException should be thrown");
             }
-        } catch (SourceException e) {
-            assertTrue(e.getMessage().contains("Error in module 'test4' in the refine of uses 'Relative{path=[" +
-                    "(urn:simple.container.demo?revision=1970-01-01)node]}': can not perform refine of 'PRESENCE' for" +
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getMessage().contains("Error in module 'test4' in the refine of uses " +
+                    "'Relative{path=[(urn:simple.container.demo?revision=1970-01-01)node]}': can not perform refine of 'PRESENCE' for" +
                     " the target 'LEAF_LIST'."));
         }
     }
@@ -121,8 +122,8 @@ public class YangParserNegativeTest {
                 TestUtils.loadModule(stream);
                 fail("YangParseException should be thrown");
             }
-        } catch (SourceException e) {
-            assertTrue(e.getMessage().contains("Invalid length constraint: <4, 10>"));
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getMessage().contains("Invalid length constraint: <4, 10>"));
         }
     }
 
@@ -134,8 +135,8 @@ public class YangParserNegativeTest {
                 TestUtils.loadModule(stream);
                 fail("Exception should be thrown");
             }
-        } catch (SourceException e) {
-            assertTrue(e.getMessage().contains("Invalid range constraint: <5, 20>"));
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getMessage().contains("Invalid range constraint: <5, 20>"));
         }
     }
 
@@ -147,11 +148,11 @@ public class YangParserNegativeTest {
                 TestUtils.loadModule(stream);
                 fail("SourceException should be thrown");
             }
-        } catch (SourceException e) {
+        } catch (ReactorException e) {
             String expected = "Error in module 'container': cannot add '(urn:simple.container" +
                     ".demo?revision=1970-01-01)foo'. Node name collision: '(urn:simple.container" +
                     ".demo?revision=1970-01-01)foo' already declared.";
-            assertTrue(e.getMessage().contains(expected));
+            assertTrue(e.getCause().getMessage().contains(expected));
         }
     }
 
@@ -163,11 +164,11 @@ public class YangParserNegativeTest {
                 TestUtils.loadModule(stream);
                 fail("SourceException should be thrown");
             }
-        } catch (SourceException e) {
+        } catch (ReactorException e) {
             String expected = "Error in module 'container-list': cannot add '(urn:simple.container" +
                     ".demo?revision=1970-01-01)foo'. Node name collision: '(urn:simple.container" +
                     ".demo?revision=1970-01-01)foo' already declared.";
-            assertTrue(e.getMessage().contains(expected));
+            assertTrue(e.getCause().getMessage().contains(expected));
         }
     }
 
@@ -179,11 +180,11 @@ public class YangParserNegativeTest {
                 TestUtils.loadModule(stream);
                 fail("SourceException should be thrown");
             }
-        } catch (SourceException e) {
+        } catch (ReactorException e) {
             String expected = "Error in module 'container-leaf': cannot add '(urn:simple.container" +
                     ".demo?revision=1970-01-01)foo'. Node name collision: '(urn:simple.container" +
                     ".demo?revision=1970-01-01)foo' already declared.";
-            assertTrue(e.getMessage().contains(expected));
+            assertTrue(e.getCause().getMessage().contains(expected));
         }
     }
 
@@ -195,11 +196,11 @@ public class YangParserNegativeTest {
                 TestUtils.loadModule(stream);
                 fail("SourceException should be thrown");
             }
-        } catch (SourceException e) {
+        } catch (ReactorException e) {
             String expected = "Error in module 'typedef': cannot add '(urn:simple.container" +
                     ".demo?revision=1970-01-01)int-ext'. Node name collision: '(urn:simple.container" +
                     ".demo?revision=1970-01-01)int-ext' already declared.";
-            assertTrue(e.getMessage().startsWith(expected));
+            assertTrue(e.getCause().getMessage().startsWith(expected));
         }
     }
 
@@ -248,10 +249,10 @@ public class YangParserNegativeTest {
                 TestUtils.loadModule(stream1);
                 fail("InferenceException should be thrown");
             }
-        } catch (InferenceException e) {
+        } catch (ReactorException e) {
             String expected = "Key 'rib-id' misses node 'rib-id' in list '(invalid:list:key:def?revision=1970-01-01)" +
                     "application-map'";
-            assertTrue(e.getMessage().startsWith(expected));
+            assertTrue(e.getCause().getMessage().startsWith(expected));
         }
     }
 
index 028989ce9b264447f93164b17fdd4f5bab2a11bb..86428b70f3c3fa652f79c443c4d4963099e020f1 100644 (file)
@@ -12,8 +12,6 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
-
 import java.io.FileNotFoundException;
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -21,7 +19,7 @@ import org.junit.Test;
 import org.opendaylight.yangtools.concepts.SemVer;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
 import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
@@ -86,8 +84,8 @@ public class SemanticVersionBorderCaseTest {
             StmtTestUtils.parseYangSources("/semantic-version/border-case/border-case-invalid-major",
                     StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid semantic version");
-        } catch (InferenceException e) {
-            assertTrue(e.getMessage()
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getMessage()
                     .startsWith("Unable to find module compatible with requested import [bar(5.5.5)]."));
         }
     }
@@ -99,8 +97,8 @@ public class SemanticVersionBorderCaseTest {
             StmtTestUtils.parseYangSources("/semantic-version/border-case/border-case-invalid-minor",
                     StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid semantic version");
-        } catch (InferenceException e) {
-            assertTrue(e.getMessage()
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getMessage()
                     .startsWith("Unable to find module compatible with requested import [bar(5.5.5)]."));
         }
     }
@@ -112,8 +110,8 @@ public class SemanticVersionBorderCaseTest {
             StmtTestUtils.parseYangSources("/semantic-version/border-case/border-case-invalid-patch",
                     StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid semantic version");
-        } catch (InferenceException e) {
-            assertTrue(e.getMessage()
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getMessage()
                     .startsWith("Unable to find module compatible with requested import [bar(5.5.5)]."));
         }
     }
index 33cc244a3593e119b819f182f01bdd40b7aa4b0c..2e1ddbc09e9009d6b72576a38a48ecd1b3faff70 100644 (file)
@@ -12,8 +12,6 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
-
 import java.io.FileNotFoundException;
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -21,7 +19,7 @@ import org.junit.Test;
 import org.opendaylight.yangtools.concepts.SemVer;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
 import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
@@ -61,8 +59,8 @@ public class SemanticVersionDefaultsTest {
         try {
             StmtTestUtils.parseYangSources("/semantic-version/defaults/default-major-invalid", StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid semantic version");
-        } catch (InferenceException e) {
-            assertTrue(e.getMessage()
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getMessage()
                     .startsWith("Unable to find module compatible with requested import [bar(0.0.0)]."));
         }
     }
index 114fbfea186fb7350446dcc55f8820234fa6815e..422ef7be08d9fe5e64801e750025cc32e6fe385c 100644 (file)
@@ -12,8 +12,6 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
-
 import java.io.FileNotFoundException;
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -21,7 +19,7 @@ import org.junit.Test;
 import org.opendaylight.yangtools.concepts.SemVer;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
 import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
@@ -47,8 +45,8 @@ public class SemanticVersionImportTest {
             StmtTestUtils.parseYangSources("/semantic-version/import/import-invalid-deprecated-1",
                     StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid import of semantic-version module");
-        } catch (InferenceException e) {
-            assertTrue(e.getMessage().startsWith(
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getMessage().startsWith(
                     "Unable to find module compatible with requested import " + "[semantic-version(1.0.0)]."));
         }
     }
@@ -60,8 +58,8 @@ public class SemanticVersionImportTest {
             StmtTestUtils.parseYangSources("/semantic-version/import/import-invalid-deprecated-2",
                     StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid import of semantic-version module");
-        } catch (InferenceException e) {
-            assertTrue(e.getMessage().startsWith(
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getMessage().startsWith(
                     "Unable to find module compatible with requested import " + "[semantic-version(0.9.9)]."));
         }
     }
@@ -73,8 +71,8 @@ public class SemanticVersionImportTest {
             StmtTestUtils.parseYangSources("/semantic-version/import/import-invalid-notsufficient-1",
                     StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid import of semantic-version module");
-        } catch (InferenceException e) {
-            assertTrue(e.getMessage().startsWith(
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getMessage().startsWith(
                     "Unable to find module compatible with requested import " + "[semantic-version(2.0.0)]."));
         }
     }
@@ -86,8 +84,8 @@ public class SemanticVersionImportTest {
             StmtTestUtils.parseYangSources("/semantic-version/import/import-invalid-notsufficient-2",
                     StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid import of semantic-version module");
-        } catch (InferenceException e) {
-            assertTrue(e.getMessage().startsWith(
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getMessage().startsWith(
                     "Unable to find module compatible with requested import " + "[semantic-version(2.0.5)]."));
         }
     }
index 7b85f55afa8b6f8a8176865f9fb1130d979b0eaf..6264b8f56ed8c9581810615fe90cf5b0a4e4ccdd 100644 (file)
@@ -12,8 +12,6 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
-
 import java.io.FileNotFoundException;
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -23,7 +21,7 @@ import org.opendaylight.yangtools.concepts.SemVer;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
 import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
@@ -37,8 +35,8 @@ public class SemanticVersionMultipleImportTest {
             StmtTestUtils.parseYangSources("/semantic-version/multiple/multiple-invalid-deprecated",
                     StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid semantic version");
-        } catch (InferenceException e) {
-            assertTrue(e.getMessage()
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getMessage()
                     .startsWith("Unable to find module compatible with requested import [bar(1.0.0)]."));
         }
     }
@@ -50,8 +48,8 @@ public class SemanticVersionMultipleImportTest {
             StmtTestUtils.parseYangSources("/semantic-version/multiple/multiple-invalid-nosufficient",
                     StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid semantic version");
-        } catch (InferenceException e) {
-            assertTrue(e.getMessage()
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getMessage()
                     .startsWith("Unable to find module compatible with requested import [bar(2.5.5)]."));
         }
     }
index e0bb3d004c785cc108533287efc20f5ac7d32369..a7b667a35e599fbf70dd1b5454b1146a04a7bfe3 100644 (file)
@@ -13,8 +13,6 @@ import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
-
 import java.io.FileNotFoundException;
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -26,8 +24,8 @@ import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
-import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
 import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
@@ -115,8 +113,8 @@ public class SemanticVersionTest {
         try {
             StmtTestUtils.parseYangSources("/semantic-version/basic-import-invalid-1", StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid semantic version");
-        } catch (InferenceException e) {
-            assertTrue(e.getMessage()
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getMessage()
                     .startsWith("Unable to find module compatible with requested import [bar(0.1.2)]."));
         }
     }
@@ -127,8 +125,8 @@ public class SemanticVersionTest {
         try {
             StmtTestUtils.parseYangSources("/semantic-version/basic-import-invalid-2", StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid semantic version");
-        } catch (InferenceException e) {
-            assertTrue(e.getMessage()
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getMessage()
                     .startsWith("Unable to find module compatible with requested import [bar(0.1.2)]."));
         }
     }
index d27ebf88cc815c98fd1c7030e69479fdd9f4769c..1b1039b7dace7b238d0e185df17e9a3aae6f9955 100644 (file)
@@ -13,8 +13,6 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
-
 import java.io.FileNotFoundException;
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -22,7 +20,7 @@ import org.junit.Test;
 import org.opendaylight.yangtools.concepts.SemVer;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
 import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
@@ -67,8 +65,8 @@ public class YinSemanticVersionTest {
         try {
             StmtTestUtils.parseYinSources("/semantic-version/yin-input/basic-import-invalid", StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid semantic version");
-        } catch (InferenceException e) {
-            assertTrue(e.getMessage()
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getMessage()
                     .startsWith("Unable to find module compatible with requested import [bar(0.1.2)]."));
         }
     }