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 extension org.apache.commons.text.StringEscapeUtils.escapeJava
13 import com.google.common.collect.ImmutableList
14 import com.google.common.collect.Lists
15 import com.google.common.io.BaseEncoding
16 import java.beans.ConstructorProperties
17 import java.util.ArrayList
18 import java.util.Arrays
19 import java.util.Collections
22 import java.util.Objects
23 import java.util.regex.Pattern
24 import org.opendaylight.mdsal.binding.model.api.ConcreteType
25 import org.opendaylight.mdsal.binding.model.api.Constant
26 import org.opendaylight.mdsal.binding.model.api.Enumeration
27 import org.opendaylight.mdsal.binding.model.api.GeneratedProperty
28 import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject
29 import org.opendaylight.mdsal.binding.model.api.Restrictions
30 import org.opendaylight.mdsal.binding.model.api.Type
31 import org.opendaylight.mdsal.binding.model.util.TypeConstants
32 import org.opendaylight.yangtools.yang.binding.CodeHelpers
33 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition
36 * Template for generating JAVA class.
38 class ClassTemplate extends BaseTemplate {
40 protected val List<GeneratedProperty> properties
41 protected val List<GeneratedProperty> finalProperties
42 protected val List<GeneratedProperty> parentProperties
43 protected val Iterable<GeneratedProperty> allProperties;
44 protected val Restrictions restrictions
47 * List of enumeration which are generated as JAVA enum type.
49 protected val List<Enumeration> enums
52 * List of constant instances which are generated as JAVA public static final attributes.
54 protected val List<Constant> consts
56 protected val GeneratedTransferObject genTO;
58 private val AbstractRangeGenerator<?> rangeGenerator
61 * Creates instance of this class with concrete <code>genType</code>.
63 * @param genType generated transfer object which will be transformed to JAVA class source code
65 new(GeneratedTransferObject genType) {
66 this(new TopLevelJavaGeneratedType(genType), genType)
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(AbstractJavaGeneratedType javaType, GeneratedTransferObject genType) {
75 super(javaType, genType)
77 this.properties = genType.properties
78 this.finalProperties = GeneratorUtil.resolveReadOnlyPropertiesFromTO(genTO.properties)
79 this.parentProperties = GeneratorUtil.getPropertiesOfAllParents(genTO)
80 this.restrictions = genType.restrictions
82 var List<GeneratedProperty> sorted = new ArrayList<GeneratedProperty>();
83 sorted.addAll(properties);
84 sorted.addAll(parentProperties);
85 Collections.sort(sorted, [p1, p2|
86 p1.name.compareTo(p2.name)
89 this.allProperties = sorted
90 this.enums = genType.enumerations
91 this.consts = genType.constantDefinitions
93 if (restrictions !== null && restrictions.rangeConstraint.present) {
94 rangeGenerator = requireNonNull(AbstractRangeGenerator.forType(findProperty(genType, "value").returnType))
101 * Generates JAVA class source code (class body only).
103 * @return string with JAVA class body source code
105 def CharSequence generateAsInnerClass() {
106 return generateBody(true)
109 override protected body() {
114 * Template method which generates class body.
116 * @param isInnerClass boolean value which specify if generated class is|isn't inner
117 * @return string with class source code in JAVA format
119 def protected generateBody(boolean isInnerClass) '''
120 «wrapToDocumentation(formatDataForJavaDoc(type))»
121 «annotationDeclaration»
122 «generateClassDeclaration(isInnerClass)» {
124 «innerClassesDeclarations»
126 «constantsDeclarations»
129 «IF restrictions !== null»
130 «IF restrictions.lengthConstraint.present»
131 «LengthGenerator.generateLengthChecker("_value", findProperty(genTO, "value").returnType, restrictions.lengthConstraint.get, this)»
133 «IF restrictions.rangeConstraint.present»
134 «rangeGenerator.generateRangeChecker("_value", restrictions.rangeConstraint.get, this)»
142 «FOR field : properties SEPARATOR "\n"»
149 «IF (genTO.isTypedef() && genTO.getBaseType instanceof BitsTypeDefinition)»
150 «generateGetValueForBitsTypeDef»
157 «generateToString(genTO.toStringIdentifiers)»
163 * Template method which generates the method <code>getValue()</code> for typedef,
164 * which base type is BitsDefinition.
166 * @return string with the <code>getValue()</code> method definition in JAVA format
168 def protected generateGetValueForBitsTypeDef() '''
170 public boolean[] getValue() {
171 return new boolean[]{
172 «FOR property: genTO.properties SEPARATOR ','»
180 * Template method which generates inner classes inside this interface.
182 * @return string with the source code for inner classes in JAVA format
184 def protected innerClassesDeclarations() '''
185 «IF !type.enclosedTypes.empty»
186 «FOR innerClass : type.enclosedTypes SEPARATOR "\n"»
187 «IF (innerClass instanceof GeneratedTransferObject)»
188 «new ClassTemplate(javaType.getEnclosedType(innerClass.identifier), innerClass).generateAsInnerClass»
194 def protected constructors() '''
196 «genUnionConstructor»
198 «allValuesConstructor»
200 «IF !allProperties.empty»
203 «IF properties.empty && !parentProperties.empty »
208 def protected allValuesConstructor() '''
209 «IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
210 @«ConstructorProperties.importedName»("value")
212 public «type.name»(«allProperties.asArgumentsDeclaration») {
213 «IF false == parentProperties.empty»
214 super(«parentProperties.asArguments»);
216 «FOR p : allProperties»
217 «generateRestrictions(type, p.fieldName.toString, p.returnType)»
221 * If we have patterns, we need to apply them to the value field. This is a sad
222 * consequence of how this code is structured.
224 IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
225 «Objects.importedName».requireNonNull(_value, "Supplied value may not be null");
227 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
228 «CodeHelpers.importedName».checkPattern(_value, «Constants.MEMBER_PATTERN_LIST», «Constants.MEMBER_REGEX_LIST»);
234 «IF p.returnType.importedName.contains("[]")»
235 «IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name
237 this.«p.fieldName» = «p.fieldName».clone();
239 this.«p.fieldName» = «p.fieldName» == null ? null : «p.fieldName».clone();
242 this.«p.fieldName» = «p.fieldName»;
249 def protected genUnionConstructor() '''
250 «FOR p : allProperties»
251 «val List<GeneratedProperty> other = new ArrayList(properties)»
253 «genConstructor(p, other)»
259 def protected genConstructor(GeneratedProperty property, GeneratedProperty... other) '''
260 public «type.name»(«property.returnType.importedName + " " + property.name») {
261 «IF false == parentProperties.empty»
262 super(«parentProperties.asArguments»);
265 «generateRestrictions(type, property.fieldName.toString, property.returnType)»
267 this.«property.fieldName» = «property.name»;
269 this.«p.fieldName» = null;
274 def private static paramValue(Type returnType, String paramName) {
275 if (returnType instanceof ConcreteType) {
278 return paramName + ".getValue()"
282 def private generateRestrictions(Type type, String paramName, Type returnType) '''
283 «val restrictions = type.restrictions»
284 «IF restrictions !== null»
285 «IF restrictions.lengthConstraint.present || restrictions.rangeConstraint.present»
286 if («paramName» != null) {
287 «IF restrictions.lengthConstraint.present»
288 «LengthGenerator.generateLengthCheckerCall(paramName, paramValue(returnType, paramName))»
290 «IF restrictions.rangeConstraint.present»
291 «rangeGenerator.generateRangeCheckerCall(paramName, paramValue(returnType, paramName))»
298 def protected copyConstructor() '''
300 * Creates a copy from Source Object.
302 * @param source Source object
304 public «type.name»(«type.name» source) {
305 «IF false == parentProperties.empty»
309 this.«p.fieldName» = source.«p.fieldName»;
314 def protected parentConstructor() '''
316 * Creates a new instance from «genTO.superType.importedName»
318 * @param source Source object
320 public «type.name»(«genTO.superType.importedName» source) {
325 def protected defaultInstance() '''
326 «IF genTO.typedef && !allProperties.empty && !genTO.unionType»
327 «val prop = allProperties.get(0)»
328 «IF !("org.opendaylight.yangtools.yang.binding.InstanceIdentifier".equals(prop.returnType.fullyQualifiedName))»
329 public static «genTO.name» getDefaultInstance(String defaultValue) {
330 «IF "byte[]".equals(prop.returnType.name)»
331 «BaseEncoding.importedName» baseEncoding = «BaseEncoding.importedName».base64();
332 return new «genTO.name»(baseEncoding.decode(defaultValue));
333 «ELSEIF "java.lang.String".equals(prop.returnType.fullyQualifiedName)»
334 return new «genTO.name»(defaultValue);
335 «ELSEIF allProperties.size > 1»
337 «ELSEIF "java.lang.Boolean".equals(prop.returnType.fullyQualifiedName)»
338 return new «genTO.name»(«Boolean.importedName».valueOf(defaultValue));
339 «ELSEIF "java.lang.Byte".equals(prop.returnType.fullyQualifiedName)»
340 return new «genTO.name»(«Byte.importedName».valueOf(defaultValue));
341 «ELSEIF "java.lang.Short".equals(prop.returnType.fullyQualifiedName)»
342 return new «genTO.name»(«Short.importedName».valueOf(defaultValue));
343 «ELSEIF "java.lang.Integer".equals(prop.returnType.fullyQualifiedName)»
344 return new «genTO.name»(«Integer.importedName».valueOf(defaultValue));
345 «ELSEIF "java.lang.Long".equals(prop.returnType.fullyQualifiedName)»
346 return new «genTO.name»(«Long.importedName».valueOf(defaultValue));
348 return new «genTO.name»(new «prop.returnType.importedName»(defaultValue));
355 def protected bitsArgs() '''
356 «List.importedName»<«String.importedName»> properties = «Lists.importedName».newArrayList(«allProperties.propsAsArgs»);
357 if (!properties.contains(defaultValue)) {
358 throw new «IllegalArgumentException.importedName»("invalid default parameter");
361 return new «genTO.name»(
362 «FOR prop : allProperties SEPARATOR ","»
363 properties.get(i++).equals(defaultValue) ? «Boolean.importedName».TRUE : null
368 def protected propsAsArgs(Iterable<GeneratedProperty> properties) '''
369 «FOR prop : properties SEPARATOR ","»
375 * Template method which generates JAVA class declaration.
377 * @param isInnerClass boolean value which specify if generated class is|isn't inner
378 * @return string with class declaration in JAVA format
380 def protected generateClassDeclaration(boolean isInnerClass) '''
384 ELSEIF (type.abstract)»«
388 ENDIF»class «type.name»«
389 IF (genTO.superType !== null)»«
390 " extends "»«genTO.superType.importedName»«
392 «IF (!type.implements.empty)»«
394 FOR type : type.implements SEPARATOR ", "»«
401 * Template method which generates JAVA enum type.
403 * @return string with inner enum source code in JAVA format
405 def protected enumDeclarations() '''
407 «FOR e : enums SEPARATOR "\n"»
408 «new EnumTemplate(javaType.getEnclosedType(e.identifier), e).generateAsInnerClass»
413 def protected suidDeclaration() '''
414 «IF genTO.SUID !== null»
415 private static final long serialVersionUID = «genTO.SUID.value»L;
419 def protected annotationDeclaration() '''
420 «IF genTO.getAnnotations !== null»
421 «FOR e : genTO.getAnnotations»
428 * Template method which generates JAVA constants.
430 * @return string with constants in JAVA format
432 def protected constantsDeclarations() '''
435 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
436 «val cValue = c.value as Map<String, String>»
437 public static final «List.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
438 FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»);
439 «IF cValue.size == 1»
440 private static final «Pattern.importedName» «Constants.MEMBER_PATTERN_LIST» = «Pattern.importedName».compile(«TypeConstants.PATTERN_CONSTANT_NAME».get(0));
441 private static final String «Constants.MEMBER_REGEX_LIST» = "«cValue.values.get(0).escapeJava»";
443 private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST» = «CodeHelpers.importedName».compilePatterns(«TypeConstants.PATTERN_CONSTANT_NAME»);
444 private static final String[] «Constants.MEMBER_REGEX_LIST» = { «
445 FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
455 * Template method which generates JAVA class attributes.
457 * @return string with the class attributes in JAVA format
459 def protected generateFields() '''
460 «IF !properties.empty»
462 private«IF isReadOnly(f)» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
467 protected def isReadOnly(GeneratedProperty field) {
468 return field.readOnly
472 * Template method which generates the method <code>hashCode()</code>.
474 * @return string with the <code>hashCode()</code> method definition in JAVA format
476 def protected generateHashCode() '''
477 «IF !genTO.hashCodeIdentifiers.empty»
478 @«Override.importedName»
479 public int hashCode() {
480 final int prime = 31;
482 «FOR property : genTO.hashCodeIdentifiers»
483 «IF property.returnType.name.contains("[")»
484 result = prime * result + «Arrays.importedName».hashCode(«property.fieldName»);
486 result = prime * result + «Objects.importedName».hashCode(«property.fieldName»);
495 * Template method which generates the method <code>equals()</code>.
497 * @return string with the <code>equals()</code> method definition in JAVA format
499 def protected generateEquals() '''
500 «IF !genTO.equalsIdentifiers.empty»
501 @«Override.importedName»
502 public boolean equals(java.lang.Object obj) {
509 if (getClass() != obj.getClass()) {
512 «type.name» other = («type.name») obj;
513 «FOR property : genTO.equalsIdentifiers»
514 «val fieldName = property.fieldName»
515 «IF property.returnType.name.contains("[")»
516 if (!«Arrays.importedName».equals(«fieldName», other.«fieldName»)) {
518 if (!«Objects.importedName».equals(«fieldName», other.«fieldName»)) {
528 def GeneratedProperty getPropByName(String name) {
529 for (GeneratedProperty prop : allProperties) {
530 if (prop.name.equals(name)) {