Fix a minor typo
[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.mdsal.binding.spec.naming.BindingMapping.getClassName
11 import static extension org.opendaylight.mdsal.binding.spec.naming.BindingMapping.getRootPackageName
12 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.MODEL_BINDING_PROVIDER_CLASS_NAME
13 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.MODULE_INFO_CLASS_NAME
14 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.MODULE_INFO_QNAMEOF_METHOD_NAME
15
16 import com.google.common.base.Preconditions
17 import com.google.common.collect.ImmutableSet
18 import java.util.Comparator
19 import java.util.HashSet
20 import java.util.Optional
21 import java.util.Set
22 import java.util.TreeMap
23 import java.util.function.Function
24 import org.eclipse.xtend.lib.annotations.Accessors
25 import org.gaul.modernizer_maven_annotations.SuppressModernizer
26 import org.opendaylight.yangtools.yang.binding.YangModuleInfo
27 import org.opendaylight.yangtools.yang.common.Revision
28 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext
29 import org.opendaylight.yangtools.yang.model.api.Module
30 import org.opendaylight.yangtools.yang.model.api.ModuleLike
31 import org.opendaylight.yangtools.yang.model.api.Submodule
32
33 /**
34  * Template for {@link YangModuleInfo} implementation for a particular module. Aside from fulfilling that contract,
35  * this class provides a static {@code createQName(String)} method, which is used by co-generated code to initialize
36  * QNAME constants.
37  */
38 @SuppressModernizer
39 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
70     var importedTypes = CORE_IMPORT_STR
71
72     @Accessors
73     val String packageName
74
75     @Accessors
76     val String modelBindingProviderName
77
78     new(Module module, EffectiveModelContext ctx, Function<ModuleLike, Optional<String>> moduleFilePathResolver) {
79         Preconditions.checkArgument(module !== null, "Module must not be null.")
80         this.module = module
81         this.ctx = ctx
82         this.moduleFilePathResolver = moduleFilePathResolver
83         packageName = module.QNameModule.rootPackageName;
84         modelBindingProviderName = '''«packageName».«MODEL_BINDING_PROVIDER_CLASS_NAME»'''
85     }
86
87     def String generate() {
88         val Set<Submodule> submodules = new HashSet
89         collectSubmodules(submodules, module)
90
91         val body = '''
92             /**
93              * The {@link ResourceYangModuleInfo} for {@code «module.name»} module.
94              */
95             @«JavaFileTemplate.GENERATED»("mdsal-binding-generator")
96             public final class «MODULE_INFO_CLASS_NAME» extends ResourceYangModuleInfo {
97                 «val rev = module.revision»
98                 private static final @NonNull QName NAME = QName.create("«module.QNameModule.namespace.toString»", «IF rev.present»"«rev.get.toString»", «ENDIF»"«module.name»").intern();
99                 private static final @NonNull YangModuleInfo INSTANCE = new «MODULE_INFO_CLASS_NAME»();
100
101                 private final @NonNull ImmutableSet<YangModuleInfo> importedModules;
102
103                 /**
104                  * Return the singleton instance of this class.
105                  *
106                  * @return The singleton instance
107                  */
108                 public static @NonNull YangModuleInfo getInstance() {
109                     return INSTANCE;
110                 }
111
112                 /**
113                  * Create an interned {@link QName} with specified {@code localName} and namespace/revision of this
114                  * module.
115                  *
116                  * @param localName local name
117                  * @return A QName
118                  * @throws NullPointerException if {@code localName} is null
119                  * @throws IllegalArgumentException if localName is not a valid YANG identifier
120                  */
121                 public static @NonNull QName «MODULE_INFO_QNAMEOF_METHOD_NAME»(final String localName) {
122                     return QName.create(NAME, localName).intern();
123                 }
124
125                 «classBody(module, MODULE_INFO_CLASS_NAME, submodules)»
126             }
127         '''
128         return '''
129             package «packageName»;
130
131             «importedTypes»
132
133             «body»
134         '''.toString
135     }
136
137     def String generateModelProvider() '''
138         package «packageName»;
139
140         import java.lang.Override;
141         import java.util.ServiceLoader;
142         import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
143         import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
144
145         /**
146          * The {@link YangModelBindingProvider} for {@code «module.name»} module. This class should not be used
147          * directly, but rather through {@link ServiceLoader}.
148          */
149         @«JavaFileTemplate.GENERATED»("mdsal-binding-generator")
150         public final class «MODEL_BINDING_PROVIDER_CLASS_NAME» implements YangModelBindingProvider {
151             /**
152              * Construct a new provider.
153              */
154             public «MODEL_BINDING_PROVIDER_CLASS_NAME»() {
155                 // No-op
156             }
157
158             @Override
159             public YangModuleInfo getModuleInfo() {
160                 return «MODULE_INFO_CLASS_NAME».getInstance();
161             }
162         }
163     '''
164
165     private static def void collectSubmodules(Set<Submodule> dest, ModuleLike module) {
166         for (Submodule submodule : module.submodules) {
167             if (dest.add(submodule)) {
168                 collectSubmodules(dest, submodule)
169             }
170         }
171     }
172
173     private def CharSequence classBody(ModuleLike m, String className, Set<Submodule> submodules) '''
174         private «className»() {
175             «IF !m.imports.empty || !submodules.empty»
176                 «extendImports»
177                 Set<YangModuleInfo> set = new HashSet<>();
178             «ENDIF»
179             «IF !m.imports.empty»
180                 «FOR imp : m.imports»
181                     «val name = imp.moduleName»
182                     «val rev = imp.revision»
183                     «IF !rev.present»
184                         «val TreeMap<Optional<Revision>, Module> sorted = new TreeMap(REVISION_COMPARATOR)»
185                         «FOR module : ctx.modules»
186                             «IF module.name.equals(name)»
187                                 «sorted.put(module.revision, module)»
188                             «ENDIF»
189                         «ENDFOR»
190                         set.add(«sorted.lastEntry().value.QNameModule.rootPackageName».«MODULE_INFO_CLASS_NAME».getInstance());
191                     «ELSE»
192                         set.add(«(ctx.findModule(name, rev).get.QNameModule).rootPackageName».«MODULE_INFO_CLASS_NAME».getInstance());
193                     «ENDIF»
194                 «ENDFOR»
195             «ENDIF»
196             «FOR submodule : submodules»
197                 set.add(«submodule.name.className»Info.getInstance());
198             «ENDFOR»
199             «IF m.imports.empty && submodules.empty»
200                 importedModules = ImmutableSet.of();
201             «ELSE»
202                 importedModules = ImmutableSet.copyOf(set);
203             «ENDIF»
204         }
205
206         @Override
207         public QName getName() {
208             return NAME;
209         }
210
211         @Override
212         protected String resourceName() {
213             return "«sourcePath(m)»";
214         }
215
216         @Override
217         public ImmutableSet<YangModuleInfo> getImportedModules() {
218             return importedModules;
219         }
220         «generateSubInfo(submodules)»
221     '''
222
223     private def void extendImports() {
224         importedTypes = EXT_IMPORT_STR
225     }
226
227     private def sourcePath(ModuleLike module) {
228         val opt = moduleFilePathResolver.apply(module)
229         Preconditions.checkState(opt.isPresent, "Module %s does not have a file path", module)
230         return opt.get
231     }
232
233     private def generateSubInfo(Set<Submodule> submodules) '''
234         «FOR submodule : submodules»
235             «val className = submodule.name.className»
236
237             private static final class «className»Info extends ResourceYangModuleInfo {
238                 «val rev = submodule.revision»
239                 private final @NonNull QName NAME = QName.create("«submodule.QNameModule.namespace.toString»", «
240                 IF rev.present»"«rev.get.toString»", «ENDIF»"«submodule.name»").intern();
241                 private static final @NonNull YangModuleInfo INSTANCE = new «className»Info();
242
243                 private final @NonNull ImmutableSet<YangModuleInfo> importedModules;
244
245                 public static @NonNull YangModuleInfo getInstance() {
246                     return INSTANCE;
247                 }
248
249                 «classBody(submodule, className + "Info", ImmutableSet.of)»
250             }
251         «ENDFOR»
252     '''
253 }