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 java.util.Objects.requireNonNull
11 import static org.opendaylight.mdsal.binding.model.util.Types.BOOLEAN;
12 import static org.opendaylight.mdsal.binding.model.util.Types.BYTE_ARRAY;
13 import static org.opendaylight.mdsal.binding.model.util.Types.STRING;
14 import static extension org.apache.commons.text.StringEscapeUtils.escapeJava
16 import com.google.common.base.Preconditions
17 import com.google.common.collect.ImmutableList
18 import com.google.common.collect.Lists
19 import java.beans.ConstructorProperties
20 import java.util.ArrayList
21 import java.util.Base64;
22 import java.util.Comparator
25 import javax.management.ConstructorParameters
26 import org.gaul.modernizer_maven_annotations.SuppressModernizer
27 import org.opendaylight.mdsal.binding.model.api.ConcreteType
28 import org.opendaylight.mdsal.binding.model.api.Constant
29 import org.opendaylight.mdsal.binding.model.api.Enumeration
30 import org.opendaylight.mdsal.binding.model.api.GeneratedProperty
31 import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject
32 import org.opendaylight.mdsal.binding.model.api.Restrictions
33 import org.opendaylight.mdsal.binding.model.api.Type
34 import org.opendaylight.mdsal.binding.model.util.TypeConstants
35 import org.opendaylight.yangtools.yang.common.Empty
36 import org.opendaylight.yangtools.yang.common.Uint16
37 import org.opendaylight.yangtools.yang.common.Uint32
38 import org.opendaylight.yangtools.yang.common.Uint64
39 import org.opendaylight.yangtools.yang.common.Uint8
40 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition
43 * Template for generating JAVA class.
46 class ClassTemplate extends BaseTemplate {
47 static val Comparator<GeneratedProperty> PROP_COMPARATOR = Comparator.comparing([prop | prop.name])
49 protected val List<GeneratedProperty> properties
50 protected val List<GeneratedProperty> finalProperties
51 protected val List<GeneratedProperty> parentProperties
52 protected val List<GeneratedProperty> allProperties
53 protected val Restrictions restrictions
56 * List of enumeration which are generated as JAVA enum type.
58 protected val List<Enumeration> enums
61 * List of constant instances which are generated as JAVA public static final attributes.
63 protected val List<Constant> consts
65 protected val GeneratedTransferObject genTO
67 val AbstractRangeGenerator<?> rangeGenerator
70 * Creates instance of this class with concrete <code>genType</code>.
72 * @param genType generated transfer object which will be transformed to JAVA class source code
74 new(GeneratedTransferObject genType) {
75 this(new TopLevelJavaGeneratedType(genType), genType)
79 * Creates instance of this class with concrete <code>genType</code>.
81 * @param genType generated transfer object which will be transformed to JAVA class source code
83 new(AbstractJavaGeneratedType javaType, GeneratedTransferObject genType) {
84 super(javaType, genType)
86 this.properties = genType.properties
87 this.finalProperties = GeneratorUtil.resolveReadOnlyPropertiesFromTO(genTO.properties)
88 this.parentProperties = GeneratorUtil.getPropertiesOfAllParents(genTO)
89 this.restrictions = genType.restrictions
91 val sorted = new ArrayList();
92 sorted.addAll(properties);
93 sorted.addAll(parentProperties);
94 sorted.sort(PROP_COMPARATOR);
96 this.allProperties = sorted
97 this.enums = genType.enumerations
98 this.consts = genType.constantDefinitions
100 if (restrictions !== null && restrictions.rangeConstraint.present) {
101 rangeGenerator = requireNonNull(AbstractRangeGenerator.forType(findProperty(genType, "value").returnType))
103 rangeGenerator = null
108 * Generates JAVA class source code (class body only).
110 * @return string with JAVA class body source code
112 def CharSequence generateAsInnerClass() {
113 return generateBody(true)
116 override protected body() {
121 * Template method which generates class body.
123 * @param isInnerClass boolean value which specify if generated class is|isn't inner
124 * @return string with class source code in JAVA format
126 def protected generateBody(boolean isInnerClass) '''
127 «wrapToDocumentation(formatDataForJavaDoc(type))»
128 «annotationDeclaration»
129 «generateClassDeclaration(isInnerClass)» {
131 «innerClassesDeclarations»
133 «constantsDeclarations»
136 «IF restrictions !== null»
137 «IF restrictions.lengthConstraint.present»
138 «LengthGenerator.generateLengthChecker("_value", findProperty(genTO, "value").returnType, restrictions.lengthConstraint.get, this)»
140 «IF restrictions.rangeConstraint.present»
141 «rangeGenerator.generateRangeChecker("_value", restrictions.rangeConstraint.get, this)»
149 «FOR field : properties SEPARATOR "\n"»
156 «IF (genTO.isTypedef() && genTO.getBaseType instanceof BitsTypeDefinition)»
157 «generateGetValueForBitsTypeDef»
164 «generateToString(genTO.toStringIdentifiers)»
170 * Template method which generates the method <code>getValue()</code> for typedef,
171 * which base type is BitsDefinition.
173 * @return string with the <code>getValue()</code> method definition in JAVA format
175 def protected generateGetValueForBitsTypeDef() '''
177 public boolean[] getValue() {
178 return new boolean[]{
179 «FOR property: genTO.properties SEPARATOR ','»
187 * Template method which generates inner classes inside this interface.
189 * @return string with the source code for inner classes in JAVA format
191 def protected innerClassesDeclarations() '''
192 «IF !type.enclosedTypes.empty»
193 «FOR innerClass : type.enclosedTypes SEPARATOR "\n"»
194 «generateInnerClass(innerClass)»
199 def protected constructors() '''
201 «genUnionConstructor»
202 «ELSEIF genTO.typedef && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
206 «allValuesConstructor»
210 «IF !allProperties.empty»
213 «IF properties.empty && !parentProperties.empty »
218 def private allValuesConstructor() '''
219 public «type.name»(«allProperties.asArgumentsDeclaration») {
220 «IF false == parentProperties.empty»
221 super(«parentProperties.asArguments»);
223 «FOR p : allProperties»
224 «generateRestrictions(type, p.fieldName, p.returnType)»
228 «val fieldName = p.fieldName»
229 «IF p.returnType.name.endsWith("[]")»
230 this.«fieldName» = «fieldName» == null ? null : «fieldName».clone();
232 this.«fieldName» = «fieldName»;
238 def private typedefConstructor() '''
239 @«ConstructorParameters.importedName»("value")
240 @«ConstructorProperties.importedName»("value")
241 public «type.name»(«allProperties.asArgumentsDeclaration») {
242 «IF false == parentProperties.empty»
243 super(«parentProperties.asArguments»);
245 «FOR p : allProperties»
246 «generateRestrictions(type, p.fieldName, p.returnType)»
249 * If we have patterns, we need to apply them to the value field. This is a sad consequence of how this code is
252 «CODEHELPERS.importedName».requireValue(_value);
253 «genPatternEnforcer("_value")»
256 «val fieldName = p.fieldName»
257 «IF p.returnType.name.endsWith("[]")»
258 this.«fieldName» = «fieldName».clone();
260 this.«fieldName» = «fieldName»;
266 def private legacyConstructor() {
267 if (!hasUintProperties) {
271 val compatUint = CODEHELPERS.importedName + ".compatUint("
275 * Utility migration constructor.
277 «FOR prop : allProperties»
278 * @param «prop.fieldName» «prop.name»«IF prop.isUintType» in legacy Java type«ENDIF»
280 * @deprecated Use {#link «type.name»(«FOR prop : allProperties SEPARATOR ", "»«prop.returnType.importedJavadocName»«ENDFOR»)} instead.
282 @Deprecated(forRemoval = true)
283 public «type.getName»(«FOR prop : allProperties SEPARATOR ", "»«prop.legacyType.importedName» «prop.fieldName»«ENDFOR») {
284 this(«FOR prop : allProperties SEPARATOR ", "»«IF prop.isUintType»«compatUint»«prop.fieldName»)«ELSE»«prop.fieldName»«ENDIF»«ENDFOR»);
289 def protected genUnionConstructor() '''
290 «FOR p : allProperties»
291 «val List<GeneratedProperty> other = new ArrayList(properties)»
293 «genConstructor(p, other)»
298 def protected genConstructor(GeneratedProperty property, Iterable<GeneratedProperty> other) '''
299 public «type.name»(«property.returnType.importedName + " " + property.name») {
300 «IF false == parentProperties.empty»
301 super(«parentProperties.asArguments»);
304 «val fieldName = property.fieldName»
305 «generateRestrictions(type, fieldName, property.returnType)»
307 this.«fieldName» = «property.name»;
309 this.«p.fieldName» = null;
314 def private genPatternEnforcer(String ref) '''
316 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
317 «CODEHELPERS.importedName».checkPattern(«ref», «Constants.MEMBER_PATTERN_LIST», «Constants.MEMBER_REGEX_LIST»);
322 def private static paramValue(Type returnType, String paramName) {
323 if (returnType instanceof ConcreteType) {
326 return paramName + ".getValue()"
330 def private generateRestrictions(Type type, String paramName, Type returnType) '''
331 «val restrictions = type.restrictions»
332 «IF restrictions !== null»
333 «IF restrictions.lengthConstraint.present || restrictions.rangeConstraint.present»
334 if («paramName» != null) {
335 «IF restrictions.lengthConstraint.present»
336 «LengthGenerator.generateLengthCheckerCall(paramName, paramValue(returnType, paramName))»
338 «IF restrictions.rangeConstraint.present»
339 «rangeGenerator.generateRangeCheckerCall(paramName, paramValue(returnType, paramName))»
346 def protected copyConstructor() '''
348 * Creates a copy from Source Object.
350 * @param source Source object
352 public «type.name»(«type.name» source) {
353 «IF false == parentProperties.empty»
357 «val fieldName = p.fieldName»
358 this.«fieldName» = source.«fieldName»;
363 def protected parentConstructor() '''
365 * Creates a new instance from «genTO.superType.importedName»
367 * @param source Source object
369 public «type.name»(«genTO.superType.importedName» source) {
371 «genPatternEnforcer("getValue()")»
375 def protected defaultInstance() '''
376 «IF genTO.typedef && !allProperties.empty && !genTO.unionType»
377 «val prop = allProperties.get(0)»
378 «IF !("org.opendaylight.yangtools.yang.binding.InstanceIdentifier".equals(prop.returnType.fullyQualifiedName))»
379 public static «genTO.name» getDefaultInstance(String defaultValue) {
380 «IF BYTE_ARRAY.equals(prop.returnType)»
381 return new «genTO.name»(«Base64.importedName».getDecoder().decode(defaultValue));
382 «ELSEIF STRING.equals(prop.returnType)»
383 return new «genTO.name»(defaultValue);
384 «ELSEIF Constants.EMPTY.equals(prop.returnType)»
385 «Preconditions.importedName».checkArgument(defaultValue.isEmpty(), "Invalid value %s", defaultValue);
386 return new «genTO.name»(«Empty.importedName».getInstance());
387 «ELSEIF allProperties.size > 1»
389 «ELSEIF BOOLEAN.equals(prop.returnType)»
390 return new «genTO.name»(«Boolean.importedName».valueOf(defaultValue));
391 «ELSEIF "java.lang.Byte".equals(prop.returnType.fullyQualifiedName)»
392 return new «genTO.name»(«Byte.importedName».valueOf(defaultValue));
393 «ELSEIF "java.lang.Short".equals(prop.returnType.fullyQualifiedName)»
394 return new «genTO.name»(«Short.importedName».valueOf(defaultValue));
395 «ELSEIF "java.lang.Integer".equals(prop.returnType.fullyQualifiedName)»
396 return new «genTO.name»(«Integer.importedName».valueOf(defaultValue));
397 «ELSEIF "java.lang.Long".equals(prop.returnType.fullyQualifiedName)»
398 return new «genTO.name»(«Long.importedName».valueOf(defaultValue));
399 «ELSEIF "org.opendaylight.yangtools.yang.common.Uint8".equals(prop.returnType.fullyQualifiedName)»
400 return new «genTO.name»(«Uint8.importedName».valueOf(defaultValue));
401 «ELSEIF "org.opendaylight.yangtools.yang.common.Uint16".equals(prop.returnType.fullyQualifiedName)»
402 return new «genTO.name»(«Uint16.importedName».valueOf(defaultValue));
403 «ELSEIF "org.opendaylight.yangtools.yang.common.Uint32".equals(prop.returnType.fullyQualifiedName)»
404 return new «genTO.name»(«Uint32.importedName».valueOf(defaultValue));
405 «ELSEIF "org.opendaylight.yangtools.yang.common.Uint64".equals(prop.returnType.fullyQualifiedName)»
406 return new «genTO.name»(«Uint64.importedName».valueOf(defaultValue));
408 return new «genTO.name»(new «prop.returnType.importedName»(defaultValue));
415 def protected bitsArgs() '''
416 «JU_LIST.importedName»<«STRING.importedName»> properties = «Lists.importedName».newArrayList(«allProperties.propsAsArgs»);
417 if (!properties.contains(defaultValue)) {
418 throw new «IllegalArgumentException.importedName»("invalid default parameter");
421 return new «genTO.name»(
422 «FOR prop : allProperties SEPARATOR ","»
423 properties.get(i++).equals(defaultValue) ? «Boolean.importedName».TRUE : null
428 def protected propsAsArgs(Iterable<GeneratedProperty> properties) '''
429 «FOR prop : properties SEPARATOR ","»
435 * Template method which generates JAVA class declaration.
437 * @param isInnerClass boolean value which specify if generated class is|isn't inner
438 * @return string with class declaration in JAVA format
440 def protected generateClassDeclaration(boolean isInnerClass) '''
444 ELSEIF (type.abstract)»«
448 ENDIF»class «type.name»«
449 IF (genTO.superType !== null)»«
450 " extends "»«genTO.superType.importedName»«
452 «IF (!type.implements.empty)»«
454 FOR type : type.implements SEPARATOR ", "»«
461 * Template method which generates JAVA enum type.
463 * @return string with inner enum source code in JAVA format
465 def protected enumDeclarations() '''
467 «FOR e : enums SEPARATOR "\n"»
468 «new EnumTemplate(javaType.getEnclosedType(e.identifier), e).generateAsInnerClass»
473 def protected suidDeclaration() '''
474 «IF genTO.SUID !== null»
475 private static final long serialVersionUID = «genTO.SUID.value»L;
479 def protected annotationDeclaration() '''
480 «IF genTO.getAnnotations !== null»
481 «FOR e : genTO.getAnnotations»
488 * Template method which generates JAVA constants.
490 * @return string with constants in JAVA format
492 def protected constantsDeclarations() '''
495 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
496 «val cValue = c.value as Map<String, String>»
497 «val jurPatternRef = JUR_PATTERN.importedName»
498 public static final «JU_LIST.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
499 FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»);
500 «IF cValue.size == 1»
501 private static final «jurPatternRef» «Constants.MEMBER_PATTERN_LIST» = «jurPatternRef».compile(«TypeConstants.PATTERN_CONSTANT_NAME».get(0));
502 private static final String «Constants.MEMBER_REGEX_LIST» = "«cValue.values.iterator.next.escapeJava»";
504 private static final «jurPatternRef»[] «Constants.MEMBER_PATTERN_LIST» = «CODEHELPERS.importedName».compilePatterns(«TypeConstants.PATTERN_CONSTANT_NAME»);
505 private static final String[] «Constants.MEMBER_REGEX_LIST» = { «
506 FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
516 * Template method which generates JAVA class attributes.
518 * @return string with the class attributes in JAVA format
520 def protected generateFields() '''
521 «IF !properties.empty»
523 private«IF isReadOnly(f)» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
528 protected def isReadOnly(GeneratedProperty field) {
529 return field.readOnly
533 * Template method which generates the method <code>hashCode()</code>.
535 * @return string with the <code>hashCode()</code> method definition in JAVA format
537 def protected generateHashCode() {
538 val size = genTO.hashCodeIdentifiers.size
543 @«OVERRIDE.importedName»
544 public int hashCode() {
546 «hashCodeResult(genTO.hashCodeIdentifiers)»
549 return «CODEHELPERS.importedName».wrapperHashCode(«genTO.hashCodeIdentifiers.get(0).fieldName»);
556 * Template method which generates the method <code>equals()</code>.
558 * @return string with the <code>equals()</code> method definition in JAVA format
560 def private generateEquals() '''
561 «IF !genTO.equalsIdentifiers.empty»
562 @«OVERRIDE.importedName»
563 public final boolean equals(java.lang.Object obj) {
567 if (!(obj instanceof «type.name»)) {
570 final «type.name» other = («type.name») obj;
571 «FOR property : genTO.equalsIdentifiers»
572 «val fieldName = property.fieldName»
573 if (!«property.importedUtilClass».equals(«fieldName», other.«fieldName»)) {
582 def GeneratedProperty getPropByName(String name) {
583 for (GeneratedProperty prop : allProperties) {
584 if (prop.name.equals(name)) {
591 def private hasUintProperties() {
592 for (GeneratedProperty prop : allProperties) {
593 if (prop.isUintType) {
600 def private static isUintType(GeneratedProperty prop) {
601 UINT_TYPES.containsKey(prop.returnType)
604 def private static legacyType(GeneratedProperty prop) {
605 val type = prop.returnType
606 val uint = UINT_TYPES.get(type)