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.CodeHelpers
45 import org.opendaylight.yangtools.yang.binding.DataObject
46 import org.opendaylight.yangtools.yang.binding.Identifiable
49 * Template for generating JAVA builder classes.
52 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 the name of the BuilderFor interface
67 val static BUILDERFOR = Builder.simpleName;
70 * Constant with suffix for the classes which are generated from the builder classes.
72 val static IMPL = 'Impl'
75 * Generated property is set if among methods is found one with the name GET_AUGMENTATION_METHOD_NAME
77 var GeneratedProperty augmentField
80 * Set of class attributes (fields) which are derived from the getter methods names
82 val Set<GeneratedProperty> properties
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) {
92 this.properties = propertiesFromMethods(createMethods)
97 * Returns set of method signature instances which contains all the methods of the <code>genType</code>
98 * and all the methods of the implemented interfaces.
100 * @returns set of method signature instances
102 def private Set<MethodSignature> createMethods() {
103 val Set<MethodSignature> methods = new LinkedHashSet();
104 methods.addAll(type.methodDefinitions)
105 collectImplementedMethods(methods, type.implements)
106 val Set<MethodSignature> sortedMethods = ImmutableSortedSet.orderedBy(METHOD_COMPARATOR).addAll(methods).build()
112 * Adds to the <code>methods</code> set all the methods of the <code>implementedIfcs</code>
113 * and recursively their implemented interfaces.
115 * @param methods set of method signatures
116 * @param implementedIfcs list of implemented interfaces
118 def private void collectImplementedMethods(Set<MethodSignature> methods, List<Type> implementedIfcs) {
119 if (implementedIfcs === null || implementedIfcs.empty) {
122 for (implementedIfc : implementedIfcs) {
123 if ((implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))) {
124 val ifc = implementedIfc as GeneratedType
125 methods.addAll(ifc.methodDefinitions)
126 collectImplementedMethods(methods, ifc.implements)
127 } else if (implementedIfc.fullyQualifiedName == Augmentable.name) {
128 for (m : Augmentable.methods) {
129 if (m.name == GET_AUGMENTATION_METHOD_NAME) {
130 val identifier = JavaTypeName.create(m.returnType)
131 val tmpGenTO = new CodegenGeneratedTOBuilder(identifier)
132 val refType = new ReferencedTypeImpl(identifier)
133 val generic = new ReferencedTypeImpl(type.identifier)
134 val parametrizedReturnType = Types.parameterizedTypeFor(refType, generic)
135 tmpGenTO.addMethod(m.name).setReturnType(parametrizedReturnType)
136 augmentField = tmpGenTO.build.methodDefinitions.first.propertyFromGetter
144 * Returns the first element of the list <code>elements</code>.
146 * @param list of elements
148 def private <E> first(List<E> elements) {
153 * Creates set of generated property instances from getter <code>methods</code>.
155 * @param set of method signature instances which should be transformed to list of properties
156 * @return set of generated property instances which represents the getter <code>methods</code>
158 def private propertiesFromMethods(Collection<MethodSignature> methods) {
159 if (methods === null || methods.isEmpty()) {
160 return Collections.emptySet
162 val Set<GeneratedProperty> result = new LinkedHashSet
164 val createdField = m.propertyFromGetter
165 if (createdField !== null) {
166 result.add(createdField)
173 * Creates generated property instance from the getter <code>method</code> name and return type.
175 * @param method method signature from which is the method name and return type obtained
176 * @return generated property instance for the getter <code>method</code>
177 * @throws IllegalArgumentException<ul>
178 * <li>if the <code>method</code> equals <code>null</code></li>
179 * <li>if the name of the <code>method</code> equals <code>null</code></li>
180 * <li>if the name of the <code>method</code> is empty</li>
181 * <li>if the return type of the <code>method</code> equals <code>null</code></li>
184 def private GeneratedProperty propertyFromGetter(MethodSignature method) {
185 if (method === null || method.name === null || method.name.empty || method.returnType === null) {
186 throw new IllegalArgumentException("Method, method name, method return type reference cannot be NULL or empty!")
189 if (Types.BOOLEAN.equals(method.returnType)) {
192 if (method.name.startsWith(prefix)) {
193 val fieldName = method.getName().substring(prefix.length()).toFirstLower
194 val tmpGenTO = new CodegenGeneratedTOBuilder(JavaTypeName.create("foo", "foo"))
195 tmpGenTO.addProperty(fieldName).setReturnType(method.returnType)
196 return tmpGenTO.build.properties.first
200 override isLocalInnerClass(JavaTypeName name) {
201 // Builders do not have inner types
206 * Template method which generates JAVA class body for builder class and for IMPL class.
208 * @return string with JAVA source code
211 «wrapToDocumentation(formatDataForJavaDoc(type))»
212 public class «type.name»«BUILDER» implements «BUILDERFOR»<«type.importedName»> {
214 «generateFields(false)»
216 «constantsDeclarations()»
218 «generateAugmentField(false)»
220 «generateConstructorsFromIfcs(type)»
222 «generateCopyConstructor(false)»
224 «generateMethodFieldsFrom(type)»
226 «generateGetters(false)»
231 public «type.name» build() {
232 return new «type.name»«IMPL»(this);
235 private static final class «type.name»«IMPL» implements «type.name» {
237 «implementedInterfaceGetter»
239 «generateFields(true)»
241 «generateAugmentField(true)»
243 «generateCopyConstructor(true)»
245 «generateGetters(true)»
251 «generateToString(properties)»
258 * Generate default constructor and constructor for every implemented interface from uses statements.
260 def private generateConstructorsFromIfcs(Type type) '''
261 public «type.name»«BUILDER»() {
263 «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»
264 «val ifc = type as GeneratedType»
265 «FOR impl : ifc.implements»
266 «generateConstructorFromIfc(impl)»
272 * Generate constructor with argument of given type.
274 def private Object generateConstructorFromIfc(Type impl) '''
275 «IF (impl instanceof GeneratedType)»
276 «IF !(impl.methodDefinitions.empty)»
277 public «type.name»«BUILDER»(«impl.fullyQualifiedName» arg) {
278 «printConstructorPropertySetter(impl)»
281 «FOR implTypeImplement : impl.implements»
282 «generateConstructorFromIfc(implTypeImplement)»
287 def private Object printConstructorPropertySetter(Type implementedIfc) '''
288 «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
289 «val ifc = implementedIfc as GeneratedType»
290 «FOR getter : ifc.methodDefinitions»
291 this._«getter.propertyNameFromGetter» = arg.«getter.name»();
293 «FOR impl : ifc.implements»
294 «printConstructorPropertySetter(impl)»
300 * Generate 'fieldsFrom' method to set builder properties based on type of given argument.
302 def private generateMethodFieldsFrom(Type type) '''
303 «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»
304 «val ifc = type as GeneratedType»
305 «IF ifc.hasImplementsFromUses»
306 «val List<Type> done = ifc.getBaseIfcs»
307 «generateMethodFieldsFromComment(ifc)»
308 public void fieldsFrom(«DataObject.importedName» arg) {
309 boolean isValidArg = false;
310 «FOR impl : ifc.getAllIfcs»
311 «generateIfCheck(impl, done)»
313 «CodeHelpers.importedName».validValue(isValidArg, arg, "«ifc.getAllIfcs.toListOfNames»");
319 def private generateMethodFieldsFromComment(GeneratedType type) '''
321 * Set fields from given grouping argument. Valid argument is instance of one of following types:
323 «FOR impl : type.getAllIfcs»
324 * <li>«impl.fullyQualifiedName»</li>
328 * @param arg grouping object
329 * @throws IllegalArgumentException if given argument is none of valid types
334 * Method is used to find out if given type implements any interface from uses.
336 def boolean hasImplementsFromUses(GeneratedType type) {
338 for (impl : type.getAllIfcs) {
339 if ((impl instanceof GeneratedType) && !((impl as GeneratedType).methodDefinitions.empty)) {
346 def private generateIfCheck(Type impl, List<Type> done) '''
347 «IF (impl instanceof GeneratedType) && !((impl as GeneratedType).methodDefinitions.empty)»
348 «val implType = impl as GeneratedType»
349 if (arg instanceof «implType.fullyQualifiedName») {
350 «printPropertySetter(implType)»
356 def private printPropertySetter(Type implementedIfc) '''
357 «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
358 «val ifc = implementedIfc as GeneratedType»
359 «FOR getter : ifc.methodDefinitions»
360 this._«getter.propertyNameFromGetter» = ((«implementedIfc.fullyQualifiedName»)arg).«getter.name»();
365 private def List<Type> getBaseIfcs(GeneratedType type) {
366 val List<Type> baseIfcs = new ArrayList();
367 for (ifc : type.implements) {
368 if (ifc instanceof GeneratedType && !(ifc as GeneratedType).methodDefinitions.empty) {
375 private def Set<Type> getAllIfcs(Type type) {
376 val Set<Type> baseIfcs = new HashSet()
377 if (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject)) {
378 val ifc = type as GeneratedType
379 for (impl : ifc.implements) {
380 if (impl instanceof GeneratedType && !(impl as GeneratedType).methodDefinitions.empty) {
383 baseIfcs.addAll(impl.getAllIfcs)
389 private def List<String> toListOfNames(Collection<Type> types) {
390 val List<String> names = new ArrayList
392 names.add(type.fullyQualifiedName)
398 * Template method which generates class attributes.
400 * @param boolean value which specify whether field is|isn't final
401 * @return string with class attributes and their types
403 def private generateFields(boolean _final) '''
404 «IF properties !== null»
406 private«IF _final» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
411 def private generateAugmentField(boolean isPrivate) '''
412 «IF augmentField !== null»
413 «IF isPrivate»private «ENDIF»«Map.importedName»<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> «augmentField.name» = «Collections.importedName».emptyMap();
417 def private constantsDeclarations() '''
418 «FOR c : type.getConstantDefinitions»
419 «IF c.getName.startsWith(TypeConstants.PATTERN_CONSTANT_NAME)»
420 «val cValue = c.value as Map<String, String>»
421 «val String fieldSuffix = c.getName.substring(TypeConstants.PATTERN_CONSTANT_NAME.length)»
422 «IF cValue.size == 1»
423 private static final «Pattern.importedName» «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «Pattern.importedName».compile("«cValue.keySet.get(0).escapeJava»");
424 private static final String «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = "«cValue.values.get(0).escapeJava»";
426 private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «CodeHelpers.importedName».compilePatterns(«ImmutableList.importedName».of(
427 «FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»));
428 private static final String[] «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = { «
429 FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
437 def private generateCheckers(GeneratedProperty field, Restrictions restrictions, Type actualType) '''
438 «IF restrictions.rangeConstraint.present»
439 «AbstractRangeGenerator.forType(actualType).generateRangeChecker(field.name.toFirstUpper,
440 restrictions.rangeConstraint.get, this)»
442 «IF restrictions.lengthConstraint.present»
443 «LengthGenerator.generateLengthChecker(field.fieldName.toString, actualType, restrictions.lengthConstraint.get, this)»
447 def private checkArgument(GeneratedProperty property, Restrictions restrictions, Type actualType) '''
448 «IF restrictions.getRangeConstraint.isPresent»
449 «IF actualType instanceof ConcreteType»
450 «AbstractRangeGenerator.forType(actualType).generateRangeCheckerCall(property.getName.toFirstUpper, "value")»
452 «AbstractRangeGenerator.forType(actualType).generateRangeCheckerCall(property.getName.toFirstUpper, "value.getValue()")»
455 «IF restrictions.getLengthConstraint.isPresent»
456 «IF actualType instanceof ConcreteType»
457 «LengthGenerator.generateLengthCheckerCall(property.fieldName.toString, "value")»
459 «LengthGenerator.generateLengthCheckerCall(property.fieldName.toString, "value.getValue()")»
463 «val fieldUpperCase = property.fieldName.toString.toUpperCase()»
464 «FOR currentConstant : type.getConstantDefinitions»
465 «IF currentConstant.getName.startsWith(TypeConstants.PATTERN_CONSTANT_NAME)
466 && fieldUpperCase.equals(currentConstant.getName.substring(TypeConstants.PATTERN_CONSTANT_NAME.length))»
467 «CodeHelpers.importedName».checkPattern(value, «Constants.MEMBER_PATTERN_LIST»«property.fieldName», «Constants.MEMBER_REGEX_LIST»«property.fieldName»);
472 def private Restrictions restrictionsForSetter(Type actualType) {
473 if (actualType instanceof GeneratedType) {
476 return actualType.restrictions;
479 def private generateListSetter(GeneratedProperty field, Type actualType) '''
480 «val restrictions = restrictionsForSetter(actualType)»
481 «IF restrictions !== null»
482 «generateCheckers(field, restrictions, actualType)»
484 public «type.getName»Builder set«field.getName.toFirstUpper»(final «field.returnType.importedName» values) {
485 «IF restrictions !== null»
486 if (values != null) {
487 for («actualType.getFullyQualifiedName» value : values) {
488 «checkArgument(field, restrictions, actualType)»
492 this.«field.fieldName.toString» = values;
498 def private generateSetter(GeneratedProperty field, Type actualType) '''
499 «val restrictions = restrictionsForSetter(actualType)»
500 «IF restrictions !== null»
501 «generateCheckers(field, restrictions, actualType)»
504 public «type.getName»Builder set«field.getName.toFirstUpper»(final «field.returnType.importedName» value) {
505 «IF restrictions !== null»
507 «checkArgument(field, restrictions, actualType)»
510 this.«field.fieldName.toString» = value;
515 private def Type getActualType(ParameterizedType ptype) {
516 return ptype.getActualTypeArguments.get(0)
520 * Template method which generates setter methods
522 * @return string with the setter methods
524 def private generateSetters() '''
525 «FOR property : properties»
526 «IF property.returnType instanceof ParameterizedType
527 && (property.returnType as ParameterizedType).rawType.equals(Types.LIST_TYPE)»
528 «generateListSetter(property, getActualType(property.returnType as ParameterizedType))»
530 «generateSetter(property, property.returnType)»
534 «IF augmentField !== null»
535 public «type.name»«BUILDER» add«augmentField.name.toFirstUpper»(«Class.importedName»<? extends «augmentField.returnType.importedName»> augmentationType, «augmentField.returnType.importedName» augmentationValue) {
536 if (augmentationValue == null) {
537 return remove«augmentField.name.toFirstUpper»(augmentationType);
540 if (!(this.«augmentField.name» instanceof «HashMap.importedName»)) {
541 this.«augmentField.name» = new «HashMap.importedName»<>();
544 this.«augmentField.name».put(augmentationType, augmentationValue);
548 public «type.name»«BUILDER» remove«augmentField.name.toFirstUpper»(«Class.importedName»<? extends «augmentField.returnType.importedName»> augmentationType) {
549 if (this.«augmentField.name» instanceof «HashMap.importedName») {
550 this.«augmentField.name».remove(augmentationType);
557 def private CharSequence generateCopyConstructor(boolean impl) '''
558 «IF impl»private«ELSE»public«ENDIF» «type.name»«IF impl»«IMPL»«ELSE»«BUILDER»«ENDIF»(«type.name»«IF impl»«BUILDER»«ENDIF» base) {
559 «val allProps = new ArrayList(properties)»
560 «val isList = implementsIfc(type, Types.parameterizedTypeFor(Types.typeForClass(Identifiable), type))»
561 «val keyType = type.getKey»
562 «IF isList && keyType !== null»
563 «val keyProps = new ArrayList((keyType as GeneratedTransferObject).properties)»
564 «Collections.sort(keyProps,
566 return p1.name.compareTo(p2.name)
569 «FOR field : keyProps»
570 «removeProperty(allProps, field.name)»
572 «removeProperty(allProps, "key")»
573 if (base.getKey() == null) {
574 this._key = new «keyType.importedName»(
575 «FOR keyProp : keyProps SEPARATOR ", "»
576 base.«keyProp.getterMethodName»()
579 «FOR field : keyProps»
580 this.«field.fieldName» = base.«field.getterMethodName»();
583 this._key = base.getKey();
584 «FOR field : keyProps»
585 this.«field.fieldName» = _key.«field.getterMethodName»();
589 «FOR field : allProps»
590 this.«field.fieldName» = base.«field.getterMethodName»();
592 «IF augmentField !== null»
594 this.«augmentField.name» = «ImmutableMap.importedName».copyOf(base.«augmentField.name»);
596 if (base instanceof «type.name»«IMPL») {
597 «type.name»«IMPL» impl = («type.name»«IMPL») base;
598 if (!impl.«augmentField.name».isEmpty()) {
599 this.«augmentField.name» = new «HashMap.importedName»<>(impl.«augmentField.name»);
601 } else if (base instanceof «AugmentationHolder.importedName») {
602 @SuppressWarnings("unchecked")
603 «AugmentationHolder.importedName»<«type.importedName»> casted =(«AugmentationHolder.importedName»<«type.importedName»>) base;
604 if (!casted.augmentations().isEmpty()) {
605 this.«augmentField.name» = new «HashMap.importedName»<>(casted.augmentations());
613 private def boolean implementsIfc(GeneratedType type, Type impl) {
614 for (Type ifc : type.implements) {
615 if (ifc.equals(impl)) {
622 private def Type getKey(GeneratedType type) {
623 for (m : type.methodDefinitions) {
624 if ("getKey".equals(m.name)) {
631 private def void removeProperty(Collection<GeneratedProperty> props, String name) {
632 var GeneratedProperty toRemove = null
634 if (p.name.equals(name)) {
638 if (toRemove !== null) {
639 props.remove(toRemove);
644 * Template method which generate getter methods for IMPL class.
646 * @return string with getter methods
648 def private generateGetters(boolean addOverride) '''
649 «IF !properties.empty»
650 «FOR field : properties SEPARATOR '\n'»
651 «IF addOverride»@Override«ENDIF»
655 «IF augmentField !== null»
657 @SuppressWarnings("unchecked")
658 «IF addOverride»@Override«ENDIF»
659 public <E extends «augmentField.returnType.importedName»> E get«augmentField.name.toFirstUpper»(«Class.importedName»<E> augmentationType) {
660 return (E) «augmentField.name».get(«CodeHelpers.importedName».nonNullValue(augmentationType, "augmentationType"));
666 * Template method which generates the method <code>hashCode()</code>.
668 * @return string with the <code>hashCode()</code> method definition in JAVA format
670 def protected generateHashCode() '''
671 «IF !properties.empty || augmentField !== null»
672 private int hash = 0;
673 private volatile boolean hashValid = false;
676 public int hashCode() {
681 final int prime = 31;
683 «FOR property : properties»
684 «IF property.returnType.name.contains("[")»
685 result = prime * result + «Arrays.importedName».hashCode(«property.fieldName»);
687 result = prime * result + «Objects.importedName».hashCode(«property.fieldName»);
690 «IF augmentField !== null»
691 result = prime * result + «Objects.importedName».hashCode(«augmentField.name»);
702 * Template method which generates the method <code>equals()</code>.
704 * @return string with the <code>equals()</code> method definition in JAVA format
706 def protected generateEquals() '''
707 «IF !properties.empty || augmentField !== null»
709 public boolean equals(«Object.importedName» obj) {
713 if (!(obj instanceof «DataObject.importedName»)) {
716 if (!«type.importedName».class.equals(((«DataObject.importedName»)obj).getImplementedInterface())) {
719 «type.importedName» other = («type.importedName»)obj;
720 «FOR property : properties»
721 «val fieldName = property.fieldName»
722 «IF property.returnType.name.contains("[")»
723 if (!«Arrays.importedName».equals(«fieldName», other.«property.getterMethodName»())) {
725 if (!«Objects.importedName».equals(«fieldName», other.«property.getterMethodName»())) {
730 «IF augmentField !== null»
731 if (getClass() == obj.getClass()) {
732 // Simple case: we are comparing against self
733 «type.name»«IMPL» otherImpl = («type.name»«IMPL») obj;
734 «val fieldName = augmentField.name»
735 if (!«Objects.importedName».equals(«fieldName», otherImpl.«fieldName»)) {
739 // Hard case: compare our augments with presence there...
740 for («Map.importedName».Entry<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> e : «augmentField.name».entrySet()) {
741 if (!e.getValue().equals(other.getAugmentation(e.getKey()))) {
745 // .. and give the other one the chance to do the same
746 if (!obj.equals(this)) {
756 def override generateToString(Collection<GeneratedProperty> properties) '''
757 «IF properties !== null»
759 public «String.importedName» toString() {
760 final «MoreObjects.importedName».ToStringHelper helper = «MoreObjects.importedName».toStringHelper("«type.name»");
761 «FOR property : properties»
762 «CodeHelpers.importedName».appendValue(helper, "«property.fieldName»", «property.fieldName»);
764 «IF augmentField !== null»
765 «CodeHelpers.importedName».appendValue(helper, "«augmentField.name»", «augmentField.name».values());
767 return helper.toString();
772 def implementedInterfaceGetter() '''
774 public «Class.importedName»<«type.importedName»> getImplementedInterface() {
775 return «type.importedName».class;
779 private def createDescription(GeneratedType type) {
781 Class that builds {@link «type.importedName»} instances.
783 @see «type.importedName»
787 override def protected String formatDataForJavaDoc(GeneratedType type) {
788 val typeDescription = createDescription(type)
791 «IF !typeDescription.nullOrEmpty»