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.yangtools.yang.binding.BindingMapping.AUGMENTATION_FIELD
12 import static org.opendaylight.yangtools.yang.binding.BindingMapping.AUGMENTABLE_AUGMENTATION_NAME
14 import com.google.common.base.MoreObjects
15 import com.google.common.collect.ImmutableMap
16 import com.google.common.collect.ImmutableSortedSet
17 import com.google.common.collect.ImmutableList
18 import java.util.ArrayList
19 import java.util.Arrays
20 import java.util.Collection
21 import java.util.Collections
22 import java.util.HashMap
23 import java.util.HashSet
24 import java.util.LinkedHashSet
27 import java.util.Objects
29 import java.util.regex.Pattern
30 import org.opendaylight.mdsal.binding.model.api.ConcreteType
31 import org.opendaylight.mdsal.binding.model.api.GeneratedProperty
32 import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject
33 import org.opendaylight.mdsal.binding.model.api.GeneratedType
34 import org.opendaylight.mdsal.binding.model.api.JavaTypeName
35 import org.opendaylight.mdsal.binding.model.api.MethodSignature
36 import org.opendaylight.mdsal.binding.model.api.Type
37 import org.opendaylight.mdsal.binding.model.api.ParameterizedType
38 import org.opendaylight.mdsal.binding.model.api.Restrictions
39 import org.opendaylight.mdsal.binding.model.util.ReferencedTypeImpl
40 import org.opendaylight.mdsal.binding.model.util.Types
41 import org.opendaylight.mdsal.binding.model.util.generated.type.builder.CodegenGeneratedTOBuilder
42 import org.opendaylight.mdsal.binding.model.util.TypeConstants
43 import org.opendaylight.yangtools.concepts.Builder
44 import org.opendaylight.yangtools.yang.binding.Augmentable
45 import org.opendaylight.yangtools.yang.binding.AugmentationHolder
46 import org.opendaylight.yangtools.yang.binding.BindingMapping
47 import org.opendaylight.yangtools.yang.binding.CodeHelpers
48 import org.opendaylight.yangtools.yang.binding.DataObject
49 import org.opendaylight.yangtools.yang.binding.Identifiable
52 * Template for generating JAVA builder classes.
55 class BuilderTemplate extends BaseTemplate {
57 * Constant with the suffix for builder classes.
59 val static BUILDER = 'Builder'
62 * Constant with suffix for the classes which are generated from the builder classes.
64 val static IMPL = 'Impl'
67 * Generated property is set if among methods is found one with the name GET_AUGMENTATION_METHOD_NAME.
72 * Set of class attributes (fields) which are derived from the getter methods names.
74 val Set<GeneratedProperty> properties
77 * GeneratedType for key type, null if this type does not have a key.
81 static val METHOD_COMPARATOR = new AlphabeticallyTypeMemberComparator<MethodSignature>();
84 * Constructs new instance of this class.
85 * @throws IllegalArgumentException if <code>genType</code> equals <code>null</code>
87 new(GeneratedType genType) {
88 super(new TopLevelJavaGeneratedType(builderName(genType), genType), genType)
89 this.properties = propertiesFromMethods(createMethods)
93 def static builderName(GeneratedType genType) {
94 val name = genType.identifier
95 name.createSibling(name.simpleName + "Builder")
99 * Returns set of method signature instances which contains all the methods of the <code>genType</code>
100 * and all the methods of the implemented interfaces.
102 * @returns set of method signature instances
104 def private Set<MethodSignature> createMethods() {
105 val Set<MethodSignature> methods = new LinkedHashSet();
106 methods.addAll(type.methodDefinitions)
107 collectImplementedMethods(methods, type.implements)
108 val Set<MethodSignature> sortedMethods = ImmutableSortedSet.orderedBy(METHOD_COMPARATOR).addAll(methods).build()
114 * Adds to the <code>methods</code> set all the methods of the <code>implementedIfcs</code>
115 * and recursively their implemented interfaces.
117 * @param methods set of method signatures
118 * @param implementedIfcs list of implemented interfaces
120 def private void collectImplementedMethods(Set<MethodSignature> methods, List<Type> implementedIfcs) {
121 if (implementedIfcs === null || implementedIfcs.empty) {
124 for (implementedIfc : implementedIfcs) {
125 if ((implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))) {
126 val ifc = implementedIfc as GeneratedType
127 methods.addAll(ifc.methodDefinitions)
128 collectImplementedMethods(methods, ifc.implements)
129 } else if (implementedIfc.fullyQualifiedName == Augmentable.name) {
130 val m = Augmentable.getDeclaredMethod(AUGMENTABLE_AUGMENTATION_NAME, Class)
131 val identifier = JavaTypeName.create(m.returnType)
132 val refType = new ReferencedTypeImpl(identifier)
133 val generic = new ReferencedTypeImpl(type.identifier)
134 augmentType = Types.parameterizedTypeFor(refType, generic)
140 * Returns the first element of the list <code>elements</code>.
142 * @param list of elements
144 def private <E> first(List<E> elements) {
149 * Creates set of generated property instances from getter <code>methods</code>.
151 * @param set of method signature instances which should be transformed to list of properties
152 * @return set of generated property instances which represents the getter <code>methods</code>
154 def private propertiesFromMethods(Collection<MethodSignature> methods) {
155 if (methods === null || methods.isEmpty()) {
156 return Collections.emptySet
158 val Set<GeneratedProperty> result = new LinkedHashSet
160 val createdField = m.propertyFromGetter
161 if (createdField !== null) {
162 result.add(createdField)
169 * Creates generated property instance from the getter <code>method</code> name and return type.
171 * @param method method signature from which is the method name and return type obtained
172 * @return generated property instance for the getter <code>method</code>
173 * @throws IllegalArgumentException<ul>
174 * <li>if the <code>method</code> equals <code>null</code></li>
175 * <li>if the name of the <code>method</code> equals <code>null</code></li>
176 * <li>if the name of the <code>method</code> is empty</li>
177 * <li>if the return type of the <code>method</code> equals <code>null</code></li>
180 def private GeneratedProperty propertyFromGetter(MethodSignature method) {
181 if (method === null || method.name === null || method.name.empty || method.returnType === null) {
182 throw new IllegalArgumentException("Method, method name, method return type reference cannot be NULL or empty!")
185 if (Types.BOOLEAN.equals(method.returnType)) {
188 if (method.name.startsWith(prefix)) {
189 val fieldName = method.getName().substring(prefix.length()).toFirstLower
190 val tmpGenTO = new CodegenGeneratedTOBuilder(JavaTypeName.create("foo", "foo"))
191 tmpGenTO.addProperty(fieldName).setReturnType(method.returnType)
192 return tmpGenTO.build.properties.first
196 override isLocalInnerClass(JavaTypeName name) {
197 // Builders do not have inner types
202 * Template method which generates JAVA class body for builder class and for IMPL class.
204 * @return string with JAVA source code
207 «wrapToDocumentation(formatDataForJavaDoc(type))»
208 public class «type.name»«BUILDER» implements «Builder.importedName»<«type.importedName»> {
210 «generateFields(false)»
212 «constantsDeclarations()»
214 «generateAugmentField(false)»
216 «generateConstructorsFromIfcs(type)»
218 «generateCopyConstructor(false)»
220 «generateMethodFieldsFrom(type)»
222 «generateGetters(false)»
226 @«Override.importedName»
227 public «type.name» build() {
228 return new «type.name»«IMPL»(this);
231 private static final class «type.name»«IMPL» implements «type.name» {
233 «implementedInterfaceGetter»
235 «generateFields(true)»
237 «generateAugmentField(true)»
239 «generateCopyConstructor(true)»
241 «generateGetters(true)»
247 «generateToString(properties)»
254 * Generate default constructor and constructor for every implemented interface from uses statements.
256 def private generateConstructorsFromIfcs(Type type) '''
257 public «type.name»«BUILDER»() {
259 «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»
260 «val ifc = type as GeneratedType»
261 «FOR impl : ifc.implements»
262 «generateConstructorFromIfc(impl)»
268 * Generate constructor with argument of given type.
270 def private Object generateConstructorFromIfc(Type impl) '''
271 «IF (impl instanceof GeneratedType)»
272 «IF !(impl.methodDefinitions.empty)»
273 public «type.name»«BUILDER»(«impl.fullyQualifiedName» arg) {
274 «printConstructorPropertySetter(impl)»
277 «FOR implTypeImplement : impl.implements»
278 «generateConstructorFromIfc(implTypeImplement)»
283 def private Object printConstructorPropertySetter(Type implementedIfc) '''
284 «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
285 «val ifc = implementedIfc as GeneratedType»
286 «FOR getter : ifc.methodDefinitions»
287 this._«getter.propertyNameFromGetter» = arg.«getter.name»();
289 «FOR impl : ifc.implements»
290 «printConstructorPropertySetter(impl)»
296 * Generate 'fieldsFrom' method to set builder properties based on type of given argument.
298 def private generateMethodFieldsFrom(Type type) '''
299 «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»
300 «val ifc = type as GeneratedType»
301 «IF ifc.hasImplementsFromUses»
302 «val List<Type> done = ifc.getBaseIfcs»
303 «generateMethodFieldsFromComment(ifc)»
304 public void fieldsFrom(«DataObject.importedName» arg) {
305 boolean isValidArg = false;
306 «FOR impl : ifc.getAllIfcs»
307 «generateIfCheck(impl, done)»
309 «CodeHelpers.importedName».validValue(isValidArg, arg, "«ifc.getAllIfcs.toListOfNames»");
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 !== null»
402 private«IF _final» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
405 «IF keyType !== null»
406 private«IF _final» final«ENDIF» «keyType.importedName» key;
410 def private generateAugmentField(boolean isPrivate) '''
411 «IF augmentType !== null»
412 «IF isPrivate»private «ENDIF»«Map.importedName»<«Class.importedName»<? extends «augmentType.importedName»>, «augmentType.importedName»> «AUGMENTATION_FIELD» = «Collections.importedName».emptyMap();
416 def private constantsDeclarations() '''
417 «FOR c : type.getConstantDefinitions»
418 «IF c.getName.startsWith(TypeConstants.PATTERN_CONSTANT_NAME)»
419 «val cValue = c.value as Map<String, String>»
420 «val String fieldSuffix = c.getName.substring(TypeConstants.PATTERN_CONSTANT_NAME.length)»
421 «IF cValue.size == 1»
422 private static final «Pattern.importedName» «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «Pattern.importedName».compile("«cValue.keySet.get(0).escapeJava»");
423 private static final String «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = "«cValue.values.get(0).escapeJava»";
425 private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «CodeHelpers.importedName».compilePatterns(«ImmutableList.importedName».of(
426 «FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»));
427 private static final String[] «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = { «
428 FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
436 def private generateCheckers(GeneratedProperty field, Restrictions restrictions, Type actualType) '''
437 «IF restrictions.rangeConstraint.present»
438 «AbstractRangeGenerator.forType(actualType).generateRangeChecker(field.name.toFirstUpper,
439 restrictions.rangeConstraint.get, this)»
441 «IF restrictions.lengthConstraint.present»
442 «LengthGenerator.generateLengthChecker(field.fieldName.toString, actualType, restrictions.lengthConstraint.get, this)»
446 def private checkArgument(GeneratedProperty property, Restrictions restrictions, Type actualType) '''
447 «IF restrictions.getRangeConstraint.isPresent»
448 «IF actualType instanceof ConcreteType»
449 «AbstractRangeGenerator.forType(actualType).generateRangeCheckerCall(property.getName.toFirstUpper, "value")»
451 «AbstractRangeGenerator.forType(actualType).generateRangeCheckerCall(property.getName.toFirstUpper, "value.getValue()")»
454 «IF restrictions.getLengthConstraint.isPresent»
455 «IF actualType instanceof ConcreteType»
456 «LengthGenerator.generateLengthCheckerCall(property.fieldName.toString, "value")»
458 «LengthGenerator.generateLengthCheckerCall(property.fieldName.toString, "value.getValue()")»
462 «val fieldUpperCase = property.fieldName.toString.toUpperCase()»
463 «FOR currentConstant : type.getConstantDefinitions»
464 «IF currentConstant.getName.startsWith(TypeConstants.PATTERN_CONSTANT_NAME)
465 && fieldUpperCase.equals(currentConstant.getName.substring(TypeConstants.PATTERN_CONSTANT_NAME.length))»
466 «CodeHelpers.importedName».checkPattern(value, «Constants.MEMBER_PATTERN_LIST»«property.fieldName», «Constants.MEMBER_REGEX_LIST»«property.fieldName»);
471 def private Restrictions restrictionsForSetter(Type actualType) {
472 if (actualType instanceof GeneratedType) {
475 return actualType.restrictions;
478 def private generateListSetter(GeneratedProperty field, Type actualType) '''
479 «val restrictions = restrictionsForSetter(actualType)»
480 «IF restrictions !== null»
481 «generateCheckers(field, restrictions, actualType)»
483 public «type.getName»Builder set«field.getName.toFirstUpper»(final «field.returnType.importedName» values) {
484 «IF restrictions !== null»
485 if (values != null) {
486 for («actualType.getFullyQualifiedName» value : values) {
487 «checkArgument(field, restrictions, actualType)»
491 this.«field.fieldName.toString» = values;
497 def private generateSetter(GeneratedProperty field, Type actualType) '''
498 «val restrictions = restrictionsForSetter(actualType)»
499 «IF restrictions !== null»
500 «generateCheckers(field, restrictions, actualType)»
503 public «type.getName»Builder set«field.getName.toFirstUpper»(final «field.returnType.importedName» value) {
504 «IF restrictions !== null»
506 «checkArgument(field, restrictions, actualType)»
509 this.«field.fieldName.toString» = value;
514 private def Type getActualType(ParameterizedType ptype) {
515 return ptype.getActualTypeArguments.get(0)
519 * Template method which generates setter methods
521 * @return string with the setter methods
523 def private generateSetters() '''
524 «IF keyType !== null»
525 public «type.getName»Builder withKey(final «keyType.importedName» key) {
530 «FOR property : properties»
531 «IF property.returnType instanceof ParameterizedType && Types.isListType(property.returnType)»
532 «generateListSetter(property, getActualType(property.returnType as ParameterizedType))»
534 «generateSetter(property, property.returnType)»
538 «IF augmentType !== null»
539 public «type.name»«BUILDER» add«AUGMENTATION_FIELD.toFirstUpper»(«Class.importedName»<? extends «augmentType.importedName»> augmentationType, «augmentType.importedName» augmentationValue) {
540 if (augmentationValue == null) {
541 return remove«AUGMENTATION_FIELD.toFirstUpper»(augmentationType);
544 if (!(this.«AUGMENTATION_FIELD» instanceof «HashMap.importedName»)) {
545 this.«AUGMENTATION_FIELD» = new «HashMap.importedName»<>();
548 this.«AUGMENTATION_FIELD».put(augmentationType, augmentationValue);
552 public «type.name»«BUILDER» remove«AUGMENTATION_FIELD.toFirstUpper»(«Class.importedName»<? extends «augmentType.importedName»> augmentationType) {
553 if (this.«AUGMENTATION_FIELD» instanceof «HashMap.importedName») {
554 this.«AUGMENTATION_FIELD».remove(augmentationType);
561 def private CharSequence generateCopyConstructor(boolean impl) '''
562 «IF impl»private«ELSE»public«ENDIF» «type.name»«IF impl»«IMPL»«ELSE»«BUILDER»«ENDIF»(«type.name»«IF impl»«BUILDER»«ENDIF» base) {
563 «val allProps = new ArrayList(properties)»
564 «val isList = implementsIfc(type, Types.parameterizedTypeFor(Types.typeForClass(Identifiable), type))»
565 «IF isList && keyType !== null»
566 «val keyProps = new ArrayList((keyType as GeneratedTransferObject).properties)»
567 «Collections.sort(keyProps, [ p1, p2 | return p1.name.compareTo(p2.name) ])»
568 «FOR field : keyProps»
569 «removeProperty(allProps, field.name)»
571 if (base.«BindingMapping.IDENTIFIABLE_KEY_NAME»() == null) {
572 this.key = new «keyType.importedName»(
573 «FOR keyProp : keyProps SEPARATOR ", "»
574 base.«keyProp.getterMethodName»()
577 «FOR field : keyProps»
578 this.«field.fieldName» = base.«field.getterMethodName»();
581 this.key = base.«BindingMapping.IDENTIFIABLE_KEY_NAME»();
582 «FOR field : keyProps»
583 this.«field.fieldName» = key.«field.getterMethodName»();
587 «FOR field : allProps»
588 this.«field.fieldName» = base.«field.getterMethodName»();
590 «IF augmentType !== null»
592 this.«AUGMENTATION_FIELD» = «ImmutableMap.importedName».copyOf(base.«AUGMENTATION_FIELD»);
594 if (base instanceof «type.name»«IMPL») {
595 «type.name»«IMPL» impl = («type.name»«IMPL») base;
596 if (!impl.«AUGMENTATION_FIELD».isEmpty()) {
597 this.«AUGMENTATION_FIELD» = new «HashMap.importedName»<>(impl.«AUGMENTATION_FIELD»);
599 } else if (base instanceof «AugmentationHolder.importedName») {
600 @SuppressWarnings("unchecked")
601 «AugmentationHolder.importedName»<«type.importedName»> casted =(«AugmentationHolder.importedName»<«type.importedName»>) base;
602 if (!casted.augmentations().isEmpty()) {
603 this.«AUGMENTATION_FIELD» = new «HashMap.importedName»<>(casted.augmentations());
611 private def boolean implementsIfc(GeneratedType type, Type impl) {
612 for (Type ifc : type.implements) {
613 if (ifc.equals(impl)) {
620 private def getKey(GeneratedType type) {
621 for (m : type.methodDefinitions) {
622 if (BindingMapping.IDENTIFIABLE_KEY_NAME.equals(m.name)) {
628 private def void removeProperty(Collection<GeneratedProperty> props, String name) {
629 var GeneratedProperty toRemove = null
631 if (p.name.equals(name)) {
635 if (toRemove !== null) {
636 props.remove(toRemove);
641 * Template method which generate getter methods for IMPL class.
643 * @return string with getter methods
645 def private generateGetters(boolean addOverride) '''
646 «IF keyType !== null»
647 «IF addOverride»@«Override.importedName»«ENDIF»
648 public «keyType.importedName» «BindingMapping.IDENTIFIABLE_KEY_NAME»() {
653 «IF !properties.empty»
654 «FOR field : properties SEPARATOR '\n'»
655 «IF addOverride»@«Override.importedName»«ENDIF»
659 «IF augmentType !== null»
661 @SuppressWarnings("unchecked")
662 «IF addOverride»@«Override.importedName»«ENDIF»
663 public <E extends «augmentType.importedName»> E «AUGMENTABLE_AUGMENTATION_NAME»(«Class.importedName»<E> augmentationType) {
664 return (E) «AUGMENTATION_FIELD».get(«CodeHelpers.importedName».nonNullValue(augmentationType, "augmentationType"));
670 * Template method which generates the method <code>hashCode()</code>.
672 * @return string with the <code>hashCode()</code> method definition in JAVA format
674 def protected generateHashCode() '''
675 «IF !properties.empty || augmentType !== null»
676 private int hash = 0;
677 private volatile boolean hashValid = false;
679 @«Override.importedName»
680 public int hashCode() {
685 final int prime = 31;
687 «FOR property : properties»
688 «IF property.returnType.name.contains("[")»
689 result = prime * result + «Arrays.importedName».hashCode(«property.fieldName»);
691 result = prime * result + «Objects.importedName».hashCode(«property.fieldName»);
694 «IF augmentType !== null»
695 result = prime * result + «Objects.importedName».hashCode(«AUGMENTATION_FIELD»);
706 * Template method which generates the method <code>equals()</code>.
708 * @return string with the <code>equals()</code> method definition in JAVA format
710 def protected generateEquals() '''
711 «IF !properties.empty || augmentType !== null»
712 @«Override.importedName»
713 public boolean equals(«Object.importedName» obj) {
717 if (!(obj instanceof «DataObject.importedName»)) {
720 if (!«type.importedName».class.equals(((«DataObject.importedName»)obj).getImplementedInterface())) {
723 «type.importedName» other = («type.importedName»)obj;
724 «FOR property : properties»
725 «val fieldName = property.fieldName»
726 «IF property.returnType.name.contains("[")»
727 if (!«Arrays.importedName».equals(«fieldName», other.«property.getterMethodName»())) {
729 if (!«Objects.importedName».equals(«fieldName», other.«property.getterMethodName»())) {
734 «IF augmentType !== null»
735 if (getClass() == obj.getClass()) {
736 // Simple case: we are comparing against self
737 «type.name»«IMPL» otherImpl = («type.name»«IMPL») obj;
738 if (!«Objects.importedName».equals(«AUGMENTATION_FIELD», otherImpl.«AUGMENTATION_FIELD»)) {
742 // Hard case: compare our augments with presence there...
743 for («Map.importedName».Entry<«Class.importedName»<? extends «augmentType.importedName»>, «augmentType.importedName»> e : «AUGMENTATION_FIELD».entrySet()) {
744 if (!e.getValue().equals(other.«AUGMENTABLE_AUGMENTATION_NAME»(e.getKey()))) {
748 // .. and give the other one the chance to do the same
749 if (!obj.equals(this)) {
759 override generateToString(Collection<GeneratedProperty> properties) '''
760 «IF properties !== null»
761 @«Override.importedName»
762 public «String.importedName» toString() {
763 final «MoreObjects.importedName».ToStringHelper helper = «MoreObjects.importedName».toStringHelper("«type.name»");
764 «FOR property : properties»
765 «CodeHelpers.importedName».appendValue(helper, "«property.fieldName»", «property.fieldName»);
767 «IF augmentType !== null»
768 «CodeHelpers.importedName».appendValue(helper, "«AUGMENTATION_FIELD»", «AUGMENTATION_FIELD».values());
770 return helper.toString();
775 def implementedInterfaceGetter() '''
776 @«Override.importedName»
777 public «Class.importedName»<«type.importedName»> getImplementedInterface() {
778 return «type.importedName».class;
782 private def createDescription(GeneratedType type) {
784 Class that builds {@link «type.importedName»} instances.
786 @see «type.importedName»
790 override protected String formatDataForJavaDoc(GeneratedType type) {
791 val typeDescription = createDescription(type)
794 «IF !typeDescription.nullOrEmpty»