BUG-4688: switch semantic imports to queries 21/64621/4
authorRobert Varga <robert.varga@pantheon.tech>
Mon, 23 Oct 2017 16:49:42 +0000 (18:49 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Tue, 24 Oct 2017 10:55:55 +0000 (12:55 +0200)
Use NamespaceKeyCriterion-based lookup for resolution of semver-based
imports. This simplifies the code quite a bit and makes it obvious
that revision and semver imports have really different mechanics.

Change-Id: I27696fbd4903037477c8c4b091f4ab83891bc53c
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/ImportStatementDefinition.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/ModuleStatementSupport.java
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionBorderCaseTest.java
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionDefaultsTest.java
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionImportTest.java
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionMultipleImportTest.java
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionTest.java
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/yin/YinOpenconfigVersionTest.java
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/SemanticVersionModuleNamespace.java

index 2de0a4f731fd04eafc082b19e8053b309be2e51c..872ad380c779c98ca6ebb164e1ea5591f9e7bfed 100644 (file)
@@ -14,12 +14,11 @@ import static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.f
 import static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.firstAttributeOf;
 
 import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
 import com.google.common.base.Verify;
 import java.net.URI;
 import java.util.Collection;
 import java.util.Date;
-import java.util.Map.Entry;
-import java.util.NavigableMap;
 import java.util.Optional;
 import org.opendaylight.yangtools.concepts.SemVer;
 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
@@ -221,35 +220,110 @@ public class ImportStatementDefinition extends
     }
 
     private static class SemanticVersionImport {
+
+        private abstract static class CompatibleCriterion extends NamespaceKeyCriterion<SemVerSourceIdentifier> {
+            private final String moduleName;
+
+            CompatibleCriterion(final String moduleName) {
+                this.moduleName = requireNonNull(moduleName);
+            }
+
+            @Override
+            public boolean match(final SemVerSourceIdentifier key) {
+                return moduleName.equals(key.getName());
+            }
+
+            @Override
+            public String toString() {
+                return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
+            }
+
+            protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+                return toStringHelper.add("moduleName", moduleName);
+            }
+        }
+
+        private static final class NoVerCompatibleCriterion extends CompatibleCriterion {
+            NoVerCompatibleCriterion(final String moduleName) {
+                super(moduleName);
+            }
+
+            @Override
+            public SemVerSourceIdentifier select(final SemVerSourceIdentifier first,
+                    final SemVerSourceIdentifier second) {
+                // TODO Auto-generated method stub
+                return null;
+            }
+        }
+
+        private static final class SemVerCompatibleCriterion extends CompatibleCriterion {
+            private final SemVer semVer;
+
+            SemVerCompatibleCriterion(final String moduleName, final SemVer semVer) {
+                super(moduleName);
+                this.semVer = requireNonNull(semVer);
+            }
+
+            @Override
+            public boolean match(final SemVerSourceIdentifier key) {
+                if (!super.match(key)) {
+                    return false;
+                }
+                final Optional<SemVer> optKeyVer = key.getSemanticVersion();
+                if (!optKeyVer.isPresent()) {
+                    return false;
+                }
+
+                final SemVer keyVer = optKeyVer.get();
+                if (semVer.getMajor() != keyVer.getMajor()) {
+                    return false;
+                }
+                if (semVer.getMinor() > keyVer.getMinor()) {
+                    return false;
+                }
+                return semVer.getMinor() < keyVer.getMinor() || semVer.getPatch() <= keyVer.getPatch();
+            }
+
+            @Override
+            public SemVerSourceIdentifier select(final SemVerSourceIdentifier first,
+                    final SemVerSourceIdentifier second) {
+                return first.getSemanticVersion().get().compareTo(second.getSemanticVersion().get()) >= 0 ? first
+                        : second;
+            }
+
+            @Override
+            protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+                return super.addToStringAttributes(toStringHelper).add("version", semVer);
+            }
+        }
+
+
         private SemanticVersionImport() {
             throw new UnsupportedOperationException("Utility class");
         }
 
         private static void onLinkageDeclared(
                 final Mutable<String, ImportStatement, EffectiveStatement<String, ImportStatement>> stmt) {
-            final ModuleIdentifier impIdentifier = getImportedModuleIdentifier(stmt);
             final ModelActionBuilder importAction = stmt.newInferenceAction(SOURCE_LINKAGE);
-            final Prerequisite<StmtContext<?, ?, ?>> imported = importAction.requiresCtx(stmt, ModuleNamespace.class,
-                    impIdentifier, SOURCE_LINKAGE);
-            final Prerequisite<Mutable<?, ?, ?>> linkageTarget = importAction
-                    .mutatesCtx(stmt.getRoot(), SOURCE_LINKAGE);
+            final String moduleName = stmt.getStatementArgument();
+            final SemVer semanticVersion = stmt.getFromNamespace(SemanticVersionNamespace.class, stmt);
+            final CompatibleCriterion criterion = semanticVersion == null ? new NoVerCompatibleCriterion(moduleName)
+                    : new SemVerCompatibleCriterion(moduleName, semanticVersion);
+
+            final Prerequisite<StmtContext<?, ?, ?>> imported = importAction.requiresCtx(stmt,
+                SemanticVersionModuleNamespace.class, criterion, SOURCE_LINKAGE);
+            final Prerequisite<Mutable<?, ?, ?>> linkageTarget = importAction.mutatesCtx(stmt.getRoot(),
+                SOURCE_LINKAGE);
 
             importAction.apply(new InferenceAction() {
                 @Override
                 public void apply(final InferenceContext ctx) {
-                    final Entry<SemVer, StmtContext<?, ?, ?>> importedModuleEntry = findRecentCompatibleModuleEntry(
-                            impIdentifier.getName(), stmt);
-                    if (importedModuleEntry == null) {
-                        throw new InferenceException(stmt.getStatementSourceReference(),
-                            "Unable to find module compatible with requested import [%s(%s)].",
-                            impIdentifier.getName(), getRequestedImportVersionString(stmt));
-                    }
-
-                    final StmtContext<?, ?, ?> importedModule = importedModuleEntry.getValue();
+                    final StmtContext<?, ?, ?> importedModule = imported.resolve(ctx);
+                    final SemVer importedVersion = stmt.getFromNamespace(SemanticVersionNamespace.class, stmt);
                     final ModuleIdentifier importedModuleIdentifier = importedModule.getFromNamespace(
                         ModuleCtxToModuleIdentifier.class, importedModule);
                     final SemVerSourceIdentifier semVerModuleIdentifier = createSemVerModuleIdentifier(
-                        importedModuleIdentifier, importedModuleEntry.getKey());
+                        importedModuleIdentifier, importedVersion);
 
                     linkageTarget.resolve(ctx).addToNs(ImportedModuleContext.class, importedModuleIdentifier,
                         importedModule);
@@ -266,8 +340,8 @@ public class ImportStatementDefinition extends
                 public void prerequisiteFailed(final Collection<? extends Prerequisite<?>> failed) {
                     if (failed.contains(imported)) {
                         throw new InferenceException(stmt.getStatementSourceReference(),
-                                "Unable to find module compatible with requested import [%s(%s)].",
-                                impIdentifier.getName(), getRequestedImportVersionString(stmt));
+                                "Unable to find module compatible with requested import [%s(%s)].", moduleName,
+                                getRequestedImportVersionString(stmt));
                     }
                 }
             });
