2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.mdsal.binding.java.api.generator
10 import static extension org.apache.commons.text.StringEscapeUtils.escapeJava
11 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTATION_FIELD
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
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.mdsal.binding.spec.naming.BindingMapping
31 import org.opendaylight.yangtools.concepts.Builder
32 import org.opendaylight.yangtools.yang.binding.AugmentationHolder
33 import org.opendaylight.yangtools.yang.binding.CodeHelpers
34 import org.opendaylight.yangtools.yang.binding.DataObject
37 * Template for generating JAVA builder classes.
39 class BuilderTemplate extends AbstractBuilderTemplate {
41 * Constant used as suffix for builder name.
43 public static val BUILDER = "Builder";
46 * Constructs new instance of this class.
47 * @throws IllegalArgumentException if <code>genType</code> equals <code>null</code>
49 new(GeneratedType genType, GeneratedType targetType, Set<GeneratedProperty> properties, Type augmentType,
51 super(genType, targetType, properties, augmentType, keyType)
54 override isLocalInnerClass(JavaTypeName name) {
55 // Builders do not have inner types
60 * Template method which generates JAVA class body for builder class and for IMPL class.
62 * @return string with JAVA source code
65 «wrapToDocumentation(formatDataForJavaDoc(type))»
66 public class «type.name» implements «Builder.importedName»<«targetType.importedName»> {
68 «generateFields(false)»
70 «constantsDeclarations()»
72 «generateAugmentField(false)»
74 «generateConstructorsFromIfcs()»
76 public «generateCopyConstructor(targetType, type.enclosedTypes.get(0))»
78 «generateMethodFieldsFrom()»
80 «generateGetters(false)»
84 @«Override.importedName»
85 public «targetType.name» build() {
86 return new «type.enclosedTypes.get(0).importedName»(this);
89 «new BuilderImplTemplate(this, type.enclosedTypes.get(0)).body»
94 * Generate default constructor and constructor for every implemented interface from uses statements.
96 def private generateConstructorsFromIfcs() '''
97 public «type.name»() {
99 «IF (!(targetType instanceof GeneratedTransferObject))»
100 «FOR impl : targetType.implements»
101 «generateConstructorFromIfc(impl)»
107 * Generate constructor with argument of given type.
109 def private Object generateConstructorFromIfc(Type impl) '''
110 «IF (impl instanceof GeneratedType)»
111 «IF !(impl.methodDefinitions.empty)»
112 public «type.name»(«impl.fullyQualifiedName» arg) {
113 «printConstructorPropertySetter(impl)»
116 «FOR implTypeImplement : impl.implements»
117 «generateConstructorFromIfc(implTypeImplement)»
122 def private Object printConstructorPropertySetter(Type implementedIfc) '''
123 «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
124 «val ifc = implementedIfc as GeneratedType»
125 «FOR getter : ifc.methodDefinitions»
126 this._«getter.propertyNameFromGetter» = arg.«getter.name»();
128 «FOR impl : ifc.implements»
129 «printConstructorPropertySetter(impl)»
135 * Generate 'fieldsFrom' method to set builder properties based on type of given argument.
137 def private generateMethodFieldsFrom() '''
138 «IF (!(targetType instanceof GeneratedTransferObject))»
139 «IF targetType.hasImplementsFromUses»
140 «val List<Type> done = targetType.getBaseIfcs»
141 «generateMethodFieldsFromComment(targetType)»
142 public void fieldsFrom(«DataObject.importedName» arg) {
143 boolean isValidArg = false;
144 «FOR impl : targetType.getAllIfcs»
145 «generateIfCheck(impl, done)»
147 «CodeHelpers.importedName».validValue(isValidArg, arg, "«targetType.getAllIfcs.toListOfNames»");
153 def private generateMethodFieldsFromComment(GeneratedType type) '''
155 * Set fields from given grouping argument. Valid argument is instance of one of following types:
157 «FOR impl : type.getAllIfcs»
158 * <li>«impl.fullyQualifiedName»</li>
162 * @param arg grouping object
163 * @throws IllegalArgumentException if given argument is none of valid types
168 * Method is used to find out if given type implements any interface from uses.
170 def boolean hasImplementsFromUses(GeneratedType type) {
172 for (impl : type.getAllIfcs) {
173 if ((impl instanceof GeneratedType) && !((impl as GeneratedType).methodDefinitions.empty)) {
180 def private generateIfCheck(Type impl, List<Type> done) '''
181 «IF (impl instanceof GeneratedType) && !((impl as GeneratedType).methodDefinitions.empty)»
182 «val implType = impl as GeneratedType»
183 if (arg instanceof «implType.fullyQualifiedName») {
184 «printPropertySetter(implType)»
190 def private printPropertySetter(Type implementedIfc) '''
191 «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
192 «val ifc = implementedIfc as GeneratedType»
193 «FOR getter : ifc.methodDefinitions»
194 this._«getter.propertyNameFromGetter» = ((«implementedIfc.fullyQualifiedName»)arg).«getter.name»();
199 private def List<Type> getBaseIfcs(GeneratedType type) {
200 val List<Type> baseIfcs = new ArrayList();
201 for (ifc : type.implements) {
202 if (ifc instanceof GeneratedType && !(ifc as GeneratedType).methodDefinitions.empty) {
209 private def Set<Type> getAllIfcs(Type type) {
210 val Set<Type> baseIfcs = new HashSet()
211 if (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject)) {
212 val ifc = type as GeneratedType
213 for (impl : ifc.implements) {
214 if (impl instanceof GeneratedType && !(impl as GeneratedType).methodDefinitions.empty) {
217 baseIfcs.addAll(impl.getAllIfcs)
223 private def List<String> toListOfNames(Collection<Type> types) {
224 val List<String> names = new ArrayList
226 names.add(type.fullyQualifiedName)
231 def private constantsDeclarations() '''
232 «FOR c : type.getConstantDefinitions»
233 «IF c.getName.startsWith(TypeConstants.PATTERN_CONSTANT_NAME)»
234 «val cValue = c.value as Map<String, String>»
235 «val String fieldSuffix = c.getName.substring(TypeConstants.PATTERN_CONSTANT_NAME.length)»
236 «IF cValue.size == 1»
237 private static final «Pattern.importedName» «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «Pattern.importedName».compile("«cValue.keySet.get(0).escapeJava»");
238 private static final String «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = "«cValue.values.get(0).escapeJava»";
240 private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «CodeHelpers.importedName».compilePatterns(«ImmutableList.importedName».of(
241 «FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»));
242 private static final String[] «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = { «
243 FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
251 def private generateListSetter(GeneratedProperty field, Type actualType) '''
252 «val restrictions = restrictionsForSetter(actualType)»
253 «IF restrictions !== null»
254 «generateCheckers(field, restrictions, actualType)»
256 public «type.getName» set«field.getName.toFirstUpper»(final «field.returnType.importedName» values) {
257 «IF restrictions !== null»
258 if (values != null) {
259 for («actualType.getFullyQualifiedName» value : values) {
260 «checkArgument(field, restrictions, actualType, "value")»
264 this.«field.fieldName.toString» = values;
270 def private generateSetter(GeneratedProperty field, Type actualType) '''
271 «val restrictions = restrictionsForSetter(actualType)»
272 «IF restrictions !== null»
273 «generateCheckers(field, restrictions, actualType)»
276 public «type.getName» set«field.getName.toFirstUpper»(final «field.returnType.importedName» value) {
277 «IF restrictions !== null»
279 «checkArgument(field, restrictions, actualType, "value")»
282 this.«field.fieldName.toString» = value;
287 private def Type getActualType(ParameterizedType ptype) {
288 return ptype.getActualTypeArguments.get(0)
292 * Template method which generates setter methods
294 * @return string with the setter methods
296 def private generateSetters() '''
297 «IF keyType !== null»
298 public «type.getName» withKey(final «keyType.importedName» key) {
303 «FOR property : properties»
304 «IF property.returnType instanceof ParameterizedType && Types.isListType(property.returnType)»
305 «generateListSetter(property, getActualType(property.returnType as ParameterizedType))»
307 «generateSetter(property, property.returnType)»
311 «IF augmentType !== null»
312 public «type.name» add«AUGMENTATION_FIELD.toFirstUpper»(«Class.importedName»<? extends «augmentType.importedName»> augmentationType, «augmentType.importedName» augmentationValue) {
313 if (augmentationValue == null) {
314 return remove«AUGMENTATION_FIELD.toFirstUpper»(augmentationType);
317 if (!(this.«AUGMENTATION_FIELD» instanceof «HashMap.importedName»)) {
318 this.«AUGMENTATION_FIELD» = new «HashMap.importedName»<>();
321 this.«AUGMENTATION_FIELD».put(augmentationType, augmentationValue);
325 public «type.name» remove«AUGMENTATION_FIELD.toFirstUpper»(«Class.importedName»<? extends «augmentType.importedName»> augmentationType) {
326 if (this.«AUGMENTATION_FIELD» instanceof «HashMap.importedName») {
327 this.«AUGMENTATION_FIELD».remove(augmentationType);
334 private def createDescription(GeneratedType type) {
336 Class that builds {@link «type.importedName»} instances.
338 @see «type.importedName»
342 override protected String formatDataForJavaDoc(GeneratedType type) {
343 val typeDescription = createDescription(type)
346 «IF !typeDescription.nullOrEmpty»
352 override protected generateCopyKeys(List<GeneratedProperty> keyProps) '''
353 this.key = base.«BindingMapping.IDENTIFIABLE_KEY_NAME»();
354 «FOR field : keyProps»
355 this.«field.fieldName» = base.«field.getterMethodName»();
359 override protected generateCopyAugmentation(Type implType) {
360 val implTypeRef = implType.importedName
361 val augmentationHolderRef = AugmentationHolder.importedName
362 val typeRef = targetType.importedName
363 val hashMapRef = HashMap.importedName
365 if (base instanceof «implTypeRef») {
366 «implTypeRef» impl = («implTypeRef») base;
367 if (!impl.«AUGMENTATION_FIELD».isEmpty()) {
368 this.«AUGMENTATION_FIELD» = new «hashMapRef»<>(impl.«AUGMENTATION_FIELD»);
370 } else if (base instanceof «augmentationHolderRef») {
371 @SuppressWarnings("unchecked")
372 «augmentationHolderRef»<«typeRef»> casted =(«augmentationHolderRef»<«typeRef»>) base;
373 if (!casted.augmentations().isEmpty()) {
374 this.«AUGMENTATION_FIELD» = new «hashMapRef»<>(casted.augmentations());