From 7200fc552bb7d5b6b2bff77cf27951ca545d94ac Mon Sep 17 00:00:00 2001 From: Martin Vitez Date: Thu, 21 Aug 2014 15:14:41 +0200 Subject: [PATCH] BUG-1537: improved YangModuleInfo. YangModuleInfo is generated also for submodules in form as inner class in YangModuleInfo class of module to which it belongs. Method YangModuleInfo.getImportedModules() returns also YangModuleInfo classes of submodules which this module includes. Added getSubmodules() method to Module interface. Added test. Change-Id: Id949835d960eee3197d249f7a83be2975a63d6b3 Signed-off-by: Martin Vitez --- .../generator/impl/BindingGeneratorImpl.java | 21 +- .../generator/YangModuleInfoTemplate.xtend | 49 +++- .../maven/YangModuleInfoCompilationTest.java | 225 ++++++++++++++++++ .../yang-module-info/import-module.yang | 12 + .../yang-module-info/main-module.yang | 25 ++ .../yang-module-info/submodule1.yang | 17 ++ .../yang-module-info/submodule2.yang | 21 ++ .../yang-module-info/submodule3.yang | 13 + .../yangtools/yang/model/api/Module.java | 3 +- .../parser/builder/impl/BuilderUtils.java | 51 +++- .../parser/builder/impl/ModuleBuilder.java | 13 +- .../yang/parser/builder/impl/ModuleImpl.java | 7 + .../yang/parser/impl/YangParserImpl.java | 1 + 13 files changed, 433 insertions(+), 25 deletions(-) create mode 100644 code-generator/maven-sal-api-gen-plugin/src/test/java/org/opendaylight/yangtools/yang/unified/doc/generator/maven/YangModuleInfoCompilationTest.java create mode 100644 code-generator/maven-sal-api-gen-plugin/src/test/resources/yang-module-info/import-module.yang create mode 100644 code-generator/maven-sal-api-gen-plugin/src/test/resources/yang-module-info/main-module.yang create mode 100644 code-generator/maven-sal-api-gen-plugin/src/test/resources/yang-module-info/submodule1.yang create mode 100644 code-generator/maven-sal-api-gen-plugin/src/test/resources/yang-module-info/submodule2.yang create mode 100644 code-generator/maven-sal-api-gen-plugin/src/test/resources/yang-module-info/submodule3.yang diff --git a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/BindingGeneratorImpl.java b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/BindingGeneratorImpl.java index 9c66f8d6a6..f42678e7c2 100644 --- a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/BindingGeneratorImpl.java +++ b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/BindingGeneratorImpl.java @@ -285,16 +285,17 @@ public class BindingGeneratorImpl implements BindingGenerator { } final String packageName = packageNameForGeneratedType(basePackageName, node.getPath()); final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(packageName, node, childOf); - genType.addComment(node.getDescription()); - genType.setDescription(createDescription(node, genType.getFullyQualifiedName())); - genType.setModuleName(module.getName()); - genType.setReference(node.getReference()); - genType.setSchemaPath(node.getPath().getPathFromRoot()); if (node instanceof DataNodeContainer) { genCtx.get(module).addChildNodeType(node, genType); groupingsToGenTypes(module, ((DataNodeContainer) node).getGroupings()); processUsesAugments((DataNodeContainer) node, module); + addImplementedInterfaceFromUses((DataNodeContainer) node, genType); } + genType.addComment(node.getDescription()); + genType.setDescription(createDescription(node, genType.getFullyQualifiedName())); + genType.setModuleName(module.getName()); + genType.setReference(node.getReference()); + genType.setSchemaPath(node.getPath().getPathFromRoot()); return genType; } @@ -535,6 +536,8 @@ public class BindingGeneratorImpl implements BindingGenerator { genCtx.get(module).addChildNodeType(notification, notificationInterface); // Notification object + groupingsToGenTypes(module, notification.getGroupings()); + addImplementedInterfaceFromUses(notification, notificationInterface); resolveDataSchemaNodes(module, basePackageName, notificationInterface, notificationInterface, notification.getChildNodes()); @@ -680,9 +683,10 @@ public class BindingGeneratorImpl implements BindingGenerator { private void groupingToGenType(final String basePackageName, final GroupingDefinition grouping, final Module module) { final String packageName = packageNameForGeneratedType(basePackageName, grouping.getPath()); final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(packageName, grouping); + groupingsToGenTypes(module, grouping.getGroupings()); + addImplementedInterfaceFromUses(grouping, genType); genCtx.get(module).addGroupingType(grouping.getPath(), genType); resolveDataSchemaNodes(module, basePackageName, genType, genType, grouping.getChildNodes()); - groupingsToGenTypes(module, grouping.getGroupings()); processUsesAugments(grouping, module); } @@ -1180,6 +1184,7 @@ public class BindingGeneratorImpl implements BindingGenerator { if (caseNode != null && !caseNode.isAddedByUses() && !caseNode.isAugmenting()) { final String packageName = packageNameForGeneratedType(basePackageName, caseNode.getPath()); final GeneratedTypeBuilder caseTypeBuilder = addDefaultInterfaceDefinition(packageName, caseNode); + addImplementedInterfaceFromUses(caseNode, caseTypeBuilder); caseTypeBuilder.addImplementsType(refChoiceType); genCtx.get(module).addCaseType(caseNode.getPath(), caseTypeBuilder); genCtx.get(module).addChoiceToCaseMapping(refChoiceType, caseTypeBuilder, caseNode); @@ -1614,10 +1619,6 @@ public class BindingGeneratorImpl implements BindingGenerator { it.addImplementsType(augmentable(it)); } - if (schemaNode instanceof DataNodeContainer) { - addImplementedInterfaceFromUses((DataNodeContainer) schemaNode, it); - } - return it; } diff --git a/code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/YangModuleInfoTemplate.xtend b/code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/YangModuleInfoTemplate.xtend index 4feee7dbc0..08f19bdd70 100644 --- a/code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/YangModuleInfoTemplate.xtend +++ b/code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/YangModuleInfoTemplate.xtend @@ -33,6 +33,7 @@ import com.google.common.collect.ImmutableSet import static org.opendaylight.yangtools.yang.binding.BindingMapping.* import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider import com.google.common.base.Preconditions +import org.opendaylight.yangtools.yang.binding.BindingMapping class YangModuleInfoTemplate { @@ -72,7 +73,7 @@ class YangModuleInfoTemplate { return INSTANCE; } - «module.classBody» + «classBody(module, MODULE_INFO_CLASS_NAME)» } ''' return ''' @@ -96,10 +97,12 @@ class YangModuleInfoTemplate { } - private def CharSequence classBody(Module m) ''' - private «MODULE_INFO_CLASS_NAME»() { - «IF m.imports.size != 0» + private def CharSequence classBody(Module m, String className) ''' + private «className»() { + «IF !m.imports.empty || !m.submodules.empty» «Set.importedName»<«YangModuleInfo.importedName»> set = new «HashSet.importedName»<>(); + «ENDIF» + «IF !m.imports.empty» «FOR imp : m.imports» «val name = imp.moduleName» «val rev = imp.revision» @@ -116,10 +119,18 @@ class YangModuleInfoTemplate { set.add(«BindingGeneratorUtil.moduleNamespaceToPackageName(ctx.findModuleByName(name, rev))».«MODULE_INFO_CLASS_NAME».getInstance()); «ENDIF» «ENDFOR» - importedModules = «ImmutableSet.importedName».copyOf(set); - «ELSE» + «ENDIF» + «IF !m.submodules.empty» + «FOR submodule : m.submodules» + set.add(«BindingMapping.getClassName(submodule.name)»Info.getInstance()); + «ENDFOR» + «ENDIF» + «IF m.imports.empty && m.submodules.empty» importedModules = «Collections.importedName».emptySet(); + «ELSE» + importedModules = «ImmutableSet.importedName».copyOf(set); «ENDIF» + «InputStream.importedName» stream = «MODULE_INFO_CLASS_NAME».class.getResourceAsStream(resourcePath); if (stream == null) { throw new IllegalStateException("Resource '" + resourcePath + "' is missing"); @@ -172,6 +183,9 @@ class YangModuleInfoTemplate { sb.append("]"); return sb.toString(); } + + «generateSubInfo(m)» + ''' def getSourcePath() { @@ -286,4 +300,27 @@ class YangModuleInfoTemplate { return builder.toString(); } + private def generateSubInfo(Module module) ''' + «FOR submodule : module.submodules» + private static final class «BindingMapping.getClassName(submodule.name)»Info implements «YangModuleInfo.importedName» { + + private static final «YangModuleInfo.importedName» INSTANCE = new «BindingMapping.getClassName(submodule.name)»Info(); + + private final «String.importedName» name = "«submodule.name»"; + private final «String.importedName» namespace = "«submodule.namespace.toString»"; + «val DateFormat df = new SimpleDateFormat("yyyy-MM-dd")» + private final «String.importedName» revision = "«df.format(submodule.revision)»"; + private final «String.importedName» resourcePath = "/«submodule.moduleSourcePath.replace(java.io.File.separatorChar, '/')»"; + + private final «Set.importedName» importedModules; + + public static «YangModuleInfo.importedName» getInstance() { + return INSTANCE; + } + + «classBody(submodule, BindingMapping.getClassName(submodule.name + "Info"))» + } + «ENDFOR» + ''' + } diff --git a/code-generator/maven-sal-api-gen-plugin/src/test/java/org/opendaylight/yangtools/yang/unified/doc/generator/maven/YangModuleInfoCompilationTest.java b/code-generator/maven-sal-api-gen-plugin/src/test/java/org/opendaylight/yangtools/yang/unified/doc/generator/maven/YangModuleInfoCompilationTest.java new file mode 100644 index 0000000000..1208f190f3 --- /dev/null +++ b/code-generator/maven-sal-api-gen-plugin/src/test/java/org/opendaylight/yangtools/yang/unified/doc/generator/maven/YangModuleInfoCompilationTest.java @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.yang.unified.doc.generator.maven; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.FileNotFoundException; +import java.lang.reflect.Method; +import java.net.URI; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl; +import org.opendaylight.yangtools.yang.binding.YangModuleInfo; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.parser.api.YangContextParser; +import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl; +import org.sonatype.plexus.build.incremental.DefaultBuildContext; + +/** + * Test correct generation of YangModuleInfo class. + * + */ +// TODO: most of private static methods are copied from +// binding-java-api-generator project - reorganize compilation tests +public class YangModuleInfoCompilationTest { + public static final String FS = File.separator; + private static final String BASE_PKG = "org.opendaylight.yang.gen.v1"; + + private static final String TEST_PATH = "target" + FS + "test"; + private static final File TEST_DIR = new File(TEST_PATH); + + private static final String GENERATOR_OUTPUT_PATH = TEST_PATH + FS + "src"; + private static final File GENERATOR_OUTPUT_DIR = new File(GENERATOR_OUTPUT_PATH); + private static final String COMPILER_OUTPUT_PATH = TEST_PATH + FS + "bin"; + private static final File COMPILER_OUTPUT_DIR = new File(COMPILER_OUTPUT_PATH); + + @BeforeClass + public static void createTestDirs() { + if (TEST_DIR.exists()) { + deleteTestDir(TEST_DIR); + } + assertTrue(GENERATOR_OUTPUT_DIR.mkdirs()); + assertTrue(COMPILER_OUTPUT_DIR.mkdirs()); + } + + @Test + public void compilationTest() throws Exception { + final File sourcesOutputDir = new File(GENERATOR_OUTPUT_PATH + FS + "yang"); + assertTrue("Failed to create test file '" + sourcesOutputDir + "'", sourcesOutputDir.mkdirs()); + final File compiledOutputDir = new File(COMPILER_OUTPUT_PATH + FS + "yang"); + assertTrue("Failed to create test file '" + compiledOutputDir + "'", compiledOutputDir.mkdirs()); + + generateTestSources("/yang-module-info", sourcesOutputDir); + + // Test if $YangModuleInfoImpl.java file is generated + final String BASE_PATH = "org" + FS + "opendaylight" + FS + "yang" + FS + "gen" + FS + "v1"; + final String NS_TEST = BASE_PATH + FS + "yang" + FS + "test" + FS + "main" + FS + "rev140630"; + File parent = new File(sourcesOutputDir, NS_TEST); + File keyArgs = new File(parent, "$YangModuleInfoImpl.java"); + assertTrue(keyArgs.exists()); + + // Test if sources are compilable + testCompilation(sourcesOutputDir, compiledOutputDir); + + // Create URLClassLoader + File[] roots = File.listRoots(); + URL[] urls = new URL[roots.length + 1]; + for (int i = 0; i < roots.length; i++) { + urls[i] = roots[i].toURI().toURL(); + + } + urls[roots.length] = compiledOutputDir.toURI().toURL(); + ClassLoader loader = new URLClassLoader(urls); + + // Load class + Class yangModuleInfoClass = Class.forName(BASE_PKG + ".yang.test.main.rev140630.$YangModuleInfoImpl", true, + loader); + + // Test generated $YangModuleInfoImpl class + assertFalse(yangModuleInfoClass.isInterface()); + Method getInstance = assertContainsMethod(yangModuleInfoClass, YangModuleInfo.class, "getInstance"); + Object yangModuleInfo = getInstance.invoke(null); + + // Test getImportedModules method + Method getImportedModules = assertContainsMethod(yangModuleInfoClass, Set.class, "getImportedModules"); + Object importedModules = getImportedModules.invoke(yangModuleInfo); + assertTrue(importedModules instanceof Set); + + YangModuleInfo infoImport = null; + YangModuleInfo infoSub1 = null; + YangModuleInfo infoSub2 = null; + YangModuleInfo infoSub3 = null; + for (Object importedModule : (Set) importedModules) { + assertTrue(importedModule instanceof YangModuleInfo); + YangModuleInfo ymi = (YangModuleInfo) importedModule; + String name = ymi.getName(); + + switch (name) { + case "import-module": + infoImport = ymi; + break; + case "submodule1": + infoSub1 = ymi; + break; + case "submodule2": + infoSub2 = ymi; + break; + case "submodule3": + infoSub3 = ymi; + } + } + assertNotNull(infoImport); + assertNotNull(infoSub1); + assertNotNull(infoSub2); + assertNotNull(infoSub3); + + cleanUp(sourcesOutputDir, compiledOutputDir); + } + + private void generateTestSources(String resourceDirPath, File sourcesOutputDir) throws Exception { + final List sourceFiles = getSourceFiles(resourceDirPath); + YangContextParser parser = new YangParserImpl(); + final SchemaContext context = parser.parseFiles(sourceFiles); + CodeGeneratorImpl codegen = new CodeGeneratorImpl(); + codegen.setBuildContext(new DefaultBuildContext()); + codegen.generateSources(context, sourcesOutputDir, context.getModules()); + } + + private static void testCompilation(File sourcesOutputDir, File compiledOutputDir) { + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); + List filesList = getJavaFiles(sourcesOutputDir); + Iterable compilationUnits = fileManager.getJavaFileObjectsFromFiles(filesList); + Iterable options = Arrays.asList("-d", compiledOutputDir.getAbsolutePath()); + boolean compiled = compiler.getTask(null, null, null, options, null, compilationUnits).call(); + assertTrue(compiled); + } + + private static List getJavaFiles(File directory) { + List result = new ArrayList<>(); + File[] filesToRead = directory.listFiles(); + if (filesToRead != null) { + for (File file : filesToRead) { + if (file.isDirectory()) { + result.addAll(getJavaFiles(file)); + } else { + String absPath = file.getAbsolutePath(); + if (absPath.endsWith(".java")) { + result.add(file); + } + } + } + } + return result; + } + + private static List getSourceFiles(String path) throws Exception { + final URI resPath = YangModuleInfoCompilationTest.class.getResource(path).toURI(); + final File sourcesDir = new File(resPath); + if (sourcesDir.exists()) { + final List sourceFiles = new ArrayList<>(); + final File[] fileArray = sourcesDir.listFiles(); + if (fileArray == null) { + throw new IllegalArgumentException("Unable to locate files in " + sourcesDir); + } + sourceFiles.addAll(Arrays.asList(fileArray)); + return sourceFiles; + } else { + throw new FileNotFoundException("Testing files were not found(" + sourcesDir.getName() + ")"); + } + } + + private static void deleteTestDir(File file) { + if (file.isDirectory()) { + File[] filesToDelete = file.listFiles(); + if (filesToDelete != null) { + for (File f : filesToDelete) { + deleteTestDir(f); + } + } + } + if (!file.delete()) { + throw new RuntimeException("Failed to clean up after test"); + } + } + + private static Method assertContainsMethod(Class clazz, Class returnType, String name, Class... args) { + try { + Method m = clazz.getDeclaredMethod(name, args); + assertEquals(returnType, m.getReturnType()); + return m; + } catch (NoSuchMethodException e) { + throw new AssertionError("Method " + name + " with args " + Arrays.toString(args) + + " does not exists in class " + clazz.getSimpleName()); + } + } + + private static void cleanUp(File... resourceDirs) { + for (File resourceDir : resourceDirs) { + if (resourceDir.exists()) { + deleteTestDir(resourceDir); + } + } + } + +} diff --git a/code-generator/maven-sal-api-gen-plugin/src/test/resources/yang-module-info/import-module.yang b/code-generator/maven-sal-api-gen-plugin/src/test/resources/yang-module-info/import-module.yang new file mode 100644 index 0000000000..4c29dcb295 --- /dev/null +++ b/code-generator/maven-sal-api-gen-plugin/src/test/resources/yang-module-info/import-module.yang @@ -0,0 +1,12 @@ +module import-module { + yang-version 1; + namespace "yang:test:import"; + prefix "i"; + + revision "2013-11-19" { + } + + + container imp-cont {} + +} diff --git a/code-generator/maven-sal-api-gen-plugin/src/test/resources/yang-module-info/main-module.yang b/code-generator/maven-sal-api-gen-plugin/src/test/resources/yang-module-info/main-module.yang new file mode 100644 index 0000000000..c20b20f506 --- /dev/null +++ b/code-generator/maven-sal-api-gen-plugin/src/test/resources/yang-module-info/main-module.yang @@ -0,0 +1,25 @@ +module main-module { + + namespace "yang:test:main"; + prefix m; + + include submodule1 { + revision-date 2014-04-02; + } + + include submodule2 { + revision-date 2014-06-30; + } + + include submodule3 { + revision-date 2014-06-30; + } + + import import-module { + prefix n; + } + + revision 2014-06-30 { + } + +} diff --git a/code-generator/maven-sal-api-gen-plugin/src/test/resources/yang-module-info/submodule1.yang b/code-generator/maven-sal-api-gen-plugin/src/test/resources/yang-module-info/submodule1.yang new file mode 100644 index 0000000000..02e14789a4 --- /dev/null +++ b/code-generator/maven-sal-api-gen-plugin/src/test/resources/yang-module-info/submodule1.yang @@ -0,0 +1,17 @@ +submodule submodule1 { + + belongs-to main-module { + prefix m; + } + + revision 2014-04-02 { + } + + + typedef sub1-type { + type string; + } + + container sub1-cont {} + +} diff --git a/code-generator/maven-sal-api-gen-plugin/src/test/resources/yang-module-info/submodule2.yang b/code-generator/maven-sal-api-gen-plugin/src/test/resources/yang-module-info/submodule2.yang new file mode 100644 index 0000000000..fcdf5e1f3d --- /dev/null +++ b/code-generator/maven-sal-api-gen-plugin/src/test/resources/yang-module-info/submodule2.yang @@ -0,0 +1,21 @@ +submodule submodule2 { + + belongs-to main-module { + prefix m; + } + + include submodule1 { + revision-date 2014-04-02; + } + + revision 2014-06-30 { + } + + + grouping sub2-gr { + leaf sub2-leaf { + type string; + } + } + +} diff --git a/code-generator/maven-sal-api-gen-plugin/src/test/resources/yang-module-info/submodule3.yang b/code-generator/maven-sal-api-gen-plugin/src/test/resources/yang-module-info/submodule3.yang new file mode 100644 index 0000000000..2602814a9d --- /dev/null +++ b/code-generator/maven-sal-api-gen-plugin/src/test/resources/yang-module-info/submodule3.yang @@ -0,0 +1,13 @@ +submodule submodule3 { + + belongs-to main-module { + prefix m; + } + + revision 2014-06-30 { + } + + + container sub3-cont {} + +} diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/Module.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/Module.java index 23eb68ef1d..9a28d8fe63 100644 --- a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/Module.java +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/Module.java @@ -9,7 +9,6 @@ package org.opendaylight.yangtools.yang.model.api; import java.util.List; import java.util.Set; - import javax.annotation.concurrent.Immutable; /** @@ -128,6 +127,8 @@ public interface Module extends DataNodeContainer, SourceStreamAware, ModuleIden */ Set getImports(); + Set getSubmodules(); + /** * Returns FeatureDefinition instances which contain data from * feature statements defined in the module. diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/impl/BuilderUtils.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/impl/BuilderUtils.java index 4a61d7b0f6..d2bb65227e 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/impl/BuilderUtils.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/impl/BuilderUtils.java @@ -35,10 +35,13 @@ import java.util.Set; import java.util.TreeMap; import org.antlr.v4.runtime.tree.ParseTree; import org.apache.commons.io.IOUtils; +import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Belongs_to_stmtContext; import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Module_header_stmtsContext; import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Module_stmtContext; import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Namespace_stmtContext; import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_stmtsContext; +import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Submodule_header_stmtsContext; +import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Submodule_stmtContext; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; @@ -822,11 +825,17 @@ public final class BuilderUtils { public static Map> createYangNamespaceContext( final Collection modules, final Optional context) { - Map> map = new HashMap<>(); + Map> namespaceContext = new HashMap<>(); + Set submodules = new HashSet<>(); + // first read ParseTree collection and separate modules and submodules for (ParseTree module : modules) { for (int i = 0; i < module.getChildCount(); i++) { ParseTree moduleTree = module.getChild(i); - if (moduleTree instanceof Module_stmtContext) { + if (moduleTree instanceof Submodule_stmtContext) { + // put submodule context to separate collection + submodules.add((Submodule_stmtContext) moduleTree); + } else if (moduleTree instanceof Module_stmtContext) { + // get name, revision and namespace from module Module_stmtContext moduleCtx = (Module_stmtContext) moduleTree; final String moduleName = ParserListenerUtils.stringFromNode(moduleCtx); Date rev = null; @@ -854,28 +863,56 @@ public final class BuilderUtils { } } } - TreeMap revToNs = map.get(moduleName); + // update namespaceContext + TreeMap revToNs = namespaceContext.get(moduleName); if (revToNs == null) { revToNs = new TreeMap<>(); revToNs.put(rev, namespace); - map.put(moduleName, revToNs); + namespaceContext.put(moduleName, revToNs); } revToNs.put(rev, namespace); } } } + // after all ParseTree-s are parsed update namespaceContext with modules + // from SchemaContext if (context.isPresent()) { for (Module module : context.get().getModules()) { - TreeMap revToNs = map.get(module.getName()); + TreeMap revToNs = namespaceContext.get(module.getName()); if (revToNs == null) { revToNs = new TreeMap<>(); revToNs.put(module.getRevision(), module.getNamespace()); - map.put(module.getName(), revToNs); + namespaceContext.put(module.getName(), revToNs); } revToNs.put(module.getRevision(), module.getNamespace()); } } - return map; + // when all modules are processed, traverse submodules and update + // namespaceContext with mapping for submodules + for (Submodule_stmtContext submodule : submodules) { + final String moduleName = ParserListenerUtils.stringFromNode(submodule); + for (int i = 0; i < submodule.getChildCount(); i++) { + ParseTree subHeaderCtx = submodule.getChild(i); + if (subHeaderCtx instanceof Submodule_header_stmtsContext) { + for (int j = 0; j < subHeaderCtx.getChildCount(); j++) { + ParseTree belongsCtx = subHeaderCtx.getChild(j); + if (belongsCtx instanceof Belongs_to_stmtContext) { + final String belongsTo = ParserListenerUtils.stringFromNode(belongsCtx); + TreeMap ns = namespaceContext.get(belongsTo); + if (ns == null) { + throw new YangParseException(moduleName, submodule.getStart().getLine(), String.format( + "Unresolved belongs-to statement: %s", belongsTo)); + } + // submodule get namespace and revision from module + TreeMap subNs = new TreeMap<>(); + subNs.put(ns.firstKey(), ns.firstEntry().getValue()); + namespaceContext.put(moduleName, subNs); + } + } + } + } + } + return namespaceContext; } } diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/impl/ModuleBuilder.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/impl/ModuleBuilder.java index 3d43e0025f..5c4afe6ae1 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/impl/ModuleBuilder.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/impl/ModuleBuilder.java @@ -79,7 +79,9 @@ public class ModuleBuilder extends AbstractDocumentedDataNodeContainerBuilder im final Map imports = new HashMap<>(); final Map importedModules = new HashMap<>(); - private final Map includedModules = new HashMap<>(); + final Set addedSubmodules = new HashSet<>(); + final Set submodules = new HashSet<>(); + final Map includedModules = new HashMap<>(); private final Set augments = new LinkedHashSet<>(); private final List augmentBuilders = new ArrayList<>(); @@ -171,6 +173,11 @@ public class ModuleBuilder extends AbstractDocumentedDataNodeContainerBuilder im buildChildren(); + // SUBMODULES + for (ModuleBuilder submodule : addedSubmodules) { + submodules.add(submodule.build()); + } + // FEATURES for (FeatureBuilder fb : addedFeatures) { features.add(fb.build()); @@ -374,6 +381,10 @@ public class ModuleBuilder extends AbstractDocumentedDataNodeContainerBuilder im includedModules.put(name, revision); } + public void addSubmodule(final ModuleBuilder submodule) { + addedSubmodules.add(submodule); + } + protected String getSource() { return source; } diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/impl/ModuleImpl.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/impl/ModuleImpl.java index d2c3729871..00018b8dda 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/impl/ModuleImpl.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/impl/ModuleImpl.java @@ -36,6 +36,7 @@ public final class ModuleImpl extends AbstractDocumentedDataNodeContainer implem private final String organization; private final String contact; private final Set imports; + private final Set submodules; private final Set features; private final Set notifications; private final Set augmentations; @@ -61,6 +62,7 @@ public final class ModuleImpl extends AbstractDocumentedDataNodeContainer implem this.name = checkNotNull(name, "Missing name"); this.sourcePath = sourcePath; //TODO: can this be nullable? this.imports = ImmutableSet. copyOf(builder.imports.values()); + this.submodules = ImmutableSet. copyOf(builder.submodules); this.prefix = builder.getPrefix(); this.qnameModule = QNameModule.create(builder.getNamespace(), @@ -125,6 +127,11 @@ public final class ModuleImpl extends AbstractDocumentedDataNodeContainer implem return imports; } + @Override + public Set getSubmodules() { + return submodules; + } + @Override public Set getFeatures() { return features; diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserImpl.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserImpl.java index 1ed5986df5..38055d908d 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserImpl.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserImpl.java @@ -503,6 +503,7 @@ public final class YangParserImpl implements YangContextParser { } private void addSubmoduleToModule(final ModuleBuilder submodule, final ModuleBuilder module) { + module.addSubmodule(submodule); submodule.setParent(module); module.getDirtyNodes().addAll(submodule.getDirtyNodes()); module.getImports().putAll(submodule.getImports()); -- 2.36.6