*/
package org.opendaylight.mdsal.binding.java.api.generator
-import static extension org.opendaylight.mdsal.binding.spec.naming.BindingMapping.getClassName
-import static extension org.opendaylight.mdsal.binding.spec.naming.BindingMapping.getRootPackageName
-import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.MODEL_BINDING_PROVIDER_CLASS_NAME
-import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.MODULE_INFO_CLASS_NAME
-import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.MODULE_INFO_QNAMEOF_METHOD_NAME
+import static extension org.opendaylight.yangtools.yang.binding.contract.Naming.getClassName
+import static extension org.opendaylight.yangtools.yang.binding.contract.Naming.getServicePackageName
+import static org.opendaylight.yangtools.yang.binding.contract.Naming.MODEL_BINDING_PROVIDER_CLASS_NAME
+import static org.opendaylight.yangtools.yang.binding.contract.Naming.MODULE_INFO_CLASS_NAME
+import static org.opendaylight.yangtools.yang.binding.contract.Naming.MODULE_INFO_QNAMEOF_METHOD_NAME
+import static org.opendaylight.yangtools.yang.binding.contract.Naming.MODULE_INFO_YANGDATANAMEOF_METHOD_NAME
import com.google.common.base.Preconditions
import com.google.common.collect.ImmutableSet
-import java.util.Collections
import java.util.Comparator
import java.util.HashSet
-import java.util.LinkedHashMap
-import java.util.Map
import java.util.Optional
import java.util.Set
import java.util.TreeMap
import java.util.function.Function
import org.eclipse.xtend.lib.annotations.Accessors
-import org.opendaylight.mdsal.binding.model.api.ParameterizedType
-import org.opendaylight.mdsal.binding.model.api.Type
-import org.opendaylight.mdsal.binding.model.api.WildcardType
-import org.opendaylight.mdsal.binding.model.util.Types
-import org.opendaylight.yangtools.yang.binding.ResourceYangModuleInfo
-import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider
+import org.opendaylight.yangtools.rfc8040.model.api.YangDataSchemaNode
import org.opendaylight.yangtools.yang.binding.YangModuleInfo
-import org.opendaylight.yangtools.yang.common.QName
import org.opendaylight.yangtools.yang.common.Revision
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext
import org.opendaylight.yangtools.yang.model.api.Module
-import org.opendaylight.yangtools.yang.model.api.SchemaContext
+import org.opendaylight.yangtools.yang.model.api.ModuleLike
+import org.opendaylight.yangtools.yang.model.api.Submodule
/**
* Template for {@link YangModuleInfo} implementation for a particular module. Aside from fulfilling that contract,
* this class provides a static {@code createQName(String)} method, which is used by co-generated code to initialize
* QNAME constants.
*/
-class YangModuleInfoTemplate {
+final class YangModuleInfoTemplate {
static val Comparator<Optional<Revision>> REVISION_COMPARATOR =
[ Optional<Revision> first, Optional<Revision> second | Revision.compare(first, second) ]
+ // These are always imported. Note we need to import even java.lang members, as there can be conflicting definitions
+ // in our package
+ static val CORE_IMPORT_STR = '''
+ import com.google.common.collect.ImmutableSet;
+ import java.lang.Override;
+ import java.lang.String;
+ import org.eclipse.jdt.annotation.NonNull;
+ import org.opendaylight.yangtools.yang.binding.ResourceYangModuleInfo;
+ import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+ import org.opendaylight.yangtools.yang.common.QName;
+ '''
+ static val EXT_IMPORT_STR = '''
+ import com.google.common.collect.ImmutableSet;
+ import java.lang.Override;
+ import java.lang.String;
+ import java.util.HashSet;
+ import java.util.Set;
+ import org.eclipse.jdt.annotation.NonNull;
+ import org.opendaylight.yangtools.yang.binding.ResourceYangModuleInfo;
+ import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+ import org.opendaylight.yangtools.yang.common.QName;
+ '''
+
val Module module
- val SchemaContext ctx
- val Map<String, String> importMap = new LinkedHashMap()
- val Function<Module, Optional<String>> moduleFilePathResolver
+ val EffectiveModelContext ctx
+ val Function<ModuleLike, Optional<String>> moduleFilePathResolver
+ val boolean hasYangData
+
+ var importedTypes = CORE_IMPORT_STR
@Accessors
val String packageName
@Accessors
val String modelBindingProviderName
- new(Module module, SchemaContext ctx, Function<Module, Optional<String>> moduleFilePathResolver) {
+ new(Module module, EffectiveModelContext ctx, Function<ModuleLike, Optional<String>> moduleFilePathResolver) {
Preconditions.checkArgument(module !== null, "Module must not be null.")
this.module = module
this.ctx = ctx
this.moduleFilePathResolver = moduleFilePathResolver
- packageName = module.QNameModule.rootPackageName;
+ packageName = module.QNameModule.getServicePackageName;
modelBindingProviderName = '''«packageName».«MODEL_BINDING_PROVIDER_CLASS_NAME»'''
+ hasYangData = module.unknownSchemaNodes.stream.anyMatch([s | s instanceof YangDataSchemaNode])
}
def String generate() {
+ val Set<Submodule> submodules = new HashSet
+ collectSubmodules(submodules, module)
+
val body = '''
- public final class «MODULE_INFO_CLASS_NAME» extends «ResourceYangModuleInfo.importedName» {
+ /**
+ * The {@link ResourceYangModuleInfo} for {@code «module.name»} module.
+ */
+ @«JavaFileTemplate.GENERATED»("mdsal-binding-generator")
+ public final class «MODULE_INFO_CLASS_NAME» extends ResourceYangModuleInfo {
«val rev = module.revision»
- private static final «QName.importedName» NAME = «QName.importedName».create("«module.namespace.toString»", «IF rev.present»"«rev.get.toString»", «ENDIF»"«module.name»").intern();
- private static final «YangModuleInfo.importedName» INSTANCE = new «MODULE_INFO_CLASS_NAME»();
+ private static final @NonNull QName NAME = QName.create("«module.QNameModule.namespace().toString»", «IF rev.present»"«rev.orElseThrow.toString»", «ENDIF»"«module.name»").intern();
+ private static final @NonNull YangModuleInfo INSTANCE = new «MODULE_INFO_CLASS_NAME»();
- private final «Set.importedName»<«YangModuleInfo.importedName»> importedModules;
+ private final @NonNull ImmutableSet<YangModuleInfo> importedModules;
- public static «YangModuleInfo.importedName» getInstance() {
+ /**
+ * Return the singleton instance of this class.
+ *
+ * @return The singleton instance
+ */
+ public static @NonNull YangModuleInfo getInstance() {
return INSTANCE;
}
- public static «QName.importedName» «MODULE_INFO_QNAMEOF_METHOD_NAME»(final «String.importedName» localName) {
- return «QName.importedName».create(NAME, localName).intern();
+ /**
+ * Create an interned {@link QName} with specified {@code localName} and namespace/revision of this
+ * module.
+ *
+ * @param localName local name
+ * @return A QName
+ * @throws NullPointerException if {@code localName} is {@code null}
+ * @throws IllegalArgumentException if {@code localName} is not a valid YANG identifier
+ */
+ public static @NonNull QName «MODULE_INFO_QNAMEOF_METHOD_NAME»(final String localName) {
+ return QName.create(NAME, localName).intern();
+ }
+ «IF hasYangData»
+
+ /**
+ * Create an interned {@link YangDataName} with specified {@code templateName} and namespace/revision of
+ * this module.
+ *
+ * @param templateName template name
+ * @return A YangDataName
+ * @throws NullPointerException if {@code templateName} is {@code null}
+ * @throws IllegalArgumentException if {@code templateName} is empty
+ */
+ public static @NonNull YangDataName «MODULE_INFO_YANGDATANAMEOF_METHOD_NAME»(final String templateName) {
+ return new YangDataName(NAME.getModule(), templateName).intern();
}
+ «ENDIF»
- «classBody(module, MODULE_INFO_CLASS_NAME)»
+ «classBody(module, MODULE_INFO_CLASS_NAME, submodules)»
}
'''
return '''
package «packageName»;
- «imports»
+ «importedTypes»
+ «IF hasYangData»
+ import org.opendaylight.yangtools.yang.common.YangDataName;
+ «ENDIF»
«body»
'''.toString
}
- def String generateModelProvider() {
- '''
- package «packageName»;
+ def String generateModelProvider() '''
+ package «packageName»;
+
+ import java.lang.Override;
+ import java.util.ServiceLoader;
+ import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
+ import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+
+ /**
+ * The {@link YangModelBindingProvider} for {@code «module.name»} module. This class should not be used
+ * directly, but rather through {@link ServiceLoader}.
+ */
+ @«JavaFileTemplate.GENERATED»("mdsal-binding-generator")
+ public final class «MODEL_BINDING_PROVIDER_CLASS_NAME» implements YangModelBindingProvider {
+ /**
+ * Construct a new provider.
+ */
+ public «MODEL_BINDING_PROVIDER_CLASS_NAME»() {
+ // No-op
+ }
- public final class «MODEL_BINDING_PROVIDER_CLASS_NAME» implements «YangModelBindingProvider.name» {
- @«Override.importedName»
- public «YangModuleInfo.name» getModuleInfo() {
- return «MODULE_INFO_CLASS_NAME».getInstance();
- }
+ @Override
+ public YangModuleInfo getModuleInfo() {
+ return «MODULE_INFO_CLASS_NAME».getInstance();
}
- '''
+ }
+ '''
+ private static def void collectSubmodules(Set<Submodule> dest, ModuleLike module) {
+ for (Submodule submodule : module.submodules) {
+ if (dest.add(submodule)) {
+ collectSubmodules(dest, submodule)
+ }
+ }
}
- private def CharSequence classBody(Module m, String className) '''
+ private def CharSequence classBody(ModuleLike m, String className, Set<Submodule> submodules) '''
private «className»() {
- «IF !m.imports.empty || !m.submodules.empty»
- «Set.importedName»<«YangModuleInfo.importedName»> set = new «HashSet.importedName»<>();
+ «IF !m.imports.empty || !submodules.empty»
+ «extendImports»
+ Set<YangModuleInfo> set = new HashSet<>();
«ENDIF»
«IF !m.imports.empty»
«FOR imp : m.imports»
- «val name = imp.moduleName»
+ «val name = imp.moduleName.localName»
«val rev = imp.revision»
- «IF !rev.present»
- «val Set<Module> modules = ctx.modules»
+ «IF rev.empty»
«val TreeMap<Optional<Revision>, Module> sorted = new TreeMap(REVISION_COMPARATOR)»
- «FOR module : modules»
- «IF module.name.equals(name)»
+ «FOR module : ctx.modules»
+ «IF name.equals(module.name)»
«sorted.put(module.revision, module)»
«ENDIF»
«ENDFOR»
- set.add(«sorted.lastEntry().value.QNameModule.rootPackageName».«MODULE_INFO_CLASS_NAME».getInstance());
+ set.add(«sorted.lastEntry().value.QNameModule.getServicePackageName».«MODULE_INFO_CLASS_NAME».getInstance());
«ELSE»
- set.add(«(ctx.findModule(name, rev).get.QNameModule).rootPackageName».«MODULE_INFO_CLASS_NAME».getInstance());
+ set.add(«(ctx.findModule(name, rev).orElseThrow.QNameModule).getServicePackageName».«MODULE_INFO_CLASS_NAME».getInstance());
«ENDIF»
«ENDFOR»
«ENDIF»
- «IF !m.submodules.empty»
- «FOR submodule : m.submodules»
- set.add(«submodule.name.className»Info.getInstance());
- «ENDFOR»
- «ENDIF»
- «IF m.imports.empty && m.submodules.empty»
- importedModules = «Collections.importedName».emptySet();
+ «FOR submodule : submodules»
+ set.add(«submodule.name.className»Info.getInstance());
+ «ENDFOR»
+ «IF m.imports.empty && submodules.empty»
+ importedModules = ImmutableSet.of();
«ELSE»
- importedModules = «ImmutableSet.importedName».copyOf(set);
+ importedModules = ImmutableSet.copyOf(set);
«ENDIF»
}
- @«Override.importedName»
- public «QName.importedName» getName() {
+ @Override
+ public QName getName() {
return NAME;
}
- @«Override.importedName»
- protected «String.importedName» resourceName() {
+ @Override
+ protected String resourceName() {
return "«sourcePath(m)»";
}
- @«Override.importedName»
- public «Set.importedName»<«YangModuleInfo.importedName»> getImportedModules() {
+ @Override
+ public ImmutableSet<YangModuleInfo> getImportedModules() {
return importedModules;
}
-
- «generateSubInfo(m)»
-
- '''
-
- private def sourcePath(Module module) {
- val opt = moduleFilePathResolver.apply(module)
- Preconditions.checkState(opt.isPresent, "Module %s does not have a file path", module)
- return opt.get
- }
-
- private def imports() '''
- «IF !importMap.empty»
- «FOR entry : importMap.entrySet»
- «IF entry.value != module.QNameModule.rootPackageName»
- import «entry.value».«entry.key»;
- «ENDIF»
- «ENDFOR»
- «ENDIF»
+ «generateSubInfo(submodules)»
'''
- final protected def importedName(Class<?> cls) {
- val Type intype = Types.typeForClass(cls)
- putTypeIntoImports(intype)
- intype.explicitType
+ private def void extendImports() {
+ importedTypes = EXT_IMPORT_STR
}
- final def void putTypeIntoImports(Type type) {
- val String typeName = type.name
- val String typePackageName = type.packageName
- if (typePackageName.startsWith("java.lang") || typePackageName.empty) {
- return
- }
- if (!importMap.containsKey(typeName)) {
- importMap.put(typeName, typePackageName)
- }
- if (type instanceof ParameterizedType) {
- val Type[] params = type.actualTypeArguments
- if (params !== null) {
- for (Type param : params) {
- putTypeIntoImports(param)
- }
- }
- }
- }
-
- final def String getExplicitType(Type type) {
- val String typePackageName = type.packageName
- val String typeName = type.name
- val String importedPackageName = importMap.get(typeName)
- var StringBuilder builder
- if (typePackageName.equals(importedPackageName)) {
- builder = new StringBuilder(typeName)
- if (builder.toString().equals("Void")) {
- return "void"
- }
- addActualTypeParameters(builder, type)
- } else {
- if (type.equals(Types.voidType())) {
- return "void"
- }
- builder = new StringBuilder()
- if (!typePackageName.empty) {
- builder.append(typePackageName).append(Constants.DOT).append(typeName)
- } else {
- builder.append(typeName)
- }
- addActualTypeParameters(builder, type)
- }
- return builder.toString()
- }
-
- final def StringBuilder addActualTypeParameters(StringBuilder builder, Type type) {
- if (type instanceof ParameterizedType) {
- val Type[] pTypes = type.actualTypeArguments
- builder.append('<').append(getParameters(pTypes)).append('>')
- }
- return builder
- }
-
- final def String getParameters(Type[] pTypes) {
- if (pTypes === null || pTypes.length == 0) {
- return "?"
- }
- val StringBuilder builder = new StringBuilder()
-
- var int i = 0
- for (pType : pTypes) {
- val Type t = pTypes.get(i)
-
- var String separator = ","
- if (i == (pTypes.length - 1)) {
- separator = ""
- }
-
- var String wildcardParam = ""
- if (t.equals(Types.voidType())) {
- builder.append("java.lang.Void").append(separator)
- } else {
-
- if (t instanceof WildcardType) {
- wildcardParam = "? extends "
- }
-
- builder.append(wildcardParam).append(t.explicitType).append(separator)
- i = i + 1
- }
- }
- return builder.toString()
+ private def sourcePath(ModuleLike module) {
+ val opt = moduleFilePathResolver.apply(module)
+ Preconditions.checkState(opt.isPresent, "Module %s does not have a file path", module)
+ return opt.orElseThrow
}
- private def generateSubInfo(Module module) '''
- «FOR submodule : module.submodules»
+ private def generateSubInfo(Set<Submodule> submodules) '''
+ «FOR submodule : submodules»
«val className = submodule.name.className»
- private static final class «className»Info extends «ResourceYangModuleInfo.importedName» {
+
+ private static final class «className»Info extends ResourceYangModuleInfo {
«val rev = submodule.revision»
- private final «QName.importedName» NAME = «QName.importedName».create("«
- submodule.namespace.toString»", «IF rev.present»"«rev.get.toString»", «ENDIF»"«submodule.name»").intern();
- private static final «YangModuleInfo.importedName» INSTANCE = new «className»Info();
+ private final @NonNull QName NAME = QName.create("«submodule.QNameModule.namespace().toString»", «
+ IF rev.present»"«rev.orElseThrow.toString»", «ENDIF»"«submodule.name»").intern();
+ private static final @NonNull YangModuleInfo INSTANCE = new «className»Info();
- private final «Set.importedName»<YangModuleInfo> importedModules;
+ private final @NonNull ImmutableSet<YangModuleInfo> importedModules;
- public static «YangModuleInfo.importedName» getInstance() {
+ public static @NonNull YangModuleInfo getInstance() {
return INSTANCE;
}
- «classBody(submodule, className + "Info")»
+ «classBody(submodule, className + "Info", ImmutableSet.of)»
}
«ENDFOR»
'''