faafa586bc2f32fd664724b6e97ea15cda608eee
[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.YangModelBindingProvider
27 import org.opendaylight.yangtools.yang.binding.YangModuleInfo
28 import org.opendaylight.yangtools.yang.common.Revision
29 import org.opendaylight.yangtools.yang.model.api.Module
30 import org.opendaylight.yangtools.yang.model.api.SchemaContext
31
32 /**
33  * Template for {@link YangModuleInfo} implementation for a particular module. Aside from fulfilling that contract,
34  * this class provides a static {@code createQName(String)} method, which is used by co-generated code to initialize
35  * QNAME constants.
36  */
37 @SuppressModernizer
38 class YangModuleInfoTemplate {
39     static val Comparator<Optional<Revision>> REVISION_COMPARATOR =
40         [ Optional<Revision> first, Optional<Revision> second | Revision.compare(first, second) ]
41
42     // These are always imported. Note we need to import even java.lang members, as there can be conflicting definitions
43     // in our package
44     static val CORE_IMPORT_STR = '''
45         import com.google.common.collect.ImmutableSet;
46         import java.lang.Override;
47         import java.lang.String;
48         import org.eclipse.jdt.annotation.NonNull;
49         import org.opendaylight.yangtools.yang.binding.ResourceYangModuleInfo;
50         import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
51         import org.opendaylight.yangtools.yang.common.QName;
52     '''
53     static val EXT_IMPORT_STR = '''
54         import com.google.common.collect.ImmutableSet;
55         import java.lang.Override;
56         import java.lang.String;
57         import java.util.HashSet;
58         import java.util.Set;
59         import org.eclipse.jdt.annotation.NonNull;
60         import org.opendaylight.yangtools.yang.binding.ResourceYangModuleInfo;
61         import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
62         import org.opendaylight.yangtools.yang.common.QName;
63     '''
64
65     val Module module
66     val SchemaContext ctx
67     val Function<Module, Optional<String>> moduleFilePathResolver
68
69     var importedTypes = CORE_IMPORT_STR
70
71     @Accessors
72     val String packageName
73
74     @Accessors
75     val String modelBindingProviderName
76
77     new(Module module, SchemaContext ctx, Function<Module, Optional<String>> moduleFilePathResolver) {
78         Preconditions.checkArgument(module !== null, "Module must not be null.")
79         this.module = module
80         this.ctx = ctx
81         this.moduleFilePathResolver = moduleFilePathResolver
82         packageName = module.QNameModule.rootPackageName;
83         modelBindingProviderName = '''«packageName».«MODEL_BINDING_PROVIDER_CLASS_NAME»'''
84     }
85
86     def String generate() {
87         val Set<Module> submodules = new HashSet
88         collectSubmodules(submodules, module)
89
90         val body = '''
91             public final class «MODULE_INFO_CLASS_NAME» extends ResourceYangModuleInfo {
92                 «val rev = module.revision»
93                 private static final @NonNull QName NAME = QName.create("«module.namespace.toString»", «IF rev.present»"«rev.get.toString»", «ENDIF»"«module.name»").intern();
94                 private static final @NonNull YangModuleInfo INSTANCE = new «MODULE_INFO_CLASS_NAME»();
95
96                 private final @NonNull ImmutableSet<YangModuleInfo> importedModules;
97
98                 public static YangModuleInfo getInstance() {
99                     return INSTANCE;
100                 }
101
102                 public static @NonNull QName «MODULE_INFO_QNAMEOF_METHOD_NAME»(final String localName) {
103                     return QName.create(NAME, localName).intern();
104                 }
105
106                 «classBody(module, MODULE_INFO_CLASS_NAME, submodules)»
107             }
108         '''
109         return '''
110             package «packageName»;
111
112             «importedTypes»
113
114             «body»
115         '''.toString
116     }
117
118     def String generateModelProvider() '''
119         package «packageName»;
120
121         import java.lang.Override;
122         import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
123         import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
124
125         public final class «MODEL_BINDING_PROVIDER_CLASS_NAME» implements YangModelBindingProvider {
126             @Override
127             public YangModuleInfo getModuleInfo() {
128                 return «MODULE_INFO_CLASS_NAME».getInstance();
129             }
130         }
131     '''
132
133     private static def void collectSubmodules(Set<Module> dest, Module module) {
134         for (Module submodule : module.submodules) {
135             if (dest.add(submodule)) {
136                 collectSubmodules(dest, submodule)
137             }
138         }
139     }
140
141     private def CharSequence classBody(Module m, String className, Set<Module> submodules) '''
142         private «className»() {
143             «IF !m.imports.empty || !submodules.empty»
144                 «extendImports»
145                 Set<YangModuleInfo> set = new HashSet<>();
146             «ENDIF»
147             «IF !m.imports.empty»
148                 «FOR imp : m.imports»
149                     «val name = imp.moduleName»
150                     «val rev = imp.revision»
151                     «IF !rev.present»
152                         «val Set<Module> modules = ctx.modules»
153                         «val TreeMap<Optional<Revision>, Module> sorted = new TreeMap(REVISION_COMPARATOR)»
154                         «FOR module : 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(Module 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<Module> 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 }