2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.mdsal.binding.java.api.generator
10 import static extension org.opendaylight.yangtools.yang.binding.contract.Naming.getClassName
11 import static extension org.opendaylight.yangtools.yang.binding.contract.Naming.getServicePackageName
12 import static org.opendaylight.yangtools.yang.binding.contract.Naming.MODEL_BINDING_PROVIDER_CLASS_NAME
13 import static org.opendaylight.yangtools.yang.binding.contract.Naming.MODULE_INFO_CLASS_NAME
14 import static org.opendaylight.yangtools.yang.binding.contract.Naming.MODULE_INFO_QNAMEOF_METHOD_NAME
15 import static org.opendaylight.yangtools.yang.binding.contract.Naming.MODULE_INFO_YANGDATANAMEOF_METHOD_NAME
17 import com.google.common.base.Preconditions
18 import com.google.common.collect.ImmutableSet
19 import java.util.Comparator
20 import java.util.HashSet
21 import java.util.Optional
23 import java.util.TreeMap
24 import java.util.function.Function
25 import org.eclipse.xtend.lib.annotations.Accessors
26 import org.opendaylight.yangtools.rfc8040.model.api.YangDataSchemaNode
27 import org.opendaylight.yangtools.yang.binding.YangModuleInfo
28 import org.opendaylight.yangtools.yang.common.Revision
29 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext
30 import org.opendaylight.yangtools.yang.model.api.Module
31 import org.opendaylight.yangtools.yang.model.api.ModuleLike
32 import org.opendaylight.yangtools.yang.model.api.Submodule
35 * Template for {@link YangModuleInfo} implementation for a particular module. Aside from fulfilling that contract,
36 * this class provides a static {@code createQName(String)} method, which is used by co-generated code to initialize
39 final class YangModuleInfoTemplate {
40 static val Comparator<Optional<Revision>> REVISION_COMPARATOR =
41 [ Optional<Revision> first, Optional<Revision> second | Revision.compare(first, second) ]
43 // These are always imported. Note we need to import even java.lang members, as there can be conflicting definitions
45 static val CORE_IMPORT_STR = '''
46 import com.google.common.collect.ImmutableSet;
47 import java.lang.Override;
48 import java.lang.String;
49 import org.eclipse.jdt.annotation.NonNull;
50 import org.opendaylight.yangtools.yang.binding.ResourceYangModuleInfo;
51 import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
52 import org.opendaylight.yangtools.yang.common.QName;
54 static val EXT_IMPORT_STR = '''
55 import com.google.common.collect.ImmutableSet;
56 import java.lang.Override;
57 import java.lang.String;
58 import java.util.HashSet;
60 import org.eclipse.jdt.annotation.NonNull;
61 import org.opendaylight.yangtools.yang.binding.ResourceYangModuleInfo;
62 import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
63 import org.opendaylight.yangtools.yang.common.QName;
67 val EffectiveModelContext ctx
68 val Function<ModuleLike, Optional<String>> moduleFilePathResolver
69 val boolean hasYangData
71 var importedTypes = CORE_IMPORT_STR
74 val String packageName
77 val String modelBindingProviderName
79 new(Module module, EffectiveModelContext ctx, Function<ModuleLike, Optional<String>> moduleFilePathResolver) {
80 Preconditions.checkArgument(module !== null, "Module must not be null.")
83 this.moduleFilePathResolver = moduleFilePathResolver
84 packageName = module.QNameModule.getServicePackageName;
85 modelBindingProviderName = '''«packageName».«MODEL_BINDING_PROVIDER_CLASS_NAME»'''
86 hasYangData = module.unknownSchemaNodes.stream.anyMatch([s | s instanceof YangDataSchemaNode])
89 def String generate() {
90 val Set<Submodule> submodules = new HashSet
91 collectSubmodules(submodules, module)
95 * The {@link ResourceYangModuleInfo} for {@code «module.name»} module.
97 @«JavaFileTemplate.GENERATED»("mdsal-binding-generator")
98 public final class «MODULE_INFO_CLASS_NAME» extends ResourceYangModuleInfo {
99 «val rev = module.revision»
100 private static final @NonNull QName NAME = QName.create("«module.QNameModule.namespace.toString»", «IF rev.present»"«rev.orElseThrow.toString»", «ENDIF»"«module.name»").intern();
101 private static final @NonNull YangModuleInfo INSTANCE = new «MODULE_INFO_CLASS_NAME»();
103 private final @NonNull ImmutableSet<YangModuleInfo> importedModules;
106 * Return the singleton instance of this class.
108 * @return The singleton instance
110 public static @NonNull YangModuleInfo getInstance() {
115 * Create an interned {@link QName} with specified {@code localName} and namespace/revision of this
118 * @param localName local name
120 * @throws NullPointerException if {@code localName} is {@code null}
121 * @throws IllegalArgumentException if {@code localName} is not a valid YANG identifier
123 public static @NonNull QName «MODULE_INFO_QNAMEOF_METHOD_NAME»(final String localName) {
124 return QName.create(NAME, localName).intern();
129 * Create an interned {@link YangDataName} with specified {@code templateName} and namespace/revision of
132 * @param templateName template name
133 * @return A YangDataName
134 * @throws NullPointerException if {@code templateName} is {@code null}
135 * @throws IllegalArgumentException if {@code templateName} is empty
137 public static @NonNull YangDataName «MODULE_INFO_YANGDATANAMEOF_METHOD_NAME»(final String templateName) {
138 return new YangDataName(NAME.getModule(), templateName).intern();
142 «classBody(module, MODULE_INFO_CLASS_NAME, submodules)»
146 package «packageName»;
150 import org.opendaylight.yangtools.yang.common.YangDataName;
157 def String generateModelProvider() '''
158 package «packageName»;
160 import java.lang.Override;
161 import java.util.ServiceLoader;
162 import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
163 import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
166 * The {@link YangModelBindingProvider} for {@code «module.name»} module. This class should not be used
167 * directly, but rather through {@link ServiceLoader}.
169 @«JavaFileTemplate.GENERATED»("mdsal-binding-generator")
170 public final class «MODEL_BINDING_PROVIDER_CLASS_NAME» implements YangModelBindingProvider {
172 * Construct a new provider.
174 public «MODEL_BINDING_PROVIDER_CLASS_NAME»() {
179 public YangModuleInfo getModuleInfo() {
180 return «MODULE_INFO_CLASS_NAME».getInstance();
185 private static def void collectSubmodules(Set<Submodule> dest, ModuleLike module) {
186 for (Submodule submodule : module.submodules) {
187 if (dest.add(submodule)) {
188 collectSubmodules(dest, submodule)
193 private def CharSequence classBody(ModuleLike m, String className, Set<Submodule> submodules) '''
194 private «className»() {
195 «IF !m.imports.empty || !submodules.empty»
197 Set<YangModuleInfo> set = new HashSet<>();
199 «IF !m.imports.empty»
200 «FOR imp : m.imports»
201 «val name = imp.moduleName.localName»
202 «val rev = imp.revision»
204 «val TreeMap<Optional<Revision>, Module> sorted = new TreeMap(REVISION_COMPARATOR)»
205 «FOR module : ctx.modules»
206 «IF name.equals(module.name)»
207 «sorted.put(module.revision, module)»
210 set.add(«sorted.lastEntry().value.QNameModule.getServicePackageName».«MODULE_INFO_CLASS_NAME».getInstance());
212 set.add(«(ctx.findModule(name, rev).orElseThrow.QNameModule).getServicePackageName».«MODULE_INFO_CLASS_NAME».getInstance());
216 «FOR submodule : submodules»
217 set.add(«submodule.name.className»Info.getInstance());
219 «IF m.imports.empty && submodules.empty»
220 importedModules = ImmutableSet.of();
222 importedModules = ImmutableSet.copyOf(set);
227 public QName getName() {
232 protected String resourceName() {
233 return "«sourcePath(m)»";
237 public ImmutableSet<YangModuleInfo> getImportedModules() {
238 return importedModules;
240 «generateSubInfo(submodules)»
243 private def void extendImports() {
244 importedTypes = EXT_IMPORT_STR
247 private def sourcePath(ModuleLike module) {
248 val opt = moduleFilePathResolver.apply(module)
249 Preconditions.checkState(opt.isPresent, "Module %s does not have a file path", module)
250 return opt.orElseThrow
253 private def generateSubInfo(Set<Submodule> submodules) '''
254 «FOR submodule : submodules»
255 «val className = submodule.name.className»
257 private static final class «className»Info extends ResourceYangModuleInfo {
258 «val rev = submodule.revision»
259 private final @NonNull QName NAME = QName.create("«submodule.QNameModule.namespace.toString»", «
260 IF rev.present»"«rev.orElseThrow.toString»", «ENDIF»"«submodule.name»").intern();
261 private static final @NonNull YangModuleInfo INSTANCE = new «className»Info();
263 private final @NonNull ImmutableSet<YangModuleInfo> importedModules;
265 public static @NonNull YangModuleInfo getInstance() {
269 «classBody(submodule, className + "Info", ImmutableSet.of)»