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 com.google.common.collect.ImmutableSortedSet
11 import com.google.common.collect.Range
12 import java.util.ArrayList
13 import java.util.Arrays
14 import java.util.Collection
15 import java.util.Collections
16 import java.util.HashMap
17 import java.util.HashSet
18 import java.util.LinkedHashSet
22 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl
23 import org.opendaylight.yangtools.binding.generator.util.Types
24 import org.opendaylight.yangtools.binding.generator.util.generated.type.builder.GeneratedTOBuilderImpl
25 import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType
26 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
27 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
28 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
29 import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature
30 import org.opendaylight.yangtools.sal.binding.model.api.Type
31 import org.opendaylight.yangtools.yang.binding.Augmentable
32 import org.opendaylight.yangtools.yang.binding.DataObject
33 import org.opendaylight.yangtools.yang.binding.Identifiable
34 import org.opendaylight.yangtools.concepts.Builder
35 import org.opendaylight.yangtools.yang.binding.AugmentationHolder
36 import org.opendaylight.yangtools.sal.binding.model.api.Restrictions
37 import java.math.BigDecimal
38 import java.math.BigInteger
39 import com.google.common.collect.ImmutableList
42 * Template for generating JAVA builder classes.
45 class BuilderTemplate extends BaseTemplate {
48 * Constant with the name of the concrete method.
50 val static GET_AUGMENTATION_METHOD_NAME = "getAugmentation"
53 * Constant with the suffix for builder classes.
55 val static BUILDER = 'Builder'
58 * Constant with the name of the BuilderFor interface
60 val static BUILDERFOR = Builder.simpleName;
63 * Constant with suffix for the classes which are generated from the builder classes.
65 val static IMPL = 'Impl'
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> properties
77 private static val METHOD_COMPARATOR = new AlphabeticallyTypeMemberComparator<MethodSignature>();
80 * Constructs new instance of this class.
81 * @throws IllegalArgumentException if <code>genType</code> equals <code>null</code>
83 new(GeneratedType genType) {
85 this.properties = propertiesFromMethods(createMethods)
86 importMap.put(Builder.simpleName, Builder.package.name)
90 * Returns set of method signature instances which contains all the methods of the <code>genType</code>
91 * and all the methods of the implemented interfaces.
93 * @returns set of method signature instances
95 def private Set<MethodSignature> createMethods() {
96 val Set<MethodSignature> methods = new LinkedHashSet();
97 methods.addAll(type.methodDefinitions)
98 collectImplementedMethods(methods, type.implements)
99 val Set<MethodSignature> sortedMethods = ImmutableSortedSet.orderedBy(METHOD_COMPARATOR).addAll(methods).build()
105 * Adds to the <code>methods</code> set all the methods of the <code>implementedIfcs</code>
106 * and recursively their implemented interfaces.
108 * @param methods set of method signatures
109 * @param implementedIfcs list of implemented interfaces
111 def private void collectImplementedMethods(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 collectImplementedMethods(methods, ifc.implements)
120 } else if (implementedIfc.fullyQualifiedName == Augmentable.name) {
121 for (m : Augmentable.methods) {
122 if (m.name == GET_AUGMENTATION_METHOD_NAME) {
123 val fullyQualifiedName = m.returnType.name
124 val pkg = fullyQualifiedName.package
125 val name = fullyQualifiedName.name
126 val tmpGenTO = new GeneratedTOBuilderImpl(pkg, name)
127 val refType = new ReferencedTypeImpl(pkg, name)
128 val generic = new ReferencedTypeImpl(type.packageName, type.name)
129 val parametrizedReturnType = Types.parameterizedTypeFor(refType, generic)
130 tmpGenTO.addMethod(m.name).setReturnType(parametrizedReturnType)
131 augmentField = tmpGenTO.toInstance.methodDefinitions.first.propertyFromGetter
139 * Returns the first element of the list <code>elements</code>.
141 * @param list of elements
143 def private <E> first(List<E> elements) {
148 * Returns the name of the package from <code>fullyQualifiedName</code>.
150 * @param fullyQualifiedName string with fully qualified type name (package + type)
151 * @return string with the package name
153 def private String getPackage(String fullyQualifiedName) {
154 val lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT)
155 return if (lastDotIndex == -1) "" else fullyQualifiedName.substring(0, lastDotIndex)
159 * Returns the name of tye type from <code>fullyQualifiedName</code>
161 * @param fullyQualifiedName string with fully qualified type name (package + type)
162 * @return string with the name of the type
164 def private String getName(String fullyQualifiedName) {
165 val lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT)
166 return if (lastDotIndex == -1) fullyQualifiedName else fullyQualifiedName.substring(lastDotIndex + 1)
170 * Creates set of generated property instances from getter <code>methods</code>.
172 * @param set of method signature instances which should be transformed to list of properties
173 * @return set of generated property instances which represents the getter <code>methods</code>
175 def private propertiesFromMethods(Collection<MethodSignature> methods) {
176 if (methods == null || methods.isEmpty()) {
177 return Collections.emptySet
179 val Set<GeneratedProperty> result = new LinkedHashSet
181 val createdField = m.propertyFromGetter
182 if (createdField != null) {
183 result.add(createdField)
190 * Creates generated property instance from the getter <code>method</code> name and return type.
192 * @param method method signature from which is the method name and return type obtained
193 * @return generated property instance for the getter <code>method</code>
194 * @throws IllegalArgumentException<ul>
195 * <li>if the <code>method</code> equals <code>null</code></li>
196 * <li>if the name of the <code>method</code> equals <code>null</code></li>
197 * <li>if the name of the <code>method</code> is empty</li>
198 * <li>if the return type of the <code>method</code> equals <code>null</code></li>
201 def private GeneratedProperty propertyFromGetter(MethodSignature method) {
202 if (method == null || method.name == null || method.name.empty || method.returnType == null) {
203 throw new IllegalArgumentException("Method, method name, method return type reference cannot be NULL or empty!")
206 if(Types.BOOLEAN.equals(method.returnType)) {
209 if (method.name.startsWith(prefix)) {
210 val fieldName = method.getName().substring(prefix.length()).toFirstLower
211 val tmpGenTO = new GeneratedTOBuilderImpl("foo", "foo")
212 tmpGenTO.addProperty(fieldName).setReturnType(method.returnType)
213 return tmpGenTO.toInstance.properties.first
218 * Template method which generates JAVA class body for builder class and for IMPL class.
220 * @return string with JAVA source code
223 «wrapToDocumentation(formatDataForJavaDoc(type))»
224 public class «type.name»«BUILDER» implements «BUILDERFOR» <«type.importedName»> {
226 «generateFields(false)»
228 «generateAugmentField(false)»
230 «generateConstructorsFromIfcs(type)»
232 «generateCopyConstructor(false)»
234 «generateMethodFieldsFrom(type)»
236 «generateGetters(false)»
240 public «type.name» build() {
241 return new «type.name»«IMPL»(this);
244 private static final class «type.name»«IMPL» implements «type.name» {
246 «implementedInterfaceGetter»
248 «generateFields(true)»
250 «generateAugmentField(true)»
252 «generateCopyConstructor(true)»
254 «generateGetters(true)»
260 «generateToString(properties)»
267 * Generate default constructor and constructor for every implemented interface from uses statements.
269 def private generateConstructorsFromIfcs(Type type) '''
270 public «type.name»«BUILDER»() {
272 «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»
273 «val ifc = type as GeneratedType»
274 «FOR impl : ifc.implements»
275 «generateConstructorFromIfc(impl)»
281 * Generate constructor with argument of given type.
283 def private Object generateConstructorFromIfc(Type impl) '''
284 «IF (impl instanceof GeneratedType)»
285 «IF !(impl.methodDefinitions.empty)»
286 public «type.name»«BUILDER»(«impl.fullyQualifiedName» arg) {
287 «printConstructorPropertySetter(impl)»
290 «FOR implTypeImplement : impl.implements»
291 «generateConstructorFromIfc(implTypeImplement)»
296 def private Object printConstructorPropertySetter(Type implementedIfc) '''
297 «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
298 «val ifc = implementedIfc as GeneratedType»
299 «FOR getter : ifc.methodDefinitions»
300 this._«getter.propertyNameFromGetter» = arg.«getter.name»();
302 «FOR impl : ifc.implements»
303 «printConstructorPropertySetter(impl)»
309 * Generate 'fieldsFrom' method to set builder properties based on type of given argument.
311 def private generateMethodFieldsFrom(Type type) '''
312 «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»
313 «val ifc = type as GeneratedType»
314 «IF ifc.hasImplementsFromUses»
315 «val List<Type> done = ifc.getBaseIfcs»
316 «generateMethodFieldsFromComment(ifc)»
317 public void fieldsFrom(«DataObject.importedName» arg) {
318 boolean isValidArg = false;
319 «FOR impl : ifc.getAllIfcs»
320 «generateIfCheck(impl, done)»
323 throw new IllegalArgumentException(
324 "expected one of: «ifc.getAllIfcs.toListOfNames» \n" +
333 def private generateMethodFieldsFromComment(GeneratedType type) '''
335 *Set fields from given grouping argument. Valid argument is instance of one of following types:
337 «FOR impl : type.getAllIfcs»
338 * <li>«impl.fullyQualifiedName»</li>
342 * @param arg grouping object
343 * @throws IllegalArgumentException if given argument is none of valid types
348 * Method is used to find out if given type implements any interface from uses.
350 def boolean hasImplementsFromUses(GeneratedType type) {
352 for (impl : type.getAllIfcs) {
353 if ((impl instanceof GeneratedType) && !((impl as GeneratedType).methodDefinitions.empty)) {
360 def private generateIfCheck(Type impl, List<Type> done) '''
361 «IF (impl instanceof GeneratedType) && !((impl as GeneratedType).methodDefinitions.empty)»
362 «val implType = impl as GeneratedType»
363 if (arg instanceof «implType.fullyQualifiedName») {
364 «printPropertySetter(implType)»
370 def private printPropertySetter(Type implementedIfc) '''
371 «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
372 «val ifc = implementedIfc as GeneratedType»
373 «FOR getter : ifc.methodDefinitions»
374 this._«getter.propertyNameFromGetter» = ((«implementedIfc.fullyQualifiedName»)arg).«getter.name»();
379 private def List<Type> getBaseIfcs(GeneratedType type) {
380 val List<Type> baseIfcs = new ArrayList();
381 for (ifc : type.implements) {
382 if (ifc instanceof GeneratedType && !(ifc as GeneratedType).methodDefinitions.empty) {
389 private def Set<Type> getAllIfcs(Type type) {
390 val Set<Type> baseIfcs = new HashSet()
391 if (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject)) {
392 val ifc = type as GeneratedType
393 for (impl : ifc.implements) {
394 if (impl instanceof GeneratedType && !(impl as GeneratedType).methodDefinitions.empty) {
397 baseIfcs.addAll(impl.getAllIfcs)
403 private def List<String> toListOfNames(Collection<Type> types) {
404 val List<String> names = new ArrayList
406 names.add(type.fullyQualifiedName)
412 * Template method which generates class attributes.
414 * @param boolean value which specify whether field is|isn't final
415 * @return string with class attributes and their types
417 def private generateFields(boolean _final) '''
418 «IF properties !== null»
420 private«IF _final» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
421 «val restrictions = f.returnType.restrictions»
422 «IF !_final && restrictions != null»
423 «IF !(restrictions.lengthConstraints.empty)»
424 private static «List.importedName»<«Range.importedName»<«f.returnType.importedNumber»>> «f.fieldName»_length;
426 «IF !(restrictions.rangeConstraints.empty)»
427 private static «List.importedName»<«Range.importedName»<«f.returnType.importedNumber»>> «f.fieldName»_range;
434 def private generateAugmentField(boolean isPrivate) '''
435 «IF augmentField != null»
436 «IF isPrivate»private «ENDIF»«Map.importedName»<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> «augmentField.name» = «Collections.importedName».emptyMap();
441 * Template method which generates setter methods
443 * @return string with the setter methods
445 def private generateSetters() '''
446 «FOR field : properties SEPARATOR '\n'»
447 «val length = field.fieldName + "_length"»
448 «val range = field.fieldName + "_range"»
449 public «type.name»«BUILDER» set«field.name.toFirstUpper»(«field.returnType.importedName» value) {
450 «generateRestrictions(field, "value", length, range)»
451 this.«field.fieldName» = value;
454 «generateLengthMethod(length, field.returnType, type.name+BUILDER, length)»
455 «generateRangeMethod(range, field.returnType.restrictions, field.returnType, type.name+BUILDER, range)»
457 «IF augmentField != null»
459 public «type.name»«BUILDER» add«augmentField.name.toFirstUpper»(«Class.importedName»<? extends «augmentField.returnType.importedName»> augmentationType, «augmentField.returnType.importedName» augmentation) {
460 if (augmentation == null) {
461 return remove«augmentField.name.toFirstUpper»(augmentationType);
464 if (!(this.«augmentField.name» instanceof «HashMap.importedName»)) {
465 this.«augmentField.name» = new «HashMap.importedName»<>();
468 this.«augmentField.name».put(augmentationType, augmentation);
472 public «type.name»«BUILDER» remove«augmentField.name.toFirstUpper»(«Class.importedName»<? extends «augmentField.returnType.importedName»> augmentationType) {
473 if (this.«augmentField.name» instanceof «HashMap.importedName») {
474 this.«augmentField.name».remove(augmentationType);
481 def generateRestrictions(GeneratedProperty field, String paramName, String lengthGetter, String rangeGetter) '''
482 «val Type type = field.returnType»
483 «IF type instanceof ConcreteType»
484 «createRestrictions(type, paramName, type.name.contains("["), lengthGetter, rangeGetter)»
485 «ELSEIF type instanceof GeneratedTransferObject»
486 «createRestrictions(type, paramName, isArrayType(type as GeneratedTransferObject), lengthGetter, rangeGetter)»
490 def private createRestrictions(Type type, String paramName, boolean isArray, String lengthGetter, String rangeGetter) '''
491 «val restrictions = type.getRestrictions»
492 «IF restrictions !== null»
493 «val boolean isNestedType = !(type instanceof ConcreteType)»
494 «IF !restrictions.lengthConstraints.empty»
495 «generateLengthRestriction(type, paramName, lengthGetter, isNestedType, isArray)»
497 «IF !restrictions.rangeConstraints.empty»
498 «generateRangeRestriction(type, paramName, rangeGetter, isNestedType)»
503 def private generateLengthRestriction(Type type, String paramName, String getterName, boolean isNestedType, boolean isArray) '''
504 «val restrictions = type.getRestrictions»
505 if («paramName» != null) {
506 «val clazz = restrictions.lengthConstraints.iterator.next.min.class»
507 «printLengthConstraint(type, clazz, paramName, isNestedType, isArray)»
508 boolean isValidLength = false;
509 for («Range.importedName»<«clazz.importedNumber»> r : «getterName»()) {
510 if (r.contains(_constraint)) {
511 isValidLength = true;
514 if (!isValidLength) {
515 throw new IllegalArgumentException(String.format("Invalid length: %s, expected: %s.", «paramName», «getterName»));
520 def private generateRangeRestriction(Type type, String paramName, String getterName, boolean isNestedType) '''
521 if («paramName» != null) {
522 «printRangeConstraint(type, paramName, isNestedType)»
523 boolean isValidRange = false;
524 for («Range.importedName»<«type.importedNumber»> r : «getterName»()) {
525 if (r.contains(_constraint)) {
530 throw new IllegalArgumentException(String.format("Invalid range: %s, expected: %s.", «paramName», «getterName»));
535 def private generateLengthMethod(String methodName, Type type, String className, String varName) '''
536 «val Restrictions restrictions = type.restrictions»
537 «IF restrictions != null && !(restrictions.lengthConstraints.empty)»
538 «val numberClass = restrictions.lengthConstraints.iterator.next.min.class»
539 public static «List.importedName»<«Range.importedName»<«numberClass.importedNumber»>> «methodName»() {
540 «IF numberClass.equals(typeof(BigDecimal))»
541 «lengthBody(restrictions, numberClass, className, varName)»
543 «lengthBody(restrictions, typeof(BigInteger), className, varName)»
549 def private lengthBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
550 if («varName» == null) {
551 synchronized («className».class) {
552 if («varName» == null) {
553 «ImmutableList.importedName».Builder<«Range.importedName»<«numberClass.importedName»>> builder = «ImmutableList.importedName».builder();
554 «FOR r : restrictions.lengthConstraints»
555 builder.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», «numericValue(numberClass, r.max)»));
557 «varName» = builder.build();
564 def private generateRangeMethod(String methodName, Restrictions restrictions, Type returnType, String className, String varName) '''
565 «IF restrictions != null && !(restrictions.rangeConstraints.empty)»
566 «val number = returnType.importedNumber»
567 public static «List.importedName»<«Range.importedName»<«number»>> «methodName»() {
568 «IF returnType.fullyQualifiedName.equals(BigDecimal.canonicalName)»
569 «rangeBody(restrictions, BigDecimal, className, varName)»
571 «rangeBody(restrictions, BigInteger, className, varName)»
577 def private rangeBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
578 if («varName» == null) {
579 synchronized («className».class) {
580 if («varName» == null) {
581 «ImmutableList.importedName».Builder<«Range.importedName»<«numberClass.importedName»>> builder = «ImmutableList.importedName».builder();
582 «FOR r : restrictions.rangeConstraints»
583 builder.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», «numericValue(numberClass, r.max)»));
585 «varName» = builder.build();
592 def private CharSequence generateCopyConstructor(boolean impl) '''
593 «IF impl»private«ELSE»public«ENDIF» «type.name»«IF impl»«IMPL»«ELSE»«BUILDER»«ENDIF»(«type.name»«IF impl»«BUILDER»«ENDIF» base) {
594 «val allProps = new ArrayList(properties)»
595 «val isList = implementsIfc(type, Types.parameterizedTypeFor(Types.typeForClass(Identifiable), type))»
596 «val keyType = type.getKey»
597 «IF isList && keyType != null»
598 «val keyProps = new ArrayList((keyType as GeneratedTransferObject).properties)»
599 «Collections.sort(keyProps,
601 return p1.name.compareTo(p2.name)
604 «FOR field : keyProps»
605 «removeProperty(allProps, field.name)»
607 «removeProperty(allProps, "key")»
608 if (base.getKey() == null) {
609 this._key = new «keyType.importedName»(
610 «FOR keyProp : keyProps SEPARATOR ", "»
611 base.«keyProp.getterMethodName»()
614 «FOR field : keyProps»
615 this.«field.fieldName» = base.«field.getterMethodName»();
618 this._key = base.getKey();
619 «FOR field : keyProps»
620 this.«field.fieldName» = _key.«field.getterMethodName»();
624 «FOR field : allProps»
625 this.«field.fieldName» = base.«field.getterMethodName»();
627 «IF augmentField != null»
629 switch (base.«augmentField.name».size()) {
631 this.«augmentField.name» = «Collections.importedName».emptyMap();
634 final «Map.importedName».Entry<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> e = base.«augmentField.name».entrySet().iterator().next();
635 this.«augmentField.name» = «Collections.importedName».<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»>singletonMap(e.getKey(), e.getValue());
638 this.«augmentField.name» = new «HashMap.importedName»<>(base.«augmentField.name»);
641 if (base instanceof «type.name»«IMPL») {
642 «type.name»«IMPL» impl = («type.name»«IMPL») base;
643 if (!impl.«augmentField.name».isEmpty()) {
644 this.«augmentField.name» = new «HashMap.importedName»<>(impl.«augmentField.name»);
646 } else if (base instanceof «AugmentationHolder.importedName») {
647 @SuppressWarnings("unchecked")
648 «AugmentationHolder.importedName»<«type.importedName»> casted =(«AugmentationHolder.importedName»<«type.importedName»>) base;
649 if (!casted.augmentations().isEmpty()) {
650 this.«augmentField.name» = new «HashMap.importedName»<>(casted.augmentations());
658 private def boolean implementsIfc(GeneratedType type, Type impl) {
659 for (Type ifc : type.implements) {
660 if (ifc.equals(impl)) {
667 private def Type getKey(GeneratedType type) {
668 for (m : type.methodDefinitions) {
669 if ("getKey".equals(m.name)) {
676 private def void removeProperty(Collection<GeneratedProperty> props, String name) {
677 var GeneratedProperty toRemove = null
679 if (p.name.equals(name)) {
683 if (toRemove != null) {
684 props.remove(toRemove);
689 * Template method which generate getter methods for IMPL class.
691 * @return string with getter methods
693 def private generateGetters(boolean addOverride) '''
694 «IF !properties.empty»
695 «FOR field : properties SEPARATOR '\n'»
696 «IF addOverride»@Override«ENDIF»
700 «IF augmentField != null»
702 @SuppressWarnings("unchecked")
703 «IF addOverride»@Override«ENDIF»
704 public <E extends «augmentField.returnType.importedName»> E get«augmentField.name.toFirstUpper»(«Class.importedName»<E> augmentationType) {
705 if (augmentationType == null) {
706 throw new IllegalArgumentException("Augmentation Type reference cannot be NULL!");
708 return (E) «augmentField.name».get(augmentationType);
714 * Template method which generates the method <code>hashCode()</code>.
716 * @return string with the <code>hashCode()</code> method definition in JAVA format
718 def protected generateHashCode() '''
719 «IF !properties.empty || augmentField != null»
721 public int hashCode() {
722 final int prime = 31;
724 «FOR property : properties»
725 «IF property.returnType.name.contains("[")»
726 result = prime * result + ((«property.fieldName» == null) ? 0 : «Arrays.importedName».hashCode(«property.fieldName»));
728 result = prime * result + ((«property.fieldName» == null) ? 0 : «property.fieldName».hashCode());
731 «IF augmentField != null»
732 result = prime * result + ((«augmentField.name» == null) ? 0 : «augmentField.name».hashCode());
740 * Template method which generates the method <code>equals()</code>.
742 * @return string with the <code>equals()</code> method definition in JAVA format
744 def protected generateEquals() '''
745 «IF !properties.empty || augmentField != null»
747 public boolean equals(«Object.importedName» obj) {
751 if (!(obj instanceof «DataObject.importedName»)) {
754 if (!«type.importedName».class.equals(((«DataObject.importedName»)obj).getImplementedInterface())) {
757 «type.importedName» other = («type.importedName»)obj;
758 «FOR property : properties»
759 «val fieldName = property.fieldName»
760 if («fieldName» == null) {
761 if (other.«property.getterMethodName»() != null) {
764 «IF property.returnType.name.contains("[")»
765 } else if(!«Arrays.importedName».equals(«fieldName», other.«property.getterMethodName»())) {
767 } else if(!«fieldName».equals(other.«property.getterMethodName»())) {
772 «IF augmentField != null»
773 if (getClass() == obj.getClass()) {
774 // Simple case: we are comparing against self
775 «type.name»«IMPL» otherImpl = («type.name»«IMPL») obj;
776 «val fieldName = augmentField.name»
777 if («fieldName» == null) {
778 if (otherImpl.«fieldName» != null) {
781 } else if(!«fieldName».equals(otherImpl.«fieldName»)) {
785 // Hard case: compare our augments with presence there...
786 for («Map.importedName».Entry<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> e : «augmentField.name».entrySet()) {
787 if (!e.getValue().equals(other.getAugmentation(e.getKey()))) {
791 // .. and give the other one the chance to do the same
792 if (!obj.equals(this)) {
802 def override generateToString(Collection<GeneratedProperty> properties) '''
803 «IF !(properties === null)»
805 public «String.importedName» toString() {
806 «StringBuilder.importedName» builder = new «StringBuilder.importedName» ("«type.name» [");
807 boolean first = true;
809 «FOR property : properties»
810 if («property.fieldName» != null) {
814 builder.append(", ");
816 builder.append("«property.fieldName»=");
817 «IF property.returnType.name.contains("[")»
818 builder.append(«Arrays.importedName».toString(«property.fieldName»));
820 builder.append(«property.fieldName»);
824 «IF augmentField != null»
828 builder.append(", ");
830 builder.append("«augmentField.name»=");
831 builder.append(«augmentField.name».values());
833 return builder.append(']').toString();
838 override protected getFullyQualifiedName() {
839 '''«type.fullyQualifiedName»Builder'''.toString
842 def implementedInterfaceGetter() '''
843 public «Class.importedName»<«type.importedName»> getImplementedInterface() {
844 return «type.importedName».class;
848 private def createDescription(GeneratedType type) {
850 Class that builds {@link «type.importedName»} instances.
852 @see «type.importedName»
856 override def protected String formatDataForJavaDoc(GeneratedType type) {
857 val typeDescription = createDescription(type)
860 «IF !typeDescription.nullOrEmpty»