}
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;
}
genCtx.get(module).addChildNodeType(notification, notificationInterface);
// Notification object
+ groupingsToGenTypes(module, notification.getGroupings());
+ addImplementedInterfaceFromUses(notification, notificationInterface);
resolveDataSchemaNodes(module, basePackageName, notificationInterface, notificationInterface,
notification.getChildNodes());
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);
}
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);
it.addImplementsType(augmentable(it));
}
- if (schemaNode instanceof DataNodeContainer) {
- addImplementedInterfaceFromUses((DataNodeContainer) schemaNode, it);
- }
-
return it;
}
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 {
return INSTANCE;
}
- «module.classBody»
+ «classBody(module, MODULE_INFO_CLASS_NAME)»
}
'''
return '''
}
- 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»
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");
sb.append("]");
return sb.toString();
}
+
+ «generateSubInfo(m)»
+
'''
def getSourcePath() {
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»<YangModuleInfo> importedModules;
+
+ public static «YangModuleInfo.importedName» getInstance() {
+ return INSTANCE;
+ }
+
+ «classBody(submodule, BindingMapping.getClassName(submodule.name + "Info"))»
+ }
+ «ENDFOR»
+ '''
+
}
--- /dev/null
+/*
+ * 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<File> 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<File> filesList = getJavaFiles(sourcesOutputDir);
+ Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(filesList);
+ Iterable<String> options = Arrays.asList("-d", compiledOutputDir.getAbsolutePath());
+ boolean compiled = compiler.getTask(null, null, null, options, null, compilationUnits).call();
+ assertTrue(compiled);
+ }
+
+ private static List<File> getJavaFiles(File directory) {
+ List<File> 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<File> getSourceFiles(String path) throws Exception {
+ final URI resPath = YangModuleInfoCompilationTest.class.getResource(path).toURI();
+ final File sourcesDir = new File(resPath);
+ if (sourcesDir.exists()) {
+ final List<File> 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);
+ }
+ }
+ }
+
+}
--- /dev/null
+module import-module {
+ yang-version 1;
+ namespace "yang:test:import";
+ prefix "i";
+
+ revision "2013-11-19" {
+ }
+
+
+ container imp-cont {}
+
+}
--- /dev/null
+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 {
+ }
+
+}
--- /dev/null
+submodule submodule1 {
+
+ belongs-to main-module {
+ prefix m;
+ }
+
+ revision 2014-04-02 {
+ }
+
+
+ typedef sub1-type {
+ type string;
+ }
+
+ container sub1-cont {}
+
+}
--- /dev/null
+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;
+ }
+ }
+
+}
--- /dev/null
+submodule submodule3 {
+
+ belongs-to main-module {
+ prefix m;
+ }
+
+ revision 2014-06-30 {
+ }
+
+
+ container sub3-cont {}
+
+}
import java.util.List;
import java.util.Set;
-
import javax.annotation.concurrent.Immutable;
/**
*/
Set<ModuleImport> getImports();
+ Set<Module> getSubmodules();
+
/**
* Returns <code>FeatureDefinition</code> instances which contain data from
* <b>feature</b> statements defined in the module.
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;
public static Map<String, TreeMap<Date, URI>> createYangNamespaceContext(
final Collection<? extends ParseTree> modules, final Optional<SchemaContext> context) {
- Map<String, TreeMap<Date, URI>> map = new HashMap<>();
+ Map<String, TreeMap<Date, URI>> namespaceContext = new HashMap<>();
+ Set<Submodule_stmtContext> 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;
}
}
}
- TreeMap<Date, URI> revToNs = map.get(moduleName);
+ // update namespaceContext
+ TreeMap<Date, URI> 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<Date, URI> revToNs = map.get(module.getName());
+ TreeMap<Date, URI> 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<Date, URI> 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<Date, URI> subNs = new TreeMap<>();
+ subNs.put(ns.firstKey(), ns.firstEntry().getValue());
+ namespaceContext.put(moduleName, subNs);
+ }
+ }
+ }
+ }
+ }
+ return namespaceContext;
}
}
final Map<String, ModuleImport> imports = new HashMap<>();
final Map<String, ModuleBuilder> importedModules = new HashMap<>();
- private final Map<String, Date> includedModules = new HashMap<>();
+ final Set<ModuleBuilder> addedSubmodules = new HashSet<>();
+ final Set<Module> submodules = new HashSet<>();
+ final Map<String, Date> includedModules = new HashMap<>();
private final Set<AugmentationSchema> augments = new LinkedHashSet<>();
private final List<AugmentationSchemaBuilder> augmentBuilders = new ArrayList<>();
buildChildren();
+ // SUBMODULES
+ for (ModuleBuilder submodule : addedSubmodules) {
+ submodules.add(submodule.build());
+ }
+
// FEATURES
for (FeatureBuilder fb : addedFeatures) {
features.add(fb.build());
includedModules.put(name, revision);
}
+ public void addSubmodule(final ModuleBuilder submodule) {
+ addedSubmodules.add(submodule);
+ }
+
protected String getSource() {
return source;
}
private final String organization;
private final String contact;
private final Set<ModuleImport> imports;
+ private final Set<Module> submodules;
private final Set<FeatureDefinition> features;
private final Set<NotificationDefinition> notifications;
private final Set<AugmentationSchema> augmentations;
this.name = checkNotNull(name, "Missing name");
this.sourcePath = sourcePath; //TODO: can this be nullable?
this.imports = ImmutableSet.<ModuleImport> copyOf(builder.imports.values());
+ this.submodules = ImmutableSet.<Module> copyOf(builder.submodules);
this.prefix = builder.getPrefix();
this.qnameModule = QNameModule.create(builder.getNamespace(),
return imports;
}
+ @Override
+ public Set<Module> getSubmodules() {
+ return submodules;
+ }
+
@Override
public Set<FeatureDefinition> getFeatures() {
return features;
}
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());