1 package org.opendaylight.yangtools.sal.java.api.generator
3 import java.util.LinkedHashSet
7 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl
8 import org.opendaylight.yangtools.binding.generator.util.Types
9 import org.opendaylight.yangtools.binding.generator.util.generated.type.builder.GeneratedTOBuilderImpl
10 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
11 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
12 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
13 import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature
14 import org.opendaylight.yangtools.sal.binding.model.api.Type
15 import org.opendaylight.yangtools.yang.binding.Augmentable
18 * Template for generating JAVA builder classes.
20 class BuilderTemplate {
23 * Constant with prefix for getter methods.
25 val static GET_PREFIX = "get"
28 * Constant with the name of the concrete package prefix.
30 val static JAVA_UTIL = "java.util"
33 * Constant with the name of the concrete JAVA type
35 val static HASH_MAP = "HashMap"
38 * Constant with the name of the concrete JAVA interface.
40 val static MAP = "Map"
43 * Constant with the name of the concrete method.
45 val static GET_AUGMENTATION_METHOD_NAME = "getAugmentation"
48 * Constant with the suffix for builder classes.
50 val static BUILDER = 'Builder'
53 * Constant with suffix for the classes which are generated from the builder classes.
55 val static IMPL = 'Impl'
58 * Reference to type for which is generated builder class
60 val GeneratedType genType
63 * Map of imports. The keys are type names and the values are package names.
65 val Map<String, String> imports
68 * Generated property is set if among methods is found one with the name GET_AUGMENTATION_METHOD_NAME
70 var GeneratedProperty augmentField
73 * Set of class attributes (fields) which are derived from the getter methods names
75 val Set<GeneratedProperty> fields
78 * Constructs new instance of this class.
79 * @throws IllegalArgumentException if <code>genType</code> equals <code>null</code>
81 new(GeneratedType genType) {
82 if (genType == null) {
83 throw new IllegalArgumentException("Generated type reference cannot be NULL!")
86 this.genType = genType
87 this.imports = GeneratorUtil.createChildImports(genType)
88 this.fields = createFieldsFromMethods(createMethods)
92 * Returns set of method signature instances which contains all the methods of the <code>genType</code>
93 * and all the methods of the implemented interfaces.
95 * @returns set of method signature instances
97 def private Set<MethodSignature> createMethods() {
98 val Set<MethodSignature> methods = new LinkedHashSet
99 methods.addAll(genType.methodDefinitions)
100 storeMethodsOfImplementedIfcs(methods, genType.implements)
105 * Adds to the <code>methods</code> set all the methods of the <code>implementedIfcs</code>
106 * and recursivelly their implemented interfaces.
108 * @param methods set of method signatures
109 * @param implementedIfcs list of implemented interfaces
111 def private void storeMethodsOfImplementedIfcs(Set<MethodSignature> methods, List<Type> implementedIfcs) {
112 if (implementedIfcs == null || implementedIfcs.empty) {
115 for (implementedIfc : implementedIfcs) {
116 if ((implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))) {
117 val ifc = implementedIfc as GeneratedType
118 methods.addAll(ifc.methodDefinitions)
119 storeMethodsOfImplementedIfcs(methods, ifc.implements)
120 } else if (implementedIfc.fullyQualifiedName == Augmentable.name) {
121 for (m : Augmentable.methods) {
122 if (m.name == GET_AUGMENTATION_METHOD_NAME) {
123 addToImports(JAVA_UTIL, HASH_MAP)
124 addToImports(JAVA_UTIL, MAP)
125 val fullyQualifiedName = m.returnType.name
126 val pkg = fullyQualifiedName.package
127 val name = fullyQualifiedName.name
128 addToImports(pkg, name)
129 val tmpGenTO = new GeneratedTOBuilderImpl(pkg, name)
130 val type = new ReferencedTypeImpl(pkg, name)
131 val generic = new ReferencedTypeImpl(genType.packageName, genType.name)
132 val parametrizedReturnType = Types.parameterizedTypeFor(type, generic)
133 tmpGenTO.addMethod(m.name).setReturnType(parametrizedReturnType)
134 augmentField = tmpGenTO.toInstance.methodDefinitions.first.createFieldFromGetter
142 * Adds to the <code>imports</code> map the package <code>typePackageName</code>.
144 * @param typePackageName
145 * string with the name of the package which is added to <code>imports</code> as a value
147 * string with the name of the package which is added to <code>imports</code> as a key
149 def private void addToImports(String typePackageName,String typeName) {
150 if (typePackageName.startsWith("java.lang") || typePackageName.isEmpty()) {
153 if (!imports.containsKey(typeName)) {
154 imports.put(typeName, typePackageName)
159 * Returns the first element of the list <code>elements</code>.
161 * @param list of elements
163 def private <E> first(List<E> elements) {
168 * Returns the name of the package from <code>fullyQualifiedName</code>.
170 * @param fullyQualifiedName string with fully qualified type name (package + type)
171 * @return string with the package name
173 def private String getPackage(String fullyQualifiedName) {
174 val lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT)
175 return if (lastDotIndex == -1) "" else fullyQualifiedName.substring(0, lastDotIndex)
179 * Returns the name of tye type from <code>fullyQualifiedName</code>
181 * @param fullyQualifiedName string with fully qualified type name (package + type)
182 * @return string with the name of the type
184 def private String getName(String fullyQualifiedName) {
185 val lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT)
186 return if (lastDotIndex == -1) fullyQualifiedName else fullyQualifiedName.substring(lastDotIndex + 1)
190 * Creates set of generated property instances from getter <code>methods</code>.
192 * @param set of method signature instances which should be transformed to list of properties
193 * @return set of generated property instances which represents the getter <code>methods</code>
195 def private createFieldsFromMethods(Set<MethodSignature> methods) {
196 val Set<GeneratedProperty> result = new LinkedHashSet
198 if (methods == null || methods.isEmpty()) {
203 val createdField = m.createFieldFromGetter
204 if (createdField != null) {
205 result.add(createdField)
212 * Creates generated property instance from the getter <code>method</code> name and return type.
214 * @param method method signature from which is the method name and return type obtained
215 * @return generated property instance for the getter <code>method</code>
216 * @throws IllegalArgumentException<ul>
217 * <li>if the <code>method</code> equals <code>null</code></li>
218 * <li>if the name of the <code>method</code> equals <code>null</code></li>
219 * <li>if the name of the <code>method</code> is empty</li>
220 * <li>if the return type of the <code>method</code> equals <code>null</code></li>
223 def private GeneratedProperty createFieldFromGetter(MethodSignature method) {
224 if (method == null || method.name == null || method.name.empty || method.returnType == null) {
225 throw new IllegalArgumentException("Method, method name, method return type reference cannot be NULL or empty!")
227 if (method.name.startsWith(GET_PREFIX)) {
228 val fieldName = method.getName().substring(GET_PREFIX.length()).toFirstLower
229 val tmpGenTO = new GeneratedTOBuilderImpl("foo", "foo")
230 tmpGenTO.addProperty(fieldName).setReturnType(method.returnType)
231 return tmpGenTO.toInstance.properties.first
236 * Builds string which contains JAVA source code.
238 * @return string with JAVA source code
240 def String generate() {
241 val body = generateBody
242 val pkgAndImports = generatePkgAndImports
243 return pkgAndImports.toString + body.toString
247 * Template method which generates JAVA class body for builder class and for IMPL class.
249 * @return string with JAVA source code
251 def private generateBody() '''
252 public class «genType.name»«BUILDER» {
254 «generateFields(false)»
258 public «genType.name» build() {
259 return new «genType.name»«IMPL»();
262 private class «genType.name»«IMPL» implements «genType.name» {
264 «generateFields(true)»
266 «generateConstructor»
276 * Template method which generates class attributes.
278 * @param boolean value which specify whether field is|isn't final
279 * @return string with class attributes and their types
281 def private generateFields(boolean _final) '''
284 private «IF _final»final«ENDIF» «f.returnType.resolveName» «f.name»;
287 «IF augmentField != null»
288 private Map<Class<? extends «augmentField.returnType.resolveName»>, «augmentField.returnType.resolveName»> «augmentField.name» = new HashMap<>();
293 * Template method which generates setter methods
295 * @return string with the setter methods
297 def private generateSetters() '''
298 «FOR field : fields SEPARATOR '\n'»
299 public «genType.name»«BUILDER» set«field.name.toFirstUpper»(«field.returnType.resolveName» «field.name») {
300 this.«field.name» = «field.name»;
304 «IF augmentField != null»
306 public «genType.name»«BUILDER» add«augmentField.name.toFirstUpper»(Class<? extends «augmentField.returnType.resolveName»> augmentationType, «augmentField.returnType.resolveName» augmentation) {
307 this.«augmentField.name».put(augmentationType, augmentation);
314 * Template method which generate constructor for IMPL class.
316 * @return string with IMPL class constructor
318 def private generateConstructor() '''
319 private «genType.name»«IMPL»() {
322 this.«field.name» = «genType.name»«BUILDER».this.«field.name»;
325 «IF augmentField != null»
326 this.«augmentField.name».putAll(«genType.name»«BUILDER».this.«augmentField.name»);
332 * Template method which generate getter methods for IMPL class.
334 * @return string with getter methods
336 def private generateGetters() '''
338 «FOR field : fields SEPARATOR '\n'»
340 public «field.returnType.resolveName» get«field.name.toFirstUpper»() {
345 «IF augmentField != null»
347 @SuppressWarnings("unchecked")
349 public <E extends «augmentField.returnType.resolveName»> E get«augmentField.name.toFirstUpper»(Class<E> augmentationType) {
350 if (augmentationType == null) {
351 throw new IllegalArgumentException("Augmentation Type reference cannot be NULL!");
353 return (E) «augmentField.name».get(augmentationType);
359 * Template method which generate package name line and import lines.
361 * @result string with package and import lines in JAVA format
363 def private generatePkgAndImports() '''
364 package «genType.packageName»;
368 «FOR entry : imports.entrySet»
369 import «entry.value».«entry.key»;
376 * Adds package to imports if it is necessary and returns necessary type name (with or without package name)
378 * @param type JAVA <code>Type</code>
379 * @return string with the type name (with or without package name)
381 def private resolveName(Type type) {
382 GeneratorUtil.putTypeIntoImports(genType, type, imports);
383 GeneratorUtil.getExplicitType(genType, type, imports)