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.generator.util.BaseYangTypes.EMPTY_TYPE;
12 import static org.opendaylight.mdsal.binding.model.util.Types.BOOLEAN;
13 import static org.opendaylight.mdsal.binding.model.util.Types.BYTE_ARRAY;
14 import static org.opendaylight.mdsal.binding.model.util.Types.STRING;
15 import static extension org.apache.commons.text.StringEscapeUtils.escapeJava
17 import com.google.common.base.Preconditions
18 import com.google.common.collect.ImmutableList
19 import com.google.common.collect.Lists
20 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
21 import java.beans.ConstructorProperties
22 import java.util.ArrayList
23 import java.util.Base64;
24 import java.util.Comparator
27 import javax.management.ConstructorParameters
28 import org.gaul.modernizer_maven_annotations.SuppressModernizer
29 import org.opendaylight.mdsal.binding.model.api.ConcreteType
30 import org.opendaylight.mdsal.binding.model.api.Constant
31 import org.opendaylight.mdsal.binding.model.api.Enumeration
32 import org.opendaylight.mdsal.binding.model.api.GeneratedProperty
33 import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject
34 import org.opendaylight.mdsal.binding.model.api.Restrictions
35 import org.opendaylight.mdsal.binding.model.api.Type
36 import org.opendaylight.mdsal.binding.model.util.TypeConstants
37 import org.opendaylight.yangtools.yang.common.Empty
38 import org.opendaylight.yangtools.yang.common.Uint16
39 import org.opendaylight.yangtools.yang.common.Uint32
40 import org.opendaylight.yangtools.yang.common.Uint64
41 import org.opendaylight.yangtools.yang.common.Uint8
42 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition
43 import org.opendaylight.mdsal.binding.model.util.BindingTypes
44 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping
47 * Template for generating JAVA class.
50 class ClassTemplate extends BaseTemplate {
51 static val Comparator<GeneratedProperty> PROP_COMPARATOR = Comparator.comparing([prop | prop.name])
53 protected val List<GeneratedProperty> properties
54 protected val List<GeneratedProperty> finalProperties
55 protected val List<GeneratedProperty> parentProperties
56 protected val List<GeneratedProperty> allProperties
57 protected val Restrictions restrictions
60 * List of enumeration which are generated as JAVA enum type.
62 protected val List<Enumeration> enums
65 * List of constant instances which are generated as JAVA public static final attributes.
67 protected val List<Constant> consts
69 protected val GeneratedTransferObject genTO
71 val AbstractRangeGenerator<?> rangeGenerator
74 * Creates instance of this class with concrete <code>genType</code>.
76 * @param genType generated transfer object which will be transformed to JAVA class source code
78 new(GeneratedTransferObject genType) {
79 this(new TopLevelJavaGeneratedType(genType), genType)
83 * Creates instance of this class with concrete <code>genType</code>.
85 * @param genType generated transfer object which will be transformed to JAVA class source code
87 new(AbstractJavaGeneratedType javaType, GeneratedTransferObject genType) {
88 super(javaType, genType)
90 this.properties = genType.properties
91 this.finalProperties = GeneratorUtil.resolveReadOnlyPropertiesFromTO(genTO.properties)
92 this.parentProperties = GeneratorUtil.getPropertiesOfAllParents(genTO)
93 this.restrictions = genType.restrictions
95 val sorted = new ArrayList();
96 sorted.addAll(properties);
97 sorted.addAll(parentProperties);
98 sorted.sort(PROP_COMPARATOR);
100 this.allProperties = sorted
101 this.enums = genType.enumerations
102 this.consts = genType.constantDefinitions
104 if (restrictions !== null && restrictions.rangeConstraint.present) {
105 rangeGenerator = requireNonNull(AbstractRangeGenerator.forType(TypeUtils.encapsulatedValueType(genType)))
107 rangeGenerator = null
112 * Generates JAVA class source code (class body only).
114 * @return string with JAVA class body source code
116 def CharSequence generateAsInnerClass() {
117 return generateBody(true)
120 override protected body() {
125 * Template method which generates class body.
127 * @param isInnerClass boolean value which specify if generated class is|isn't inner
128 * @return string with class source code in JAVA format
130 def protected generateBody(boolean isInnerClass) '''
131 «wrapToDocumentation(formatDataForJavaDoc(type))»
132 «annotationDeclaration»
133 «generateClassDeclaration(isInnerClass)» {
135 «innerClassesDeclarations»
137 «constantsDeclarations»
140 «IF restrictions !== null»
141 «IF restrictions.lengthConstraint.present»
142 «LengthGenerator.generateLengthChecker("_value", TypeUtils.encapsulatedValueType(genTO),
143 restrictions.lengthConstraint.get, this)»
145 «IF restrictions.rangeConstraint.present»
146 «rangeGenerator.generateRangeChecker("_value", restrictions.rangeConstraint.get, this)»
156 «IF (genTO.isTypedef() && genTO.getBaseType instanceof BitsTypeDefinition)»
157 «generateGetValueForBitsTypeDef»
164 «generateToString(genTO.toStringIdentifiers)»
169 def private propertyMethods() {
170 if (properties.empty) {
173 isScalarTypeObject ? scalarTypeObjectValue(properties.get(0)) : defaultProperties
176 def private isScalarTypeObject() {
177 for (impl : genTO.implements) {
178 if (BindingTypes.SCALAR_TYPE_OBJECT.identifier.equals(impl.identifier)) {
185 def private defaultProperties() '''
186 «FOR field : properties SEPARATOR "\n"»
194 def private scalarTypeObjectValue(GeneratedProperty field) '''
195 @«OVERRIDE.importedName»
196 public «field.returnType.importedName» «BindingMapping.SCALAR_TYPE_OBJECT_GET_VALUE_NAME»() {
197 return «field.fieldName»«IF field.returnType.name.endsWith("[]")».clone()«ENDIF»;
202 * Template method which generates the method <code>getValue()</code> for typedef,
203 * which base type is BitsDefinition.
205 * @return string with the <code>getValue()</code> method definition in JAVA format
207 def protected generateGetValueForBitsTypeDef() '''
209 public boolean[] getValue() {
210 return new boolean[]{
211 «FOR property: genTO.properties SEPARATOR ','»
219 * Template method which generates inner classes inside this interface.
221 * @return string with the source code for inner classes in JAVA format
223 def protected innerClassesDeclarations() '''
224 «IF !type.enclosedTypes.empty»
225 «FOR innerClass : type.enclosedTypes SEPARATOR "\n"»
226 «generateInnerClass(innerClass)»
231 def protected constructors() '''
233 «genUnionConstructor»
234 «ELSEIF genTO.typedef && allProperties.size == 1 && allProperties.get(0).name.equals(TypeConstants.VALUE_PROP)»
237 «allValuesConstructor»
240 «IF !allProperties.empty»
243 «IF properties.empty && !parentProperties.empty »
248 def private allValuesConstructor() '''
249 public «type.name»(«allProperties.asArgumentsDeclaration») {
250 «IF !parentProperties.empty»
251 super(«parentProperties.asArguments»);
253 «FOR p : allProperties»
254 «generateRestrictions(type, p.fieldName, p.returnType)»
258 «val fieldName = p.fieldName»
259 «IF p.returnType.name.endsWith("[]")»
260 this.«fieldName» = «fieldName» == null ? null : «fieldName».clone();
262 this.«fieldName» = «fieldName»;
268 def private typedefConstructor() '''
269 @«ConstructorParameters.importedName»("«TypeConstants.VALUE_PROP»")
270 @«ConstructorProperties.importedName»("«TypeConstants.VALUE_PROP»")
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)»
279 * If we have patterns, we need to apply them to the value field. This is a sad consequence of how this code is
282 «CODEHELPERS.importedName».requireValue(_value);
283 «genPatternEnforcer("_value")»
286 «val fieldName = p.fieldName»
287 «IF p.returnType.name.endsWith("[]")»
288 this.«fieldName» = «fieldName».clone();
290 this.«fieldName» = «fieldName»;
296 def protected genUnionConstructor() '''
297 «FOR p : allProperties»
298 «val List<GeneratedProperty> other = new ArrayList(properties)»
300 «genConstructor(p, other)»
305 def protected genConstructor(GeneratedProperty property, Iterable<GeneratedProperty> other) '''
306 public «type.name»(«property.returnType.importedName + " " + property.name») {
307 «IF !parentProperties.empty»
308 super(«parentProperties.asArguments»);
311 «val fieldName = property.fieldName»
312 «generateRestrictions(type, fieldName, property.returnType)»
314 this.«fieldName» = «property.name»;
316 this.«p.fieldName» = null;
321 def private genPatternEnforcer(String ref) '''
323 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
324 «CODEHELPERS.importedName».checkPattern(«ref», «Constants.MEMBER_PATTERN_LIST», «Constants.MEMBER_REGEX_LIST»);
329 def private static paramValue(Type returnType, String paramName) {
330 if (returnType instanceof ConcreteType) {
333 return paramName + ".getValue()"
337 def private generateRestrictions(Type type, String paramName, Type returnType) '''
338 «val restrictions = type.restrictions»
339 «IF restrictions !== null»
340 «IF restrictions.lengthConstraint.present || restrictions.rangeConstraint.present»
341 if («paramName» != null) {
342 «IF restrictions.lengthConstraint.present»
343 «LengthGenerator.generateLengthCheckerCall(paramName, paramValue(returnType, paramName))»
345 «IF restrictions.rangeConstraint.present»
346 «rangeGenerator.generateRangeCheckerCall(paramName, paramValue(returnType, paramName))»
353 def protected copyConstructor() '''
355 * Creates a copy from Source Object.
357 * @param source Source object
359 public «type.name»(«type.name» source) {
360 «IF !parentProperties.empty»
364 «val fieldName = p.fieldName»
365 this.«fieldName» = source.«fieldName»;
370 def protected parentConstructor() '''
372 * Creates a new instance from «genTO.superType.importedName»
374 * @param source Source object
376 public «type.name»(«genTO.superType.importedName» source) {
378 «genPatternEnforcer("getValue()")»
382 def protected defaultInstance() '''
383 «IF genTO.typedef && !allProperties.empty && !genTO.unionType»
384 «val prop = allProperties.get(0)»
385 «IF !("org.opendaylight.yangtools.yang.binding.InstanceIdentifier".equals(prop.returnType.fullyQualifiedName))»
386 public static «genTO.name» getDefaultInstance(String defaultValue) {
387 «IF BYTE_ARRAY.equals(prop.returnType)»
388 return new «genTO.name»(«Base64.importedName».getDecoder().decode(defaultValue));
389 «ELSEIF STRING.equals(prop.returnType)»
390 return new «genTO.name»(defaultValue);
391 «ELSEIF EMPTY_TYPE.equals(prop.returnType)»
392 «Preconditions.importedName».checkArgument(defaultValue.isEmpty(), "Invalid value %s", defaultValue);
393 return new «genTO.name»(«Empty.importedName».getInstance());
394 «ELSEIF allProperties.size > 1»
396 «ELSEIF BOOLEAN.equals(prop.returnType)»
397 return new «genTO.name»(«Boolean.importedName».valueOf(defaultValue));
398 «ELSEIF "java.lang.Byte".equals(prop.returnType.fullyQualifiedName)»
399 return new «genTO.name»(«Byte.importedName».valueOf(defaultValue));
400 «ELSEIF "java.lang.Short".equals(prop.returnType.fullyQualifiedName)»
401 return new «genTO.name»(«Short.importedName».valueOf(defaultValue));
402 «ELSEIF "java.lang.Integer".equals(prop.returnType.fullyQualifiedName)»
403 return new «genTO.name»(«Integer.importedName».valueOf(defaultValue));
404 «ELSEIF "java.lang.Long".equals(prop.returnType.fullyQualifiedName)»
405 return new «genTO.name»(«Long.importedName».valueOf(defaultValue));
406 «ELSEIF "org.opendaylight.yangtools.yang.common.Uint8".equals(prop.returnType.fullyQualifiedName)»
407 return new «genTO.name»(«Uint8.importedName».valueOf(defaultValue));
408 «ELSEIF "org.opendaylight.yangtools.yang.common.Uint16".equals(prop.returnType.fullyQualifiedName)»
409 return new «genTO.name»(«Uint16.importedName».valueOf(defaultValue));
410 «ELSEIF "org.opendaylight.yangtools.yang.common.Uint32".equals(prop.returnType.fullyQualifiedName)»
411 return new «genTO.name»(«Uint32.importedName».valueOf(defaultValue));
412 «ELSEIF "org.opendaylight.yangtools.yang.common.Uint64".equals(prop.returnType.fullyQualifiedName)»
413 return new «genTO.name»(«Uint64.importedName».valueOf(defaultValue));
415 return new «genTO.name»(new «prop.returnType.importedName»(defaultValue));
422 @SuppressFBWarnings(value = "DLS_DEAD_LOCAL_STORE", justification = "FOR with SEPARATOR, not needing for value")
423 def protected bitsArgs() '''
424 «JU_LIST.importedName»<«STRING.importedName»> properties = «Lists.importedName».newArrayList(«allProperties.propsAsArgs»);
425 if (!properties.contains(defaultValue)) {
426 throw new «IllegalArgumentException.importedName»("invalid default parameter");
429 return new «genTO.name»(
430 «FOR prop : allProperties SEPARATOR ","»
431 properties.get(i++).equals(defaultValue) ? «Boolean.importedName».TRUE : null
436 def protected propsAsArgs(Iterable<GeneratedProperty> properties) '''
437 «FOR prop : properties SEPARATOR ","»
443 * Template method which generates JAVA class declaration.
445 * @param isInnerClass boolean value which specify if generated class is|isn't inner
446 * @return string with class declaration in JAVA format
448 def protected generateClassDeclaration(boolean isInnerClass) '''
452 ELSEIF (type.abstract)»«
456 ENDIF»class «type.name»«
457 IF (genTO.superType !== null)»«
458 " extends "»«genTO.superType.importedName»«
460 «IF (!type.implements.empty)»«
462 FOR type : type.implements SEPARATOR ", "»«
469 * Template method which generates JAVA enum type.
471 * @return string with inner enum source code in JAVA format
473 def protected enumDeclarations() '''
475 «FOR e : enums SEPARATOR "\n"»
476 «new EnumTemplate(javaType.getEnclosedType(e.identifier), e).generateAsInnerClass»
481 def protected suidDeclaration() '''
482 «IF genTO.SUID !== null»
483 private static final long serialVersionUID = «genTO.SUID.value»L;
487 def protected annotationDeclaration() '''
488 «IF genTO.getAnnotations !== null»
489 «FOR e : genTO.getAnnotations»
496 * Template method which generates JAVA constants.
498 * @return string with constants in JAVA format
500 def protected constantsDeclarations() '''
503 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
504 «val cValue = c.value as Map<String, String>»
505 «val jurPatternRef = JUR_PATTERN.importedName»
506 public static final «JU_LIST.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
507 FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»);
508 «IF cValue.size == 1»
509 private static final «jurPatternRef» «Constants.MEMBER_PATTERN_LIST» = «jurPatternRef».compile(«TypeConstants.PATTERN_CONSTANT_NAME».get(0));
510 private static final String «Constants.MEMBER_REGEX_LIST» = "«cValue.values.iterator.next.escapeJava»";
512 private static final «jurPatternRef»[] «Constants.MEMBER_PATTERN_LIST» = «CODEHELPERS.importedName».compilePatterns(«TypeConstants.PATTERN_CONSTANT_NAME»);
513 private static final String[] «Constants.MEMBER_REGEX_LIST» = { «
514 FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
524 * Template method which generates JAVA class attributes.
526 * @return string with the class attributes in JAVA format
528 def protected generateFields() '''
529 «IF !properties.empty»
531 private«IF isReadOnly(f)» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
536 protected def isReadOnly(GeneratedProperty field) {
537 return field.readOnly
541 * Template method which generates the method <code>hashCode()</code>.
543 * @return string with the <code>hashCode()</code> method definition in JAVA format
545 def protected generateHashCode() {
546 val size = genTO.hashCodeIdentifiers.size
551 @«OVERRIDE.importedName»
552 public int hashCode() {
554 «hashCodeResult(genTO.hashCodeIdentifiers)»
557 return «CODEHELPERS.importedName».wrapperHashCode(«genTO.hashCodeIdentifiers.get(0).fieldName»);
564 * Template method which generates the method <code>equals()</code>.
566 * @return string with the <code>equals()</code> method definition in JAVA format
568 def private generateEquals() '''
569 «IF !genTO.equalsIdentifiers.empty»
570 @«OVERRIDE.importedName»
571 public final boolean equals(java.lang.Object obj) {
575 if (!(obj instanceof «type.name»)) {
578 final «type.name» other = («type.name») obj;
579 «FOR property : genTO.equalsIdentifiers»
580 «val fieldName = property.fieldName»
581 if (!«property.importedUtilClass».equals(«fieldName», other.«fieldName»)) {
590 def GeneratedProperty getPropByName(String name) {
591 for (GeneratedProperty prop : allProperties) {
592 if (prop.name.equals(name)) {