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