@@ -278,31 +352,7 @@ public class ImportStatementDefinition extends
         }
 
         private static String getRequestedImportVersionString(final StmtContext<?, ?, ?> stmt) {
-            return getRequestedImportVersion(stmt).map(SemVer::toString).orElse("<any");
-        }
-
-        private static Entry<SemVer, StmtContext<?, ?, ?>> findRecentCompatibleModuleEntry(final String moduleName,
-                final StmtContext<String, ImportStatement, EffectiveStatement<String, ImportStatement>> impStmt) {
-            NavigableMap<SemVer, StmtContext<?, ?, ?>> allRelevantModulesMap = impStmt.getFromNamespace(
-                    SemanticVersionModuleNamespace.class, moduleName);
-            if (allRelevantModulesMap == null) {
-                return null;
-            }
-
-            final Optional<SemVer> optImportVer = getRequestedImportVersion(impStmt);
-            if (optImportVer.isPresent()) {
-                final SemVer importVer = optImportVer.get();
-                allRelevantModulesMap = allRelevantModulesMap.subMap(importVer, true,
-                    SemVer.create(importVer.getMajor() + 1), false);
-            }
-
-            return allRelevantModulesMap.lastEntry();
-        }
-
-        private static ModuleIdentifier getImportedModuleIdentifier(
-                final StmtContext<String, ImportStatement, ?> impStmt) {
-            return ModuleIdentifierImpl.create(impStmt.getStatementArgument(), Optional.empty(),
-                    Optional.of(SimpleDateFormatUtil.DEFAULT_DATE_IMP));
+            return getRequestedImportVersion(stmt).map(SemVer::toString).orElse("<any>");
         }
 
         private static SemVerSourceIdentifier createSemVerModuleIdentifier(
index 0ddbf23ccfb1448423490c59102ba11635825100..74d0c02363ecb1454f0a2069809ff820f0bc5d88 100644 (file)
@@ -11,9 +11,7 @@ import static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.f
 
 import java.net.URI;
 import java.util.Date;
-import java.util.NavigableMap;
 import java.util.Optional;
-import java.util.TreeMap;
 import org.opendaylight.yangtools.concepts.SemVer;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
@@ -23,6 +21,7 @@ import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.NamespaceStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.PrefixStatement;
+import org.opendaylight.yangtools.yang.model.repo.api.SemVerSourceIdentifier;
 import org.opendaylight.yangtools.yang.model.util.ModuleIdentifierImpl;
 import org.opendaylight.yangtools.yang.parser.spi.ModuleNamespace;
 import org.opendaylight.yangtools.yang.parser.spi.NamespaceToModule;
@@ -171,29 +170,17 @@ public class ModuleStatementSupport extends
         stmt.addToNs(ImportPrefixToModuleCtx.class, modulePrefix, stmt);
 
         if (stmt.isEnabledSemanticVersioning()) {
-            addToSemVerModuleNamespace(stmt);
+            addToSemVerModuleNamespace(stmt, moduleIdentifier);
         }
     }
 
