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 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 «generateInnerClass(innerClass)»
192 def protected constructors() '''
194 «genUnionConstructor»
196 «allValuesConstructor»
198 «IF !allProperties.empty»
201 «IF properties.empty && !parentProperties.empty »
206 def protected allValuesConstructor() '''
207 «IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
208 @«ConstructorProperties.importedName»("value")
210 public «type.name»(«allProperties.asArgumentsDeclaration») {
211 «IF false == parentProperties.empty»
212 super(«parentProperties.asArguments»);
214 «FOR p : allProperties»
215 «generateRestrictions(type, p.fieldName.toString, p.returnType)»
219 * If we have patterns, we need to apply them to the value field. This is a sad
220 * consequence of how this code is structured.
222 IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
223 «Objects.importedName».requireNonNull(_value, "Supplied value may not be null");
224 «genPatternEnforcer("_value")»
228 «IF p.returnType.importedName.contains("[]")»
229 «IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name
231 this.«p.fieldName» = «p.fieldName».clone();
233 this.«p.fieldName» = «p.fieldName» == null ? null : «p.fieldName».clone();
236 this.«p.fieldName» = «p.fieldName»;
243 def protected genUnionConstructor() '''
244 «FOR p : allProperties»
245 «val List<GeneratedProperty> other = new ArrayList(properties)»
247 «genConstructor(p, other)»
253 def protected genConstructor(GeneratedProperty property, GeneratedProperty... other) '''
254 public «type.name»(«property.returnType.importedName + " " + property.name») {
255 «IF false == parentProperties.empty»
256 super(«parentProperties.asArguments»);
259 «generateRestrictions(type, property.fieldName.toString, property.returnType)»
261 this.«property.fieldName» = «property.name»;
263 this.«p.fieldName» = null;
268 def private genPatternEnforcer(String ref) '''
270 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
271 «CodeHelpers.importedName».checkPattern(«ref», «Constants.MEMBER_PATTERN_LIST», «Constants.MEMBER_REGEX_LIST»);
276 def private static paramValue(Type returnType, String paramName) {
277 if (returnType instanceof ConcreteType) {
280 return paramName + ".getValue()"
284 def private generateRestrictions(Type type, String paramName, Type returnType) '''
285 «val restrictions = type.restrictions»
286 «IF restrictions !== null»
287 «IF restrictions.lengthConstraint.present || restrictions.rangeConstraint.present»
288 if («paramName» != null) {
289 «IF restrictions.lengthConstraint.present»
290 «LengthGenerator.generateLengthCheckerCall(paramName, paramValue(returnType, paramName))»
292 «IF restrictions.rangeConstraint.present»
293 «rangeGenerator.generateRangeCheckerCall(paramName, paramValue(returnType, paramName))»
300 def protected copyConstructor() '''
302 * Creates a copy from Source Object.
304 * @param source Source object
306 public «type.name»(«type.name» source) {
307 «IF false == parentProperties.empty»
311 this.«p.fieldName» = source.«p.fieldName»;
316 def protected parentConstructor() '''
318 * Creates a new instance from «genTO.superType.importedName»
320 * @param source Source object
322 public «type.name»(«genTO.superType.importedName» source) {
324 «genPatternEnforcer("getValue()")»
328 def protected defaultInstance() '''
329 «IF genTO.typedef && !allProperties.empty && !genTO.unionType»
330 «val prop = allProperties.get(0)»
331 «IF !("org.opendaylight.yangtools.yang.binding.InstanceIdentifier".equals(prop.returnType.fullyQualifiedName))»
332 public static «genTO.name» getDefaultInstance(String defaultValue) {
333 «IF "byte[]".equals(prop.returnType.name)»
334 «BaseEncoding.importedName» baseEncoding = «BaseEncoding.importedName».base64();
335 return new «genTO.name»(baseEncoding.decode(defaultValue));
336 «ELSEIF "java.lang.String".equals(prop.returnType.fullyQualifiedName)»
337 return new «genTO.name»(defaultValue);
338 «ELSEIF allProperties.size > 1»
340 «ELSEIF "java.lang.Boolean".equals(prop.returnType.fullyQualifiedName)»
341 return new «genTO.name»(«Boolean.importedName».valueOf(defaultValue));
342 «ELSEIF "java.lang.Byte".equals(prop.returnType.fullyQualifiedName)»
343 return new «genTO.name»(«Byte.importedName».valueOf(defaultValue));
344 «ELSEIF "java.lang.Short".equals(prop.returnType.fullyQualifiedName)»
345 return new «genTO.name»(«Short.importedName».valueOf(defaultValue));
346 «ELSEIF "java.lang.Integer".equals(prop.returnType.fullyQualifiedName)»
347 return new «genTO.name»(«Integer.importedName».valueOf(defaultValue));
348 «ELSEIF "java.lang.Long".equals(prop.returnType.fullyQualifiedName)»
349 return new «genTO.name»(«Long.importedName».valueOf(defaultValue));
351 return new «genTO.name»(new «prop.returnType.importedName»(defaultValue));
358 def protected bitsArgs() '''
359 «List.importedName»<«String.importedName»> properties = «Lists.importedName».newArrayList(«allProperties.propsAsArgs»);
360 if (!properties.contains(defaultValue)) {
361 throw new «IllegalArgumentException.importedName»("invalid default parameter");
364 return new «genTO.name»(
365 «FOR prop : allProperties SEPARATOR ","»
366 properties.get(i++).equals(defaultValue) ? «Boolean.importedName».TRUE : null
371 def protected propsAsArgs(Iterable<GeneratedProperty> properties) '''
372 «FOR prop : properties SEPARATOR ","»
378 * Template method which generates JAVA class declaration.
380 * @param isInnerClass boolean value which specify if generated class is|isn't inner
381 * @return string with class declaration in JAVA format
383 def protected generateClassDeclaration(boolean isInnerClass) '''
387 ELSEIF (type.abstract)»«
391 ENDIF»class «type.name»«
392 IF (genTO.superType !== null)»«
393 " extends "»«genTO.superType.importedName»«
395 «IF (!type.implements.empty)»«
397 FOR type : type.implements SEPARATOR ", "»«
404 * Template method which generates JAVA enum type.
406 * @return string with inner enum source code in JAVA format
408 def protected enumDeclarations() '''
410 «FOR e : enums SEPARATOR "\n"»
411 «new EnumTemplate(javaType.getEnclosedType(e.identifier), e).generateAsInnerClass»
416 def protected suidDeclaration() '''
417 «IF genTO.SUID !== null»
418 private static final long serialVersionUID = «genTO.SUID.value»L;
422 def protected annotationDeclaration() '''
423 «IF genTO.getAnnotations !== null»
424 «FOR e : genTO.getAnnotations»
431 * Template method which generates JAVA constants.
433 * @return string with constants in JAVA format
435 def protected constantsDeclarations() '''
438 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
439 «val cValue = c.value as Map<String, String>»
440 public static final «List.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
441 FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»);
442 «IF cValue.size == 1»
443 private static final «Pattern.importedName» «Constants.MEMBER_PATTERN_LIST» = «Pattern.importedName».compile(«TypeConstants.PATTERN_CONSTANT_NAME».get(0));
444 private static final String «Constants.MEMBER_REGEX_LIST» = "«cValue.values.get(0).escapeJava»";
446 private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST» = «CodeHelpers.importedName».compilePatterns(«TypeConstants.PATTERN_CONSTANT_NAME»);
447 private static final String[] «Constants.MEMBER_REGEX_LIST» = { «
448 FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
458 * Template method which generates JAVA class attributes.
460 * @return string with the class attributes in JAVA format
462 def protected generateFields() '''
463 «IF !properties.empty»
465 private«IF isReadOnly(f)» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
470 protected def isReadOnly(GeneratedProperty field) {
471 return field.readOnly
475 * Template method which generates the method <code>hashCode()</code>.
477 * @return string with the <code>hashCode()</code> method definition in JAVA format
479 def protected generateHashCode() '''
480 «IF !genTO.hashCodeIdentifiers.empty»
481 @«Override.importedName»
482 public int hashCode() {
483 final int prime = 31;
485 «FOR property : genTO.hashCodeIdentifiers»
486 «IF property.returnType.name.contains("[")»
487 result = prime * result + «Arrays.importedName».hashCode(«property.fieldName»);
489 result = prime * result + «Objects.importedName».hashCode(«property.fieldName»);
498 * Template method which generates the method <code>equals()</code>.
500 * @return string with the <code>equals()</code> method definition in JAVA format
502 def protected generateEquals() '''
503 «IF !genTO.equalsIdentifiers.empty»
504 @«Override.importedName»
505 public boolean equals(java.lang.Object obj) {
512 if (getClass() != obj.getClass()) {
515 «type.name» other = («type.name») obj;
516 «FOR property : genTO.equalsIdentifiers»
517 «val fieldName = property.fieldName»
518 «IF property.returnType.name.contains("[")»
519 if (!«Arrays.importedName».equals(«fieldName», other.«fieldName»)) {
521 if (!«Objects.importedName».equals(«fieldName», other.«fieldName»)) {
531 def GeneratedProperty getPropByName(String name) {
532 for (GeneratedProperty prop : allProperties) {
533 if (prop.name.equals(name)) {