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;
12 import com.google.common.base.MoreObjects
13 import com.google.common.collect.ImmutableMap
14 import com.google.common.collect.ImmutableSortedSet
15 import com.google.common.collect.ImmutableList
16 import java.util.ArrayList
17 import java.util.Arrays
18 import java.util.Collection
19 import java.util.Collections
20 import java.util.HashMap
21 import java.util.HashSet
22 import java.util.LinkedHashSet
25 import java.util.Objects
27 import java.util.regex.Pattern
28 import org.opendaylight.mdsal.binding.model.api.ConcreteType
29 import org.opendaylight.mdsal.binding.model.api.GeneratedProperty
30 import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject
31 import org.opendaylight.mdsal.binding.model.api.GeneratedType
32 import org.opendaylight.mdsal.binding.model.api.MethodSignature
33 import org.opendaylight.mdsal.binding.model.api.Type
34 import org.opendaylight.mdsal.binding.model.api.ParameterizedType
35 import org.opendaylight.mdsal.binding.model.api.Restrictions
36 import org.opendaylight.mdsal.binding.model.util.ReferencedTypeImpl
37 import org.opendaylight.mdsal.binding.model.util.Types
38 import org.opendaylight.mdsal.binding.model.util.generated.type.builder.CodegenGeneratedTOBuilder
39 import org.opendaylight.mdsal.binding.model.util.TypeConstants
40 import org.opendaylight.yangtools.concepts.Builder
41 import org.opendaylight.yangtools.yang.binding.Augmentable
42 import org.opendaylight.yangtools.yang.binding.AugmentationHolder
43 import org.opendaylight.yangtools.yang.binding.CodeHelpers
44 import org.opendaylight.yangtools.yang.binding.DataObject
45 import org.opendaylight.yangtools.yang.binding.Identifiable
48 * Template for generating JAVA builder classes.
51 class BuilderTemplate extends BaseTemplate {
54 * Constant with the name of the concrete method.
56 val static GET_AUGMENTATION_METHOD_NAME = "getAugmentation"
59 * Constant with the suffix for builder classes.
61 val static BUILDER = 'Builder'
64 * Constant with the name of the BuilderFor interface
66 val static BUILDERFOR = Builder.simpleName;
69 * Constant with suffix for the classes which are generated from the builder classes.
71 val static IMPL = 'Impl'
74 * Generated property is set if among methods is found one with the name GET_AUGMENTATION_METHOD_NAME
76 var GeneratedProperty augmentField
79 * Set of class attributes (fields) which are derived from the getter methods names
81 val Set<GeneratedProperty> properties
83 private static val METHOD_COMPARATOR = new AlphabeticallyTypeMemberComparator<MethodSignature>();
86 * Constructs new instance of this class.
87 * @throws IllegalArgumentException if <code>genType</code> equals <code>null</code>
89 new(GeneratedType genType) {
91 this.properties = propertiesFromMethods(createMethods)
92 addImport(Builder.simpleName, Builder.package.name)
96 * Returns set of method signature instances which contains all the methods of the <code>genType</code>
97 * and all the methods of the implemented interfaces.
99 * @returns set of method signature instances
101 def private Set<MethodSignature> createMethods() {
102 val Set<MethodSignature> methods = new LinkedHashSet();
103 methods.addAll(type.methodDefinitions)
104 collectImplementedMethods(methods, type.implements)
105 val Set<MethodSignature> sortedMethods = ImmutableSortedSet.orderedBy(METHOD_COMPARATOR).addAll(methods).build()
111 * Adds to the <code>methods</code> set all the methods of the <code>implementedIfcs</code>
112 * and recursively their implemented interfaces.
114 * @param methods set of method signatures
115 * @param implementedIfcs list of implemented interfaces
117 def private void collectImplementedMethods(Set<MethodSignature> methods, List<Type> implementedIfcs) {
118 if (implementedIfcs === null || implementedIfcs.empty) {
121 for (implementedIfc : implementedIfcs) {
122 if ((implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))) {
123 val ifc = implementedIfc as GeneratedType
124 methods.addAll(ifc.methodDefinitions)
125 collectImplementedMethods(methods, ifc.implements)
126 } else if (implementedIfc.fullyQualifiedName == Augmentable.name) {
127 for (m : Augmentable.methods) {
128 if (m.name == GET_AUGMENTATION_METHOD_NAME) {
129 val fullyQualifiedName = m.returnType.name
130 val pkg = fullyQualifiedName.package
131 val name = fullyQualifiedName.name
132 val tmpGenTO = new CodegenGeneratedTOBuilder(pkg, name)
133 val refType = new ReferencedTypeImpl(pkg, name)
134 val generic = new ReferencedTypeImpl(type.packageName, type.name)
135 val parametrizedReturnType = Types.parameterizedTypeFor(refType, generic)
136 tmpGenTO.addMethod(m.name).setReturnType(parametrizedReturnType)
137 augmentField = tmpGenTO.build.methodDefinitions.first.propertyFromGetter
145 * Returns the first element of the list <code>elements</code>.
147 * @param list of elements
149 def private <E> first(List<E> elements) {
154 * Returns the name of the package from <code>fullyQualifiedName</code>.
156 * @param fullyQualifiedName string with fully qualified type name (package + type)
157 * @return string with the package name
159 def private String getPackage(String fullyQualifiedName) {
160 val lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT)
161 return if (lastDotIndex == -1) "" else fullyQualifiedName.substring(0, lastDotIndex)
165 * Returns the name of tye type from <code>fullyQualifiedName</code>
167 * @param fullyQualifiedName string with fully qualified type name (package + type)
168 * @return string with the name of the type
170 def private String getName(String fullyQualifiedName) {
171 val lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT)
172 return if (lastDotIndex == -1) fullyQualifiedName else fullyQualifiedName.substring(lastDotIndex + 1)
176 * Creates set of generated property instances from getter <code>methods</code>.
178 * @param set of method signature instances which should be transformed to list of properties
179 * @return set of generated property instances which represents the getter <code>methods</code>
181 def private propertiesFromMethods(Collection<MethodSignature> methods) {
182 if (methods === null || methods.isEmpty()) {
183 return Collections.emptySet
185 val Set<GeneratedProperty> result = new LinkedHashSet
187 val createdField = m.propertyFromGetter
188 if (createdField !== null) {
189 result.add(createdField)
196 * Creates generated property instance from the getter <code>method</code> name and return type.
198 * @param method method signature from which is the method name and return type obtained
199 * @return generated property instance for the getter <code>method</code>
200 * @throws IllegalArgumentException<ul>
201 * <li>if the <code>method</code> equals <code>null</code></li>
202 * <li>if the name of the <code>method</code> equals <code>null</code></li>
203 * <li>if the name of the <code>method</code> is empty</li>
204 * <li>if the return type of the <code>method</code> equals <code>null</code></li>
207 def private GeneratedProperty propertyFromGetter(MethodSignature method) {
208 if (method === null || method.name === null || method.name.empty || method.returnType === null) {
209 throw new IllegalArgumentException("Method, method name, method return type reference cannot be NULL or empty!")
212 if (Types.BOOLEAN.equals(method.returnType)) {
215 if (method.name.startsWith(prefix)) {
216 val fieldName = method.getName().substring(prefix.length()).toFirstLower
217 val tmpGenTO = new CodegenGeneratedTOBuilder("foo", "foo")
218 tmpGenTO.addProperty(fieldName).setReturnType(method.returnType)
219 return tmpGenTO.build.properties.first
223 override isLocalInnerClass(String importedTypePackageName) {
224 // Builders do not have inner types
229 * Template method which generates JAVA class body for builder class and for IMPL class.
231 * @return string with JAVA source code
234 «wrapToDocumentation(formatDataForJavaDoc(type))»
235 public class «type.name»«BUILDER» implements «BUILDERFOR»<«type.importedName»> {
237 «generateFields(false)»
239 «constantsDeclarations()»
241 «generateAugmentField(false)»
243 «generateConstructorsFromIfcs(type)»
245 «generateCopyConstructor(false)»
247 «generateMethodFieldsFrom(type)»
249 «generateGetters(false)»
254 public «type.name» build() {
255 return new «type.name»«IMPL»(this);
258 private static final class «type.name»«IMPL» implements «type.name» {
260 «implementedInterfaceGetter»
262 «generateFields(true)»
264 «generateAugmentField(true)»
266 «generateCopyConstructor(true)»
268 «generateGetters(true)»
274 «generateToString(properties)»
281 * Generate default constructor and constructor for every implemented interface from uses statements.
283 def private generateConstructorsFromIfcs(Type type) '''
284 public «type.name»«BUILDER»() {
286 «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»
287 «val ifc = type as GeneratedType»
288 «FOR impl : ifc.implements»
289 «generateConstructorFromIfc(impl)»
295 * Generate constructor with argument of given type.
297 def private Object generateConstructorFromIfc(Type impl) '''
298 «IF (impl instanceof GeneratedType)»
299 «IF !(impl.methodDefinitions.empty)»
300 public «type.name»«BUILDER»(«impl.fullyQualifiedName» arg) {
301 «printConstructorPropertySetter(impl)»
304 «FOR implTypeImplement : impl.implements»
305 «generateConstructorFromIfc(implTypeImplement)»
310 def private Object printConstructorPropertySetter(Type implementedIfc) '''
311 «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
312 «val ifc = implementedIfc as GeneratedType»
313 «FOR getter : ifc.methodDefinitions»
314 this._«getter.propertyNameFromGetter» = arg.«getter.name»();
316 «FOR impl : ifc.implements»
317 «printConstructorPropertySetter(impl)»
323 * Generate 'fieldsFrom' method to set builder properties based on type of given argument.
325 def private generateMethodFieldsFrom(Type type) '''
326 «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»
327 «val ifc = type as GeneratedType»
328 «IF ifc.hasImplementsFromUses»
329 «val List<Type> done = ifc.getBaseIfcs»
330 «generateMethodFieldsFromComment(ifc)»
331 public void fieldsFrom(«DataObject.importedName» arg) {
332 boolean isValidArg = false;
333 «FOR impl : ifc.getAllIfcs»
334 «generateIfCheck(impl, done)»
336 «CodeHelpers.importedName».validValue(isValidArg, arg, "«ifc.getAllIfcs.toListOfNames»");
342 def private generateMethodFieldsFromComment(GeneratedType type) '''
344 * Set fields from given grouping argument. Valid argument is instance of one of following types:
346 «FOR impl : type.getAllIfcs»
347 * <li>«impl.fullyQualifiedName»</li>
351 * @param arg grouping object
352 * @throws IllegalArgumentException if given argument is none of valid types
357 * Method is used to find out if given type implements any interface from uses.
359 def boolean hasImplementsFromUses(GeneratedType type) {
361 for (impl : type.getAllIfcs) {
362 if ((impl instanceof GeneratedType) && !((impl as GeneratedType).methodDefinitions.empty)) {
369 def private generateIfCheck(Type impl, List<Type> done) '''
370 «IF (impl instanceof GeneratedType) && !((impl as GeneratedType).methodDefinitions.empty)»
371 «val implType = impl as GeneratedType»
372 if (arg instanceof «implType.fullyQualifiedName») {
373 «printPropertySetter(implType)»
379 def private printPropertySetter(Type implementedIfc) '''
380 «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
381 «val ifc = implementedIfc as GeneratedType»
382 «FOR getter : ifc.methodDefinitions»
383 this._«getter.propertyNameFromGetter» = ((«implementedIfc.fullyQualifiedName»)arg).«getter.name»();
388 private def List<Type> getBaseIfcs(GeneratedType type) {
389 val List<Type> baseIfcs = new ArrayList();
390 for (ifc : type.implements) {
391 if (ifc instanceof GeneratedType && !(ifc as GeneratedType).methodDefinitions.empty) {
398 private def Set<Type> getAllIfcs(Type type) {
399 val Set<Type> baseIfcs = new HashSet()
400 if (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject)) {
401 val ifc = type as GeneratedType
402 for (impl : ifc.implements) {
403 if (impl instanceof GeneratedType && !(impl as GeneratedType).methodDefinitions.empty) {
406 baseIfcs.addAll(impl.getAllIfcs)
412 private def List<String> toListOfNames(Collection<Type> types) {
413 val List<String> names = new ArrayList
415 names.add(type.fullyQualifiedName)
421 * Template method which generates class attributes.
423 * @param boolean value which specify whether field is|isn't final
424 * @return string with class attributes and their types
426 def private generateFields(boolean _final) '''
427 «IF properties !== null»
429 private«IF _final» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
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();
440 def private constantsDeclarations() '''
441 «FOR c : type.getConstantDefinitions»
442 «IF c.getName.startsWith(TypeConstants.PATTERN_CONSTANT_NAME)»
443 «val cValue = c.value as Map<String, String>»
444 «val String fieldSuffix = c.getName.substring(TypeConstants.PATTERN_CONSTANT_NAME.length).toLowerCase»
445 public static final «List.importedName»<String> «c.getName» = «ImmutableList.importedName».of(
446 «FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»);
447 «IF cValue.size == 1»
448 private static final «Pattern.importedName» «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «Pattern.importedName».compile(«c.getName».get(0));
449 private static final String «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = "«cValue.values.get(0).escapeJava»";
451 private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «CodeHelpers.importedName».compilePatterns(«c.getName»);
452 private static final String[] «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = { «
453 FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
461 def private generateCheckers(GeneratedProperty field, Restrictions restrictions, Type actualType) '''
462 «IF restrictions.rangeConstraint.present»
463 «AbstractRangeGenerator.forType(actualType).generateRangeChecker(field.name.toFirstUpper,
464 restrictions.rangeConstraint.get, this)»
466 «IF restrictions.lengthConstraint.present»
467 «LengthGenerator.generateLengthChecker(field.fieldName.toString, actualType, restrictions.lengthConstraint.get, this)»
471 def private checkArgument(GeneratedProperty property, Restrictions restrictions, Type actualType) '''
472 «IF restrictions.getRangeConstraint.isPresent»
473 «IF actualType instanceof ConcreteType»
474 «AbstractRangeGenerator.forType(actualType).generateRangeCheckerCall(property.getName.toFirstUpper, "value")»
476 «AbstractRangeGenerator.forType(actualType).generateRangeCheckerCall(property.getName.toFirstUpper, "value.getValue()")»
479 «IF restrictions.getLengthConstraint.isPresent»
480 «IF actualType instanceof ConcreteType»
481 «LengthGenerator.generateLengthCheckerCall(property.fieldName.toString, "value")»
483 «LengthGenerator.generateLengthCheckerCall(property.fieldName.toString, "value.getValue()")»
487 «val fieldUpperCase = property.fieldName.toString.toUpperCase()»
488 «FOR currentConstant : type.getConstantDefinitions»
489 «IF currentConstant.getName.startsWith(TypeConstants.PATTERN_CONSTANT_NAME)
490 && fieldUpperCase.equals(currentConstant.getName.substring(TypeConstants.PATTERN_CONSTANT_NAME.length))»
491 «CodeHelpers.importedName».checkPattern(value, «Constants.MEMBER_PATTERN_LIST»«property.fieldName», «Constants.MEMBER_REGEX_LIST»«property.fieldName»);
496 def private Restrictions restrictionsForSetter(Type actualType) {
497 if (actualType instanceof GeneratedType) {
500 return actualType.restrictions;
503 def private generateListSetter(GeneratedProperty field, Type actualType) '''
504 «val restrictions = restrictionsForSetter(actualType)»
505 «IF restrictions !== null»
506 «generateCheckers(field, restrictions, actualType)»
508 public «type.getName»Builder set«field.getName.toFirstUpper»(final «field.returnType.importedName» values) {
509 «IF restrictions !== null»
510 if (values != null) {
511 for («actualType.getFullyQualifiedName» value : values) {
512 «checkArgument(field, restrictions, actualType)»
516 this.«field.fieldName.toString» = values;
522 def private generateSetter(GeneratedProperty field, Type actualType) '''
523 «val restrictions = restrictionsForSetter(actualType)»
524 «IF restrictions !== null»
525 «generateCheckers(field, restrictions, actualType)»
528 public «type.getName»Builder set«field.getName.toFirstUpper»(final «field.returnType.importedName» value) {
529 «IF restrictions !== null»
531 «checkArgument(field, restrictions, actualType)»
534 this.«field.fieldName.toString» = value;
539 private def Type getActualType(ParameterizedType ptype) {
540 return ptype.getActualTypeArguments.get(0)
544 * Template method which generates setter methods
546 * @return string with the setter methods
548 def private generateSetters() '''
549 «FOR property : properties»
550 «IF property.returnType instanceof ParameterizedType
551 && (property.returnType as ParameterizedType).rawType.equals(Types.LIST_TYPE)»
552 «generateListSetter(property, getActualType(property.returnType as ParameterizedType))»
554 «generateSetter(property, property.returnType)»
558 «IF augmentField !== null»
559 public «type.name»«BUILDER» add«augmentField.name.toFirstUpper»(«Class.importedName»<? extends «augmentField.returnType.importedName»> augmentationType, «augmentField.returnType.importedName» augmentationValue) {
560 if (augmentationValue == null) {
561 return remove«augmentField.name.toFirstUpper»(augmentationType);
564 if (!(this.«augmentField.name» instanceof «HashMap.importedName»)) {
565 this.«augmentField.name» = new «HashMap.importedName»<>();
568 this.«augmentField.name».put(augmentationType, augmentationValue);
572 public «type.name»«BUILDER» remove«augmentField.name.toFirstUpper»(«Class.importedName»<? extends «augmentField.returnType.importedName»> augmentationType) {
573 if (this.«augmentField.name» instanceof «HashMap.importedName») {
574 this.«augmentField.name».remove(augmentationType);
581 def private CharSequence generateCopyConstructor(boolean impl) '''
582 «IF impl»private«ELSE»public«ENDIF» «type.name»«IF impl»«IMPL»«ELSE»«BUILDER»«ENDIF»(«type.name»«IF impl»«BUILDER»«ENDIF» base) {
583 «val allProps = new ArrayList(properties)»
584 «val isList = implementsIfc(type, Types.parameterizedTypeFor(Types.typeForClass(Identifiable), type))»
585 «val keyType = type.getKey»
586 «IF isList && keyType !== null»
587 «val keyProps = new ArrayList((keyType as GeneratedTransferObject).properties)»
588 «Collections.sort(keyProps,
590 return p1.name.compareTo(p2.name)
593 «FOR field : keyProps»
594 «removeProperty(allProps, field.name)»
596 «removeProperty(allProps, "key")»
597 if (base.getKey() == null) {
598 this._key = new «keyType.importedName»(
599 «FOR keyProp : keyProps SEPARATOR ", "»
600 base.«keyProp.getterMethodName»()
603 «FOR field : keyProps»
604 this.«field.fieldName» = base.«field.getterMethodName»();
607 this._key = base.getKey();
608 «FOR field : keyProps»
609 this.«field.fieldName» = _key.«field.getterMethodName»();
613 «FOR field : allProps»
614 this.«field.fieldName» = base.«field.getterMethodName»();
616 «IF augmentField !== null»
618 this.«augmentField.name» = «ImmutableMap.importedName».copyOf(base.«augmentField.name»);
620 if (base instanceof «type.name»«IMPL») {
621 «type.name»«IMPL» impl = («type.name»«IMPL») base;
622 if (!impl.«augmentField.name».isEmpty()) {
623 this.«augmentField.name» = new «HashMap.importedName»<>(impl.«augmentField.name»);
625 } else if (base instanceof «AugmentationHolder.importedName») {
626 @SuppressWarnings("unchecked")
627 «AugmentationHolder.importedName»<«type.importedName»> casted =(«AugmentationHolder.importedName»<«type.importedName»>) base;
628 if (!casted.augmentations().isEmpty()) {
629 this.«augmentField.name» = new «HashMap.importedName»<>(casted.augmentations());
637 private def boolean implementsIfc(GeneratedType type, Type impl) {
638 for (Type ifc : type.implements) {
639 if (ifc.equals(impl)) {
646 private def Type getKey(GeneratedType type) {
647 for (m : type.methodDefinitions) {
648 if ("getKey".equals(m.name)) {
655 private def void removeProperty(Collection<GeneratedProperty> props, String name) {
656 var GeneratedProperty toRemove = null
658 if (p.name.equals(name)) {
662 if (toRemove !== null) {
663 props.remove(toRemove);
668 * Template method which generate getter methods for IMPL class.
670 * @return string with getter methods
672 def private generateGetters(boolean addOverride) '''
673 «IF !properties.empty»
674 «FOR field : properties SEPARATOR '\n'»
675 «IF addOverride»@Override«ENDIF»
679 «IF augmentField !== null»
681 @SuppressWarnings("unchecked")
682 «IF addOverride»@Override«ENDIF»
683 public <E extends «augmentField.returnType.importedName»> E get«augmentField.name.toFirstUpper»(«Class.importedName»<E> augmentationType) {
684 return (E) «augmentField.name».get(«CodeHelpers.importedName».nonNullValue(augmentationType, "augmentationType"));
690 * Template method which generates the method <code>hashCode()</code>.
692 * @return string with the <code>hashCode()</code> method definition in JAVA format
694 def protected generateHashCode() '''
695 «IF !properties.empty || augmentField !== null»
696 private int hash = 0;
697 private volatile boolean hashValid = false;
700 public int hashCode() {
705 final int prime = 31;
707 «FOR property : properties»
708 «IF property.returnType.name.contains("[")»
709 result = prime * result + «Arrays.importedName».hashCode(«property.fieldName»);
711 result = prime * result + «Objects.importedName».hashCode(«property.fieldName»);
714 «IF augmentField !== null»
715 result = prime * result + «Objects.importedName».hashCode(«augmentField.name»);
726 * Template method which generates the method <code>equals()</code>.
728 * @return string with the <code>equals()</code> method definition in JAVA format
730 def protected generateEquals() '''
731 «IF !properties.empty || augmentField !== null»
733 public boolean equals(«Object.importedName» obj) {
737 if (!(obj instanceof «DataObject.importedName»)) {
740 if (!«type.importedName».class.equals(((«DataObject.importedName»)obj).getImplementedInterface())) {
743 «type.importedName» other = («type.importedName»)obj;
744 «FOR property : properties»
745 «val fieldName = property.fieldName»
746 «IF property.returnType.name.contains("[")»
747 if (!«Arrays.importedName».equals(«fieldName», other.«property.getterMethodName»())) {
749 if (!«Objects.importedName».equals(«fieldName», other.«property.getterMethodName»())) {
754 «IF augmentField !== null»
755 if (getClass() == obj.getClass()) {
756 // Simple case: we are comparing against self
757 «type.name»«IMPL» otherImpl = («type.name»«IMPL») obj;
758 «val fieldName = augmentField.name»
759 if (!«Objects.importedName».equals(«fieldName», otherImpl.«fieldName»)) {
763 // Hard case: compare our augments with presence there...
764 for («Map.importedName».Entry<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> e : «augmentField.name».entrySet()) {
765 if (!e.getValue().equals(other.getAugmentation(e.getKey()))) {
769 // .. and give the other one the chance to do the same
770 if (!obj.equals(this)) {
780 def override generateToString(Collection<GeneratedProperty> properties) '''
781 «IF properties !== null»
783 public «String.importedName» toString() {
784 final «MoreObjects.importedName».ToStringHelper helper = «MoreObjects.importedName».toStringHelper("«type.name»");
785 «FOR property : properties»
786 «CodeHelpers.importedName».appendValue(helper, "«property.fieldName»", «property.fieldName»);
788 «IF augmentField !== null»
789 «CodeHelpers.importedName».appendValue(helper, "«augmentField.name»", «augmentField.name».values());
791 return helper.toString();
796 def implementedInterfaceGetter() '''
798 public «Class.importedName»<«type.importedName»> getImplementedInterface() {
799 return «type.importedName».class;
803 private def createDescription(GeneratedType type) {
805 Class that builds {@link «type.importedName»} instances.
807 @see «type.importedName»
811 override def protected String formatDataForJavaDoc(GeneratedType type) {
812 val typeDescription = createDescription(type)
815 «IF !typeDescription.nullOrEmpty»