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.GeneratedType
30 import org.opendaylight.mdsal.binding.model.api.Restrictions
31 import org.opendaylight.mdsal.binding.model.api.Type
32 import org.opendaylight.mdsal.binding.model.util.TypeConstants
33 import org.opendaylight.yangtools.yang.binding.CodeHelpers
34 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition
37 * Template for generating JAVA class.
39 class ClassTemplate extends BaseTemplate {
41 protected val List<GeneratedProperty> properties
42 protected val List<GeneratedProperty> finalProperties
43 protected val List<GeneratedProperty> parentProperties
44 protected val Iterable<GeneratedProperty> allProperties;
45 protected val Restrictions restrictions
48 * List of enumeration which are generated as JAVA enum type.
50 protected val List<Enumeration> enums
53 * List of constant instances which are generated as JAVA public static final attributes.
55 protected val List<Constant> consts
58 * List of generated types which are enclosed inside <code>genType</code>
60 protected val List<GeneratedType> enclosedGeneratedTypes;
62 protected val GeneratedTransferObject genTO;
64 private val AbstractRangeGenerator<?> rangeGenerator
67 * Creates instance of this class with concrete <code>genType</code>.
69 * @param genType generated transfer object which will be transformed to JAVA class source code
71 new(GeneratedTransferObject genType) {
74 this.properties = genType.properties
75 this.finalProperties = GeneratorUtil.resolveReadOnlyPropertiesFromTO(genTO.properties)
76 this.parentProperties = GeneratorUtil.getPropertiesOfAllParents(genTO)
77 this.restrictions = genType.restrictions
79 var List<GeneratedProperty> sorted = new ArrayList<GeneratedProperty>();
80 sorted.addAll(properties);
81 sorted.addAll(parentProperties);
82 Collections.sort(sorted, [p1, p2|
83 p1.name.compareTo(p2.name)
86 this.allProperties = sorted
87 this.enums = genType.enumerations
88 this.consts = genType.constantDefinitions
89 this.enclosedGeneratedTypes = genType.enclosedTypes
91 if (restrictions !== null && restrictions.rangeConstraint.present) {
92 rangeGenerator = requireNonNull(AbstractRangeGenerator.forType(findProperty(genType, "value").returnType))
99 * Generates JAVA class source code (class body only).
101 * @return string with JAVA class body source code
103 def CharSequence generateAsInnerClass() {
104 return generateBody(true)
107 override protected body() {
112 * Template method which generates class body.
114 * @param isInnerClass boolean value which specify if generated class is|isn't inner
115 * @return string with class source code in JAVA format
117 def protected generateBody(boolean isInnerClass) '''
118 «wrapToDocumentation(formatDataForJavaDoc(type))»
119 «annotationDeclaration»
120 «generateClassDeclaration(isInnerClass)» {
122 «innerClassesDeclarations»
124 «constantsDeclarations»
127 «IF restrictions !== null»
128 «IF restrictions.lengthConstraint.present»
129 «LengthGenerator.generateLengthChecker("_value", findProperty(genTO, "value").returnType, restrictions.lengthConstraint.get)»
131 «IF restrictions.rangeConstraint.present»
132 «rangeGenerator.generateRangeChecker("_value", restrictions.rangeConstraint.get)»
140 «FOR field : properties SEPARATOR "\n"»
147 «IF (genTO.isTypedef() && genTO.getBaseType instanceof BitsTypeDefinition)»
148 «generateGetValueForBitsTypeDef»
155 «generateToString(genTO.toStringIdentifiers)»
161 * Template method which generates the method <code>getValue()</code> for typedef,
162 * which base type is BitsDefinition.
164 * @return string with the <code>getValue()</code> method definition in JAVA format
166 def protected generateGetValueForBitsTypeDef() '''
168 public boolean[] getValue() {
169 return new boolean[]{
170 «FOR property: genTO.properties SEPARATOR ','»
178 * Template method which generates inner classes inside this interface.
180 * @return string with the source code for inner classes in JAVA format
182 def protected innerClassesDeclarations() '''
183 «IF !enclosedGeneratedTypes.empty»
184 «FOR innerClass : enclosedGeneratedTypes SEPARATOR "\n"»
185 «IF (innerClass instanceof GeneratedTransferObject)»
186 «val classTemplate = new ClassTemplate(innerClass)»
187 «classTemplate.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.getRestrictions»
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 «val enumTemplate = new EnumTemplate(e)»
409 «enumTemplate.generateAsInnerClass»
414 def protected suidDeclaration() '''
415 «IF genTO.SUID !== null»
416 private static final long serialVersionUID = «genTO.SUID.value»L;
420 def protected annotationDeclaration() '''
421 «IF genTO.getAnnotations !== null»
422 «FOR e : genTO.getAnnotations»
429 * Template method which generates JAVA constants.
431 * @return string with constants in JAVA format
433 def protected constantsDeclarations() '''
436 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
437 «val cValue = c.value as Map<String, String>»
438 public static final «List.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
439 FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»);
440 «IF cValue.size == 1»
441 private static final «Pattern.importedName» «Constants.MEMBER_PATTERN_LIST» = «Pattern.importedName».compile(«TypeConstants.PATTERN_CONSTANT_NAME».get(0));
442 private static final String «Constants.MEMBER_REGEX_LIST» = "«cValue.values.get(0).escapeJava»";
444 private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST» = «CodeHelpers.importedName».compilePatterns(«TypeConstants.PATTERN_CONSTANT_NAME»);
445 private static final String[] «Constants.MEMBER_REGEX_LIST» = { «
446 FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
456 * Template method which generates JAVA class attributes.
458 * @return string with the class attributes in JAVA format
460 def protected generateFields() '''
461 «IF !properties.empty»
463 private«IF isReadOnly(f)» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
468 protected def isReadOnly(GeneratedProperty field) {
469 return field.readOnly
473 * Template method which generates the method <code>hashCode()</code>.
475 * @return string with the <code>hashCode()</code> method definition in JAVA format
477 def protected generateHashCode() '''
478 «IF !genTO.hashCodeIdentifiers.empty»
480 public int hashCode() {
481 final int prime = 31;
483 «FOR property : genTO.hashCodeIdentifiers»
484 «IF property.returnType.name.contains("[")»
485 result = prime * result + «Arrays.importedName».hashCode(«property.fieldName»);
487 result = prime * result + «Objects.importedName».hashCode(«property.fieldName»);
496 * Template method which generates the method <code>equals()</code>.
498 * @return string with the <code>equals()</code> method definition in JAVA format
500 def protected generateEquals() '''
501 «IF !genTO.equalsIdentifiers.empty»
503 public boolean equals(java.lang.Object obj) {
510 if (getClass() != obj.getClass()) {
513 «type.name» other = («type.name») obj;
514 «FOR property : genTO.equalsIdentifiers»
515 «val fieldName = property.fieldName»
516 «IF property.returnType.name.contains("[")»
517 if (!«Arrays.importedName».equals(«fieldName», other.«fieldName»)) {
519 if (!«Objects.importedName».equals(«fieldName», other.«fieldName»)) {
529 def GeneratedProperty getPropByName(String name) {
530 for (GeneratedProperty prop : allProperties) {
531 if (prop.name.equals(name)) {