Changed $Yang* generation package and name
[mdsal.git] / binding / mdsal-binding-java-api-generator / src / main / java / org / opendaylight / mdsal / binding / java / api / generator / YangModuleInfoTemplate.xtend
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.mdsal.binding.java.api.generator
9
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
16
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
22 import java.util.Set
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
33
34 /**
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
37  * QNAME constants.
38  */
39 final class YangModuleInfoTemplate {
40     static val Comparator<Optional<Revision>> REVISION_COMPARATOR =
41         [ Optional<Revision> first, Optional<Revision> second | Revision.compare(first, second) ]
42
43     // These are always imported. Note we need to import even java.lang members, as there can be conflicting definitions
44     // in our package
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;
53     '''
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;
59         import java.util.Set;
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;
64     '''
65
66     val Module module
67     val EffectiveModelContext ctx
68     val Function<ModuleLike, Optional<String>> moduleFilePathResolver
69     val boolean hasYangData
70
71     var importedTypes = CORE_IMPORT_STR
72
73     @Accessors
74     val String packageName
75
76     @Accessors
77     val String modelBindingProviderName
78
79     new(Module module, EffectiveModelContext ctx, Function<ModuleLike, Optional<String>> moduleFilePathResolver) {
80         Preconditions.checkArgument(module !== null, "Module must not be null.")
81         this.module = module
82         this.ctx = ctx
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])
87     }
88
89     def String generate() {
90         val Set<Submodule> submodules = new HashSet
91         collectSubmodules(submodules, module)
92
93         val body = '''
94             /**
95              * The {@link ResourceYangModuleInfo} for {@code «module.name»} module.
96              */
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»();
102
103                 private final @NonNull ImmutableSet<YangModuleInfo> importedModules;
104
105                 /**
106                  * Return the singleton instance of this class.
107                  *
108                  * @return The singleton instance
109                  */
110                 public static @NonNull YangModuleInfo getInstance() {
111                     return INSTANCE;
112                 }
113
114                 /**
115                  * Create an interned {@link QName} with specified {@code localName} and namespace/revision of this
116                  * module.
117                  *
118                  * @param localName local name
119                  * @return A QName
120                  * @throws NullPointerException if {@code localName} is {@code null}
121                  * @throws IllegalArgumentException if {@code localName} is not a valid YANG identifier
122                  */
123                 public static @NonNull QName «MODULE_INFO_QNAMEOF_METHOD_NAME»(final String localName) {
124                     return QName.create(NAME, localName).intern();
125                 }
126             «IF hasYangData»
127
128                 /**
129                  * Create an interned {@link YangDataName} with specified {@code templateName} and namespace/revision of
130                  * this module.
131                  *
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
136                  */
137                 public static @NonNull YangDataName «MODULE_INFO_YANGDATANAMEOF_METHOD_NAME»(final String templateName) {
138                     return new YangDataName(NAME.getModule(), templateName).intern();
139                 }
140             «ENDIF»
141
142                 «classBody(module, MODULE_INFO_CLASS_NAME, submodules)»
143             }
144         '''
145         return '''
146             package «packageName»;
147
148             «importedTypes»
149             «IF hasYangData»
150             import org.opendaylight.yangtools.yang.common.YangDataName;
151             «ENDIF»
152
153             «body»
154         '''.toString
155     }
156
157     def String generateModelProvider() '''
158         package «packageName»;
159
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;
164
165         /**
166          * The {@link YangModelBindingProvider} for {@code «module.name»} module. This class should not be used
167          * directly, but rather through {@link ServiceLoader}.
168          */
169         @«JavaFileTemplate.GENERATED»("mdsal-binding-generator")
170         public final class «MODEL_BINDING_PROVIDER_CLASS_NAME» implements YangModelBindingProvider {
171             /**
172              * Construct a new provider.
173              */
174             public «MODEL_BINDING_PROVIDER_CLASS_NAME»() {
175                 // No-op
176             }
177
178             @Override
179             public YangModuleInfo getModuleInfo() {
180                 return «MODULE_INFO_CLASS_NAME».getInstance();
181             }
182         }
183     '''
184
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)
189             }
190         }
191     }
192
193     private def CharSequence classBody(ModuleLike m, String className, Set<Submodule> submodules) '''
194         private «className»() {
195             «IF !m.imports.empty || !submodules.empty»
196                 «extendImports»
197                 Set<YangModuleInfo> set = new HashSet<>();
198             «ENDIF»
199             «IF !m.imports.empty»
200                 «FOR imp : m.imports»
201                     «val name = imp.moduleName.localName»
202                     «val rev = imp.revision»
203                     «IF rev.empty»
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)»
208                             «ENDIF»
209                         «ENDFOR»
210                         set.add(«sorted.lastEntry().value.QNameModule.getServicePackageName».«MODULE_INFO_CLASS_NAME».getInstance());
211                     «ELSE»
212                         set.add(«(ctx.findModule(name, rev).orElseThrow.QNameModule).getServicePackageName».«MODULE_INFO_CLASS_NAME».getInstance());
213                     «ENDIF»
214                 «ENDFOR»
215             «ENDIF»
216             «FOR submodule : submodules»
217                 set.add(«submodule.name.className»Info.getInstance());
218             «ENDFOR»
219             «IF m.imports.empty && submodules.empty»
220                 importedModules = ImmutableSet.of();
221             «ELSE»
222                 importedModules = ImmutableSet.copyOf(set);
223             «ENDIF»
224         }
225
226         @Override
227         public QName getName() {
228             return NAME;
229         }
230
231         @Override
232         protected String resourceName() {
233             return "«sourcePath(m)»";
234         }
235
236         @Override
237         public ImmutableSet<YangModuleInfo> getImportedModules() {
238             return importedModules;
239         }
240         «generateSubInfo(submodules)»
241     '''
242
243     private def void extendImports() {
244         importedTypes = EXT_IMPORT_STR
245     }
246
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
251     }
252
253     private def generateSubInfo(Set<Submodule> submodules) '''
254         «FOR submodule : submodules»
255             «val className = submodule.name.className»
256
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();
262
263                 private final @NonNull ImmutableSet<YangModuleInfo> importedModules;
264
265                 public static @NonNull YangModuleInfo getInstance() {
266                     return INSTANCE;
267                 }
268
269                 «classBody(submodule, className + "Info", ImmutableSet.of)»
270             }
271         «ENDFOR»
272     '''
273 }