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 private 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)»
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
532 && (property.returnType as ParameterizedType).rawType.equals(Types.LIST_TYPE)»
533 «generateListSetter(property, getActualType(property.returnType as ParameterizedType))»
535 «generateSetter(property, property.returnType)»
539 «IF augmentType !== null»
540 public «type.name»«BUILDER» add«AUGMENTATION_FIELD.toFirstUpper»(«Class.importedName»<? extends «augmentType.importedName»> augmentationType, «augmentType.importedName» augmentationValue) {
541 if (augmentationValue == null) {
542 return remove«AUGMENTATION_FIELD.toFirstUpper»(augmentationType);
545 if (!(this.«AUGMENTATION_FIELD» instanceof «HashMap.importedName»)) {
546 this.«AUGMENTATION_FIELD» = new «HashMap.importedName»<>();
549 this.«AUGMENTATION_FIELD».put(augmentationType, augmentationValue);
553 public «type.name»«BUILDER» remove«AUGMENTATION_FIELD.toFirstUpper»(«Class.importedName»<? extends «augmentType.importedName»> augmentationType) {
554 if (this.«AUGMENTATION_FIELD» instanceof «HashMap.importedName») {
555 this.«AUGMENTATION_FIELD».remove(augmentationType);
562 def private CharSequence generateCopyConstructor(boolean impl) '''
563 «IF impl»private«ELSE»public«ENDIF» «type.name»«IF impl»«IMPL»«ELSE»«BUILDER»«ENDIF»(«type.name»«IF impl»«BUILDER»«ENDIF» base) {
564 «val allProps = new ArrayList(properties)»
565 «val isList = implementsIfc(type, Types.parameterizedTypeFor(Types.typeForClass(Identifiable), type))»
566 «IF isList && keyType !== null»
567 «val keyProps = new ArrayList((keyType as GeneratedTransferObject).properties)»
568 «Collections.sort(keyProps, [ p1, p2 | return p1.name.compareTo(p2.name) ])»
569 «FOR field : keyProps»
570 «removeProperty(allProps, field.name)»
572 if (base.«BindingMapping.IDENTIFIABLE_KEY_NAME»() == null) {
573 this.key = new «keyType.importedName»(
574 «FOR keyProp : keyProps SEPARATOR ", "»
575 base.«keyProp.getterMethodName»()
578 «FOR field : keyProps»
579 this.«field.fieldName» = base.«field.getterMethodName»();
582 this.key = base.«BindingMapping.IDENTIFIABLE_KEY_NAME»();
583 «FOR field : keyProps»
584 this.«field.fieldName» = key.«field.getterMethodName»();
588 «FOR field : allProps»
589 this.«field.fieldName» = base.«field.getterMethodName»();
591 «IF augmentType !== null»
593 this.«AUGMENTATION_FIELD» = «ImmutableMap.importedName».copyOf(base.«AUGMENTATION_FIELD»);
595 if (base instanceof «type.name»«IMPL») {
596 «type.name»«IMPL» impl = («type.name»«IMPL») base;
597 if (!impl.«AUGMENTATION_FIELD».isEmpty()) {
598 this.«AUGMENTATION_FIELD» = new «HashMap.importedName»<>(impl.«AUGMENTATION_FIELD»);
600 } else if (base instanceof «AugmentationHolder.importedName») {
601 @SuppressWarnings("unchecked")
602 «AugmentationHolder.importedName»<«type.importedName»> casted =(«AugmentationHolder.importedName»<«type.importedName»>) base;
603 if (!casted.augmentations().isEmpty()) {
604 this.«AUGMENTATION_FIELD» = new «HashMap.importedName»<>(casted.augmentations());
612 private def boolean implementsIfc(GeneratedType type, Type impl) {
613 for (Type ifc : type.implements) {
614 if (ifc.equals(impl)) {
621 private def getKey(GeneratedType type) {
622 for (m : type.methodDefinitions) {
623 if (BindingMapping.IDENTIFIABLE_KEY_NAME.equals(m.name)) {
629 private def void removeProperty(Collection<GeneratedProperty> props, String name) {
630 var GeneratedProperty toRemove = null
632 if (p.name.equals(name)) {
636 if (toRemove !== null) {
637 props.remove(toRemove);
642 * Template method which generate getter methods for IMPL class.
644 * @return string with getter methods
646 def private generateGetters(boolean addOverride) '''
647 «IF keyType !== null»
648 «IF addOverride»@Override«ENDIF»
649 public «keyType.importedName» «BindingMapping.IDENTIFIABLE_KEY_NAME»() {
654 «IF !properties.empty»
655 «FOR field : properties SEPARATOR '\n'»
656 «IF addOverride»@Override«ENDIF»
660 «IF augmentType !== null»
662 @SuppressWarnings("unchecked")
663 «IF addOverride»@Override«ENDIF»
664 public <E extends «augmentType.importedName»> E «AUGMENTABLE_AUGMENTATION_NAME»(«Class.importedName»<E> augmentationType) {
665 return (E) «AUGMENTATION_FIELD».get(«CodeHelpers.importedName».nonNullValue(augmentationType, "augmentationType"));
671 * Template method which generates the method <code>hashCode()</code>.
673 * @return string with the <code>hashCode()</code> method definition in JAVA format
675 def protected generateHashCode() '''
676 «IF !properties.empty || augmentType !== null»
677 private int hash = 0;
678 private volatile boolean hashValid = false;
681 public int hashCode() {
686 final int prime = 31;
688 «FOR property : properties»
689 «IF property.returnType.name.contains("[")»
690 result = prime * result + «Arrays.importedName».hashCode(«property.fieldName»);
692 result = prime * result + «Objects.importedName».hashCode(«property.fieldName»);
695 «IF augmentType !== null»
696 result = prime * result + «Objects.importedName».hashCode(«AUGMENTATION_FIELD»);
707 * Template method which generates the method <code>equals()</code>.
709 * @return string with the <code>equals()</code> method definition in JAVA format
711 def protected generateEquals() '''
712 «IF !properties.empty || augmentType !== null»
714 public boolean equals(«Object.importedName» obj) {
718 if (!(obj instanceof «DataObject.importedName»)) {
721 if (!«type.importedName».class.equals(((«DataObject.importedName»)obj).getImplementedInterface())) {
724 «type.importedName» other = («type.importedName»)obj;
725 «FOR property : properties»
726 «val fieldName = property.fieldName»
727 «IF property.returnType.name.contains("[")»
728 if (!«Arrays.importedName».equals(«fieldName», other.«property.getterMethodName»())) {
730 if (!«Objects.importedName».equals(«fieldName», other.«property.getterMethodName»())) {
735 «IF augmentType !== null»
736 if (getClass() == obj.getClass()) {
737 // Simple case: we are comparing against self
738 «type.name»«IMPL» otherImpl = («type.name»«IMPL») obj;
739 if (!«Objects.importedName».equals(«AUGMENTATION_FIELD», otherImpl.«AUGMENTATION_FIELD»)) {
743 // Hard case: compare our augments with presence there...
744 for («Map.importedName».Entry<«Class.importedName»<? extends «augmentType.importedName»>, «augmentType.importedName»> e : «AUGMENTATION_FIELD».entrySet()) {
745 if (!e.getValue().equals(other.«AUGMENTABLE_AUGMENTATION_NAME»(e.getKey()))) {
749 // .. and give the other one the chance to do the same
750 if (!obj.equals(this)) {
760 def override generateToString(Collection<GeneratedProperty> properties) '''
761 «IF properties !== null»
763 public «String.importedName» toString() {
764 final «MoreObjects.importedName».ToStringHelper helper = «MoreObjects.importedName».toStringHelper("«type.name»");
765 «FOR property : properties»
766 «CodeHelpers.importedName».appendValue(helper, "«property.fieldName»", «property.fieldName»);
768 «IF augmentType !== null»
769 «CodeHelpers.importedName».appendValue(helper, "«AUGMENTATION_FIELD»", «AUGMENTATION_FIELD».values());
771 return helper.toString();
776 def implementedInterfaceGetter() '''
778 public «Class.importedName»<«type.importedName»> getImplementedInterface() {
779 return «type.importedName».class;
783 private def createDescription(GeneratedType type) {
785 Class that builds {@link «type.importedName»} instances.
787 @see «type.importedName»
791 override def protected String formatDataForJavaDoc(GeneratedType type) {
792 val typeDescription = createDescription(type)
795 «IF !typeDescription.nullOrEmpty»