-    private static int compareNullableSemVer(final SemVer ver1, final SemVer ver2) {
-        if (ver1 == null) {
-            return ver2 == null ? 0 : -1;
-        }
-
-        return ver2 == null ? 1 : ver1.compareTo(ver2);
-    }
-
     private static void addToSemVerModuleNamespace(
-            final Mutable<String, ModuleStatement, EffectiveStatement<String, ModuleStatement>> stmt) {
+            final Mutable<String, ModuleStatement, EffectiveStatement<String, ModuleStatement>> stmt,
+            final ModuleIdentifier moduleIdentifier) {
         final String moduleName = stmt.getStatementArgument();
-        NavigableMap<SemVer, StmtContext<?, ?, ?>> modulesMap = stmt.getFromNamespace(
-                SemanticVersionModuleNamespace.class, moduleName);
-        if (modulesMap == null) {
-            modulesMap = new TreeMap<>(ModuleStatementSupport::compareNullableSemVer);
-        }
         final SemVer moduleSemVer = stmt.getFromNamespace(SemanticVersionNamespace.class, stmt);
-        modulesMap.put(moduleSemVer, stmt);
-        stmt.addToNs(SemanticVersionModuleNamespace.class, moduleName, modulesMap);
+        final SemVerSourceIdentifier id = SemVerSourceIdentifier.create(moduleName, moduleSemVer);
+        stmt.addToNs(SemanticVersionModuleNamespace.class, id, stmt);
     }
 
     @Override
