Fixup BuilderTemplate decomposition
[mdsal.git] / binding / mdsal-binding-java-api-generator / src / main / java / org / opendaylight / mdsal / binding / java / api / generator / BuilderTemplate.xtend
1 /*
2  * Copyright (c) 2014 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.apache.commons.text.StringEscapeUtils.escapeJava
11 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTATION_FIELD
12
13 import com.google.common.collect.ImmutableList
14 import java.util.ArrayList
15 import java.util.Collection
16 import java.util.HashMap
17 import java.util.HashSet
18 import java.util.List
19 import java.util.Map
20 import java.util.Set
21 import java.util.regex.Pattern
22 import org.opendaylight.mdsal.binding.model.api.GeneratedProperty
23 import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject
24 import org.opendaylight.mdsal.binding.model.api.GeneratedType
25 import org.opendaylight.mdsal.binding.model.api.JavaTypeName
26 import org.opendaylight.mdsal.binding.model.api.ParameterizedType
27 import org.opendaylight.mdsal.binding.model.api.Type
28 import org.opendaylight.mdsal.binding.model.util.TypeConstants
29 import org.opendaylight.mdsal.binding.model.util.Types
30 import org.opendaylight.yangtools.concepts.Builder
31 import org.opendaylight.yangtools.yang.binding.CodeHelpers
32 import org.opendaylight.yangtools.yang.binding.DataObject
33
34 /**
35  * Template for generating JAVA builder classes.
36  */
37 class BuilderTemplate extends AbstractBuilderTemplate {
38     /**
39      * Constant used as suffix for builder name.
40      */
41     public static val BUILDER = "Builder";
42
43     /**
44      * Constructs new instance of this class.
45      * @throws IllegalArgumentException if <code>genType</code> equals <code>null</code>
46      */
47     new(GeneratedType genType, GeneratedType targetType, Set<GeneratedProperty> properties, Type augmentType,
48             Type keyType) {
49         super(genType, targetType, properties, augmentType, keyType)
50     }
51
52     override isLocalInnerClass(JavaTypeName name) {
53         // Builders do not have inner types
54         return false;
55     }
56
57     /**
58      * Template method which generates JAVA class body for builder class and for IMPL class.
59      *
60      * @return string with JAVA source code
61      */
62     override body() '''
63         «wrapToDocumentation(formatDataForJavaDoc(type))»
64         public class «type.name» implements «Builder.importedName»<«targetType.importedName»> {
65
66             «generateFields(false)»
67
68             «constantsDeclarations()»
69
70             «generateAugmentField(false)»
71
72             «generateConstructorsFromIfcs()»
73
74             «generateCopyConstructor(false, targetType, type.enclosedTypes.get(0))»
75
76             «generateMethodFieldsFrom()»
77
78             «generateGetters(false)»
79
80             «generateSetters»
81
82             @«Override.importedName»
83             public «targetType.name» build() {
84                 return new «type.enclosedTypes.get(0).importedName»(this);
85             }
86
87             «new BuilderImplTemplate(this, type.enclosedTypes.get(0)).body»
88         }
89     '''
90
91     /**
92      * Generate default constructor and constructor for every implemented interface from uses statements.
93      */
94     def private generateConstructorsFromIfcs() '''
95         public «type.name»() {
96         }
97         «IF (!(targetType instanceof GeneratedTransferObject))»
98             «FOR impl : targetType.implements»
99                 «generateConstructorFromIfc(impl)»
100             «ENDFOR»
101         «ENDIF»
102     '''
103
104     /**
105      * Generate constructor with argument of given type.
106      */
107     def private Object generateConstructorFromIfc(Type impl) '''
108         «IF (impl instanceof GeneratedType)»
109             «IF !(impl.methodDefinitions.empty)»
110                 public «type.name»(«impl.fullyQualifiedName» arg) {
111                     «printConstructorPropertySetter(impl)»
112                 }
113             «ENDIF»
114             «FOR implTypeImplement : impl.implements»
115                 «generateConstructorFromIfc(implTypeImplement)»
116             «ENDFOR»
117         «ENDIF»
118     '''
119
120     def private Object printConstructorPropertySetter(Type implementedIfc) '''
121         «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
122             «val ifc = implementedIfc as GeneratedType»
123             «FOR getter : ifc.methodDefinitions»
124                 this._«getter.propertyNameFromGetter» = arg.«getter.name»();
125             «ENDFOR»
126             «FOR impl : ifc.implements»
127                 «printConstructorPropertySetter(impl)»
128             «ENDFOR»
129         «ENDIF»
130     '''
131
132     /**
133      * Generate 'fieldsFrom' method to set builder properties based on type of given argument.
134      */
135     def private generateMethodFieldsFrom() '''
136         «IF (!(targetType instanceof GeneratedTransferObject))»
137             «IF targetType.hasImplementsFromUses»
138                 «val List<Type> done = targetType.getBaseIfcs»
139                 «generateMethodFieldsFromComment(targetType)»
140                 public void fieldsFrom(«DataObject.importedName» arg) {
141                     boolean isValidArg = false;
142                     «FOR impl : targetType.getAllIfcs»
143                         «generateIfCheck(impl, done)»
144                     «ENDFOR»
145                     «CodeHelpers.importedName».validValue(isValidArg, arg, "«targetType.getAllIfcs.toListOfNames»");
146                 }
147             «ENDIF»
148         «ENDIF»
149     '''
150
151     def private generateMethodFieldsFromComment(GeneratedType type) '''
152         /**
153          * Set fields from given grouping argument. Valid argument is instance of one of following types:
154          * <ul>
155          «FOR impl : type.getAllIfcs»
156          * <li>«impl.fullyQualifiedName»</li>
157          «ENDFOR»
158          * </ul>
159          *
160          * @param arg grouping object
161          * @throws IllegalArgumentException if given argument is none of valid types
162         */
163     '''
164
165     /**
166      * Method is used to find out if given type implements any interface from uses.
167      */
168     def boolean hasImplementsFromUses(GeneratedType type) {
169         var i = 0
170         for (impl : type.getAllIfcs) {
171             if ((impl instanceof GeneratedType) &&  !((impl as GeneratedType).methodDefinitions.empty)) {
172                 i = i + 1
173             }
174         }
175         return i > 0
176     }
177
178     def private generateIfCheck(Type impl, List<Type> done) '''
179         «IF (impl instanceof GeneratedType) &&  !((impl as GeneratedType).methodDefinitions.empty)»
180             «val implType = impl as GeneratedType»
181             if (arg instanceof «implType.fullyQualifiedName») {
182                 «printPropertySetter(implType)»
183                 isValidArg = true;
184             }
185         «ENDIF»
186     '''
187
188     def private printPropertySetter(Type implementedIfc) '''
189         «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
190         «val ifc = implementedIfc as GeneratedType»
191         «FOR getter : ifc.methodDefinitions»
192             this._«getter.propertyNameFromGetter» = ((«implementedIfc.fullyQualifiedName»)arg).«getter.name»();
193         «ENDFOR»
194         «ENDIF»
195     '''
196
197     private def List<Type> getBaseIfcs(GeneratedType type) {
198         val List<Type> baseIfcs = new ArrayList();
199         for (ifc : type.implements) {
200             if (ifc instanceof GeneratedType && !(ifc as GeneratedType).methodDefinitions.empty) {
201                 baseIfcs.add(ifc)
202             }
203         }
204         return baseIfcs
205     }
206
207     private def Set<Type> getAllIfcs(Type type) {
208         val Set<Type> baseIfcs = new HashSet()
209         if (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject)) {
210             val ifc = type as GeneratedType
211             for (impl : ifc.implements) {
212                 if (impl instanceof GeneratedType && !(impl as GeneratedType).methodDefinitions.empty) {
213                     baseIfcs.add(impl)
214                 }
215                 baseIfcs.addAll(impl.getAllIfcs)
216             }
217         }
218         return baseIfcs
219     }
220
221     private def List<String> toListOfNames(Collection<Type> types) {
222         val List<String> names = new ArrayList
223         for (type : types) {
224             names.add(type.fullyQualifiedName)
225         }
226         return names
227     }
228
229     def private constantsDeclarations() '''
230         «FOR c : type.getConstantDefinitions»
231             «IF c.getName.startsWith(TypeConstants.PATTERN_CONSTANT_NAME)»
232                 «val cValue = c.value as Map<String, String>»
233                 «val String fieldSuffix = c.getName.substring(TypeConstants.PATTERN_CONSTANT_NAME.length)»
234                 «IF cValue.size == 1»
235                    private static final «Pattern.importedName» «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «Pattern.importedName».compile("«cValue.keySet.get(0).escapeJava»");
236                    private static final String «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = "«cValue.values.get(0).escapeJava»";
237                 «ELSE»
238                    private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «CodeHelpers.importedName».compilePatterns(«ImmutableList.importedName».of(
239                    «FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»));
240                    private static final String[] «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = { «
241                    FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
242                 «ENDIF»
243             «ELSE»
244                 «emitConstant(c)»
245             «ENDIF»
246         «ENDFOR»
247     '''
248
249     def private generateListSetter(GeneratedProperty field, Type actualType) '''
250         «val restrictions = restrictionsForSetter(actualType)»
251         «IF restrictions !== null»
252             «generateCheckers(field, restrictions, actualType)»
253         «ENDIF»
254         public «type.getName» set«field.getName.toFirstUpper»(final «field.returnType.importedName» values) {
255         «IF restrictions !== null»
256             if (values != null) {
257                for («actualType.getFullyQualifiedName» value : values) {
258                    «checkArgument(field, restrictions, actualType, "value")»
259                }
260             }
261         «ENDIF»
262             this.«field.fieldName.toString» = values;
263             return this;
264         }
265
266     '''
267
268     def private generateSetter(GeneratedProperty field, Type actualType) '''
269         «val restrictions = restrictionsForSetter(actualType)»
270         «IF restrictions !== null»
271             «generateCheckers(field, restrictions, actualType)»
272         «ENDIF»
273
274         public «type.getName» set«field.getName.toFirstUpper»(final «field.returnType.importedName» value) {
275         «IF restrictions !== null»
276             if (value != null) {
277                 «checkArgument(field, restrictions, actualType, "value")»
278             }
279         «ENDIF»
280             this.«field.fieldName.toString» = value;
281             return this;
282         }
283     '''
284
285     private def Type getActualType(ParameterizedType ptype) {
286         return ptype.getActualTypeArguments.get(0)
287     }
288
289     /**
290      * Template method which generates setter methods
291      *
292      * @return string with the setter methods
293      */
294     def private generateSetters() '''
295         «IF keyType !== null»
296             public «type.getName» withKey(final «keyType.importedName» key) {
297                 this.key = key;
298                 return this;
299             }
300         «ENDIF»
301         «FOR property : properties»
302             «IF property.returnType instanceof ParameterizedType && Types.isListType(property.returnType)»
303                 «generateListSetter(property, getActualType(property.returnType as ParameterizedType))»
304             «ELSE»
305                 «generateSetter(property, property.returnType)»
306             «ENDIF»
307         «ENDFOR»
308
309         «IF augmentType !== null»
310             public «type.name» add«AUGMENTATION_FIELD.toFirstUpper»(«Class.importedName»<? extends «augmentType.importedName»> augmentationType, «augmentType.importedName» augmentationValue) {
311                 if (augmentationValue == null) {
312                     return remove«AUGMENTATION_FIELD.toFirstUpper»(augmentationType);
313                 }
314
315                 if (!(this.«AUGMENTATION_FIELD» instanceof «HashMap.importedName»)) {
316                     this.«AUGMENTATION_FIELD» = new «HashMap.importedName»<>();
317                 }
318
319                 this.«AUGMENTATION_FIELD».put(augmentationType, augmentationValue);
320                 return this;
321             }
322
323             public «type.name» remove«AUGMENTATION_FIELD.toFirstUpper»(«Class.importedName»<? extends «augmentType.importedName»> augmentationType) {
324                 if (this.«AUGMENTATION_FIELD» instanceof «HashMap.importedName») {
325                     this.«AUGMENTATION_FIELD».remove(augmentationType);
326                 }
327                 return this;
328             }
329         «ENDIF»
330     '''
331
332     private def createDescription(GeneratedType type) {
333         return '''
334         Class that builds {@link «type.importedName»} instances.
335
336         @see «type.importedName»
337     '''
338     }
339
340     override protected String formatDataForJavaDoc(GeneratedType type) {
341         val typeDescription = createDescription(type)
342
343         return '''
344             «IF !typeDescription.nullOrEmpty»
345             «typeDescription»
346             «ENDIF»
347         '''.toString
348     }
349 }
350