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 «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");
226 «genPatternEnforcer("_value")»
230 «IF p.returnType.importedName.contains("[]")»
231 «IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name
233 this.«p.fieldName» = «p.fieldName».clone();
235 this.«p.fieldName» = «p.fieldName» == null ? null : «p.fieldName».clone();
238 this.«p.fieldName» = «p.fieldName»;
245 def protected genUnionConstructor() '''
246 «FOR p : allProperties»
247 «val List<GeneratedProperty> other = new ArrayList(properties)»
249 «genConstructor(p, other)»
255 def protected genConstructor(GeneratedProperty property, GeneratedProperty... other) '''
256 public «type.name»(«property.returnType.importedName + " " + property.name») {
257 «IF false == parentProperties.empty»
258 super(«parentProperties.asArguments»);
261 «generateRestrictions(type, property.fieldName.toString, property.returnType)»
263 this.«property.fieldName» = «property.name»;
265 this.«p.fieldName» = null;
270 def private genPatternEnforcer(String ref) '''
272 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
273 «CodeHelpers.importedName».checkPattern(«ref», «Constants.MEMBER_PATTERN_LIST», «Constants.MEMBER_REGEX_LIST»);
278 def private static paramValue(Type returnType, String paramName) {
279 if (returnType instanceof ConcreteType) {
282 return paramName + ".getValue()"
286 def private generateRestrictions(Type type, String paramName, Type returnType) '''
287 «val restrictions = type.restrictions»
288 «IF restrictions !== null»
289 «IF restrictions.lengthConstraint.present || restrictions.rangeConstraint.present»
290 if («paramName» != null) {
291 «IF restrictions.lengthConstraint.present»
292 «LengthGenerator.generateLengthCheckerCall(paramName, paramValue(returnType, paramName))»
294 «IF restrictions.rangeConstraint.present»
295 «rangeGenerator.generateRangeCheckerCall(paramName, paramValue(returnType, paramName))»
302 def protected copyConstructor() '''
304 * Creates a copy from Source Object.
306 * @param source Source object
308 public «type.name»(«type.name» source) {
309 «IF false == parentProperties.empty»
313 this.«p.fieldName» = source.«p.fieldName»;
318 def protected parentConstructor() '''
320 * Creates a new instance from «genTO.superType.importedName»
322 * @param source Source object
324 public «type.name»(«genTO.superType.importedName» source) {
326 «genPatternEnforcer("getValue()")»
330 def protected defaultInstance() '''
331 «IF genTO.typedef && !allProperties.empty && !genTO.unionType»
332 «val prop = allProperties.get(0)»
333 «IF !("org.opendaylight.yangtools.yang.binding.InstanceIdentifier".equals(prop.returnType.fullyQualifiedName))»
334 public static «genTO.name» getDefaultInstance(String defaultValue) {
335 «IF "byte[]".equals(prop.returnType.name)»
336 «BaseEncoding.importedName» baseEncoding = «BaseEncoding.importedName».base64();
337 return new «genTO.name»(baseEncoding.decode(defaultValue));
338 «ELSEIF "java.lang.String".equals(prop.returnType.fullyQualifiedName)»
339 return new «genTO.name»(defaultValue);
340 «ELSEIF allProperties.size > 1»
342 «ELSEIF "java.lang.Boolean".equals(prop.returnType.fullyQualifiedName)»
343 return new «genTO.name»(«Boolean.importedName».valueOf(defaultValue));
344 «ELSEIF "java.lang.Byte".equals(prop.returnType.fullyQualifiedName)»
345 return new «genTO.name»(«Byte.importedName».valueOf(defaultValue));
346 «ELSEIF "java.lang.Short".equals(prop.returnType.fullyQualifiedName)»
347 return new «genTO.name»(«Short.importedName».valueOf(defaultValue));
348 «ELSEIF "java.lang.Integer".equals(prop.returnType.fullyQualifiedName)»
349 return new «genTO.name»(«Integer.importedName».valueOf(defaultValue));
350 «ELSEIF "java.lang.Long".equals(prop.returnType.fullyQualifiedName)»
351 return new «genTO.name»(«Long.importedName».valueOf(defaultValue));
353 return new «genTO.name»(new «prop.returnType.importedName»(defaultValue));
360 def protected bitsArgs() '''
361 «List.importedName»<«String.importedName»> properties = «Lists.importedName».newArrayList(«allProperties.propsAsArgs»);
362 if (!properties.contains(defaultValue)) {
363 throw new «IllegalArgumentException.importedName»("invalid default parameter");
366 return new «genTO.name»(
367 «FOR prop : allProperties SEPARATOR ","»
368 properties.get(i++).equals(defaultValue) ? «Boolean.importedName».TRUE : null
373 def protected propsAsArgs(Iterable<GeneratedProperty> properties) '''
374 «FOR prop : properties SEPARATOR ","»
380 * Template method which generates JAVA class declaration.
382 * @param isInnerClass boolean value which specify if generated class is|isn't inner
383 * @return string with class declaration in JAVA format
385 def protected generateClassDeclaration(boolean isInnerClass) '''
389 ELSEIF (type.abstract)»«
393 ENDIF»class «type.name»«
394 IF (genTO.superType !== null)»«
395 " extends "»«genTO.superType.importedName»«
397 «IF (!type.implements.empty)»«
399 FOR type : type.implements SEPARATOR ", "»«
406 * Template method which generates JAVA enum type.
408 * @return string with inner enum source code in JAVA format
410 def protected enumDeclarations() '''
412 «FOR e : enums SEPARATOR "\n"»
413 «new EnumTemplate(javaType.getEnclosedType(e.identifier), e).generateAsInnerClass»
418 def protected suidDeclaration() '''
419 «IF genTO.SUID !== null»
420 private static final long serialVersionUID = «genTO.SUID.value»L;
424 def protected annotationDeclaration() '''
425 «IF genTO.getAnnotations !== null»
426 «FOR e : genTO.getAnnotations»
433 * Template method which generates JAVA constants.
435 * @return string with constants in JAVA format
437 def protected constantsDeclarations() '''
440 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
441 «val cValue = c.value as Map<String, String>»
442 public static final «List.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
443 FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»);
444 «IF cValue.size == 1»
445 private static final «Pattern.importedName» «Constants.MEMBER_PATTERN_LIST» = «Pattern.importedName».compile(«TypeConstants.PATTERN_CONSTANT_NAME».get(0));
446 private static final String «Constants.MEMBER_REGEX_LIST» = "«cValue.values.get(0).escapeJava»";
448 private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST» = «CodeHelpers.importedName».compilePatterns(«TypeConstants.PATTERN_CONSTANT_NAME»);
449 private static final String[] «Constants.MEMBER_REGEX_LIST» = { «
450 FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
460 * Template method which generates JAVA class attributes.
462 * @return string with the class attributes in JAVA format
464 def protected generateFields() '''
465 «IF !properties.empty»
467 private«IF isReadOnly(f)» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
472 protected def isReadOnly(GeneratedProperty field) {
473 return field.readOnly
477 * Template method which generates the method <code>hashCode()</code>.
479 * @return string with the <code>hashCode()</code> method definition in JAVA format
481 def protected generateHashCode() '''
482 «IF !genTO.hashCodeIdentifiers.empty»
483 @«Override.importedName»
484 public int hashCode() {
485 final int prime = 31;
487 «FOR property : genTO.hashCodeIdentifiers»
488 «IF property.returnType.name.contains("[")»
489 result = prime * result + «Arrays.importedName».hashCode(«property.fieldName»);
491 result = prime * result + «Objects.importedName».hashCode(«property.fieldName»);
500 * Template method which generates the method <code>equals()</code>.
502 * @return string with the <code>equals()</code> method definition in JAVA format
504 def protected generateEquals() '''
505 «IF !genTO.equalsIdentifiers.empty»
506 @«Override.importedName»
507 public boolean equals(java.lang.Object obj) {
514 if (getClass() != obj.getClass()) {
517 «type.name» other = («type.name») obj;
518 «FOR property : genTO.equalsIdentifiers»
519 «val fieldName = property.fieldName»
520 «IF property.returnType.name.contains("[")»
521 if (!«Arrays.importedName».equals(«fieldName», other.«fieldName»)) {
523 if (!«Objects.importedName».equals(«fieldName», other.«fieldName»)) {
533 def GeneratedProperty getPropByName(String name) {
534 for (GeneratedProperty prop : allProperties) {
535 if (prop.name.equals(name)) {