index aee183e1b7efad8a2eabcf4710fa215d73b39875..58c478e6d2e6a8c3341f5f07f5403829e5acde98 100644 (file)
@@ -78,7 +78,7 @@ public class OpenconfigVersionBorderCaseTest {
                     StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid openconfig version");
         } catch (ReactorException e) {
-            assertTrue(e.getCause().getMessage()
+            assertTrue(e.getCause().getCause().getMessage()
                     .startsWith("Unable to find module compatible with requested import [bar(5.5.5)]."));
         }
     }
@@ -90,7 +90,7 @@ public class OpenconfigVersionBorderCaseTest {
                     StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid openconfig version");
         } catch (ReactorException e) {
-            assertTrue(e.getCause().getMessage()
+            assertTrue(e.getCause().getCause().getMessage()
                     .startsWith("Unable to find module compatible with requested import [bar(5.5.5)]."));
         }
     }
@@ -102,7 +102,7 @@ public class OpenconfigVersionBorderCaseTest {
                     StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid openconfig version");
         } catch (ReactorException e) {
-            assertTrue(e.getCause().getMessage()
+            assertTrue(e.getCause().getCause().getMessage()
                     .startsWith("Unable to find module compatible with requested import [bar(5.5.5)]."));
         }
     }
index 2d785ef27309a3fef044fee5c59e551c4b6099d5..4ef4a678546dc8ea60e2e3fdbb11a9f6844f62b6 100644 (file)
@@ -57,7 +57,7 @@ public class OpenconfigVersionDefaultsTest {
                 StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid openconfig version");
         } catch (ReactorException e) {
-            assertTrue(e.getCause().getMessage()
+            assertTrue(e.getCause().getCause().getMessage()
                     .startsWith("Unable to find module compatible with requested import [bar(0.0.1)]."));
         }
     }
index dfc073ae06b096a795291447f323e3bbfb27d061..00e0a8cf1e162b675e22c68f78fdc0684466cf93 100644 (file)
@@ -42,8 +42,8 @@ public class OpenconfigVersionImportTest {
                     StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid import of openconfig-version module");
         } catch (ReactorException e) {
-            assertTrue(e.getCause().getMessage().startsWith(
-                    "Unable to find module compatible with requested import " + "[openconfig-extensions(1.0.0)]."));
+            assertTrue(e.getCause().getCause().getMessage().startsWith(
+                    "Unable to find module compatible with requested import [openconfig-extensions(1.0.0)]."));
         }
     }
 
@@ -54,8 +54,8 @@ public class OpenconfigVersionImportTest {
                     StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid import of openconfig-version module");
         } catch (ReactorException e) {
-            assertTrue(e.getCause().getMessage().startsWith(
-                    "Unable to find module compatible with requested import " + "[openconfig-extensions(0.9.9)]."));
+            assertTrue(e.getCause().getCause().getMessage().startsWith(
+                    "Unable to find module compatible with requested import [openconfig-extensions(0.9.9)]."));
         }
     }
 
@@ -66,8 +66,8 @@ public class OpenconfigVersionImportTest {
                     StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid import of openconfig-version module");
         } catch (ReactorException e) {
-            assertTrue(e.getCause().getMessage().startsWith(
-                    "Unable to find module compatible with requested import " + "[openconfig-extensions(2.0.0)]."));
+            assertTrue(e.getCause().getCause().getMessage().startsWith(
+                    "Unable to find module compatible with requested import [openconfig-extensions(2.0.0)]."));
         }
     }
 
