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.collect.ImmutableList
17 import com.google.common.collect.Lists
18 import java.beans.ConstructorProperties
19 import java.util.ArrayList
20 import java.util.Base64;
21 import java.util.Comparator
24 import java.util.Objects
25 import java.util.regex.Pattern
26 import org.opendaylight.mdsal.binding.model.api.ConcreteType
27 import org.opendaylight.mdsal.binding.model.api.Constant
28 import org.opendaylight.mdsal.binding.model.api.Enumeration
29 import org.opendaylight.mdsal.binding.model.api.GeneratedProperty
30 import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject
31 import org.opendaylight.mdsal.binding.model.api.Restrictions
32 import org.opendaylight.mdsal.binding.model.api.Type
33 import org.opendaylight.mdsal.binding.model.util.TypeConstants
34 import org.opendaylight.yangtools.yang.binding.CodeHelpers
35 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition
38 * Template for generating JAVA class.
40 class ClassTemplate extends BaseTemplate {
41 static val Comparator<GeneratedProperty> PROP_COMPARATOR = Comparator.comparing([prop | prop.name])
43 protected val List<GeneratedProperty> properties
44 protected val List<GeneratedProperty> finalProperties
45 protected val List<GeneratedProperty> parentProperties
46 protected val List<GeneratedProperty> allProperties
47 protected val Restrictions restrictions
50 * List of enumeration which are generated as JAVA enum type.
52 protected val List<Enumeration> enums
55 * List of constant instances which are generated as JAVA public static final attributes.
57 protected val List<Constant> consts
59 protected val GeneratedTransferObject genTO
61 val AbstractRangeGenerator<?> rangeGenerator
64 * Creates instance of this class with concrete <code>genType</code>.
66 * @param genType generated transfer object which will be transformed to JAVA class source code
68 new(GeneratedTransferObject genType) {
69 this(new TopLevelJavaGeneratedType(genType), genType)
73 * Creates instance of this class with concrete <code>genType</code>.
75 * @param genType generated transfer object which will be transformed to JAVA class source code
77 new(AbstractJavaGeneratedType javaType, GeneratedTransferObject genType) {
78 super(javaType, genType)
80 this.properties = genType.properties
81 this.finalProperties = GeneratorUtil.resolveReadOnlyPropertiesFromTO(genTO.properties)
82 this.parentProperties = GeneratorUtil.getPropertiesOfAllParents(genTO)
83 this.restrictions = genType.restrictions
85 val sorted = new ArrayList();
86 sorted.addAll(properties);
87 sorted.addAll(parentProperties);
88 sorted.sort(PROP_COMPARATOR);
90 this.allProperties = sorted
91 this.enums = genType.enumerations
92 this.consts = genType.constantDefinitions
94 if (restrictions !== null && restrictions.rangeConstraint.present) {
95 rangeGenerator = requireNonNull(AbstractRangeGenerator.forType(findProperty(genType, "value").returnType))
102 * Generates JAVA class source code (class body only).
104 * @return string with JAVA class body source code
106 def CharSequence generateAsInnerClass() {
107 return generateBody(true)
110 override protected body() {
115 * Template method which generates class body.
117 * @param isInnerClass boolean value which specify if generated class is|isn't inner
118 * @return string with class source code in JAVA format
120 def protected generateBody(boolean isInnerClass) '''
121 «wrapToDocumentation(formatDataForJavaDoc(type))»
122 «annotationDeclaration»
123 «generateClassDeclaration(isInnerClass)» {
125 «innerClassesDeclarations»
127 «constantsDeclarations»
130 «IF restrictions !== null»
131 «IF restrictions.lengthConstraint.present»
132 «LengthGenerator.generateLengthChecker("_value", findProperty(genTO, "value").returnType, restrictions.lengthConstraint.get, this)»
134 «IF restrictions.rangeConstraint.present»
135 «rangeGenerator.generateRangeChecker("_value", restrictions.rangeConstraint.get, this)»
143 «FOR field : properties SEPARATOR "\n"»
150 «IF (genTO.isTypedef() && genTO.getBaseType instanceof BitsTypeDefinition)»
151 «generateGetValueForBitsTypeDef»
158 «generateToString(genTO.toStringIdentifiers)»
164 * Template method which generates the method <code>getValue()</code> for typedef,
165 * which base type is BitsDefinition.
167 * @return string with the <code>getValue()</code> method definition in JAVA format
169 def protected generateGetValueForBitsTypeDef() '''
171 public boolean[] getValue() {
172 return new boolean[]{
173 «FOR property: genTO.properties SEPARATOR ','»
181 * Template method which generates inner classes inside this interface.
183 * @return string with the source code for inner classes in JAVA format
185 def protected innerClassesDeclarations() '''
186 «IF !type.enclosedTypes.empty»
187 «FOR innerClass : type.enclosedTypes SEPARATOR "\n"»
188 «generateInnerClass(innerClass)»
193 def protected constructors() '''
195 «genUnionConstructor»
197 «allValuesConstructor»
199 «IF !allProperties.empty»
202 «IF properties.empty && !parentProperties.empty »
207 def protected allValuesConstructor() '''
208 «IF genTO.typedef && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
209 @«ConstructorProperties.importedName»("value")
211 public «type.name»(«allProperties.asArgumentsDeclaration») {
212 «IF false == parentProperties.empty»
213 super(«parentProperties.asArguments»);
215 «FOR p : allProperties»
216 «generateRestrictions(type, p.fieldName.toString, p.returnType)»
220 * If we have patterns, we need to apply them to the value field. This is a sad
221 * consequence of how this code is structured.
223 IF genTO.typedef && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
224 «Objects.importedName».requireNonNull(_value, "Supplied value may not be null");
225 «genPatternEnforcer("_value")»
229 «val fieldName = p.fieldName»
230 «IF p.returnType.importedName.contains("[]")»
231 «IF genTO.typedef && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
232 this.«fieldName» = «fieldName».clone();
234 this.«fieldName» = «fieldName» == null ? null : «fieldName».clone();
237 this.«fieldName» = «fieldName»;
244 def protected genUnionConstructor() '''
245 «FOR p : allProperties»
246 «val List<GeneratedProperty> other = new ArrayList(properties)»
248 «genConstructor(p, other)»
254 def protected genConstructor(GeneratedProperty property, Iterable<GeneratedProperty> other) '''
255 public «type.name»(«property.returnType.importedName + " " + property.name») {
256 «IF false == parentProperties.empty»
257 super(«parentProperties.asArguments»);
260 «val fieldName = property.fieldName»
261 «generateRestrictions(type, fieldName.toString, property.returnType)»
263 this.«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 «val fieldName = p.fieldName»
314 this.«fieldName» = source.«fieldName»;
319 def protected parentConstructor() '''
321 * Creates a new instance from «genTO.superType.importedName»
323 * @param source Source object
325 public «type.name»(«genTO.superType.importedName» source) {
327 «genPatternEnforcer("getValue()")»
331 def protected defaultInstance() '''
332 «IF genTO.typedef && !allProperties.empty && !genTO.unionType»
333 «val prop = allProperties.get(0)»
334 «IF !("org.opendaylight.yangtools.yang.binding.InstanceIdentifier".equals(prop.returnType.fullyQualifiedName))»
335 public static «genTO.name» getDefaultInstance(String defaultValue) {
336 «IF BYTE_ARRAY.equals(prop.returnType)»
337 return new «genTO.name»(«Base64.importedName».getDecoder().decode(defaultValue));
338 «ELSEIF STRING.equals(prop.returnType)»
339 return new «genTO.name»(defaultValue);
340 «ELSEIF allProperties.size > 1»
342 «ELSEIF BOOLEAN.equals(prop.returnType)»
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.iterator.next.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 val size = genTO.hashCodeIdentifiers.size
487 @«Override.importedName»
488 public int hashCode() {
490 «hashCodeResult(genTO.hashCodeIdentifiers)»
493 return «CodeHelpers.importedName».wrapperHashCode(«genTO.hashCodeIdentifiers.get(0).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.importedUtilClass».equals(«fieldName», other.«fieldName»)) {
529 def GeneratedProperty getPropByName(String name) {
530 for (GeneratedProperty prop : allProperties) {
531 if (prop.name.equals(name)) {