import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
+
import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
import org.opendaylight.yangtools.binding.generator.util.BindingTypes;
import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
public class BindingGeneratorImpl implements BindingGenerator {
private static final Logger LOG = LoggerFactory.getLogger(BindingGeneratorImpl.class);
+ private static final Splitter COLON_SPLITTER = Splitter.on(':');
+ private static final Splitter BSDOT_SPLITTER = Splitter.on("\\.");
+ private static final char NEW_LINE = '\n';
+
+ /**
+ * Constant with the concrete name of identifier.
+ */
+ private static final String AUGMENT_IDENTIFIER_NAME = "augment-identifier";
+
+ /**
+ * Constant with the concrete name of namespace.
+ */
+ private static final String YANG_EXT_NAMESPACE = "urn:opendaylight:yang:extension:yang-ext";
private final Map<Module, ModuleContext> genCtx = new HashMap<>();
+ /**
+ * When set to true, generated classes will include javadoc comments which
+ * are useful for users.
+ */
+ private final boolean verboseClassComments;
+
/**
* Outer key represents the package name. Outer value represents map of all
* builders in the same package. Inner key represents the schema node name
private SchemaContext schemaContext;
/**
- * Constant with the concrete name of namespace.
+ * Create a new binding generator with verboe comments.
+ *
+ * @deprecated Use {@link #BindingGeneratorImpl(boolean)} instead.
*/
- private final static String YANG_EXT_NAMESPACE = "urn:opendaylight:yang:extension:yang-ext";
+ @Deprecated
+ public BindingGeneratorImpl() {
+ this(true);
+ }
/**
- * Constant with the concrete name of identifier.
+ * Create a new binding generator.
+ *
+ * @param verboseClassComments generate verbose comments
*/
- private final static String AUGMENT_IDENTIFIER_NAME = "augment-identifier";
-
- private final char NEW_LINE = '\n';
-
- private final char TAB = '\t';
+ public BindingGeneratorImpl(final boolean verboseClassComments) {
+ this.verboseClassComments = verboseClassComments;
+ }
/**
* Resolves generated types from <code>context</code> schema nodes of all
newType.setDescription(createDescription(identity, newType.getFullyQualifiedName()));
newType.setReference(identity.getReference());
newType.setModuleName(module.getName());
- SchemaPath path = identity.getPath();
newType.setSchemaPath(identity.getPath().getPathFromRoot());
final QName qname = identity.getQName();
final String nodeParam = node.getNodeParameter();
IdentitySchemaNode identity = null;
String basePackageName = null;
- final Iterable<String> splittedElement = Splitter.on(':').split(nodeParam);
+ final Iterable<String> splittedElement = COLON_SPLITTER.split(nodeParam);
final Iterator<String> iterator = splittedElement.iterator();
final int length = Iterables.size(splittedElement);
if (length == 1) {
private String createDescription(final Set<? extends SchemaNode> schemaNodes, final String moduleName, final String moduleSourcePath) {
final StringBuilder sb = new StringBuilder();
- final String yangSnipet = YangTemplate.generateYangSnipet(schemaNodes);
if (!isNullOrEmpty(schemaNodes)) {
final SchemaNode node = schemaNodes.iterator().next();
if (node instanceof RpcDefinition) {
sb.append("Interface for implementing the following YANG RPCs defined in module <b>" + moduleName + "</b>");
- }
- else if (node instanceof NotificationDefinition) {
+ } else if (node instanceof NotificationDefinition) {
sb.append("Interface for receiving the following YANG notifications defined in module <b>" + moduleName + "</b>");
}
}
sb.append(moduleSourcePath);
sb.append("</i>):");
sb.append(NEW_LINE);
- sb.append("<pre>");
- sb.append(NEW_LINE);
- sb.append(yangSnipet);
- sb.append("</pre>");
- sb.append(NEW_LINE);
+
+ if (verboseClassComments) {
+ sb.append("<pre>");
+ sb.append(NEW_LINE);
+ sb.append(YangTemplate.generateYangSnipet(schemaNodes));
+ sb.append("</pre>");
+ sb.append(NEW_LINE);
+ }
return sb.toString();
}
private String createDescription(final SchemaNode schemaNode, final String fullyQualifiedName) {
final StringBuilder sb = new StringBuilder();
- final Module module = findParentModule(schemaContext, schemaNode);
- final String yangSnipet = YangTemplate.generateYangSnipet(schemaNode);
final String formattedDescription = YangTemplate.formatToParagraph(schemaNode.getDescription(), 0);
- final StringBuilder linkToBuilderClass = new StringBuilder();
- final StringBuilder linkToKeyClass = new StringBuilder();
- final Splitter splitter = Splitter.on("\\.");
- final String[] namespace = Iterables.toArray(splitter.split(fullyQualifiedName), String.class);
- String className = namespace[namespace.length - 1];
-
- if (hasBuilderClass(schemaNode)) {
- linkToBuilderClass.append(className);
- linkToBuilderClass.append("Builder");
-
- if (schemaNode instanceof ListSchemaNode) {
- linkToKeyClass.append(className);
- linkToKeyClass.append("Key");
- }
- }
if (!isNullOrEmpty(formattedDescription)) {
sb.append(formattedDescription);
sb.append(NEW_LINE);
}
- sb.append("<p>");
- sb.append("This class represents the following YANG schema fragment defined in module <b>");
- sb.append(module.getName());
- sb.append("</b>");
- sb.append(NEW_LINE);
- sb.append("<br />(Source path: <i>");
- sb.append(module.getModuleSourcePath());
- sb.append("</i>):");
- sb.append(NEW_LINE);
- sb.append("<pre>");
- sb.append(NEW_LINE);
- sb.append(yangSnipet);
- sb.append("</pre>");
- sb.append(NEW_LINE);
- sb.append("The schema path to identify an instance is");
- sb.append(NEW_LINE);
- sb.append("<i>");
- sb.append(YangTemplate.formatSchemaPath(module.getName(), schemaNode.getPath().getPathFromRoot()));
- sb.append("</i>");
- sb.append(NEW_LINE);
- if (hasBuilderClass(schemaNode)) {
+ if (verboseClassComments) {
+ final Module module = findParentModule(schemaContext, schemaNode);
+ final StringBuilder linkToBuilderClass = new StringBuilder();
+ final StringBuilder linkToKeyClass = new StringBuilder();
+ final String[] namespace = Iterables.toArray(BSDOT_SPLITTER.split(fullyQualifiedName), String.class);
+ String className = namespace[namespace.length - 1];
+
+ if (hasBuilderClass(schemaNode)) {
+ linkToBuilderClass.append(className);
+ linkToBuilderClass.append("Builder");
+
+ if (schemaNode instanceof ListSchemaNode) {
+ linkToKeyClass.append(className);
+ linkToKeyClass.append("Key");
+ }
+ }
+
+ sb.append("<p>");
+ sb.append("This class represents the following YANG schema fragment defined in module <b>");
+ sb.append(module.getName());
+ sb.append("</b>");
+ sb.append(NEW_LINE);
+ sb.append("<br />(Source path: <i>");
+ sb.append(module.getModuleSourcePath());
+ sb.append("</i>):");
sb.append(NEW_LINE);
- sb.append("<p>To create instances of this class use " + "{@link " + linkToBuilderClass + "}.");
+ sb.append("<pre>");
sb.append(NEW_LINE);
- sb.append("@see ");
- sb.append(linkToBuilderClass);
- if (schemaNode instanceof ListSchemaNode) {
+ sb.append(YangTemplate.generateYangSnipet(schemaNode));
+ sb.append("</pre>");
+ sb.append(NEW_LINE);
+ sb.append("The schema path to identify an instance is");
+ sb.append(NEW_LINE);
+ sb.append("<i>");
+ sb.append(YangTemplate.formatSchemaPath(module.getName(), schemaNode.getPath().getPathFromRoot()));
+ sb.append("</i>");
+ sb.append(NEW_LINE);
+
+ if (hasBuilderClass(schemaNode)) {
+ sb.append(NEW_LINE);
+ sb.append("<p>To create instances of this class use " + "{@link " + linkToBuilderClass + "}.");
+ sb.append(NEW_LINE);
sb.append("@see ");
- sb.append(linkToKeyClass);
+ sb.append(linkToBuilderClass);
+ if (schemaNode instanceof ListSchemaNode) {
+ sb.append("@see ");
+ sb.append(linkToKeyClass);
+ }
+ sb.append(NEW_LINE);
}
- sb.append(NEW_LINE);
}
return sb.toString();
private String createDescription(final Module module) {
final StringBuilder sb = new StringBuilder();
- final String yangSnipet = YangTemplate.generateYangSnipet(module);
final String formattedDescription = YangTemplate.formatToParagraph(module.getDescription(), 0);
if (!isNullOrEmpty(formattedDescription)) {
sb.append(formattedDescription);
sb.append(NEW_LINE);
}
- sb.append("<p>");
- sb.append("This class represents the following YANG schema fragment defined in module <b>");
- sb.append(module.getName());
- sb.append("</b>");
- sb.append(NEW_LINE);
- sb.append("<br />Source path: <i>");
- sb.append(module.getModuleSourcePath());
- sb.append("</i>):");
- sb.append(NEW_LINE);
- sb.append("<pre>");
- sb.append(NEW_LINE);
- sb.append(yangSnipet);
- sb.append("</pre>");
+
+ if (verboseClassComments) {
+ sb.append("<p>");
+ sb.append("This class represents the following YANG schema fragment defined in module <b>");
+ sb.append(module.getName());
+ sb.append("</b>");
+ sb.append(NEW_LINE);
+ sb.append("<br />Source path: <i>");
+ sb.append(module.getModuleSourcePath());
+ sb.append("</i>):");
+ sb.append(NEW_LINE);
+ sb.append("<pre>");
+ sb.append(NEW_LINE);
+ sb.append(YangTemplate.generateYangSnipet(module));
+ sb.append("</pre>");
+ }
return sb.toString();
}
import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
+
import java.net.URI;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
+
import javassist.ClassPool;
+
import javax.annotation.concurrent.GuardedBy;
+
import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
import org.opendaylight.yangtools.binding.generator.util.Types;
@GuardedBy("this")
private void recreateBindingContext(final SchemaContext schemaContext) {
- BindingGeneratorImpl newBinding = new BindingGeneratorImpl();
+ BindingGeneratorImpl newBinding = new BindingGeneratorImpl(false);
newBinding.generateTypes(schemaContext);
for (Map.Entry<Module, ModuleContext> entry : newBinding.getModuleContexts().entrySet()) {
*/
package org.opendaylight.yangtools.sal.binding.generator.impl
-import java.text.SimpleDateFormat
import java.util.Collection
import java.util.Date
import java.util.List
import org.opendaylight.yangtools.yang.model.api.UsesNode
import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition
import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil
+import com.google.common.base.CharMatcher
class YangTemplate {
+ // FIXME: this is not thread-safe and seems to be unused!
private static var Module module = null
+ private static val CharMatcher NEWLINE_OR_TAB = CharMatcher.anyOf("\n\t")
+
def static String generateYangSnipet(SchemaNode schemaNode) {
if (schemaNode == null)
return ''
'''
}
- def static formatDate(Date moduleRevision) {
- val SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd")
- return dateFormat.format(moduleRevision)
- }
-
def static writeRevision(Date moduleRevision, String moduleDescription) {
val revisionIndent = 12
'''
- revision «formatDate(moduleRevision)» {
+ revision «SimpleDateFormatUtil.getRevisionFormat.format(moduleRevision)» {
description "«formatToParagraph(moduleDescription, revisionIndent)»";
}
'''
val lineIndent = computeNextLineIndent(nextLineIndent);
formattedText = formattedText.replace("*/", "*/");
- formattedText = formattedText.replace("\n", "");
- formattedText = formattedText.replace("\t", "");
+ formattedText = NEWLINE_OR_TAB.removeFrom(formattedText);
formattedText = formattedText.replaceAll(" +", " ");
val StringTokenizer tokenizer = new StringTokenizer(formattedText, " ", true);
val ns = pathElement.namespace
val localName = pathElement.localName
- sb.append("\\")
- sb.append('(')
+ sb.append("\\(")
sb.append(ns)
sb.append(')')
sb.append(localName)
this.strategy = strategy;
this.schemaContext = schema;
- BindingGeneratorImpl generator = new BindingGeneratorImpl();
+ BindingGeneratorImpl generator = new BindingGeneratorImpl(false);
generator.generateTypes(schema);
Map<Module, ModuleContext> modules = generator.getModuleContexts();
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»
+ '''
+
}
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
import org.slf4j.LoggerFactory;
import org.sonatype.plexus.build.incremental.BuildContext;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
public final class CodeGeneratorImpl implements CodeGenerator, BuildContextAware {
private static final String FS = File.separator;
private BuildContext buildContext;
outputBaseDir = outputDir == null ? getDefaultOutputBaseDir() : outputDir;
- final BindingGenerator bindingGenerator = new BindingGeneratorImpl();
+ final BindingGenerator bindingGenerator = new BindingGeneratorImpl(true);
final List<Type> types = bindingGenerator.generateTypes(context, yangModules);
final GeneratorJavaFile generator = new GeneratorJavaFile(buildContext, types);
return result;
}
- private Collection<? extends File> generateModuleInfos(File outputBaseDir, Set<Module> yangModules,
- SchemaContext context) {
+ private Collection<? extends File> generateModuleInfos(final File outputBaseDir, final Set<Module> yangModules,
+ final SchemaContext context) {
Builder<File> result = ImmutableSet.builder();
Builder<String> bindingProviders = ImmutableSet.builder();
for (Module module : yangModules) {
return result.build();
}
- private File writeMetaInfServices(File outputBaseDir, Class<YangModelBindingProvider> serviceClass,
- ImmutableSet<String> services) {
+ private File writeMetaInfServices(final File outputBaseDir, final Class<YangModelBindingProvider> serviceClass,
+ final ImmutableSet<String> services) {
File metainfServicesFolder = new File(outputBaseDir, "META-INF" + File.separator + "services");
metainfServicesFolder.mkdirs();
File serviceFile = new File(metainfServicesFolder, serviceClass.getName());
return outputBaseDir;
}
- private static void setOutputBaseDirAsSourceFolder(File outputBaseDir, MavenProject mavenProject) {
+ private static void setOutputBaseDirAsSourceFolder(final File outputBaseDir, final MavenProject mavenProject) {
Preconditions.checkNotNull(mavenProject, "Maven project needs to be set in this phase");
mavenProject.addCompileSourceRoot(outputBaseDir.getPath());
}
@Override
- public void setLog(Log log) {
+ public void setLog(final Log log) {
}
@Override
- public void setAdditionalConfig(Map<String, String> additionalConfiguration) {
+ public void setAdditionalConfig(final Map<String, String> additionalConfiguration) {
this.additionalConfig = additionalConfiguration;
}
@Override
- public void setResourceBaseDir(File resourceBaseDir) {
+ public void setResourceBaseDir(final File resourceBaseDir) {
this.resourceBaseDir = resourceBaseDir;
}
@Override
- public void setMavenProject(MavenProject project) {
+ public void setMavenProject(final MavenProject project) {
this.mavenProject = project;
this.projectBaseDir = project.getBasedir();
}
@Override
- public void setBuildContext(BuildContext buildContext) {
+ public void setBuildContext(final BuildContext buildContext) {
this.buildContext = Preconditions.checkNotNull(buildContext);
}
- private Set<File> generateYangModuleInfo(File outputBaseDir, Module module, SchemaContext ctx,
- Builder<String> providerSourceSet) {
+ private Set<File> generateYangModuleInfo(final File outputBaseDir, final Module module, final SchemaContext ctx,
+ final Builder<String> providerSourceSet) {
Builder<File> generatedFiles = ImmutableSet.<File> builder();
final YangModuleInfoTemplate template = new YangModuleInfoTemplate(module, ctx);
}
- private File writeJavaSource(File packageDir, String className, String source) {
+ private File writeJavaSource(final File packageDir, final String className, final String source) {
if (!packageDir.exists()) {
packageDir.mkdirs();
}
return file;
}
- private File writeFile(File file, String source) {
+ private File writeFile(final File file, final String source) {
try (final OutputStream stream = buildContext.newFileOutputStream(file)) {
try (final Writer fw = new OutputStreamWriter(stream)) {
try (final BufferedWriter bw = new BufferedWriter(fw)) {
--- /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 {}
+
+}
package org.opendaylight.yangtools.yang.data.codec.gson;
import com.google.gson.stream.JsonReader;
-
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
+import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
-
import org.junit.BeforeClass;
import org.junit.Test;
import org.opendaylight.yangtools.yang.common.QName;
private static String streamAsString;
@BeforeClass
- public static void initialization() throws IOException {
+ public static void initialization() throws IOException, URISyntaxException {
schemaContext = loadModules("/complexjson/yang");
- streamAsString = loadTextFile(StreamToNormalizedNodeTest.class.getResource("/complexjson/complex-json.json")
- .getPath());
+ streamAsString = loadTextFile(new File(StreamToNormalizedNodeTest.class.getResource(
+ "/complexjson/complex-json.json").toURI()));
}
/**
return parser.parseFiles(testFiles);
}
- private static String loadTextFile(final String filePath) throws IOException {
- FileReader fileReader = new FileReader(filePath);
+ private static String loadTextFile(final File file) throws IOException {
+ FileReader fileReader = new FileReader(file);
BufferedReader bufReader = new BufferedReader(fileReader);
String line = null;
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.
rpc_stmt : RPC_KEYWORD string (SEMICOLON | (LEFT_BRACE (identifier_stmt |if_feature_stmt | status_stmt | description_stmt | reference_stmt | typedef_stmt | grouping_stmt | input_stmt | output_stmt )* RIGHT_BRACE));
when_stmt : WHEN_KEYWORD string (SEMICOLON | (LEFT_BRACE (identifier_stmt |description_stmt | reference_stmt )* RIGHT_BRACE));
-augment_stmt : AUGMENT_KEYWORD string LEFT_BRACE (identifier_stmt |when_stmt | if_feature_stmt | status_stmt | description_stmt | reference_stmt | data_def_stmt | case_stmt)+ RIGHT_BRACE;
-uses_augment_stmt : AUGMENT_KEYWORD string LEFT_BRACE (identifier_stmt |when_stmt | if_feature_stmt | status_stmt | description_stmt | reference_stmt | data_def_stmt | case_stmt)+ RIGHT_BRACE;
+augment_stmt : AUGMENT_KEYWORD string LEFT_BRACE (identifier_stmt |when_stmt | if_feature_stmt | status_stmt | description_stmt | reference_stmt | data_def_stmt | case_stmt)* RIGHT_BRACE;
+uses_augment_stmt : AUGMENT_KEYWORD string LEFT_BRACE (identifier_stmt |when_stmt | if_feature_stmt | status_stmt | description_stmt | reference_stmt | data_def_stmt | case_stmt)* RIGHT_BRACE;
refine_anyxml_stmts : (identifier_stmt |must_stmt | config_stmt | mandatory_stmt | description_stmt | reference_stmt )*;
refine_case_stmts : (identifier_stmt |description_stmt | reference_stmt )*;
refine_choice_stmts : (identifier_stmt |default_stmt | config_stmt | mandatory_stmt | description_stmt | reference_stmt )*;
position_stmt : POSITION_KEYWORD string stmtend;
bit_stmt : BIT_KEYWORD string (SEMICOLON | (LEFT_BRACE (identifier_stmt |position_stmt | status_stmt | description_stmt | reference_stmt )* RIGHT_BRACE));
bits_specification : bit_stmt (bit_stmt | identifier_stmt)*;
-union_specification : type_stmt (identifier_stmt | type_stmt )+;
+union_specification : type_stmt (identifier_stmt | type_stmt )*;
identityref_specification : base_stmt ;
instance_identifier_specification : (require_instance_stmt )?;
require_instance_arg :string; // TRUE_KEYWORD | FALSE_KEYWORD;
* Returns when condition
*
* If when condition is present node defined by the parent data definition
- * statement is only valid when the returned XPath
- * expression conceptually evaluates to "true"
- * for a particular instance, then the node defined by the parent data
- * definition statement is valid; otherwise, it is not.
- *
+ * statement is only valid when the returned XPath expression conceptually
+ * evaluates to "true" for a particular instance, then the node defined by
+ * the parent data definition statement is valid; otherwise, it is not.
*
- * @return
+ * @return when condition as string
*/
String getWhenCondition();
*
* This constraint has meaning only if associated node is list or leaf-list.
*
- * @param minElements
+ * @param maxElements
* number of maximum required elements.
*/
void setMaxElements(Integer maxElements);
/**
* Build constraint definition
*
- * @return
+ * @return instance of ConstraintDefinition created from this builder
*/
ConstraintDefinition toInstance();
*
* This constraint has meaning only if associated node is list or leaf-list.
*
- * @param minElements number of maximum required elements.
+ * @param maxElements number of maximum required elements.
*/
void setMaxElements(Integer maxElements);
List<PatternConstraint> getPatterns();
/**
- * Set length restrictions for resulting type definition.
+ * Set pattern restrictions for resulting type definition.
*
- * @param lengths
- * Length restrictions of resulting type definition.
+ * @param patterns
+ * patterns restrictions of resulting type definition.
*/
void setPatterns(List<PatternConstraint> patterns);
Integer getFractionDigits();
/**
- *
- * Sets fractions digits of resulting type if it is derived
- * from <code>decimal</code> built-in type.
- *
- * @return fractions digits of resulting type
+ * Sets fractions digits of resulting type if it is derived from
+ * <code>decimal</code> built-in type.
*/
void setFractionDigits(Integer fractionDigits);
/**
* Sets extension builder, which declares this unknown node
- * @param extensionBuilder extension definition, which declares this unknown node
+ *
+ * @param extension
+ * extension builder, which declares this unknown node
*/
void setExtensionBuilder(ExtensionBuilder extension);
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
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 final class BuilderUtils {
- private static final DateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
private static final Logger LOG = LoggerFactory.getLogger(BuilderUtils.class);
- private static final Splitter SLASH_SPLITTER = Splitter.on('/').omitEmptyStrings();
private static final Splitter COLON_SPLITTER = Splitter.on(':');
private static final Date NULL_DATE = new Date(0L);
private static final String INPUT = "input";
* Class to be checked
* @param optional
* Original value
- * @return
+ * @return Optional object with type argument casted as cls
*/
private static <T> Optional<T> castOptional(final Class<T> cls, final Optional<?> optional) {
if (optional.isPresent()) {
* Find augment target node and perform augmentation.
*
* @param augment
+ * augment builder to process
* @param firstNodeParent
* parent of first node in path
- * @param path
- * path to augment target
* @return true if augmentation process succeed, false otherwise
*/
public static boolean processAugmentation(final AugmentationSchemaBuilder augment,
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;
* type body context
* @param actualPath
* current path in schema
- * @param namespace
- * current namespace
- * @param revision
- * current revision
- * @param prefix
- * current prefix
+ * @param moduleQName
+ * current module qname
* @param parent
* parent builder
* @return TypeDefinition object based on parsed values.
}
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());
* Create a new instance.
*
* FIXME: the resulting type needs to be extracted, such that we can reuse
- * the "BaseListener" aspect, which need not be exposed to the user.
- * Maybe factor out a base class into repo.spi?
+ * the "BaseListener" aspect, which need not be exposed to the user. Maybe
+ * factor out a base class into repo.spi?
*
* @param namespaceContext
* @param sourcePath
* @param walker
* @param tree
- * @return
+ * @return new instance of YangParserListenerImpl
*/
public static YangParserListenerImpl create(final Map<String, TreeMap<Date, URI>> namespaceContext,
final String sourcePath, final ParseTreeWalker walker, final ParseTree tree) {
final ParseTree treeNode = ctx.getChild(i);
if (treeNode instanceof Prefix_stmtContext) {
importPrefix = stringFromNode(treeNode);
- }
- if (treeNode instanceof Revision_date_stmtContext) {
+ } else if (treeNode instanceof Revision_date_stmtContext) {
String importRevisionStr = stringFromNode(treeNode);
try {
importRevision = SIMPLE_DATE_FORMAT.parse(importRevisionStr);
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.io.ByteSource;
+
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
+
import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
import org.opendaylight.yangtools.concepts.Identifiable;
import org.opendaylight.yangtools.concepts.ObjectRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+/**
+ * @deprecated Use {@link org.opendaylight.yangtools.yang.parser.repo.URLSchemaContextResolver}
+ * instead.
+ */
+@Deprecated
@ThreadSafe
public class URLSchemaContextResolver implements AdvancedSchemaSourceProvider<InputStream> {
/**
* Register new yang schema when it appears.
*/
- public synchronized ObjectRegistration<URL> registerSource(URL source) {
+ public synchronized ObjectRegistration<URL> registerSource(final URL source) {
checkArgument(source != null, "Supplied source must not be null");
InputStream yangStream = getInputStream(source);
YangModelDependencyInfo modelInfo = YangModelDependencyInfo.fromInputStream(yangStream);
}
@Override
- public synchronized Optional<InputStream> getSchemaSource(SourceIdentifier key) {
+ public synchronized Optional<InputStream> getSchemaSource(final SourceIdentifier key) {
SourceContext ctx = availableSources.get(key);
if (ctx != null) {
InputStream stream = getInputStream(ctx.getInstance());
}
@Override
- public Optional<InputStream> getSchemaSource(String name, Optional<String> version) {
+ public Optional<InputStream> getSchemaSource(final String name, final Optional<String> version) {
return getSchemaSource(SourceIdentifier.create(name, version));
}
- private static InputStream getInputStream(URL source) {
+ private static InputStream getInputStream(final URL source) {
InputStream stream;
try {
stream = source.openStream();
final SourceIdentifier identifier;
final YangModelDependencyInfo dependencyInfo;
- public SourceContext(URL instance, SourceIdentifier identifier, YangModelDependencyInfo modelInfo) {
+ public SourceContext(final URL instance, final SourceIdentifier identifier, final YangModelDependencyInfo modelInfo) {
super(instance);
this.identifier = identifier;
this.dependencyInfo = modelInfo;
}
}
- private synchronized void removeSource(SourceContext sourceContext) {
+ private synchronized void removeSource(final SourceContext sourceContext) {
boolean removed = availableSources.remove(sourceContext.getIdentifier(), sourceContext);
if (removed) {
tryToUpdateSchemaContext();
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.Futures;
import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource.Costs;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaListenerRegistration;
import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+import org.opendaylight.yangtools.yang.model.repo.util.InMemorySchemaSourceCache;
import org.opendaylight.yangtools.yang.parser.util.ASTSchemaSource;
import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Beta
-public class URLSchemaContextResolver implements SchemaSourceProvider<YangTextSchemaSource> {
+public class URLSchemaContextResolver implements AutoCloseable, SchemaSourceProvider<YangTextSchemaSource> {
private static final Logger LOG = LoggerFactory.getLogger(URLSchemaContextResolver.class);
- private final Cache<SourceIdentifier, YangTextSchemaSource> sources = CacheBuilder.newBuilder().build();
private final Collection<SourceIdentifier> requiredSources = new ConcurrentLinkedDeque<>();
+ private final Multimap<SourceIdentifier, YangTextSchemaSource> texts = ArrayListMultimap.create();
private final AtomicReference<Optional<SchemaContext>> currentSchemaContext =
new AtomicReference<>(Optional.<SchemaContext>absent());
+ private final InMemorySchemaSourceCache<ASTSchemaSource> cache;
+ private final SchemaListenerRegistration transReg;
private final SchemaSourceRegistry registry;
private final SchemaRepository repository;
private volatile Object version = new Object();
private volatile Object contextVersion = version;
+
private URLSchemaContextResolver(final SchemaRepository repository, final SchemaSourceRegistry registry) {
this.repository = Preconditions.checkNotNull(repository);
this.registry = Preconditions.checkNotNull(registry);
+
+ final TextToASTTransformer t = TextToASTTransformer.create(repository, registry);
+ transReg = registry.registerSchemaSourceListener(t);
+
+ cache = InMemorySchemaSourceCache.createSoftCache(registry, ASTSchemaSource.class);
}
public static URLSchemaContextResolver create(final String name) {
LOG.trace("Resolved URL {} to source {}", url, ast);
final SourceIdentifier resolvedId = ast.getIdentifier();
- final SchemaSourceRegistration<YangTextSchemaSource> reg = registry.registerSchemaSource(this,
- PotentialSchemaSource.create(resolvedId, YangTextSchemaSource.class, 0));
-
- requiredSources.add(resolvedId);
- LOG.trace("Added source {} to schema context requirements", resolvedId);
- version = new Object();
- return new AbstractURLRegistration(text) {
- @Override
- protected void removeRegistration() {
- requiredSources.remove(resolvedId);
- LOG.trace("Removed source {} from schema context requirements", resolvedId);
- version = new Object();
- reg.close();
- sources.invalidate(resolvedId);
- }
- };
+ synchronized (this) {
+ texts.put(resolvedId, text);
+ LOG.debug("Populated {} with text", resolvedId);
+
+ final SchemaSourceRegistration<YangTextSchemaSource> reg = registry.registerSchemaSource(this,
+ PotentialSchemaSource.create(resolvedId, YangTextSchemaSource.class, Costs.IMMEDIATE.getValue()));
+ requiredSources.add(resolvedId);
+ cache.schemaSourceEncountered(ast);
+ LOG.debug("Added source {} to schema context requirements", resolvedId);
+ version = new Object();
+
+ return new AbstractURLRegistration(text) {
+ @Override
+ protected void removeRegistration() {
+ synchronized (URLSchemaContextResolver.this) {
+ requiredSources.remove(resolvedId);
+ LOG.trace("Removed source {} from schema context requirements", resolvedId);
+ version = new Object();
+ reg.close();
+ texts.remove(resolvedId, text);
+ }
+ }
+ };
+ }
}
/**
Collection<SourceIdentifier> sources;
do {
v = version;
- sources = ImmutableList.copyOf(requiredSources);
+ sources = ImmutableSet.copyOf(requiredSources);
} while (v != version);
while (true) {
sc = Optional.of(f.checkedGet());
break;
} catch (SchemaResolutionException e) {
- LOG.info("Failed to fully assemble schema context for {}", sources, e);
+ LOG.debug("Failed to fully assemble schema context for {}", sources, e);
sources = e.getResolvedSources();
}
}
+ LOG.debug("Resolved schema context for {}", sources);
+
synchronized (this) {
if (contextVersion == cv) {
currentSchemaContext.set(sc);
}
@Override
- public CheckedFuture<YangTextSchemaSource, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
- final YangTextSchemaSource ret = sources.getIfPresent(sourceIdentifier);
- if (ret == null) {
+ public synchronized CheckedFuture<YangTextSchemaSource, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
+ final Collection<YangTextSchemaSource> ret = texts.get(sourceIdentifier);
+
+ LOG.debug("Lookup {} result {}", sourceIdentifier, ret);
+ if (ret.isEmpty()) {
return Futures.<YangTextSchemaSource, SchemaSourceException>immediateFailedCheckedFuture(
new MissingSchemaSourceException("URL for " + sourceIdentifier + " not registered", sourceIdentifier));
}
- return Futures.immediateCheckedFuture(ret);
+ return Futures.immediateCheckedFuture(ret.iterator().next());
+ }
+
+ @Override
+ public void close() {
+ transReg.close();
}
}
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
-
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
-
import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.ModuleImport;
*/
private static void processDependencies(final Map<String, Map<Date, ModuleNodeImpl>> moduleGraph,
final Iterable<ModuleOrModuleBuilder> mmbs) {
- Map<URI, Object> allNS = new HashMap<>();
+ Map<URI, ModuleOrModuleBuilder> allNS = new HashMap<>();
// Create edges in graph
for (ModuleOrModuleBuilder mmb : mmbs) {
// check for existence of module with same namespace
if (allNS.containsKey(ns)) {
- Object mod = allNS.get(ns);
+ ModuleOrModuleBuilder mod = allNS.get(ns);
String name = null;
Date revision = null;
-
- if(mod instanceof ModuleOrModuleBuilder) {
- ModuleOrModuleBuilder modOrmodBuilder = ((ModuleOrModuleBuilder) mod);
- if(modOrmodBuilder.isModule()) {
- mod = ((ModuleOrModuleBuilder) mod).getModule();
- } else if (modOrmodBuilder.isModuleBuilder()) {
- mod = ((ModuleOrModuleBuilder) mod).getModuleBuilder();
- } else {
- LOGGER.warn("ModuleOrModuleBuilder is neither Module or ModuleBuilder");
- }
- }
- if (mod instanceof Module) {
- name = ((Module) mod).getName();
- revision = ((Module) mod).getRevision();
- } else if (mod instanceof ModuleBuilder) {
- name = ((ModuleBuilder) mod).getName();
- revision = ((ModuleBuilder) mod).getRevision();
- } else {
- LOGGER.warn("Module has no name: {}", mod);
+ if (mod.isModule()) {
+ name = mod.getModule().getName();
+ revision = mod.getModule().getRevision();
+ } else if (mod.isModuleBuilder()) {
+ name = mod.getModuleBuilder().getName();
+ revision = mod.getModuleBuilder().getRevision();
}
if (!(fromName.equals(name))) {
LOGGER.warn(