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.JavaTypeName
33 import org.opendaylight.mdsal.binding.model.api.MethodSignature
34 import org.opendaylight.mdsal.binding.model.api.Type
35 import org.opendaylight.mdsal.binding.model.api.ParameterizedType
36 import org.opendaylight.mdsal.binding.model.api.Restrictions
37 import org.opendaylight.mdsal.binding.model.util.ReferencedTypeImpl
38 import org.opendaylight.mdsal.binding.model.util.Types
39 import org.opendaylight.mdsal.binding.model.util.generated.type.builder.CodegenGeneratedTOBuilder
40 import org.opendaylight.mdsal.binding.model.util.TypeConstants
41 import org.opendaylight.yangtools.concepts.Builder
42 import org.opendaylight.yangtools.yang.binding.Augmentable
43 import org.opendaylight.yangtools.yang.binding.AugmentationHolder
44 import org.opendaylight.yangtools.yang.binding.BindingMapping
45 import org.opendaylight.yangtools.yang.binding.CodeHelpers
46 import org.opendaylight.yangtools.yang.binding.DataObject
47 import org.opendaylight.yangtools.yang.binding.Identifiable
50 * Template for generating JAVA builder classes.
53 class BuilderTemplate extends BaseTemplate {
55 * Constant with the name of the concrete method.
57 val static GET_AUGMENTATION_METHOD_NAME = "getAugmentation"
60 * Constant with the suffix for builder classes.
62 val static BUILDER = 'Builder'
65 * Constant with suffix for the classes which are generated from the builder classes.
67 val static IMPL = 'Impl'
70 * Generated property is set if among methods is found one with the name GET_AUGMENTATION_METHOD_NAME.
72 var GeneratedProperty augmentField
75 * Set of class attributes (fields) which are derived from the getter methods names.
77 val Set<GeneratedProperty> properties
80 * GeneratedType for key type, null if this type does not have a key.
84 private static val METHOD_COMPARATOR = new AlphabeticallyTypeMemberComparator<MethodSignature>();
87 * Constructs new instance of this class.
88 * @throws IllegalArgumentException if <code>genType</code> equals <code>null</code>
90 new(GeneratedType genType) {
91 super(new TopLevelJavaGeneratedType(builderName(genType), genType), genType)
92 this.properties = propertiesFromMethods(createMethods)
96 def static builderName(GeneratedType genType) {
97 val name = genType.identifier
98 name.createSibling(name.simpleName + "Builder")
102 * Returns set of method signature instances which contains all the methods of the <code>genType</code>
103 * and all the methods of the implemented interfaces.
105 * @returns set of method signature instances
107 def private Set<MethodSignature> createMethods() {
108 val Set<MethodSignature> methods = new LinkedHashSet();
109 methods.addAll(type.methodDefinitions)
110 collectImplementedMethods(methods, type.implements)
111 val Set<MethodSignature> sortedMethods = ImmutableSortedSet.orderedBy(METHOD_COMPARATOR).addAll(methods).build()
117 * Adds to the <code>methods</code> set all the methods of the <code>implementedIfcs</code>
118 * and recursively their implemented interfaces.
120 * @param methods set of method signatures
121 * @param implementedIfcs list of implemented interfaces
123 def private void collectImplementedMethods(Set<MethodSignature> methods, List<Type> implementedIfcs) {
124 if (implementedIfcs === null || implementedIfcs.empty) {
127 for (implementedIfc : implementedIfcs) {
128 if ((implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))) {
129 val ifc = implementedIfc as GeneratedType
130 methods.addAll(ifc.methodDefinitions)
131 collectImplementedMethods(methods, ifc.implements)
132 } else if (implementedIfc.fullyQualifiedName == Augmentable.name) {
133 for (m : Augmentable.methods) {
134 if (m.name == GET_AUGMENTATION_METHOD_NAME) {
135 val identifier = JavaTypeName.create(m.returnType)
136 val tmpGenTO = new CodegenGeneratedTOBuilder(identifier)
137 val refType = new ReferencedTypeImpl(identifier)
138 val generic = new ReferencedTypeImpl(type.identifier)
139 val parametrizedReturnType = Types.parameterizedTypeFor(refType, generic)
140 tmpGenTO.addMethod(m.name).setReturnType(parametrizedReturnType)
141 augmentField = tmpGenTO.build.methodDefinitions.first.propertyFromGetter
149 * Returns the first element of the list <code>elements</code>.
151 * @param list of elements
153 def private <E> first(List<E> elements) {
158 * Creates set of generated property instances from getter <code>methods</code>.
160 * @param set of method signature instances which should be transformed to list of properties
161 * @return set of generated property instances which represents the getter <code>methods</code>
163 def private propertiesFromMethods(Collection<MethodSignature> methods) {
164 if (methods === null || methods.isEmpty()) {
165 return Collections.emptySet
167 val Set<GeneratedProperty> result = new LinkedHashSet
169 val createdField = m.propertyFromGetter
170 if (createdField !== null) {
171 result.add(createdField)
178 * Creates generated property instance from the getter <code>method</code> name and return type.
180 * @param method method signature from which is the method name and return type obtained
181 * @return generated property instance for the getter <code>method</code>
182 * @throws IllegalArgumentException<ul>
183 * <li>if the <code>method</code> equals <code>null</code></li>
184 * <li>if the name of the <code>method</code> equals <code>null</code></li>
185 * <li>if the name of the <code>method</code> is empty</li>
186 * <li>if the return type of the <code>method</code> equals <code>null</code></li>
189 def private GeneratedProperty propertyFromGetter(MethodSignature method) {
190 if (method === null || method.name === null || method.name.empty || method.returnType === null) {
191 throw new IllegalArgumentException("Method, method name, method return type reference cannot be NULL or empty!")
194 if (Types.BOOLEAN.equals(method.returnType)) {
197 if (method.name.startsWith(prefix)) {
198 val fieldName = method.getName().substring(prefix.length()).toFirstLower
199 val tmpGenTO = new CodegenGeneratedTOBuilder(JavaTypeName.create("foo", "foo"))
200 tmpGenTO.addProperty(fieldName).setReturnType(method.returnType)
201 return tmpGenTO.build.properties.first
205 override isLocalInnerClass(JavaTypeName name) {
206 // Builders do not have inner types
211 * Template method which generates JAVA class body for builder class and for IMPL class.
213 * @return string with JAVA source code
216 «wrapToDocumentation(formatDataForJavaDoc(type))»
217 public class «type.name»«BUILDER» implements «Builder.importedName»<«type.importedName»> {
219 «generateFields(false)»
221 «constantsDeclarations()»
223 «generateAugmentField(false)»
225 «generateConstructorsFromIfcs(type)»
227 «generateCopyConstructor(false)»
229 «generateMethodFieldsFrom(type)»
231 «generateGetters(false)»
236 public «type.name» build() {
237 return new «type.name»«IMPL»(this);
240 private static final class «type.name»«IMPL» implements «type.name» {
242 «implementedInterfaceGetter»
244 «generateFields(true)»
246 «generateAugmentField(true)»
248 «generateCopyConstructor(true)»
250 «generateGetters(true)»
256 «generateToString(properties)»
263 * Generate default constructor and constructor for every implemented interface from uses statements.
265 def private generateConstructorsFromIfcs(Type type) '''
266 public «type.name»«BUILDER»() {
268 «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»
269 «val ifc = type as GeneratedType»
270 «FOR impl : ifc.implements»
271 «generateConstructorFromIfc(impl)»
277 * Generate constructor with argument of given type.
279 def private Object generateConstructorFromIfc(Type impl) '''
280 «IF (impl instanceof GeneratedType)»
281 «IF !(impl.methodDefinitions.empty)»
282 public «type.name»«BUILDER»(«impl.fullyQualifiedName» arg) {
283 «printConstructorPropertySetter(impl)»
286 «FOR implTypeImplement : impl.implements»
287 «generateConstructorFromIfc(implTypeImplement)»
292 def private Object printConstructorPropertySetter(Type implementedIfc) '''
293 «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
294 «val ifc = implementedIfc as GeneratedType»
295 «FOR getter : ifc.methodDefinitions»
296 this._«getter.propertyNameFromGetter» = arg.«getter.name»();
298 «FOR impl : ifc.implements»
299 «printConstructorPropertySetter(impl)»
305 * Generate 'fieldsFrom' method to set builder properties based on type of given argument.
307 def private generateMethodFieldsFrom(Type type) '''
308 «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»
309 «val ifc = type as GeneratedType»
310 «IF ifc.hasImplementsFromUses»
311 «val List<Type> done = ifc.getBaseIfcs»
312 «generateMethodFieldsFromComment(ifc)»
313 public void fieldsFrom(«DataObject.importedName» arg) {
314 boolean isValidArg = false;
315 «FOR impl : ifc.getAllIfcs»
316 «generateIfCheck(impl, done)»
318 «CodeHelpers.importedName».validValue(isValidArg, arg, "«ifc.getAllIfcs.toListOfNames»");
324 def private generateMethodFieldsFromComment(GeneratedType type) '''
326 * Set fields from given grouping argument. Valid argument is instance of one of following types:
328 «FOR impl : type.getAllIfcs»
329 * <li>«impl.fullyQualifiedName»</li>
333 * @param arg grouping object
334 * @throws IllegalArgumentException if given argument is none of valid types
339 * Method is used to find out if given type implements any interface from uses.
341 def boolean hasImplementsFromUses(GeneratedType type) {
343 for (impl : type.getAllIfcs) {
344 if ((impl instanceof GeneratedType) && !((impl as GeneratedType).methodDefinitions.empty)) {
351 def private generateIfCheck(Type impl, List<Type> done) '''
352 «IF (impl instanceof GeneratedType) && !((impl as GeneratedType).methodDefinitions.empty)»
353 «val implType = impl as GeneratedType»
354 if (arg instanceof «implType.fullyQualifiedName») {
355 «printPropertySetter(implType)»
361 def private printPropertySetter(Type implementedIfc) '''
362 «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
363 «val ifc = implementedIfc as GeneratedType»
364 «FOR getter : ifc.methodDefinitions»
365 this._«getter.propertyNameFromGetter» = ((«implementedIfc.fullyQualifiedName»)arg).«getter.name»();
370 private def List<Type> getBaseIfcs(GeneratedType type) {
371 val List<Type> baseIfcs = new ArrayList();
372 for (ifc : type.implements) {
373 if (ifc instanceof GeneratedType && !(ifc as GeneratedType).methodDefinitions.empty) {
380 private def Set<Type> getAllIfcs(Type type) {
381 val Set<Type> baseIfcs = new HashSet()
382 if (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject)) {
383 val ifc = type as GeneratedType
384 for (impl : ifc.implements) {
385 if (impl instanceof GeneratedType && !(impl as GeneratedType).methodDefinitions.empty) {
388 baseIfcs.addAll(impl.getAllIfcs)
394 private def List<String> toListOfNames(Collection<Type> types) {
395 val List<String> names = new ArrayList
397 names.add(type.fullyQualifiedName)
403 * Template method which generates class attributes.
405 * @param boolean value which specify whether field is|isn't final
406 * @return string with class attributes and their types
408 def private generateFields(boolean _final) '''
409 «IF properties !== null»
411 private«IF _final» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
414 «IF keyType !== null»
415 private«IF _final» final«ENDIF» «keyType.importedName» key;
419 def private generateAugmentField(boolean isPrivate) '''
420 «IF augmentField !== null»
421 «IF isPrivate»private «ENDIF»«Map.importedName»<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> «augmentField.name» = «Collections.importedName».emptyMap();
425 def private constantsDeclarations() '''
426 «FOR c : type.getConstantDefinitions»
427 «IF c.getName.startsWith(TypeConstants.PATTERN_CONSTANT_NAME)»
428 «val cValue = c.value as Map<String, String>»
429 «val String fieldSuffix = c.getName.substring(TypeConstants.PATTERN_CONSTANT_NAME.length)»
430 «IF cValue.size == 1»
431 private static final «Pattern.importedName» «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «Pattern.importedName».compile("«cValue.keySet.get(0).escapeJava»");
432 private static final String «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = "«cValue.values.get(0).escapeJava»";
434 private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «CodeHelpers.importedName».compilePatterns(«ImmutableList.importedName».of(
435 «FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»));
436 private static final String[] «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = { «
437 FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
445 def private generateCheckers(GeneratedProperty field, Restrictions restrictions, Type actualType) '''
446 «IF restrictions.rangeConstraint.present»
447 «AbstractRangeGenerator.forType(actualType).generateRangeChecker(field.name.toFirstUpper,
448 restrictions.rangeConstraint.get, this)»
450 «IF restrictions.lengthConstraint.present»
451 «LengthGenerator.generateLengthChecker(field.fieldName.toString, actualType, restrictions.lengthConstraint.get, this)»
455 def private checkArgument(GeneratedProperty property, Restrictions restrictions, Type actualType) '''
456 «IF restrictions.getRangeConstraint.isPresent»
457 «IF actualType instanceof ConcreteType»
458 «AbstractRangeGenerator.forType(actualType).generateRangeCheckerCall(property.getName.toFirstUpper, "value")»
460 «AbstractRangeGenerator.forType(actualType).generateRangeCheckerCall(property.getName.toFirstUpper, "value.getValue()")»
463 «IF restrictions.getLengthConstraint.isPresent»
464 «IF actualType instanceof ConcreteType»
465 «LengthGenerator.generateLengthCheckerCall(property.fieldName.toString, "value")»
467 «LengthGenerator.generateLengthCheckerCall(property.fieldName.toString, "value.getValue()")»
471 «val fieldUpperCase = property.fieldName.toString.toUpperCase()»
472 «FOR currentConstant : type.getConstantDefinitions»
473 «IF currentConstant.getName.startsWith(TypeConstants.PATTERN_CONSTANT_NAME)
474 && fieldUpperCase.equals(currentConstant.getName.substring(TypeConstants.PATTERN_CONSTANT_NAME.length))»
475 «CodeHelpers.importedName».checkPattern(value, «Constants.MEMBER_PATTERN_LIST»«property.fieldName», «Constants.MEMBER_REGEX_LIST»«property.fieldName»);
480 def private Restrictions restrictionsForSetter(Type actualType) {
481 if (actualType instanceof GeneratedType) {
484 return actualType.restrictions;
487 def private generateListSetter(GeneratedProperty field, Type actualType) '''
488 «val restrictions = restrictionsForSetter(actualType)»
489 «IF restrictions !== null»
490 «generateCheckers(field, restrictions, actualType)»
492 public «type.getName»Builder set«field.getName.toFirstUpper»(final «field.returnType.importedName» values) {
493 «IF restrictions !== null»
494 if (values != null) {
495 for («actualType.getFullyQualifiedName» value : values) {
496 «checkArgument(field, restrictions, actualType)»
500 this.«field.fieldName.toString» = values;
506 def private generateSetter(GeneratedProperty field, Type actualType) '''
507 «val restrictions = restrictionsForSetter(actualType)»
508 «IF restrictions !== null»
509 «generateCheckers(field, restrictions, actualType)»
512 public «type.getName»Builder set«field.getName.toFirstUpper»(final «field.returnType.importedName» value) {
513 «IF restrictions !== null»
515 «checkArgument(field, restrictions, actualType)»
518 this.«field.fieldName.toString» = value;
523 private def Type getActualType(ParameterizedType ptype) {
524 return ptype.getActualTypeArguments.get(0)
528 * Template method which generates setter methods
530 * @return string with the setter methods
532 def private generateSetters() '''
533 «IF keyType !== null»
534 public «type.getName»Builder withKey(final «keyType.importedName» key) {
539 «FOR property : properties»
540 «IF property.returnType instanceof ParameterizedType
541 && (property.returnType as ParameterizedType).rawType.equals(Types.LIST_TYPE)»
542 «generateListSetter(property, getActualType(property.returnType as ParameterizedType))»
544 «generateSetter(property, property.returnType)»
548 «IF augmentField !== null»
549 public «type.name»«BUILDER» add«augmentField.name.toFirstUpper»(«Class.importedName»<? extends «augmentField.returnType.importedName»> augmentationType, «augmentField.returnType.importedName» augmentationValue) {
550 if (augmentationValue == null) {
551 return remove«augmentField.name.toFirstUpper»(augmentationType);
554 if (!(this.«augmentField.name» instanceof «HashMap.importedName»)) {
555 this.«augmentField.name» = new «HashMap.importedName»<>();
558 this.«augmentField.name».put(augmentationType, augmentationValue);
562 public «type.name»«BUILDER» remove«augmentField.name.toFirstUpper»(«Class.importedName»<? extends «augmentField.returnType.importedName»> augmentationType) {
563 if (this.«augmentField.name» instanceof «HashMap.importedName») {
564 this.«augmentField.name».remove(augmentationType);
571 def private CharSequence generateCopyConstructor(boolean impl) '''
572 «IF impl»private«ELSE»public«ENDIF» «type.name»«IF impl»«IMPL»«ELSE»«BUILDER»«ENDIF»(«type.name»«IF impl»«BUILDER»«ENDIF» base) {
573 «val allProps = new ArrayList(properties)»
574 «val isList = implementsIfc(type, Types.parameterizedTypeFor(Types.typeForClass(Identifiable), type))»
575 «IF isList && keyType !== null»
576 «val keyProps = new ArrayList((keyType as GeneratedTransferObject).properties)»
577 «Collections.sort(keyProps, [ p1, p2 | return p1.name.compareTo(p2.name) ])»
578 «FOR field : keyProps»
579 «removeProperty(allProps, field.name)»
581 if (base.«BindingMapping.IDENTIFIABLE_KEY_NAME»() == null) {
582 this.key = new «keyType.importedName»(
583 «FOR keyProp : keyProps SEPARATOR ", "»
584 base.«keyProp.getterMethodName»()
587 «FOR field : keyProps»
588 this.«field.fieldName» = base.«field.getterMethodName»();
591 this.key = base.«BindingMapping.IDENTIFIABLE_KEY_NAME»();
592 «FOR field : keyProps»
593 this.«field.fieldName» = key.«field.getterMethodName»();
597 «FOR field : allProps»
598 this.«field.fieldName» = base.«field.getterMethodName»();
600 «IF augmentField !== null»
602 this.«augmentField.name» = «ImmutableMap.importedName».copyOf(base.«augmentField.name»);
604 if (base instanceof «type.name»«IMPL») {
605 «type.name»«IMPL» impl = («type.name»«IMPL») base;
606 if (!impl.«augmentField.name».isEmpty()) {
607 this.«augmentField.name» = new «HashMap.importedName»<>(impl.«augmentField.name»);
609 } else if (base instanceof «AugmentationHolder.importedName») {
610 @SuppressWarnings("unchecked")
611 «AugmentationHolder.importedName»<«type.importedName»> casted =(«AugmentationHolder.importedName»<«type.importedName»>) base;
612 if (!casted.augmentations().isEmpty()) {
613 this.«augmentField.name» = new «HashMap.importedName»<>(casted.augmentations());
621 private def boolean implementsIfc(GeneratedType type, Type impl) {
622 for (Type ifc : type.implements) {
623 if (ifc.equals(impl)) {
630 private def getKey(GeneratedType type) {
631 for (m : type.methodDefinitions) {
632 if (BindingMapping.IDENTIFIABLE_KEY_NAME.equals(m.name)) {
638 private def void removeProperty(Collection<GeneratedProperty> props, String name) {
639 var GeneratedProperty toRemove = null
641 if (p.name.equals(name)) {
645 if (toRemove !== null) {
646 props.remove(toRemove);
651 * Template method which generate getter methods for IMPL class.
653 * @return string with getter methods
655 def private generateGetters(boolean addOverride) '''
656 «IF keyType !== null»
657 «IF addOverride»@Override«ENDIF»
658 public «keyType.importedName» «BindingMapping.IDENTIFIABLE_KEY_NAME»() {
663 «IF !properties.empty»
664 «FOR field : properties SEPARATOR '\n'»
665 «IF addOverride»@Override«ENDIF»
669 «IF augmentField !== null»
671 @SuppressWarnings("unchecked")
672 «IF addOverride»@Override«ENDIF»
673 public <E extends «augmentField.returnType.importedName»> E get«augmentField.name.toFirstUpper»(«Class.importedName»<E> augmentationType) {
674 return (E) «augmentField.name».get(«CodeHelpers.importedName».nonNullValue(augmentationType, "augmentationType"));
680 * Template method which generates the method <code>hashCode()</code>.
682 * @return string with the <code>hashCode()</code> method definition in JAVA format
684 def protected generateHashCode() '''
685 «IF !properties.empty || augmentField !== null»
686 private int hash = 0;
687 private volatile boolean hashValid = false;
690 public int hashCode() {
695 final int prime = 31;
697 «FOR property : properties»
698 «IF property.returnType.name.contains("[")»
699 result = prime * result + «Arrays.importedName».hashCode(«property.fieldName»);
701 result = prime * result + «Objects.importedName».hashCode(«property.fieldName»);
704 «IF augmentField !== null»
705 result = prime * result + «Objects.importedName».hashCode(«augmentField.name»);
716 * Template method which generates the method <code>equals()</code>.
718 * @return string with the <code>equals()</code> method definition in JAVA format
720 def protected generateEquals() '''
721 «IF !properties.empty || augmentField !== null»
723 public boolean equals(«Object.importedName» obj) {
727 if (!(obj instanceof «DataObject.importedName»)) {
730 if (!«type.importedName».class.equals(((«DataObject.importedName»)obj).getImplementedInterface())) {
733 «type.importedName» other = («type.importedName»)obj;
734 «FOR property : properties»
735 «val fieldName = property.fieldName»
736 «IF property.returnType.name.contains("[")»
737 if (!«Arrays.importedName».equals(«fieldName», other.«property.getterMethodName»())) {
739 if (!«Objects.importedName».equals(«fieldName», other.«property.getterMethodName»())) {
744 «IF augmentField !== null»
745 if (getClass() == obj.getClass()) {
746 // Simple case: we are comparing against self
747 «type.name»«IMPL» otherImpl = («type.name»«IMPL») obj;
748 «val fieldName = augmentField.name»
749 if (!«Objects.importedName».equals(«fieldName», otherImpl.«fieldName»)) {
753 // Hard case: compare our augments with presence there...
754 for («Map.importedName».Entry<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> e : «augmentField.name».entrySet()) {
755 if (!e.getValue().equals(other.getAugmentation(e.getKey()))) {
759 // .. and give the other one the chance to do the same
760 if (!obj.equals(this)) {
770 def override generateToString(Collection<GeneratedProperty> properties) '''
771 «IF properties !== null»
773 public «String.importedName» toString() {
774 final «MoreObjects.importedName».ToStringHelper helper = «MoreObjects.importedName».toStringHelper("«type.name»");
775 «FOR property : properties»
776 «CodeHelpers.importedName».appendValue(helper, "«property.fieldName»", «property.fieldName»);
778 «IF augmentField !== null»
779 «CodeHelpers.importedName».appendValue(helper, "«augmentField.name»", «augmentField.name».values());
781 return helper.toString();
786 def implementedInterfaceGetter() '''
788 public «Class.importedName»<«type.importedName»> getImplementedInterface() {
789 return «type.importedName».class;
793 private def createDescription(GeneratedType type) {
795 Class that builds {@link «type.importedName»} instances.
797 @see «type.importedName»
801 override def protected String formatDataForJavaDoc(GeneratedType type) {
802 val typeDescription = createDescription(type)
805 «IF !typeDescription.nullOrEmpty»