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.yangtools.sal.java.api.generator
10 import java.util.Arrays;
11 import java.util.LinkedHashSet
15 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl
16 import org.opendaylight.yangtools.binding.generator.util.Types
17 import org.opendaylight.yangtools.binding.generator.util.generated.type.builder.GeneratedTOBuilderImpl
18 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
19 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
20 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
21 import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature
22 import org.opendaylight.yangtools.sal.binding.model.api.Type
23 import org.opendaylight.yangtools.yang.binding.Augmentable
24 import static org.opendaylight.yangtools.binding.generator.util.Types.*
25 import java.util.HashMap
26 import java.util.Collections
27 import org.opendaylight.yangtools.yang.binding.DataObject
28 import java.util.ArrayList
29 import java.util.HashSet
30 import java.util.Collection
31 import org.opendaylight.yangtools.yang.binding.Identifiable
34 * Template for generating JAVA builder classes.
37 class BuilderTemplate extends BaseTemplate {
40 * Constant with the name of the concrete method.
42 val static GET_AUGMENTATION_METHOD_NAME = "getAugmentation"
45 * Constant with the suffix for builder classes.
47 val static BUILDER = 'Builder'
50 * Constant with suffix for the classes which are generated from the builder classes.
52 val static IMPL = 'Impl'
55 * Generated property is set if among methods is found one with the name GET_AUGMENTATION_METHOD_NAME
57 var GeneratedProperty augmentField
60 * Set of class attributes (fields) which are derived from the getter methods names
62 val Set<GeneratedProperty> properties
65 * Constructs new instance of this class.
66 * @throws IllegalArgumentException if <code>genType</code> equals <code>null</code>
68 new(GeneratedType genType) {
70 this.properties = propertiesFromMethods(createMethods)
74 * Returns set of method signature instances which contains all the methods of the <code>genType</code>
75 * and all the methods of the implemented interfaces.
77 * @returns set of method signature instances
79 def private Set<MethodSignature> createMethods() {
80 val Set<MethodSignature> methods = new LinkedHashSet
81 methods.addAll(type.methodDefinitions)
82 collectImplementedMethods(methods, type.implements)
87 * Adds to the <code>methods</code> set all the methods of the <code>implementedIfcs</code>
88 * and recursivelly their implemented interfaces.
90 * @param methods set of method signatures
91 * @param implementedIfcs list of implemented interfaces
93 def private void collectImplementedMethods(Set<MethodSignature> methods, List<Type> implementedIfcs) {
94 if (implementedIfcs == null || implementedIfcs.empty) {
97 for (implementedIfc : implementedIfcs) {
98 if ((implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))) {
99 val ifc = implementedIfc as GeneratedType
100 methods.addAll(ifc.methodDefinitions)
101 collectImplementedMethods(methods, ifc.implements)
102 } else if (implementedIfc.fullyQualifiedName == Augmentable.name) {
103 for (m : Augmentable.methods) {
104 if (m.name == GET_AUGMENTATION_METHOD_NAME) {
105 val fullyQualifiedName = m.returnType.name
106 val pkg = fullyQualifiedName.package
107 val name = fullyQualifiedName.name
108 val tmpGenTO = new GeneratedTOBuilderImpl(pkg, name)
109 val refType = new ReferencedTypeImpl(pkg, name)
110 val generic = new ReferencedTypeImpl(type.packageName, type.name)
111 val parametrizedReturnType = Types.parameterizedTypeFor(refType, generic)
112 tmpGenTO.addMethod(m.name).setReturnType(parametrizedReturnType)
113 augmentField = tmpGenTO.toInstance.methodDefinitions.first.propertyFromGetter
121 * Returns the first element of the list <code>elements</code>.
123 * @param list of elements
125 def private <E> first(List<E> elements) {
130 * Returns the name of the package from <code>fullyQualifiedName</code>.
132 * @param fullyQualifiedName string with fully qualified type name (package + type)
133 * @return string with the package name
135 def private String getPackage(String fullyQualifiedName) {
136 val lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT)
137 return if (lastDotIndex == -1) "" else fullyQualifiedName.substring(0, lastDotIndex)
141 * Returns the name of tye type from <code>fullyQualifiedName</code>
143 * @param fullyQualifiedName string with fully qualified type name (package + type)
144 * @return string with the name of the type
146 def private String getName(String fullyQualifiedName) {
147 val lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT)
148 return if (lastDotIndex == -1) fullyQualifiedName else fullyQualifiedName.substring(lastDotIndex + 1)
152 * Creates set of generated property instances from getter <code>methods</code>.
154 * @param set of method signature instances which should be transformed to list of properties
155 * @return set of generated property instances which represents the getter <code>methods</code>
157 def private propertiesFromMethods(Set<MethodSignature> methods) {
158 if (methods == null || methods.isEmpty()) {
159 return Collections.emptySet
161 val Set<GeneratedProperty> result = new LinkedHashSet
163 val createdField = m.propertyFromGetter
164 if (createdField != null) {
165 result.add(createdField)
172 * Creates generated property instance from the getter <code>method</code> name and return type.
174 * @param method method signature from which is the method name and return type obtained
175 * @return generated property instance for the getter <code>method</code>
176 * @throws IllegalArgumentException<ul>
177 * <li>if the <code>method</code> equals <code>null</code></li>
178 * <li>if the name of the <code>method</code> equals <code>null</code></li>
179 * <li>if the name of the <code>method</code> is empty</li>
180 * <li>if the return type of the <code>method</code> equals <code>null</code></li>
183 def private GeneratedProperty propertyFromGetter(MethodSignature method) {
184 if (method == null || method.name == null || method.name.empty || method.returnType == null) {
185 throw new IllegalArgumentException("Method, method name, method return type reference cannot be NULL or empty!")
188 if(BOOLEAN.equals(method.returnType)) {
191 if (method.name.startsWith(prefix)) {
192 val fieldName = method.getName().substring(prefix.length()).toFirstLower
193 val tmpGenTO = new GeneratedTOBuilderImpl("foo", "foo")
194 tmpGenTO.addProperty(fieldName).setReturnType(method.returnType)
195 return tmpGenTO.toInstance.properties.first
200 * Template method which generates JAVA class body for builder class and for IMPL class.
202 * @return string with JAVA source code
206 public class «type.name»«BUILDER» {
208 «generateFields(false)»
210 «generateAugmentField(true)»
212 «generateConstructorsFromIfcs(type)»
214 «generateMethodFieldsFrom(type)»
216 «generateGetters(false)»
220 public «type.name» build() {
221 return new «type.name»«IMPL»(this);
224 private static final class «type.name»«IMPL» implements «type.name» {
226 «implementedInterfaceGetter»
228 «generateFields(true)»
230 «generateAugmentField(false)»
232 «generateConstructor»
234 «generateGetters(true)»
240 «generateToString(properties)»
247 * Generate default constructor and constructor for every implemented interface from uses statements.
249 def private generateConstructorsFromIfcs(Type type) '''
250 public «type.name»«BUILDER»() {
252 «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»
253 «val ifc = type as GeneratedType»
254 «FOR impl : ifc.implements»
255 «generateConstructorFromIfc(impl)»
261 * Generate constructor with argument of given type.
263 def private Object generateConstructorFromIfc(Type impl) '''
264 «IF (impl instanceof GeneratedType)»
265 «val implType = impl as GeneratedType»
267 «IF !(implType.methodDefinitions.empty)»
268 public «type.name»«BUILDER»(«implType.fullyQualifiedName» arg) {
269 «printConstructorPropertySetter(implType)»
272 «FOR implTypeImplement : implType.implements»
273 «generateConstructorFromIfc(implTypeImplement)»
278 def private Object printConstructorPropertySetter(Type implementedIfc) '''
279 «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
280 «val ifc = implementedIfc as GeneratedType»
281 «FOR getter : ifc.methodDefinitions»
282 this._«getter.propertyNameFromGetter» = arg.«getter.name»();
284 «FOR impl : ifc.implements»
285 «printConstructorPropertySetter(impl)»
291 * Generate 'fieldsFrom' method to set builder properties based on type of given argument.
293 def private generateMethodFieldsFrom(Type type) '''
294 «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»
295 «val ifc = type as GeneratedType»
296 «IF ifc.hasImplementsFromUses»
297 «val List<Type> done = ifc.getBaseIfcs»
298 «generateMethodFieldsFromComment(ifc)»
299 public void fieldsFrom(«DataObject.importedName» arg) {
300 boolean isValidArg = false;
301 «FOR impl : ifc.getAllIfcs»
302 «generateIfCheck(impl, done)»
305 throw new IllegalArgumentException(
306 "expected one of: «ifc.getAllIfcs.toListOfNames» \n" +
315 def private generateMethodFieldsFromComment(GeneratedType type) '''
317 Set fields from given grouping argument. Valid argument is instance of one of following types:
319 «FOR impl : type.getAllIfcs»
320 * <li>«impl.fullyQualifiedName»</li>
324 * @param arg grouping object
325 * @throws IllegalArgumentException if given argument is none of valid types
330 * Method is used to find out if given type implements any interface from uses.
332 def boolean hasImplementsFromUses(GeneratedType type) {
334 for (impl : type.getAllIfcs) {
335 if ((impl instanceof GeneratedType) && !((impl as GeneratedType).methodDefinitions.empty)) {
342 def private generateIfCheck(Type impl, List<Type> done) '''
343 «IF (impl instanceof GeneratedType) && !((impl as GeneratedType).methodDefinitions.empty)»
344 «val implType = impl as GeneratedType»
345 if (arg instanceof «implType.fullyQualifiedName») {
346 «printPropertySetter(implType)»
352 def private printPropertySetter(Type implementedIfc) '''
353 «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
354 «val ifc = implementedIfc as GeneratedType»
355 «FOR getter : ifc.methodDefinitions»
356 this._«getter.propertyNameFromGetter» = ((«implementedIfc.fullyQualifiedName»)arg).«getter.name»();
361 private def List<Type> getBaseIfcs(GeneratedType type) {
362 val List<Type> baseIfcs = new ArrayList();
363 for (ifc : type.implements) {
364 if (ifc instanceof GeneratedType && !(ifc as GeneratedType).methodDefinitions.empty) {
371 private def Set<Type> getAllIfcs(Type type) {
372 val Set<Type> baseIfcs = new HashSet()
373 if (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject)) {
374 val ifc = type as GeneratedType
375 for (impl : ifc.implements) {
376 if (impl instanceof GeneratedType && !(impl as GeneratedType).methodDefinitions.empty) {
379 baseIfcs.addAll(impl.getAllIfcs)
385 private def List<String> toListOfNames(Collection<Type> types) {
386 val List<String> names = new ArrayList
388 names.add(type.fullyQualifiedName)
394 * Template method which generates class attributes.
396 * @param boolean value which specify whether field is|isn't final
397 * @return string with class attributes and their types
399 def private generateFields(boolean _final) '''
400 «IF !properties.empty»
402 private«IF _final» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
407 def private generateAugmentField(boolean init) '''
408 «IF augmentField != null»
409 private final «Map.importedName»<Class<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> «augmentField.name»«IF init» = new «HashMap.importedName»<>()«ENDIF»;
414 * Template method which generates setter methods
416 * @return string with the setter methods
418 def private generateSetters() '''
419 «FOR field : properties SEPARATOR '\n'»
420 public «type.name»«BUILDER» set«field.name.toFirstUpper»(«field.returnType.importedName» value) {
421 «generateRestrictions(field, "value")»
423 this.«field.fieldName» = value;
427 «IF augmentField != null»
429 public «type.name»«BUILDER» add«augmentField.name.toFirstUpper»(Class<? extends «augmentField.returnType.importedName»> augmentationType, «augmentField.returnType.importedName» augmentation) {
430 this.«augmentField.name».put(augmentationType, augmentation);
437 * Template method which generate constructor for IMPL class.
439 * @return string with IMPL class constructor
441 def private generateConstructor() '''
442 private «type.name»«IMPL»(«type.name»«BUILDER» builder) {
443 «val allProps = new ArrayList(properties)»
444 «val isList = implementsIfc(type, Types.parameterizedTypeFor(Types.typeForClass(Identifiable), type))»
445 «val keyType = type.getKey»
446 «IF isList && keyType != null»
447 «val keyProps = new ArrayList((keyType as GeneratedTransferObject).properties)»
448 «Collections.sort(keyProps,
450 return p1.name.compareTo(p2.name)
453 «FOR field : keyProps»
454 «removeProperty(allProps, field.name)»
456 «removeProperty(allProps, "key")»
457 if (builder.getKey() == null) {
458 this._key = new «keyType.importedName»(
459 «FOR keyProp : keyProps SEPARATOR ", "»
460 builder.«keyProp.getterMethodName»()
463 «FOR field : keyProps»
464 this.«field.fieldName» = builder.«field.getterMethodName»();
467 this._key = builder.getKey();
468 «FOR field : keyProps»
469 this.«field.fieldName» = _key.«field.getterMethodName»();
473 «FOR field : allProps»
474 this.«field.fieldName» = builder.«field.getterMethodName»();
476 «IF augmentField != null»
477 switch (builder.«augmentField.name».size()) {
479 this.«augmentField.name» = «Collections.importedName».emptyMap();
482 final «Map.importedName».Entry<Class<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> e = builder.«augmentField.name».entrySet().iterator().next();
483 this.«augmentField.name» = «Collections.importedName».<Class<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»>singletonMap(e.getKey(), e.getValue());
486 this.«augmentField.name» = new «HashMap.importedName»<>(builder.«augmentField.name»);
492 private def boolean implementsIfc(GeneratedType type, Type impl) {
493 for (Type ifc : type.implements) {
494 if (ifc.equals(impl)) {
501 private def Type getKey(GeneratedType type) {
502 for (m : type.methodDefinitions) {
503 if ("getKey".equals(m.name)) {
510 private def void removeProperty(Collection<GeneratedProperty> props, String name) {
511 var GeneratedProperty toRemove = null
513 if (p.name.equals(name)) {
517 if (toRemove != null) {
518 props.remove(toRemove);
523 * Template method which generate getter methods for IMPL class.
525 * @return string with getter methods
527 def private generateGetters(boolean addOverride) '''
528 «IF !properties.empty»
529 «FOR field : properties SEPARATOR '\n'»
530 «IF addOverride»@Override«ENDIF»
534 «IF augmentField != null»
536 @SuppressWarnings("unchecked")
537 «IF addOverride»@Override«ENDIF»
538 public <E extends «augmentField.returnType.importedName»> E get«augmentField.name.toFirstUpper»(Class<E> augmentationType) {
539 if (augmentationType == null) {
540 throw new IllegalArgumentException("Augmentation Type reference cannot be NULL!");
542 return (E) «augmentField.name».get(augmentationType);
548 * Template method which generates the method <code>hashCode()</code>.
550 * @return string with the <code>hashCode()</code> method definition in JAVA format
552 def protected generateHashCode() '''
553 «IF !properties.empty || augmentField != null»
555 public int hashCode() {
556 final int prime = 31;
558 «FOR property : properties»
559 «IF property.returnType.name.contains("[")»
560 result = prime * result + ((«property.fieldName» == null) ? 0 : «Arrays.importedName».hashCode(«property.fieldName»));
562 result = prime * result + ((«property.fieldName» == null) ? 0 : «property.fieldName».hashCode());
565 «IF augmentField != null»
566 result = prime * result + ((«augmentField.name» == null) ? 0 : «augmentField.name».hashCode());
574 * Template method which generates the method <code>equals()</code>.
576 * @return string with the <code>equals()</code> method definition in JAVA format
578 def protected generateEquals() '''
579 «IF !properties.empty || augmentField != null»
581 public boolean equals(java.lang.Object obj) {
588 if (getClass() != obj.getClass()) {
591 «type.name»«IMPL» other = («type.name»«IMPL») obj;
592 «FOR property : properties»
593 «val fieldName = property.fieldName»
594 if («fieldName» == null) {
595 if (other.«fieldName» != null) {
598 «IF property.returnType.name.contains("[")»
599 } else if(!«Arrays.importedName».equals(«fieldName», other.«fieldName»)) {
601 } else if(!«fieldName».equals(other.«fieldName»)) {
606 «IF augmentField != null»
607 «val fieldName = augmentField.name»
608 if («fieldName» == null) {
609 if (other.«fieldName» != null) {
612 } else if(!«fieldName».equals(other.«fieldName»)) {
621 def override generateToString(Collection<GeneratedProperty> properties) '''
622 «IF !(properties === null)»
624 public String toString() {
625 StringBuilder builder = new StringBuilder("«type.name» [");
626 boolean first = true;
628 «FOR property : properties»
629 if («property.fieldName» != null) {
633 builder.append(", ");
635 builder.append("«property.fieldName»=");
636 «IF property.returnType.name.contains("[")»
637 builder.append(«Arrays.importedName».toString(«property.fieldName»));
639 builder.append(«property.fieldName»);
643 «IF augmentField != null»
647 builder.append(", ");
649 builder.append("«augmentField.name»=");
650 builder.append(«augmentField.name».values());
652 return builder.append(']').toString();
657 override protected getFullyQualifiedName() {
658 '''«type.fullyQualifiedName»Builder'''.toString
661 def implementedInterfaceGetter() '''
662 public «Class.importedName»<«type.importedName»> getImplementedInterface() {
663 return «type.importedName».class;