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.AUGMENTABLE_AUGMENTATION_NAME
12 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTATION_FIELD
14 import com.google.common.collect.ImmutableList
15 import com.google.common.collect.ImmutableMap
16 import java.math.BigInteger
17 import java.util.ArrayList
18 import java.util.Collection
19 import java.util.HashMap
20 import java.util.HashSet
24 import java.util.regex.Pattern
25 import org.opendaylight.mdsal.binding.model.api.GeneratedProperty
26 import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject
27 import org.opendaylight.mdsal.binding.model.api.GeneratedType
28 import org.opendaylight.mdsal.binding.model.api.JavaTypeName
29 import org.opendaylight.mdsal.binding.model.api.ParameterizedType
30 import org.opendaylight.mdsal.binding.model.api.Type
31 import org.opendaylight.mdsal.binding.model.util.TypeConstants
32 import org.opendaylight.mdsal.binding.model.util.Types
33 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping
34 import org.opendaylight.yangtools.concepts.Builder
35 import org.opendaylight.yangtools.yang.binding.AugmentationHolder
36 import org.opendaylight.yangtools.yang.binding.CodeHelpers
37 import org.opendaylight.yangtools.yang.binding.DataObject
38 import org.opendaylight.yangtools.yang.common.Uint8
39 import org.opendaylight.yangtools.yang.common.Uint16
40 import org.opendaylight.yangtools.yang.common.Uint64
41 import org.opendaylight.yangtools.yang.common.Uint32
44 * Template for generating JAVA builder classes.
46 class BuilderTemplate extends AbstractBuilderTemplate {
48 * Constant used as suffix for builder name.
50 public static val BUILDER = "Builder";
52 static val UINT_TYPES = ImmutableMap.of(
53 Types.typeForClass(Uint8), Types.typeForClass(Short),
54 Types.typeForClass(Uint16), Types.typeForClass(Integer),
55 Types.typeForClass(Uint32), Types.typeForClass(Long),
56 Types.typeForClass(Uint64), Types.typeForClass(BigInteger)
60 * Constructs new instance of this class.
61 * @throws IllegalArgumentException if <code>genType</code> equals <code>null</code>
63 new(GeneratedType genType, GeneratedType targetType, Set<GeneratedProperty> properties, Type augmentType,
65 super(genType, targetType, properties, augmentType, keyType)
68 override isLocalInnerClass(JavaTypeName name) {
69 // Builders do not have inner types
74 * Template method which generates JAVA class body for builder class and for IMPL class.
76 * @return string with JAVA source code
79 «wrapToDocumentation(formatDataForJavaDoc(type))»
80 public class «type.name» implements «Builder.importedName»<«targetType.importedName»> {
82 «generateFields(false)»
84 «constantsDeclarations()»
86 «IF augmentType !== null»
87 «generateAugmentField()»
90 «generateConstructorsFromIfcs()»
92 public «generateCopyConstructor(targetType, type.enclosedTypes.get(0))»
94 «generateMethodFieldsFrom()»
96 «generateGetters(false)»
97 «IF augmentType !== null»
99 «generateAugmentation()»
104 @«Override.importedName»
105 public «targetType.name» build() {
106 return new «type.enclosedTypes.get(0).importedName»(this);
109 «new BuilderImplTemplate(this, type.enclosedTypes.get(0)).body»
114 * Generate default constructor and constructor for every implemented interface from uses statements.
116 def private generateConstructorsFromIfcs() '''
117 public «type.name»() {
119 «IF (!(targetType instanceof GeneratedTransferObject))»
120 «FOR impl : targetType.implements»
121 «generateConstructorFromIfc(impl)»
127 * Generate constructor with argument of given type.
129 def private Object generateConstructorFromIfc(Type impl) '''
130 «IF (impl instanceof GeneratedType)»
131 «IF impl.hasNonDefaultMethods»
132 public «type.name»(«impl.fullyQualifiedName» arg) {
133 «printConstructorPropertySetter(impl)»
136 «FOR implTypeImplement : impl.implements»
137 «generateConstructorFromIfc(implTypeImplement)»
142 def private Object printConstructorPropertySetter(Type implementedIfc) '''
143 «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
144 «val ifc = implementedIfc as GeneratedType»
145 «FOR getter : ifc.nonDefaultMethods»
146 «IF BindingMapping.isGetterMethodName(getter.name)»
147 this._«getter.propertyNameFromGetter» = arg.«getter.name»();
150 «FOR impl : ifc.implements»
151 «printConstructorPropertySetter(impl)»
157 * Generate 'fieldsFrom' method to set builder properties based on type of given argument.
159 def private generateMethodFieldsFrom() '''
160 «IF (!(targetType instanceof GeneratedTransferObject))»
161 «IF targetType.hasImplementsFromUses»
162 «val List<Type> done = targetType.getBaseIfcs»
163 «generateMethodFieldsFromComment(targetType)»
164 public void fieldsFrom(«DataObject.importedName» arg) {
165 boolean isValidArg = false;
166 «FOR impl : targetType.getAllIfcs»
167 «generateIfCheck(impl, done)»
169 «CodeHelpers.importedName».validValue(isValidArg, arg, "«targetType.getAllIfcs.toListOfNames»");
175 def private generateMethodFieldsFromComment(GeneratedType type) '''
177 * Set fields from given grouping argument. Valid argument is instance of one of following types:
179 «FOR impl : type.getAllIfcs»
180 * <li>«impl.fullyQualifiedName»</li>
184 * @param arg grouping object
185 * @throws IllegalArgumentException if given argument is none of valid types
190 * Method is used to find out if given type implements any interface from uses.
192 def boolean hasImplementsFromUses(GeneratedType type) {
194 for (impl : type.getAllIfcs) {
195 if (impl instanceof GeneratedType && (impl as GeneratedType).hasNonDefaultMethods) {
202 def private generateIfCheck(Type impl, List<Type> done) '''
203 «IF (impl instanceof GeneratedType && (impl as GeneratedType).hasNonDefaultMethods)»
204 «val implType = impl as GeneratedType»
205 if (arg instanceof «implType.fullyQualifiedName») {
206 «printPropertySetter(implType)»
212 def private printPropertySetter(Type implementedIfc) '''
213 «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
214 «val ifc = implementedIfc as GeneratedType»
215 «FOR getter : ifc.nonDefaultMethods»
216 «IF BindingMapping.isGetterMethodName(getter.name)»
217 this._«getter.propertyNameFromGetter» = ((«implementedIfc.fullyQualifiedName»)arg).«getter.name»();
223 private def List<Type> getBaseIfcs(GeneratedType type) {
224 val List<Type> baseIfcs = new ArrayList();
225 for (ifc : type.implements) {
226 if (ifc instanceof GeneratedType && (ifc as GeneratedType).hasNonDefaultMethods) {
233 private def Set<Type> getAllIfcs(Type type) {
234 val Set<Type> baseIfcs = new HashSet()
235 if (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject)) {
236 val ifc = type as GeneratedType
237 for (impl : ifc.implements) {
238 if (impl instanceof GeneratedType && (impl as GeneratedType).hasNonDefaultMethods) {
241 baseIfcs.addAll(impl.getAllIfcs)
247 private def List<String> toListOfNames(Collection<Type> types) {
248 val List<String> names = new ArrayList
250 names.add(type.fullyQualifiedName)
255 def private constantsDeclarations() '''
256 «FOR c : type.getConstantDefinitions»
257 «IF c.getName.startsWith(TypeConstants.PATTERN_CONSTANT_NAME)»
258 «val cValue = c.value as Map<String, String>»
259 «val String fieldSuffix = c.getName.substring(TypeConstants.PATTERN_CONSTANT_NAME.length)»
260 «IF cValue.size == 1»
261 private static final «Pattern.importedName» «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «Pattern.importedName».compile("«cValue.keySet.get(0).escapeJava»");
262 private static final String «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = "«cValue.values.get(0).escapeJava»";
264 private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «CodeHelpers.importedName».compilePatterns(«ImmutableList.importedName».of(
265 «FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»));
266 private static final String[] «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = { «
267 FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
275 def private generateListSetter(GeneratedProperty field, Type actualType) '''
276 «val restrictions = restrictionsForSetter(actualType)»
277 «IF restrictions !== null»
278 «generateCheckers(field, restrictions, actualType)»
280 public «type.getName» set«field.getName.toFirstUpper»(final «field.returnType.importedName» values) {
281 «IF restrictions !== null»
282 if (values != null) {
283 for («actualType.getFullyQualifiedName» value : values) {
284 «checkArgument(field, restrictions, actualType, "value")»
288 this.«field.fieldName.toString» = values;
294 def private generateSetter(GeneratedProperty field, Type actualType) '''
295 «val restrictions = restrictionsForSetter(actualType)»
296 «IF restrictions !== null»
297 «generateCheckers(field, restrictions, actualType)»
300 «val setterName = "set" + field.getName.toFirstUpper»
301 public «type.getName» «setterName»(final «field.returnType.importedName» value) {
302 «IF restrictions !== null»
304 «checkArgument(field, restrictions, actualType, "value")»
307 this.«field.fieldName.toString» = value;
310 «val uintType = UINT_TYPES.get(field.returnType)»
311 «IF uintType !== null»
314 * Utility migration setter.
316 * @deprecated Use {#link «setterName»(«field.returnType.importedName»)} instead.
318 @Deprecated(forRemoval = true)
319 public «type.getName» «setterName»(final «uintType.importedName» value) {
320 return «setterName»(«CodeHelpers.importedName».compatUint(value));
325 private def Type getActualType(ParameterizedType ptype) {
326 return ptype.getActualTypeArguments.get(0)
330 * Template method which generates setter methods
332 * @return string with the setter methods
334 def private generateSetters() '''
335 «IF keyType !== null»
336 public «type.getName» withKey(final «keyType.importedName» key) {
341 «FOR property : properties»
342 «IF property.returnType instanceof ParameterizedType && Types.isListType(property.returnType)»
343 «generateListSetter(property, getActualType(property.returnType as ParameterizedType))»
345 «generateSetter(property, property.returnType)»
349 «IF augmentType !== null»
350 public «type.name» add«AUGMENTATION_FIELD.toFirstUpper»(«Class.importedName»<? extends «augmentType.importedName»> augmentationType, «augmentType.importedName» augmentationValue) {
351 if (augmentationValue == null) {
352 return remove«AUGMENTATION_FIELD.toFirstUpper»(augmentationType);
355 if (!(this.«AUGMENTATION_FIELD» instanceof «HashMap.importedName»)) {
356 this.«AUGMENTATION_FIELD» = new «HashMap.importedName»<>();
359 this.«AUGMENTATION_FIELD».put(augmentationType, augmentationValue);
363 public «type.name» remove«AUGMENTATION_FIELD.toFirstUpper»(«Class.importedName»<? extends «augmentType.importedName»> augmentationType) {
364 if (this.«AUGMENTATION_FIELD» instanceof «HashMap.importedName») {
365 this.«AUGMENTATION_FIELD».remove(augmentationType);
372 private def createDescription(GeneratedType type) {
374 Class that builds {@link «type.importedName»} instances.
376 @see «type.importedName»
380 override protected String formatDataForJavaDoc(GeneratedType type) {
381 val typeDescription = createDescription(type)
384 «IF !typeDescription.nullOrEmpty»
390 private def generateAugmentation() '''
391 @«SuppressWarnings.importedName»({ "unchecked", "checkstyle:methodTypeParameterName"})
392 public <E$$ extends «augmentType.importedName»> E$$ «AUGMENTABLE_AUGMENTATION_NAME»(«Class.importedName»<E$$> augmentationType) {
393 return (E$$) «AUGMENTATION_FIELD».get(«CodeHelpers.importedName».nonNullValue(augmentationType, "augmentationType"));
397 override protected generateCopyKeys(List<GeneratedProperty> keyProps) '''
398 this.key = base.«BindingMapping.IDENTIFIABLE_KEY_NAME»();
399 «FOR field : keyProps»
400 this.«field.fieldName» = base.«field.getterMethodName»();
404 override protected generateCopyAugmentation(Type implType) {
405 val augmentationHolderRef = AugmentationHolder.importedName
406 val typeRef = targetType.importedName
407 val hashMapRef = HashMap.importedName
408 val augmentTypeRef = augmentType.importedName
410 if (base instanceof «augmentationHolderRef») {
411 @SuppressWarnings("unchecked")
412 «Map.importedName»<«Class.importedName»<? extends «augmentTypeRef»>, «augmentTypeRef»> aug =((«augmentationHolderRef»<«typeRef»>) base).augmentations();
413 if (!aug.isEmpty()) {
414 this.«AUGMENTATION_FIELD» = new «hashMapRef»<>(aug);
420 private static def hasNonDefaultMethods(GeneratedType type) {
421 !type.methodDefinitions.isEmpty && type.methodDefinitions.exists([def | !def.isDefault])
424 private static def nonDefaultMethods(GeneratedType type) {
425 type.methodDefinitions.filter([def | !def.isDefault])