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.STRING;
26 import static extension org.apache.commons.text.StringEscapeUtils.escapeJava
28 import com.google.common.base.Preconditions
29 import com.google.common.collect.ImmutableList
30 import com.google.common.collect.Lists
31 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
32 import java.beans.ConstructorProperties
33 import java.util.ArrayList
34 import java.util.Base64;
35 import java.util.Comparator
39 import javax.management.ConstructorParameters
40 import org.gaul.modernizer_maven_annotations.SuppressModernizer
41 import org.opendaylight.mdsal.binding.model.api.ConcreteType
42 import org.opendaylight.mdsal.binding.model.api.Constant
43 import org.opendaylight.mdsal.binding.model.api.Enumeration
44 import org.opendaylight.mdsal.binding.model.api.GeneratedProperty
45 import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject
46 import org.opendaylight.mdsal.binding.model.api.Restrictions
47 import org.opendaylight.mdsal.binding.model.api.Type
48 import org.opendaylight.mdsal.binding.model.util.TypeConstants
49 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping
50 import org.opendaylight.yangtools.yang.common.Empty
51 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition
54 * Template for generating JAVA class.
57 class ClassTemplate extends BaseTemplate {
58 static val Comparator<GeneratedProperty> PROP_COMPARATOR = Comparator.comparing([prop | prop.name])
59 static val VALUEOF_TYPES = Set.of(
70 protected val List<GeneratedProperty> properties
71 protected val List<GeneratedProperty> finalProperties
72 protected val List<GeneratedProperty> parentProperties
73 protected val List<GeneratedProperty> allProperties
74 protected val Restrictions restrictions
77 * List of enumeration which are generated as JAVA enum type.
79 protected val List<Enumeration> enums
82 * List of constant instances which are generated as JAVA public static final attributes.
84 protected val List<Constant> consts
86 protected val GeneratedTransferObject genTO
88 val AbstractRangeGenerator<?> rangeGenerator
91 * Creates instance of this class with concrete <code>genType</code>.
93 * @param genType generated transfer object which will be transformed to JAVA class source code
95 new(GeneratedTransferObject genType) {
96 this(new TopLevelJavaGeneratedType(genType), genType)
100 * Creates instance of this class with concrete <code>genType</code>.
102 * @param genType generated transfer object which will be transformed to JAVA class source code
104 new(AbstractJavaGeneratedType javaType, GeneratedTransferObject genType) {
105 super(javaType, genType)
107 this.properties = genType.properties
108 this.finalProperties = GeneratorUtil.resolveReadOnlyPropertiesFromTO(genTO.properties)
109 this.parentProperties = GeneratorUtil.getPropertiesOfAllParents(genTO)
110 this.restrictions = genType.restrictions
112 val sorted = new ArrayList();
113 sorted.addAll(properties);
114 sorted.addAll(parentProperties);
115 sorted.sort(PROP_COMPARATOR);
117 this.allProperties = sorted
118 this.enums = genType.enumerations
119 this.consts = genType.constantDefinitions
121 if (restrictions !== null && restrictions.rangeConstraint.present) {
122 rangeGenerator = requireNonNull(AbstractRangeGenerator.forType(TypeUtils.encapsulatedValueType(genType)))
124 rangeGenerator = null
129 * Generates JAVA class source code (class body only).
131 * @return string with JAVA class body source code
133 def CharSequence generateAsInnerClass() {
134 return generateBody(true)
137 override protected body() {
142 * Template method which generates class body.
144 * @param isInnerClass boolean value which specify if generated class is|isn't inner
145 * @return string with class source code in JAVA format
147 def protected generateBody(boolean isInnerClass) '''
148 «wrapToDocumentation(formatDataForJavaDoc(type))»
149 «annotationDeclaration»
150 «generateClassDeclaration(isInnerClass)» {
152 «innerClassesDeclarations»
154 «constantsDeclarations»
157 «IF restrictions !== null»
158 «IF restrictions.lengthConstraint.present»
159 «LengthGenerator.generateLengthChecker("_value", TypeUtils.encapsulatedValueType(genTO),
160 restrictions.lengthConstraint.get, this)»
162 «IF restrictions.rangeConstraint.present»
163 «rangeGenerator.generateRangeChecker("_value", restrictions.rangeConstraint.get, this)»
173 «IF (genTO.isTypedef() && genTO.getBaseType instanceof BitsTypeDefinition)»
174 «generateGetValueForBitsTypeDef»
181 «generateToString(genTO.toStringIdentifiers)»
186 def private propertyMethods() {
187 if (properties.empty) {
190 isScalarTypeObject ? scalarTypeObjectValue(properties.get(0)) : defaultProperties
193 def private isScalarTypeObject() {
194 for (impl : genTO.implements) {
195 if (SCALAR_TYPE_OBJECT.identifier.equals(impl.identifier)) {
202 def private defaultProperties() '''
203 «FOR field : properties SEPARATOR "\n"»
211 def private scalarTypeObjectValue(GeneratedProperty field) '''
212 @«OVERRIDE.importedName»
213 public «field.returnType.importedName» «BindingMapping.SCALAR_TYPE_OBJECT_GET_VALUE_NAME»() {
214 return «field.fieldName»«IF field.returnType.name.endsWith("[]")».clone()«ENDIF»;
219 * Template method which generates the method <code>getValue()</code> for typedef,
220 * which base type is BitsDefinition.
222 * @return string with the <code>getValue()</code> method definition in JAVA format
224 def protected generateGetValueForBitsTypeDef() '''
226 public boolean[] getValue() {
227 return new boolean[]{
228 «FOR property: genTO.properties SEPARATOR ','»
236 * Template method which generates inner classes inside this interface.
238 * @return string with the source code for inner classes in JAVA format
240 def protected innerClassesDeclarations() '''
241 «IF !type.enclosedTypes.empty»
242 «FOR innerClass : type.enclosedTypes SEPARATOR "\n"»
243 «generateInnerClass(innerClass)»
248 def protected constructors() '''
250 «genUnionConstructor»
251 «ELSEIF genTO.typedef && allProperties.size == 1 && allProperties.get(0).name.equals(TypeConstants.VALUE_PROP)»
254 «allValuesConstructor»
257 «IF !allProperties.empty»
260 «IF properties.empty && !parentProperties.empty »
265 def private allValuesConstructor() '''
266 public «type.name»(«allProperties.asArgumentsDeclaration») {
267 «IF !parentProperties.empty»
268 super(«parentProperties.asArguments»);
270 «FOR p : allProperties»
271 «generateRestrictions(type, p.fieldName, p.returnType)»
275 «val fieldName = p.fieldName»
276 «IF p.returnType.name.endsWith("[]")»
277 this.«fieldName» = «fieldName» == null ? null : «fieldName».clone();
279 this.«fieldName» = «fieldName»;
285 def private typedefConstructor() '''
286 @«ConstructorParameters.importedName»("«TypeConstants.VALUE_PROP»")
287 @«ConstructorProperties.importedName»("«TypeConstants.VALUE_PROP»")
288 public «type.name»(«allProperties.asArgumentsDeclaration») {
289 «IF !parentProperties.empty»
290 super(«parentProperties.asArguments»);
292 «FOR p : allProperties»
293 «generateRestrictions(type, p.fieldName, p.returnType)»
296 * If we have patterns, we need to apply them to the value field. This is a sad consequence of how this code is
299 «CODEHELPERS.importedName».requireValue(_value);
300 «genPatternEnforcer("_value")»
303 «val fieldName = p.fieldName»
304 «IF p.returnType.name.endsWith("[]")»
305 this.«fieldName» = «fieldName».clone();
307 this.«fieldName» = «fieldName»;
313 def protected genUnionConstructor() '''
314 «FOR p : allProperties»
315 «val List<GeneratedProperty> other = new ArrayList(properties)»
317 «genConstructor(p, other)»
322 def protected genConstructor(GeneratedProperty property, Iterable<GeneratedProperty> other) '''
323 public «type.name»(«property.returnType.importedName + " " + property.name») {
324 «IF !parentProperties.empty»
325 super(«parentProperties.asArguments»);
328 «val fieldName = property.fieldName»
329 «generateRestrictions(type, fieldName, property.returnType)»
331 this.«fieldName» = «property.name»;
333 this.«p.fieldName» = null;
338 def private genPatternEnforcer(String ref) '''
340 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
341 «CODEHELPERS.importedName».checkPattern(«ref», «Constants.MEMBER_PATTERN_LIST», «Constants.MEMBER_REGEX_LIST»);
346 def private static paramValue(Type returnType, String paramName) {
347 if (returnType instanceof ConcreteType) {
350 return paramName + ".getValue()"
354 def private generateRestrictions(Type type, String paramName, Type returnType) '''
355 «val restrictions = type.restrictions»
356 «IF restrictions !== null»
357 «IF restrictions.lengthConstraint.present || restrictions.rangeConstraint.present»
358 if («paramName» != null) {
359 «IF restrictions.lengthConstraint.present»
360 «LengthGenerator.generateLengthCheckerCall(paramName, paramValue(returnType, paramName))»
362 «IF restrictions.rangeConstraint.present»
363 «rangeGenerator.generateRangeCheckerCall(paramName, paramValue(returnType, paramName))»
370 def protected copyConstructor() '''
372 * Creates a copy from Source Object.
374 * @param source Source object
376 public «type.name»(«type.name» source) {
377 «IF !parentProperties.empty»
381 «val fieldName = p.fieldName»
382 this.«fieldName» = source.«fieldName»;
387 def protected parentConstructor() '''
389 * Creates a new instance from «genTO.superType.importedName»
391 * @param source Source object
393 public «type.name»(«genTO.superType.importedName» source) {
395 «genPatternEnforcer("getValue()")»
399 def protected defaultInstance() '''
400 «IF genTO.typedef && !allProperties.empty && !genTO.unionType»
401 «val prop = allProperties.get(0)»
402 «val propType = prop.returnType»
403 «IF !(INSTANCE_IDENTIFIER.identifier.equals(propType.identifier))»
404 public static «genTO.name» getDefaultInstance(final String defaultValue) {
405 «IF allProperties.size > 1»
407 «ELSEIF VALUEOF_TYPES.contains(propType)»
408 return new «genTO.name»(«propType.importedName».valueOf(defaultValue));
409 «ELSEIF STRING_TYPE.equals(propType)»
410 return new «genTO.name»(defaultValue);
411 «ELSEIF BINARY_TYPE.equals(propType)»
412 return new «genTO.name»(«Base64.importedName».getDecoder().decode(defaultValue));
413 «ELSEIF EMPTY_TYPE.equals(propType)»
414 «Preconditions.importedName».checkArgument(defaultValue.isEmpty(), "Invalid value %s", defaultValue);
415 return new «genTO.name»(«Empty.importedName».getInstance());
417 return new «genTO.name»(new «propType.importedName»(defaultValue));
424 @SuppressFBWarnings(value = "DLS_DEAD_LOCAL_STORE", justification = "FOR with SEPARATOR, not needing for value")
425 def protected bitsArgs() '''
426 «JU_LIST.importedName»<«STRING.importedName»> properties = «Lists.importedName».newArrayList(«allProperties.propsAsArgs»);
427 if (!properties.contains(defaultValue)) {
428 throw new «IllegalArgumentException.importedName»("invalid default parameter");
431 return new «genTO.name»(
432 «FOR prop : allProperties SEPARATOR ","»
433 properties.get(i++).equals(defaultValue) ? «Boolean.importedName».TRUE : null
438 def protected propsAsArgs(Iterable<GeneratedProperty> properties) '''
439 «FOR prop : properties SEPARATOR ","»
445 * Template method which generates JAVA class declaration.
447 * @param isInnerClass boolean value which specify if generated class is|isn't inner
448 * @return string with class declaration in JAVA format
450 def protected generateClassDeclaration(boolean isInnerClass) '''
454 ELSEIF (type.abstract)»«
458 ENDIF»class «type.name»«
459 IF (genTO.superType !== null)»«
460 " extends "»«genTO.superType.importedName»«
462 «IF (!type.implements.empty)»«
464 FOR type : type.implements SEPARATOR ", "»«
471 * Template method which generates JAVA enum type.
473 * @return string with inner enum source code in JAVA format
475 def protected enumDeclarations() '''
477 «FOR e : enums SEPARATOR "\n"»
478 «new EnumTemplate(javaType.getEnclosedType(e.identifier), e).generateAsInnerClass»
483 def protected suidDeclaration() '''
484 «IF genTO.SUID !== null»
485 private static final long serialVersionUID = «genTO.SUID.value»L;
489 def protected annotationDeclaration() '''
490 «IF genTO.getAnnotations !== null»
491 «FOR e : genTO.getAnnotations»
498 * Template method which generates JAVA constants.
500 * @return string with constants in JAVA format
502 def protected constantsDeclarations() '''
505 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
506 «val cValue = c.value as Map<String, String>»
507 «val jurPatternRef = JUR_PATTERN.importedName»
508 public static final «JU_LIST.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
509 FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»);
510 «IF cValue.size == 1»
511 private static final «jurPatternRef» «Constants.MEMBER_PATTERN_LIST» = «jurPatternRef».compile(«TypeConstants.PATTERN_CONSTANT_NAME».get(0));
512 private static final String «Constants.MEMBER_REGEX_LIST» = "«cValue.values.iterator.next.escapeJava»";
514 private static final «jurPatternRef»[] «Constants.MEMBER_PATTERN_LIST» = «CODEHELPERS.importedName».compilePatterns(«TypeConstants.PATTERN_CONSTANT_NAME»);
515 private static final String[] «Constants.MEMBER_REGEX_LIST» = { «
516 FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
526 * Template method which generates JAVA class attributes.
528 * @return string with the class attributes in JAVA format
530 def protected generateFields() '''
531 «IF !properties.empty»
533 private«IF isReadOnly(f)» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
538 protected def isReadOnly(GeneratedProperty field) {
539 return field.readOnly
543 * Template method which generates the method <code>hashCode()</code>.
545 * @return string with the <code>hashCode()</code> method definition in JAVA format
547 def protected generateHashCode() {
548 val size = genTO.hashCodeIdentifiers.size
553 @«OVERRIDE.importedName»
554 public int hashCode() {
556 «hashCodeResult(genTO.hashCodeIdentifiers)»
559 return «CODEHELPERS.importedName».wrapperHashCode(«genTO.hashCodeIdentifiers.get(0).fieldName»);
566 * Template method which generates the method <code>equals()</code>.
568 * @return string with the <code>equals()</code> method definition in JAVA format
570 def private generateEquals() '''
571 «IF !genTO.equalsIdentifiers.empty»
572 @«OVERRIDE.importedName»
573 public final boolean equals(java.lang.Object obj) {
577 if (!(obj instanceof «type.name»)) {
580 final «type.name» other = («type.name») obj;
581 «FOR property : genTO.equalsIdentifiers»
582 «val fieldName = property.fieldName»
583 if (!«property.importedUtilClass».equals(«fieldName», other.«fieldName»)) {
592 def GeneratedProperty getPropByName(String name) {
593 for (GeneratedProperty prop : allProperties) {
594 if (prop.name.equals(name)) {