1b1b4326301b5c9c2ee371b9f608db58841f5e92
[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.opendaylight.yangtools.yang.binding.YangModuleInfo
26 import org.opendaylight.yangtools.yang.common.Revision
27 import org.opendaylight.yangtools.yang.model.api.Module
28 import org.opendaylight.yangtools.yang.model.api.SchemaContext
29
30 /**
31  * Template for {@link YangModuleInfo} implementation for a particular module. Aside from fulfilling that contract,
32  * this class provides a static {@code createQName(String)} method, which is used by co-generated code to initialize
33  * QNAME constants.
34  */
35 class YangModuleInfoTemplate {
36     static val Comparator<Optional<Revision>> REVISION_COMPARATOR =
37         [ Optional<Revision> first, Optional<Revision> second | Revision.compare(first, second) ]
38
39     // These are always imported. Note we need to import even java.lang members, as there can be conflicting definitions
40     // in our package
41     static val CORE_IMPORT_STR = '''
42         import com.google.common.collect.ImmutableSet;
43         import java.lang.Override;
44         import java.lang.String;
45         import org.eclipse.jdt.annotation.NonNull;
46         import org.opendaylight.yangtools.yang.binding.ResourceYangModuleInfo;
47         import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
48         import org.opendaylight.yangtools.yang.common.QName;
49     '''
50     static val EXT_IMPORT_STR = '''
51         import com.google.common.collect.ImmutableSet;
52         import java.lang.Override;
53         import java.lang.String;
54         import java.util.HashSet;
55         import java.util.Set;
56         import org.eclipse.jdt.annotation.NonNull;
57         import org.opendaylight.yangtools.yang.binding.ResourceYangModuleInfo;
58         import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
59         import org.opendaylight.yangtools.yang.common.QName;
60     '''
61
62     val Module module
63     val SchemaContext ctx
64     val Function<Module, Optional<String>> moduleFilePathResolver
65
66     var importedTypes = CORE_IMPORT_STR
67
68     @Accessors
69     val String packageName
70
71     @Accessors
72     val String modelBindingProviderName
73
74     new(Module module, SchemaContext ctx, Function<Module, Optional<String>> moduleFilePathResolver) {
75         Preconditions.checkArgument(module !== null, "Module must not be null.")
76         this.module = module
77         this.ctx = ctx
78         this.moduleFilePathResolver = moduleFilePathResolver
79         packageName = module.QNameModule.rootPackageName;
80         modelBindingProviderName = '''«packageName».«MODEL_BINDING_PROVIDER_CLASS_NAME»'''
81     }
82
83     def String generate() {
84         val Set<Module> submodules = new HashSet
85         collectSubmodules(submodules, module)
86
87         val body = '''
88             public final class «MODULE_INFO_CLASS_NAME» extends ResourceYangModuleInfo {
89                 «val rev = module.revision»
90                 private static final @NonNull QName NAME = QName.create("«module.namespace.toString»", «IF rev.present»"«rev.get.toString»", «ENDIF»"«module.name»").intern();
91                 private static final @NonNull YangModuleInfo INSTANCE = new «MODULE_INFO_CLASS_NAME»();
92
93                 private final @NonNull ImmutableSet<YangModuleInfo> importedModules;
94
95                 public static YangModuleInfo getInstance() {
96                     return INSTANCE;
97                 }
98
99                 public static @NonNull QName «MODULE_INFO_QNAMEOF_METHOD_NAME»(final String localName) {
100                     return QName.create(NAME, localName).intern();
101                 }
102
103                 «classBody(module, MODULE_INFO_CLASS_NAME, submodules)»
104             }
105         '''
106         return '''
107             package «packageName»;
108
109             «importedTypes»
110
111             «body»
112         '''.toString
113     }
114
115     def String generateModelProvider() '''
116         package «packageName»;
117
118         import java.lang.Override;
119         import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
120         import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
121
122         public final class «MODEL_BINDING_PROVIDER_CLASS_NAME» implements YangModelBindingProvider {
123             @Override
124             public YangModuleInfo getModuleInfo() {
125                 return «MODULE_INFO_CLASS_NAME».getInstance();
126             }
127         }
128     '''
129
130     private static def void collectSubmodules(Set<Module> dest, Module module) {
131         for (Module submodule : module.submodules) {
132             if (dest.add(submodule)) {
133                 collectSubmodules(dest, submodule)
134             }
135         }
136     }
137
138     private def CharSequence classBody(Module m, String className, Set<Module> submodules) '''
139         private «className»() {
140             «IF !m.imports.empty || !submodules.empty»
141                 «extendImports»
142                 Set<YangModuleInfo> set = new HashSet<>();
143             «ENDIF»
144             «IF !m.imports.empty»
145                 «FOR imp : m.imports»
146                     «val name = imp.moduleName»
147                     «val rev = imp.revision»
148                     «IF !rev.present»
149                         «val Set<Module> modules = ctx.modules»
150                         «val TreeMap<Optional<Revision>, Module> sorted = new TreeMap(REVISION_COMPARATOR)»
151                         «FOR module : modules»
152                             «IF module.name.equals(name)»
153                                 «sorted.put(module.revision, module)»
154                             «ENDIF»
155                         «ENDFOR»
156                         set.add(«sorted.lastEntry().value.QNameModule.rootPackageName».«MODULE_INFO_CLASS_NAME».getInstance());
157                     «ELSE»
158                         set.add(«(ctx.findModule(name, rev).get.QNameModule).rootPackageName».«MODULE_INFO_CLASS_NAME».getInstance());
159                     «ENDIF»
160                 «ENDFOR»
161             «ENDIF»
162             «FOR submodule : submodules»
163                 set.add(«submodule.name.className»Info.getInstance());
164             «ENDFOR»
165             «IF m.imports.empty && submodules.empty»
166                 importedModules = ImmutableSet.of();
167             «ELSE»
168                 importedModules = ImmutableSet.copyOf(set);
169             «ENDIF»
170         }
171
172         @Override
173         public QName getName() {
174             return NAME;
175         }
176
177         @Override
178         protected String resourceName() {
179             return "«sourcePath(m)»";
180         }
181
182         @Override
183         public ImmutableSet<YangModuleInfo> getImportedModules() {
184             return importedModules;
185         }
186         «generateSubInfo(submodules)»
187     '''
188
189     private def void extendImports() {
190         importedTypes = EXT_IMPORT_STR
191     }
192
193     private def sourcePath(Module module) {
194         val opt = moduleFilePathResolver.apply(module)
195         Preconditions.checkState(opt.isPresent, "Module %s does not have a file path", module)
196         return opt.get
197     }
198
199     private def generateSubInfo(Set<Module> submodules) '''
200         «FOR submodule : submodules»
201             «val className = submodule.name.className»
202
203             private static final class «className»Info extends ResourceYangModuleInfo {
204                 «val rev = submodule.revision»
205                 private final @NonNull QName NAME = QName.create("«submodule.namespace.toString»", «
206                 IF rev.present»"«rev.get.toString»", «ENDIF»"«submodule.name»").intern();
207                 private static final @NonNull YangModuleInfo INSTANCE = new «className»Info();
208
209                 private final @NonNull ImmutableSet<YangModuleInfo> importedModules;
210
211                 public static @NonNull YangModuleInfo getInstance() {
212                     return INSTANCE;
213                 }
214
215                 «classBody(submodule, className + "Info", ImmutableSet.of)»
216             }
217         «ENDFOR»
218     '''
219 }