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.Collections
25 import java.util.regex.Pattern
26 import javax.management.ConstructorParameters
27 import org.gaul.modernizer_maven_annotations.SuppressModernizer
28 import org.opendaylight.mdsal.binding.model.api.ConcreteType
29 import org.opendaylight.mdsal.binding.model.api.Constant
30 import org.opendaylight.mdsal.binding.model.api.Enumeration
31 import org.opendaylight.mdsal.binding.model.api.GeneratedProperty
32 import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject
33 import org.opendaylight.mdsal.binding.model.api.Restrictions
34 import org.opendaylight.mdsal.binding.model.api.Type
35 import org.opendaylight.mdsal.binding.model.util.TypeConstants
36 import org.opendaylight.yangtools.yang.binding.CodeHelpers
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
45 * Template for generating JAVA class.
48 class ClassTemplate extends BaseTemplate {
50 protected val List<GeneratedProperty> properties
51 protected val List<GeneratedProperty> finalProperties
52 protected val List<GeneratedProperty> parentProperties
53 protected val Iterable<GeneratedProperty> allProperties
54 protected val Restrictions restrictions
57 * List of enumeration which are generated as JAVA enum type.
59 protected val List<Enumeration> enums
62 * List of constant instances which are generated as JAVA public static final attributes.
64 protected val List<Constant> consts
66 protected val GeneratedTransferObject genTO
68 val AbstractRangeGenerator<?> rangeGenerator
71 * Creates instance of this class with concrete <code>genType</code>.
73 * @param genType generated transfer object which will be transformed to JAVA class source code
75 new(GeneratedTransferObject genType) {
76 this(new TopLevelJavaGeneratedType(genType), genType)
80 * Creates instance of this class with concrete <code>genType</code>.
82 * @param genType generated transfer object which will be transformed to JAVA class source code
84 new(AbstractJavaGeneratedType javaType, GeneratedTransferObject genType) {
85 super(javaType, genType)
87 this.properties = genType.properties
88 this.finalProperties = GeneratorUtil.resolveReadOnlyPropertiesFromTO(genTO.properties)
89 this.parentProperties = GeneratorUtil.getPropertiesOfAllParents(genTO)
90 this.restrictions = genType.restrictions
92 var List<GeneratedProperty> sorted = new ArrayList<GeneratedProperty>();
93 sorted.addAll(properties);
94 sorted.addAll(parentProperties);
95 Collections.sort(sorted, [p1, p2|
96 p1.name.compareTo(p2.name)
99 this.allProperties = sorted
100 this.enums = genType.enumerations
101 this.consts = genType.constantDefinitions
103 if (restrictions !== null && restrictions.rangeConstraint.present) {
104 rangeGenerator = requireNonNull(AbstractRangeGenerator.forType(findProperty(genType, "value").returnType))
106 rangeGenerator = null
111 * Generates JAVA class source code (class body only).
113 * @return string with JAVA class body source code
115 def CharSequence generateAsInnerClass() {
116 return generateBody(true)
119 override protected body() {
124 * Template method which generates class body.
126 * @param isInnerClass boolean value which specify if generated class is|isn't inner
127 * @return string with class source code in JAVA format
129 def protected generateBody(boolean isInnerClass) '''
130 «wrapToDocumentation(formatDataForJavaDoc(type))»
131 «annotationDeclaration»
132 «generateClassDeclaration(isInnerClass)» {
134 «innerClassesDeclarations»
136 «constantsDeclarations»
139 «IF restrictions !== null»
140 «IF restrictions.lengthConstraint.present»
141 «LengthGenerator.generateLengthChecker("_value", findProperty(genTO, "value").returnType, restrictions.lengthConstraint.get, this)»
143 «IF restrictions.rangeConstraint.present»
144 «rangeGenerator.generateRangeChecker("_value", restrictions.rangeConstraint.get, this)»
152 «FOR field : properties SEPARATOR "\n"»
159 «IF (genTO.isTypedef() && genTO.getBaseType instanceof BitsTypeDefinition)»
160 «generateGetValueForBitsTypeDef»
167 «generateToString(genTO.toStringIdentifiers)»
173 * Template method which generates the method <code>getValue()</code> for typedef,
174 * which base type is BitsDefinition.
176 * @return string with the <code>getValue()</code> method definition in JAVA format
178 def protected generateGetValueForBitsTypeDef() '''
180 public boolean[] getValue() {
181 return new boolean[]{
182 «FOR property: genTO.properties SEPARATOR ','»
190 * Template method which generates inner classes inside this interface.
192 * @return string with the source code for inner classes in JAVA format
194 def protected innerClassesDeclarations() '''
195 «IF !type.enclosedTypes.empty»
196 «FOR innerClass : type.enclosedTypes SEPARATOR "\n"»
197 «generateInnerClass(innerClass)»
202 def protected constructors() '''
204 «genUnionConstructor»
206 «allValuesConstructor»
208 «IF !allProperties.empty»
211 «IF properties.empty && !parentProperties.empty »
216 def protected allValuesConstructor() '''
217 «IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
218 @«ConstructorParameters.importedName»("value")
219 @«ConstructorProperties.importedName»("value")
221 public «type.name»(«allProperties.asArgumentsDeclaration») {
222 «IF false == parentProperties.empty»
223 super(«parentProperties.asArguments»);
225 «FOR p : allProperties»
226 «generateRestrictions(type, p.fieldName.toString, p.returnType)»
230 * If we have patterns, we need to apply them to the value field. This is a sad
231 * consequence of how this code is structured.
233 IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
234 «CodeHelpers.importedName».requireValue(_value);
235 «genPatternEnforcer("_value")»
239 «IF p.returnType.importedName.contains("[]")»
240 «IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name
242 this.«p.fieldName» = «p.fieldName».clone();
244 this.«p.fieldName» = «p.fieldName» == null ? null : «p.fieldName».clone();
247 this.«p.fieldName» = «p.fieldName»;
254 def protected genUnionConstructor() '''
255 «FOR p : allProperties»
256 «val List<GeneratedProperty> other = new ArrayList(properties)»
258 «genConstructor(p, other)»
264 def protected genConstructor(GeneratedProperty property, GeneratedProperty... other) '''
265 public «type.name»(«property.returnType.importedName + " " + property.name») {
266 «IF false == parentProperties.empty»
267 super(«parentProperties.asArguments»);
270 «generateRestrictions(type, property.fieldName.toString, property.returnType)»
272 this.«property.fieldName» = «property.name»;
274 this.«p.fieldName» = null;
279 def private genPatternEnforcer(String ref) '''
281 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
282 «CodeHelpers.importedName».checkPattern(«ref», «Constants.MEMBER_PATTERN_LIST», «Constants.MEMBER_REGEX_LIST»);
287 def private static paramValue(Type returnType, String paramName) {
288 if (returnType instanceof ConcreteType) {
291 return paramName + ".getValue()"
295 def private generateRestrictions(Type type, String paramName, Type returnType) '''
296 «val restrictions = type.restrictions»
297 «IF restrictions !== null»
298 «IF restrictions.lengthConstraint.present || restrictions.rangeConstraint.present»
299 if («paramName» != null) {
300 «IF restrictions.lengthConstraint.present»
301 «LengthGenerator.generateLengthCheckerCall(paramName, paramValue(returnType, paramName))»
303 «IF restrictions.rangeConstraint.present»
304 «rangeGenerator.generateRangeCheckerCall(paramName, paramValue(returnType, paramName))»
311 def protected copyConstructor() '''
313 * Creates a copy from Source Object.
315 * @param source Source object
317 public «type.name»(«type.name» source) {
318 «IF false == parentProperties.empty»
322 this.«p.fieldName» = source.«p.fieldName»;
327 def protected parentConstructor() '''
329 * Creates a new instance from «genTO.superType.importedName»
331 * @param source Source object
333 public «type.name»(«genTO.superType.importedName» source) {
335 «genPatternEnforcer("getValue()")»
339 def protected defaultInstance() '''
340 «IF genTO.typedef && !allProperties.empty && !genTO.unionType»
341 «val prop = allProperties.get(0)»
342 «IF !("org.opendaylight.yangtools.yang.binding.InstanceIdentifier".equals(prop.returnType.fullyQualifiedName))»
343 public static «genTO.name» getDefaultInstance(String defaultValue) {
344 «IF BYTE_ARRAY.equals(prop.returnType)»
345 return new «genTO.name»(«Base64.importedName».getDecoder().decode(defaultValue));
346 «ELSEIF STRING.equals(prop.returnType)»
347 return new «genTO.name»(defaultValue);
348 «ELSEIF Constants.EMPTY.equals(prop.returnType)»
349 «Preconditions.importedName».checkArgument(defaultValue.isEmpty(), "Invalid value %s", defaultValue);
350 return new «genTO.name»(«Empty.importedName».getInstance());
351 «ELSEIF allProperties.size > 1»
353 «ELSEIF BOOLEAN.equals(prop.returnType)»
354 return new «genTO.name»(«Boolean.importedName».valueOf(defaultValue));
355 «ELSEIF "java.lang.Byte".equals(prop.returnType.fullyQualifiedName)»
356 return new «genTO.name»(«Byte.importedName».valueOf(defaultValue));
357 «ELSEIF "java.lang.Short".equals(prop.returnType.fullyQualifiedName)»
358 return new «genTO.name»(«Short.importedName».valueOf(defaultValue));
359 «ELSEIF "java.lang.Integer".equals(prop.returnType.fullyQualifiedName)»
360 return new «genTO.name»(«Integer.importedName».valueOf(defaultValue));
361 «ELSEIF "java.lang.Long".equals(prop.returnType.fullyQualifiedName)»
362 return new «genTO.name»(«Long.importedName».valueOf(defaultValue));
363 «ELSEIF "org.opendaylight.yangtools.yang.common.Uint8".equals(prop.returnType.fullyQualifiedName)»
364 return new «genTO.name»(«Uint8.importedName».valueOf(defaultValue));
365 «ELSEIF "org.opendaylight.yangtools.yang.common.Uint16".equals(prop.returnType.fullyQualifiedName)»
366 return new «genTO.name»(«Uint16.importedName».valueOf(defaultValue));
367 «ELSEIF "org.opendaylight.yangtools.yang.common.Uint32".equals(prop.returnType.fullyQualifiedName)»
368 return new «genTO.name»(«Uint32.importedName».valueOf(defaultValue));
369 «ELSEIF "org.opendaylight.yangtools.yang.common.Uint64".equals(prop.returnType.fullyQualifiedName)»
370 return new «genTO.name»(«Uint64.importedName».valueOf(defaultValue));
372 return new «genTO.name»(new «prop.returnType.importedName»(defaultValue));
379 def protected bitsArgs() '''
380 «List.importedName»<«String.importedName»> properties = «Lists.importedName».newArrayList(«allProperties.propsAsArgs»);
381 if (!properties.contains(defaultValue)) {
382 throw new «IllegalArgumentException.importedName»("invalid default parameter");
385 return new «genTO.name»(
386 «FOR prop : allProperties SEPARATOR ","»
387 properties.get(i++).equals(defaultValue) ? «Boolean.importedName».TRUE : null
392 def protected propsAsArgs(Iterable<GeneratedProperty> properties) '''
393 «FOR prop : properties SEPARATOR ","»
399 * Template method which generates JAVA class declaration.
401 * @param isInnerClass boolean value which specify if generated class is|isn't inner
402 * @return string with class declaration in JAVA format
404 def protected generateClassDeclaration(boolean isInnerClass) '''
408 ELSEIF (type.abstract)»«
412 ENDIF»class «type.name»«
413 IF (genTO.superType !== null)»«
414 " extends "»«genTO.superType.importedName»«
416 «IF (!type.implements.empty)»«
418 FOR type : type.implements SEPARATOR ", "»«
425 * Template method which generates JAVA enum type.
427 * @return string with inner enum source code in JAVA format
429 def protected enumDeclarations() '''
431 «FOR e : enums SEPARATOR "\n"»
432 «new EnumTemplate(javaType.getEnclosedType(e.identifier), e).generateAsInnerClass»
437 def protected suidDeclaration() '''
438 «IF genTO.SUID !== null»
439 private static final long serialVersionUID = «genTO.SUID.value»L;
443 def protected annotationDeclaration() '''
444 «IF genTO.getAnnotations !== null»
445 «FOR e : genTO.getAnnotations»
452 * Template method which generates JAVA constants.
454 * @return string with constants in JAVA format
456 def protected constantsDeclarations() '''
459 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
460 «val cValue = c.value as Map<String, String>»
461 public static final «List.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
462 FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»);
463 «IF cValue.size == 1»
464 private static final «Pattern.importedName» «Constants.MEMBER_PATTERN_LIST» = «Pattern.importedName».compile(«TypeConstants.PATTERN_CONSTANT_NAME».get(0));
465 private static final String «Constants.MEMBER_REGEX_LIST» = "«cValue.values.get(0).escapeJava»";
467 private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST» = «CodeHelpers.importedName».compilePatterns(«TypeConstants.PATTERN_CONSTANT_NAME»);
468 private static final String[] «Constants.MEMBER_REGEX_LIST» = { «
469 FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
479 * Template method which generates JAVA class attributes.
481 * @return string with the class attributes in JAVA format
483 def protected generateFields() '''
484 «IF !properties.empty»
486 private«IF isReadOnly(f)» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
491 protected def isReadOnly(GeneratedProperty field) {
492 return field.readOnly
496 * Template method which generates the method <code>hashCode()</code>.
498 * @return string with the <code>hashCode()</code> method definition in JAVA format
500 def protected generateHashCode() {
501 val size = genTO.hashCodeIdentifiers.size
506 @«Override.importedName»
507 public int hashCode() {
509 «hashCodeResult(genTO.hashCodeIdentifiers)»
512 return «CodeHelpers.importedName».wrapperHashCode(«genTO.hashCodeIdentifiers.get(0).fieldName»);
519 * Template method which generates the method <code>equals()</code>.
521 * @return string with the <code>equals()</code> method definition in JAVA format
523 def private generateEquals() '''
524 «IF !genTO.equalsIdentifiers.empty»
525 @«Override.importedName»
526 public final boolean equals(java.lang.Object obj) {
530 if (!(obj instanceof «type.name»)) {
533 final «type.name» other = («type.name») obj;
534 «FOR property : genTO.equalsIdentifiers»
535 «val fieldName = property.fieldName»
536 if (!«property.importedUtilClass».equals(«fieldName», other.«fieldName»)) {
545 def GeneratedProperty getPropByName(String name) {
546 for (GeneratedProperty prop : allProperties) {
547 if (prop.name.equals(name)) {