@@ -78,8 +78,8 @@ public class OpenconfigVersionImportTest {
                     StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid import of openconfig-version module");
         } catch (ReactorException e) {
-            assertTrue(e.getCause().getMessage().startsWith(
-                    "Unable to find module compatible with requested import " + "[openconfig-extensions(2.0.5)]."));
+            assertTrue(e.getCause().getCause().getMessage().startsWith(
+                    "Unable to find module compatible with requested import [openconfig-extensions(2.0.5)]."));
         }
     }
 }
index 933e6cd0e54f1645bae3485bc5b3dd2db1d2203b..2b1a42757d6e305e5e21bb390162e88e352e7ba6 100644 (file)
@@ -32,7 +32,7 @@ public class OpenconfigVersionMultipleImportTest {
                     StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid openconfig version");
         } catch (ReactorException e) {
-            assertTrue(e.getCause().getMessage()
+            assertTrue(e.getCause().getCause().getMessage()
                     .startsWith("Unable to find module compatible with requested import [bar(1.0.0)]."));
         }
     }
@@ -44,7 +44,7 @@ public class OpenconfigVersionMultipleImportTest {
                     StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid openconfig version");
         } catch (ReactorException e) {
-            assertTrue(e.getCause().getMessage()
+            assertTrue(e.getCause().getCause().getMessage()
                     .startsWith("Unable to find module compatible with requested import [bar(2.5.5)]."));
         }
     }
index 01234a4b213b7950cce15886f18a9ed61d44e764..700a5be7bc3c9e4c42bc1f0333bae951691eeff4 100644 (file)
@@ -112,7 +112,7 @@ public class OpenconfigVersionTest {
             StmtTestUtils.parseYangSources("/openconfig-version/basic-import-invalid-1", StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid openconfig version");
         } catch (ReactorException e) {
-            assertTrue(e.getCause().getMessage()
+            assertTrue(e.getCause().getCause().getMessage()
                     .startsWith("Unable to find module compatible with requested import [bar(0.1.2)]."));
         }
     }
@@ -123,7 +123,7 @@ public class OpenconfigVersionTest {
             StmtTestUtils.parseYangSources("/openconfig-version/basic-import-invalid-2", StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid openconfig version");
         } catch (ReactorException e) {
-            assertTrue(e.getCause().getMessage()
+            assertTrue(e.getCause().getCause().getMessage()
                     .startsWith("Unable to find module compatible with requested import [bar(0.1.2)]."));
         }
     }
index 3d103e7cb303beefc39c554672a3a76735432baf..1206e40eb37037eec34c12d940515134efd2fdaf 100644 (file)
@@ -66,7 +66,7 @@ public class YinOpenconfigVersionTest {
                 StatementParserMode.SEMVER_MODE);
             fail("Test should fail due to invalid openconfig version");
         } catch (ReactorException e) {
-            assertTrue(e.getCause().getMessage()
+            assertTrue(e.getCause().getCause().getMessage()
                     .startsWith("Unable to find module compatible with requested import [bar(0.1.2)]."));
         }
     }
index cea95fbed50fcc78b3246dc936d75c7c94603ddf..ab0692f8e97d8b17c132158d2da67401f266660d 100644 (file)
@@ -8,9 +8,9 @@
 package org.opendaylight.yangtools.yang.parser.spi.meta;
 
 import com.google.common.annotations.Beta;
-import java.util.NavigableMap;
-import org.opendaylight.yangtools.concepts.SemVer;
-import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
+import org.opendaylight.yangtools.yang.model.repo.api.SemVerSourceIdentifier;
 
 /**
  * Namespace class for storing Maps of all modules with the same name. This namespace is
@@ -18,6 +18,6 @@ import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
  */
 @Beta
 public interface SemanticVersionModuleNamespace
-    extends IdentifierNamespace<String, NavigableMap<SemVer, StmtContext<?, ?, ?>>> {
+    extends StatementNamespace<SemVerSourceIdentifier, ModuleStatement, EffectiveStatement<String, ModuleStatement>> {
 
 }