Adjust for Module/Submodule statement separation
[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             public final class «MODULE_INFO_CLASS_NAME» extends ResourceYangModuleInfo {
93                 «val rev = module.revision»
94                 private static final @NonNull QName NAME = QName.create("«module.namespace.toString»", «IF rev.present»"«rev.get.toString»", «ENDIF»"«module.name»").intern();
95                 private static final @NonNull YangModuleInfo INSTANCE = new «MODULE_INFO_CLASS_NAME»();
96
97                 private final @NonNull ImmutableSet<YangModuleInfo> importedModules;
98
99                 public static @NonNull YangModuleInfo getInstance() {
100                     return INSTANCE;
101                 }
102
103                 public static @NonNull QName «MODULE_INFO_QNAMEOF_METHOD_NAME»(final String localName) {
104                     return QName.create(NAME, localName).intern();
105                 }
106
107                 «classBody(module, MODULE_INFO_CLASS_NAME, submodules)»
108             }
109         '''
110         return '''
111             package «packageName»;
112
113             «importedTypes»
114
115             «body»
116         '''.toString
117     }
118
119     def String generateModelProvider() '''
120         package «packageName»;
121
122         import java.lang.Override;
123         import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
124         import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
125
126         public final class «MODEL_BINDING_PROVIDER_CLASS_NAME» implements YangModelBindingProvider {
127             @Override
128             public YangModuleInfo getModuleInfo() {
129                 return «MODULE_INFO_CLASS_NAME».getInstance();
130             }
131         }
132     '''
133
134     private static def void collectSubmodules(Set<Submodule> dest, ModuleLike module) {
135         for (Submodule submodule : module.submodules) {
136             if (dest.add(submodule)) {
137                 collectSubmodules(dest, submodule)
138             }
139         }
140     }
141
142     private def CharSequence classBody(ModuleLike m, String className, Set<Submodule> submodules) '''
143         private «className»() {
144             «IF !m.imports.empty || !submodules.empty»
145                 «extendImports»
146                 Set<YangModuleInfo> set = new HashSet<>();
147             «ENDIF»
148             «IF !m.imports.empty»
149                 «FOR imp : m.imports»
150                     «val name = imp.moduleName»
151                     «val rev = imp.revision»
152                     «IF !rev.present»
153                         «val TreeMap<Optional<Revision>, Module> sorted = new TreeMap(REVISION_COMPARATOR)»
154                         «FOR module : ctx.modules»
155                             «IF module.name.equals(name)»
156                                 «sorted.put(module.revision, module)»
157                             «ENDIF»
158                         «ENDFOR»
159                         set.add(«sorted.lastEntry().value.QNameModule.rootPackageName».«MODULE_INFO_CLASS_NAME».getInstance());
160                     «ELSE»
161                         set.add(«(ctx.findModule(name, rev).get.QNameModule).rootPackageName».«MODULE_INFO_CLASS_NAME».getInstance());
162                     «ENDIF»
163                 «ENDFOR»
164             «ENDIF»
165             «FOR submodule : submodules»
166                 set.add(«submodule.name.className»Info.getInstance());
167             «ENDFOR»
168             «IF m.imports.empty && submodules.empty»
169                 importedModules = ImmutableSet.of();
170             «ELSE»
171                 importedModules = ImmutableSet.copyOf(set);
172             «ENDIF»
173         }
174
175         @Override
176         public QName getName() {
177             return NAME;
178         }
179
180         @Override
181         protected String resourceName() {
182             return "«sourcePath(m)»";
183         }
184
185         @Override
186         public ImmutableSet<YangModuleInfo> getImportedModules() {
187             return importedModules;
188         }
189         «generateSubInfo(submodules)»
190     '''
191
192     private def void extendImports() {
193         importedTypes = EXT_IMPORT_STR
194     }
195
196     private def sourcePath(ModuleLike module) {
197         val opt = moduleFilePathResolver.apply(module)
198         Preconditions.checkState(opt.isPresent, "Module %s does not have a file path", module)
199         return opt.get
200     }
201
202     private def generateSubInfo(Set<Submodule> submodules) '''
203         «FOR submodule : submodules»
204             «val className = submodule.name.className»
205
206             private static final class «className»Info extends ResourceYangModuleInfo {
207                 «val rev = submodule.revision»
208                 private final @NonNull QName NAME = QName.create("«submodule.namespace.toString»", «
209                 IF rev.present»"«rev.get.toString»", «ENDIF»"«submodule.name»").intern();
210                 private static final @NonNull YangModuleInfo INSTANCE = new «className»Info();
211
212                 private final @NonNull ImmutableSet<YangModuleInfo> importedModules;
213
214                 public static @NonNull YangModuleInfo getInstance() {
215                     return INSTANCE;
216                 }
217
218                 «classBody(submodule, className + "Info", ImmutableSet.of)»
219             }
220         «ENDFOR»
221     '''
222 }