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.BaseYangTypes.BINARY_TYPE
12 import static org.opendaylight.mdsal.binding.model.util.BaseYangTypes.BOOLEAN_TYPE
13 import static org.opendaylight.mdsal.binding.model.util.BaseYangTypes.EMPTY_TYPE
14 import static org.opendaylight.mdsal.binding.model.util.BaseYangTypes.INSTANCE_IDENTIFIER
15 import static org.opendaylight.mdsal.binding.model.util.BaseYangTypes.INT16_TYPE
16 import static org.opendaylight.mdsal.binding.model.util.BaseYangTypes.INT32_TYPE
17 import static org.opendaylight.mdsal.binding.model.util.BaseYangTypes.INT64_TYPE
18 import static org.opendaylight.mdsal.binding.model.util.BaseYangTypes.INT8_TYPE
19 import static org.opendaylight.mdsal.binding.model.util.BaseYangTypes.STRING_TYPE
20 import static org.opendaylight.mdsal.binding.model.util.BaseYangTypes.UINT16_TYPE
21 import static org.opendaylight.mdsal.binding.model.util.BaseYangTypes.UINT32_TYPE
22 import static org.opendaylight.mdsal.binding.model.util.BaseYangTypes.UINT64_TYPE
23 import static org.opendaylight.mdsal.binding.model.util.BaseYangTypes.UINT8_TYPE
24 import static org.opendaylight.mdsal.binding.model.util.BindingTypes.SCALAR_TYPE_OBJECT
25 import static org.opendaylight.mdsal.binding.model.util.Types.BOOLEAN
26 import static org.opendaylight.mdsal.binding.model.util.Types.STRING;
27 import static extension org.apache.commons.text.StringEscapeUtils.escapeJava
29 import com.google.common.base.MoreObjects
30 import com.google.common.base.Preconditions
31 import com.google.common.collect.ImmutableList
32 import com.google.common.collect.Lists
33 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
34 import java.beans.ConstructorProperties
35 import java.util.ArrayList
36 import java.util.Base64;
37 import java.util.Collection
38 import java.util.Comparator
42 import javax.management.ConstructorParameters
43 import org.gaul.modernizer_maven_annotations.SuppressModernizer
44 import org.opendaylight.mdsal.binding.model.api.ConcreteType
45 import org.opendaylight.mdsal.binding.model.api.Constant
46 import org.opendaylight.mdsal.binding.model.api.Enumeration
47 import org.opendaylight.mdsal.binding.model.api.GeneratedProperty
48 import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject
49 import org.opendaylight.mdsal.binding.model.api.Restrictions
50 import org.opendaylight.mdsal.binding.model.api.Type
51 import org.opendaylight.mdsal.binding.model.util.TypeConstants
52 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping
53 import org.opendaylight.yangtools.yang.common.Empty
54 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition
57 * Template for generating JAVA class.
60 class ClassTemplate extends BaseTemplate {
61 static val Comparator<GeneratedProperty> PROP_COMPARATOR = Comparator.comparing([prop | prop.name])
62 static val VALUEOF_TYPES = Set.of(
73 protected val List<GeneratedProperty> properties
74 protected val List<GeneratedProperty> finalProperties
75 protected val List<GeneratedProperty> parentProperties
76 protected val List<GeneratedProperty> allProperties
77 protected val Restrictions restrictions
80 * List of enumeration which are generated as JAVA enum type.
82 protected val List<Enumeration> enums
85 * List of constant instances which are generated as JAVA public static final attributes.
87 protected val List<Constant> consts
89 protected val GeneratedTransferObject genTO
91 val AbstractRangeGenerator<?> rangeGenerator
94 * Creates instance of this class with concrete <code>genType</code>.
96 * @param genType generated transfer object which will be transformed to JAVA class source code
98 new(GeneratedTransferObject genType) {
99 this(new TopLevelJavaGeneratedType(genType), genType)
103 * Creates instance of this class with concrete <code>genType</code>.
105 * @param genType generated transfer object which will be transformed to JAVA class source code
107 new(AbstractJavaGeneratedType javaType, GeneratedTransferObject genType) {
108 super(javaType, genType)
110 this.properties = genType.properties
111 this.finalProperties = GeneratorUtil.resolveReadOnlyPropertiesFromTO(genTO.properties)
112 this.parentProperties = GeneratorUtil.getPropertiesOfAllParents(genTO)
113 this.restrictions = genType.restrictions
115 val sorted = new ArrayList();
116 sorted.addAll(properties);
117 sorted.addAll(parentProperties);
118 sorted.sort(PROP_COMPARATOR);
120 this.allProperties = sorted
121 this.enums = genType.enumerations
122 this.consts = genType.constantDefinitions
124 if (restrictions !== null && restrictions.rangeConstraint.present) {
125 rangeGenerator = requireNonNull(AbstractRangeGenerator.forType(TypeUtils.encapsulatedValueType(genType)))
127 rangeGenerator = null
132 * Generates JAVA class source code (class body only).
134 * @return string with JAVA class body source code
136 def CharSequence generateAsInnerClass() {
137 return generateBody(true)
140 override protected body() {
145 * Template method which generates class body.
147 * @param isInnerClass boolean value which specify if generated class is|isn't inner
148 * @return string with class source code in JAVA format
150 def protected generateBody(boolean isInnerClass) '''
151 «wrapToDocumentation(formatDataForJavaDoc(type))»
152 «annotationDeclaration»
153 «generateClassDeclaration(isInnerClass)» {
155 «innerClassesDeclarations»
157 «constantsDeclarations»
160 «IF restrictions !== null»
161 «IF restrictions.lengthConstraint.present»
162 «LengthGenerator.generateLengthChecker("_value", TypeUtils.encapsulatedValueType(genTO),
163 restrictions.lengthConstraint.get, this)»
165 «IF restrictions.rangeConstraint.present»
166 «rangeGenerator.generateRangeChecker("_value", restrictions.rangeConstraint.get, this)»
176 «IF (genTO.isTypedef() && genTO.getBaseType instanceof BitsTypeDefinition)»
177 «generateGetValueForBitsTypeDef»
184 «generateToString(genTO.toStringIdentifiers)»
189 def private propertyMethods() {
190 if (properties.empty) {
193 isScalarTypeObject ? scalarTypeObjectValue(properties.get(0)) : defaultProperties
196 def private isScalarTypeObject() {
197 for (impl : genTO.implements) {
198 if (SCALAR_TYPE_OBJECT.identifier.equals(impl.identifier)) {
205 def private defaultProperties() '''
206 «FOR field : properties SEPARATOR "\n"»
214 def private scalarTypeObjectValue(GeneratedProperty field) '''
215 @«OVERRIDE.importedName»
216 public «field.returnType.importedName» «BindingMapping.SCALAR_TYPE_OBJECT_GET_VALUE_NAME»() {
217 return «field.fieldName»«IF field.returnType.name.endsWith("[]")».clone()«ENDIF»;
222 * Template method which generates the method <code>getValue()</code> for typedef,
223 * which base type is BitsDefinition.
225 * @return string with the <code>getValue()</code> method definition in JAVA format
227 def protected generateGetValueForBitsTypeDef() '''
229 public boolean[] getValue() {
230 return new boolean[]{
231 «FOR property: genTO.properties SEPARATOR ','»
239 * Template method which generates inner classes inside this interface.
241 * @return string with the source code for inner classes in JAVA format
243 def protected innerClassesDeclarations() '''
244 «IF !type.enclosedTypes.empty»
245 «FOR innerClass : type.enclosedTypes SEPARATOR "\n"»
246 «generateInnerClass(innerClass)»
251 def protected constructors() '''
253 «genUnionConstructor»
254 «ELSEIF genTO.typedef && allProperties.size == 1 && allProperties.get(0).name.equals(TypeConstants.VALUE_PROP)»
258 «allValuesConstructor»
262 «IF !allProperties.empty»
265 «IF properties.empty && !parentProperties.empty »
270 def allValuesConstructor() '''
271 public «type.name»(«allProperties.asArgumentsDeclaration») {
272 «IF !parentProperties.empty»
273 super(«parentProperties.asArguments»);
275 «FOR p : allProperties»
276 «generateRestrictions(type, p.fieldName, p.returnType)»
280 «val fieldName = p.fieldName»
281 «IF p.returnType.name.endsWith("[]")»
282 this.«fieldName» = «fieldName» == null ? null : «fieldName».clone();
284 this.«fieldName» = «fieldName»;
290 def private typedefConstructor() '''
291 @«ConstructorParameters.importedName»("«TypeConstants.VALUE_PROP»")
292 @«ConstructorProperties.importedName»("«TypeConstants.VALUE_PROP»")
293 public «type.name»(«allProperties.asArgumentsDeclaration») {
294 «IF !parentProperties.empty»
295 super(«parentProperties.asArguments»);
297 «FOR p : allProperties»
298 «generateRestrictions(type, p.fieldName, p.returnType)»
301 * If we have patterns, we need to apply them to the value field. This is a sad consequence of how this code is
304 «CODEHELPERS.importedName».requireValue(_value);
305 «genPatternEnforcer("_value")»
308 «val fieldName = p.fieldName»
309 «IF p.returnType.name.endsWith("[]")»
310 this.«fieldName» = «fieldName».clone();
312 this.«fieldName» = «fieldName»;
318 def private legacyConstructor() {
319 if (!hasUintProperties) {
323 val compatUint = CODEHELPERS.importedName + ".compatUint("
327 * Utility migration constructor.
329 «FOR prop : allProperties»
330 * @param «prop.fieldName» «prop.name»«IF prop.isUintType» in legacy Java type«ENDIF»
332 * @deprecated Use {#link «type.name»(«FOR prop : allProperties SEPARATOR ", "»«prop.returnType.importedJavadocName»«ENDFOR»)} instead.
334 @Deprecated(forRemoval = true)
335 public «type.getName»(«FOR prop : allProperties SEPARATOR ", "»«prop.legacyType.importedName» «prop.fieldName»«ENDFOR») {
336 this(«FOR prop : allProperties SEPARATOR ", "»«IF prop.isUintType»«compatUint»«prop.fieldName»)«ELSE»«prop.fieldName»«ENDIF»«ENDFOR»);
341 def protected genUnionConstructor() '''
342 «FOR p : allProperties»
343 «val List<GeneratedProperty> other = new ArrayList(properties)»
345 «genConstructor(p, other)»
350 def protected genConstructor(GeneratedProperty property, Iterable<GeneratedProperty> other) '''
351 public «type.name»(«property.returnType.importedName + " " + property.name») {
352 «IF !parentProperties.empty»
353 super(«parentProperties.asArguments»);
356 «val fieldName = property.fieldName»
357 «generateRestrictions(type, fieldName, property.returnType)»
359 this.«fieldName» = «property.name»;
361 this.«p.fieldName» = null;
366 def private genPatternEnforcer(String ref) '''
368 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
369 «CODEHELPERS.importedName».checkPattern(«ref», «Constants.MEMBER_PATTERN_LIST», «Constants.MEMBER_REGEX_LIST»);
374 def private static paramValue(Type returnType, String paramName) {
375 if (returnType instanceof ConcreteType) {
378 return paramName + ".getValue()"
382 def generateRestrictions(Type type, String paramName, Type returnType) '''
383 «val restrictions = type.restrictions»
384 «IF restrictions !== null»
385 «IF restrictions.lengthConstraint.present || restrictions.rangeConstraint.present»
386 if («paramName» != null) {
387 «IF restrictions.lengthConstraint.present»
388 «LengthGenerator.generateLengthCheckerCall(paramName, paramValue(returnType, paramName))»
390 «IF restrictions.rangeConstraint.present»
391 «rangeGenerator.generateRangeCheckerCall(paramName, paramValue(returnType, paramName))»
398 def protected copyConstructor() '''
400 * Creates a copy from Source Object.
402 * @param source Source object
404 public «type.name»(«type.name» source) {
405 «IF !parentProperties.empty»
409 «val fieldName = p.fieldName»
410 this.«fieldName» = source.«fieldName»;
415 def protected parentConstructor() '''
417 * Creates a new instance from «genTO.superType.importedName»
419 * @param source Source object
421 public «type.name»(«genTO.superType.importedName» source) {
423 «genPatternEnforcer("getValue()")»
427 def protected defaultInstance() '''
428 «IF genTO.typedef && !allProperties.empty && !genTO.unionType»
429 «val prop = allProperties.get(0)»
430 «val propType = prop.returnType»
431 «IF !(INSTANCE_IDENTIFIER.identifier.equals(propType.identifier))»
432 public static «genTO.name» getDefaultInstance(final String defaultValue) {
433 «IF allProperties.size > 1»
435 «ELSEIF VALUEOF_TYPES.contains(propType)»
436 return new «genTO.name»(«propType.importedName».valueOf(defaultValue));
437 «ELSEIF STRING_TYPE.equals(propType)»
438 return new «genTO.name»(defaultValue);
439 «ELSEIF BINARY_TYPE.equals(propType)»
440 return new «genTO.name»(«Base64.importedName».getDecoder().decode(defaultValue));
441 «ELSEIF EMPTY_TYPE.equals(propType)»
442 «Preconditions.importedName».checkArgument(defaultValue.isEmpty(), "Invalid value %s", defaultValue);
443 return new «genTO.name»(«Empty.importedName».getInstance());
445 return new «genTO.name»(new «propType.importedName»(defaultValue));
452 @SuppressFBWarnings(value = "DLS_DEAD_LOCAL_STORE", justification = "FOR with SEPARATOR, not needing for value")
453 def protected bitsArgs() '''
454 «JU_LIST.importedName»<«STRING.importedName»> properties = «Lists.importedName».newArrayList(«allProperties.propsAsArgs»);
455 if (!properties.contains(defaultValue)) {
456 throw new «IllegalArgumentException.importedName»("invalid default parameter");
459 return new «genTO.name»(
460 «FOR prop : allProperties SEPARATOR ","»
461 properties.get(i++).equals(defaultValue) ? «BOOLEAN.importedName».TRUE : null
466 def protected propsAsArgs(Iterable<GeneratedProperty> properties) '''
467 «FOR prop : properties SEPARATOR ","»
473 * Template method which generates JAVA class declaration.
475 * @param isInnerClass boolean value which specify if generated class is|isn't inner
476 * @return string with class declaration in JAVA format
478 def protected generateClassDeclaration(boolean isInnerClass) '''
482 ELSEIF (type.abstract)»«
486 ENDIF»class «type.name»«
487 IF (genTO.superType !== null)»«
488 " extends "»«genTO.superType.importedName»«
490 «IF (!type.implements.empty)»«
492 FOR type : type.implements SEPARATOR ", "»«
499 * Template method which generates JAVA enum type.
501 * @return string with inner enum source code in JAVA format
503 def protected enumDeclarations() '''
505 «FOR e : enums SEPARATOR "\n"»
506 «new EnumTemplate(javaType.getEnclosedType(e.identifier), e).generateAsInnerClass»
511 def protected suidDeclaration() '''
512 «IF genTO.SUID !== null»
513 private static final long serialVersionUID = «genTO.SUID.value»L;
517 def protected annotationDeclaration() '''
518 «IF genTO.getAnnotations !== null»
519 «FOR e : genTO.getAnnotations»
526 * Template method which generates JAVA constants.
528 * @return string with constants in JAVA format
530 def protected constantsDeclarations() '''
533 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
534 «val cValue = c.value as Map<String, String>»
535 «val jurPatternRef = JUR_PATTERN.importedName»
536 public static final «JU_LIST.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
537 FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»);
538 «IF cValue.size == 1»
539 private static final «jurPatternRef» «Constants.MEMBER_PATTERN_LIST» = «jurPatternRef».compile(«TypeConstants.PATTERN_CONSTANT_NAME».get(0));
540 private static final String «Constants.MEMBER_REGEX_LIST» = "«cValue.values.iterator.next.escapeJava»";
542 private static final «jurPatternRef»[] «Constants.MEMBER_PATTERN_LIST» = «CODEHELPERS.importedName».compilePatterns(«TypeConstants.PATTERN_CONSTANT_NAME»);
543 private static final String[] «Constants.MEMBER_REGEX_LIST» = { «
544 FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
554 * Template method which generates JAVA class attributes.
556 * @return string with the class attributes in JAVA format
558 def protected generateFields() '''
559 «IF !properties.empty»
561 private«IF isReadOnly(f)» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
566 protected def isReadOnly(GeneratedProperty field) {
567 return field.readOnly
571 * Template method which generates the method <code>hashCode()</code>.
573 * @return string with the <code>hashCode()</code> method definition in JAVA format
575 def protected generateHashCode() {
576 val size = genTO.hashCodeIdentifiers.size
581 @«OVERRIDE.importedName»
582 public int hashCode() {
584 final int prime = 31;
586 «FOR property : genTO.hashCodeIdentifiers»
587 result = prime * result + «property.importedUtilClass».hashCode(«property.fieldName»);
591 return «CODEHELPERS.importedName».wrapperHashCode(«genTO.hashCodeIdentifiers.get(0).fieldName»);
598 * Template method which generates the method <code>equals()</code>.
600 * @return string with the <code>equals()</code> method definition in JAVA format
602 def private generateEquals() '''
603 «IF !genTO.equalsIdentifiers.empty»
604 @«OVERRIDE.importedName»
605 public final boolean equals(java.lang.Object obj) {
609 if (!(obj instanceof «type.name»)) {
612 final «type.name» other = («type.name») obj;
613 «FOR property : genTO.equalsIdentifiers»
614 «val fieldName = property.fieldName»
615 if (!«property.importedUtilClass».equals(«fieldName», other.«fieldName»)) {
624 def private generateToString(Collection<? extends GeneratedProperty> properties) '''
625 «IF !properties.empty»
626 @«OVERRIDE.importedName»
627 public «STRING.importedName» toString() {
628 final «MoreObjects.importedName».ToStringHelper helper = «MoreObjects.importedName».toStringHelper(«type.importedName».class);
629 «FOR property : properties»
630 «CODEHELPERS.importedName».appendValue(helper, "«property.fieldName»", «property.fieldName»);
632 return helper.toString();
637 def GeneratedProperty getPropByName(String name) {
638 for (GeneratedProperty prop : allProperties) {
639 if (prop.name.equals(name)) {
646 def private hasUintProperties() {
647 for (GeneratedProperty prop : allProperties) {
648 if (prop.isUintType) {
655 def private static isUintType(GeneratedProperty prop) {
656 UINT_TYPES.containsKey(prop.returnType)
659 def private static legacyType(GeneratedProperty prop) {
660 val type = prop.returnType
661 val uint = UINT_TYPES.get(type)