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.base.Preconditions
17 import com.google.common.collect.ImmutableList
18 import com.google.common.collect.Lists
19 import java.beans.ConstructorProperties
20 import java.util.ArrayList
21 import java.util.Base64;
22 import java.util.Collections
25 import java.util.regex.Pattern
26 import org.gaul.modernizer_maven_annotations.SuppressModernizer
27 import org.opendaylight.mdsal.binding.model.api.ConcreteType
28 import org.opendaylight.mdsal.binding.model.api.Constant
29 import org.opendaylight.mdsal.binding.model.api.Enumeration
30 import org.opendaylight.mdsal.binding.model.api.GeneratedProperty
31 import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject
32 import org.opendaylight.mdsal.binding.model.api.Restrictions
33 import org.opendaylight.mdsal.binding.model.api.Type
34 import org.opendaylight.mdsal.binding.model.util.TypeConstants
35 import org.opendaylight.yangtools.yang.binding.CodeHelpers
36 import org.opendaylight.yangtools.yang.common.Empty
37 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition
40 * Template for generating JAVA class.
43 class ClassTemplate extends BaseTemplate {
45 protected val List<GeneratedProperty> properties
46 protected val List<GeneratedProperty> finalProperties
47 protected val List<GeneratedProperty> parentProperties
48 protected val Iterable<GeneratedProperty> allProperties
49 protected val Restrictions restrictions
52 * List of enumeration which are generated as JAVA enum type.
54 protected val List<Enumeration> enums
57 * List of constant instances which are generated as JAVA public static final attributes.
59 protected val List<Constant> consts
61 protected val GeneratedTransferObject genTO
63 val AbstractRangeGenerator<?> rangeGenerator
66 * Creates instance of this class with concrete <code>genType</code>.
68 * @param genType generated transfer object which will be transformed to JAVA class source code
70 new(GeneratedTransferObject genType) {
71 this(new TopLevelJavaGeneratedType(genType), genType)
75 * Creates instance of this class with concrete <code>genType</code>.
77 * @param genType generated transfer object which will be transformed to JAVA class source code
79 new(AbstractJavaGeneratedType javaType, GeneratedTransferObject genType) {
80 super(javaType, genType)
82 this.properties = genType.properties
83 this.finalProperties = GeneratorUtil.resolveReadOnlyPropertiesFromTO(genTO.properties)
84 this.parentProperties = GeneratorUtil.getPropertiesOfAllParents(genTO)
85 this.restrictions = genType.restrictions
87 var List<GeneratedProperty> sorted = new ArrayList<GeneratedProperty>();
88 sorted.addAll(properties);
89 sorted.addAll(parentProperties);
90 Collections.sort(sorted, [p1, p2|
91 p1.name.compareTo(p2.name)
94 this.allProperties = sorted
95 this.enums = genType.enumerations
96 this.consts = genType.constantDefinitions
98 if (restrictions !== null && restrictions.rangeConstraint.present) {
99 rangeGenerator = requireNonNull(AbstractRangeGenerator.forType(findProperty(genType, "value").returnType))
101 rangeGenerator = null
106 * Generates JAVA class source code (class body only).
108 * @return string with JAVA class body source code
110 def CharSequence generateAsInnerClass() {
111 return generateBody(true)
114 override protected body() {
119 * Template method which generates class body.
121 * @param isInnerClass boolean value which specify if generated class is|isn't inner
122 * @return string with class source code in JAVA format
124 def protected generateBody(boolean isInnerClass) '''
125 «wrapToDocumentation(formatDataForJavaDoc(type))»
126 «annotationDeclaration»
127 «generateClassDeclaration(isInnerClass)» {
129 «innerClassesDeclarations»
131 «constantsDeclarations»
134 «IF restrictions !== null»
135 «IF restrictions.lengthConstraint.present»
136 «LengthGenerator.generateLengthChecker("_value", findProperty(genTO, "value").returnType, restrictions.lengthConstraint.get, this)»
138 «IF restrictions.rangeConstraint.present»
139 «rangeGenerator.generateRangeChecker("_value", restrictions.rangeConstraint.get, this)»
147 «FOR field : properties SEPARATOR "\n"»
154 «IF (genTO.isTypedef() && genTO.getBaseType instanceof BitsTypeDefinition)»
155 «generateGetValueForBitsTypeDef»
162 «generateToString(genTO.toStringIdentifiers)»
168 * Template method which generates the method <code>getValue()</code> for typedef,
169 * which base type is BitsDefinition.
171 * @return string with the <code>getValue()</code> method definition in JAVA format
173 def protected generateGetValueForBitsTypeDef() '''
175 public boolean[] getValue() {
176 return new boolean[]{
177 «FOR property: genTO.properties SEPARATOR ','»
185 * Template method which generates inner classes inside this interface.
187 * @return string with the source code for inner classes in JAVA format
189 def protected innerClassesDeclarations() '''
190 «IF !type.enclosedTypes.empty»
191 «FOR innerClass : type.enclosedTypes SEPARATOR "\n"»
192 «generateInnerClass(innerClass)»
197 def protected constructors() '''
199 «genUnionConstructor»
201 «allValuesConstructor»
203 «IF !allProperties.empty»
206 «IF properties.empty && !parentProperties.empty »
211 def protected allValuesConstructor() '''
212 «IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
213 @«ConstructorProperties.importedName»("value")
215 public «type.name»(«allProperties.asArgumentsDeclaration») {
216 «IF false == parentProperties.empty»
217 super(«parentProperties.asArguments»);
219 «FOR p : allProperties»
220 «generateRestrictions(type, p.fieldName.toString, p.returnType)»
224 * If we have patterns, we need to apply them to the value field. This is a sad
225 * consequence of how this code is structured.
227 IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
228 «CodeHelpers.importedName».requireValue(_value);
229 «genPatternEnforcer("_value")»
233 «IF p.returnType.importedName.contains("[]")»
234 «IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name
236 this.«p.fieldName» = «p.fieldName».clone();
238 this.«p.fieldName» = «p.fieldName» == null ? null : «p.fieldName».clone();
241 this.«p.fieldName» = «p.fieldName»;
248 def protected genUnionConstructor() '''
249 «FOR p : allProperties»
250 «val List<GeneratedProperty> other = new ArrayList(properties)»
252 «genConstructor(p, other)»
258 def protected genConstructor(GeneratedProperty property, GeneratedProperty... other) '''
259 public «type.name»(«property.returnType.importedName + " " + property.name») {
260 «IF false == parentProperties.empty»
261 super(«parentProperties.asArguments»);
264 «generateRestrictions(type, property.fieldName.toString, property.returnType)»
266 this.«property.fieldName» = «property.name»;
268 this.«p.fieldName» = null;
273 def private genPatternEnforcer(String ref) '''
275 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
276 «CodeHelpers.importedName».checkPattern(«ref», «Constants.MEMBER_PATTERN_LIST», «Constants.MEMBER_REGEX_LIST»);
281 def private static paramValue(Type returnType, String paramName) {
282 if (returnType instanceof ConcreteType) {
285 return paramName + ".getValue()"
289 def private generateRestrictions(Type type, String paramName, Type returnType) '''
290 «val restrictions = type.restrictions»
291 «IF restrictions !== null»
292 «IF restrictions.lengthConstraint.present || restrictions.rangeConstraint.present»
293 if («paramName» != null) {
294 «IF restrictions.lengthConstraint.present»
295 «LengthGenerator.generateLengthCheckerCall(paramName, paramValue(returnType, paramName))»
297 «IF restrictions.rangeConstraint.present»
298 «rangeGenerator.generateRangeCheckerCall(paramName, paramValue(returnType, paramName))»
305 def protected copyConstructor() '''
307 * Creates a copy from Source Object.
309 * @param source Source object
311 public «type.name»(«type.name» source) {
312 «IF false == parentProperties.empty»
316 this.«p.fieldName» = source.«p.fieldName»;
321 def protected parentConstructor() '''
323 * Creates a new instance from «genTO.superType.importedName»
325 * @param source Source object
327 public «type.name»(«genTO.superType.importedName» source) {
329 «genPatternEnforcer("getValue()")»
333 def protected defaultInstance() '''
334 «IF genTO.typedef && !allProperties.empty && !genTO.unionType»
335 «val prop = allProperties.get(0)»
336 «IF !("org.opendaylight.yangtools.yang.binding.InstanceIdentifier".equals(prop.returnType.fullyQualifiedName))»
337 public static «genTO.name» getDefaultInstance(String defaultValue) {
338 «IF BYTE_ARRAY.equals(prop.returnType)»
339 return new «genTO.name»(«Base64.importedName».getDecoder().decode(defaultValue));
340 «ELSEIF STRING.equals(prop.returnType)»
341 return new «genTO.name»(defaultValue);
342 «ELSEIF Constants.EMPTY.equals(prop.returnType)»
343 «Preconditions.importedName».checkArgument(defaultValue.isEmpty(), "Invalid value %s", defaultValue);
344 return new «genTO.name»(«Empty.importedName».getInstance());
345 «ELSEIF allProperties.size > 1»
347 «ELSEIF BOOLEAN.equals(prop.returnType)»
348 return new «genTO.name»(«Boolean.importedName».valueOf(defaultValue));
349 «ELSEIF "java.lang.Byte".equals(prop.returnType.fullyQualifiedName)»
350 return new «genTO.name»(«Byte.importedName».valueOf(defaultValue));
351 «ELSEIF "java.lang.Short".equals(prop.returnType.fullyQualifiedName)»
352 return new «genTO.name»(«Short.importedName».valueOf(defaultValue));
353 «ELSEIF "java.lang.Integer".equals(prop.returnType.fullyQualifiedName)»
354 return new «genTO.name»(«Integer.importedName».valueOf(defaultValue));
355 «ELSEIF "java.lang.Long".equals(prop.returnType.fullyQualifiedName)»
356 return new «genTO.name»(«Long.importedName».valueOf(defaultValue));
358 return new «genTO.name»(new «prop.returnType.importedName»(defaultValue));
365 def protected bitsArgs() '''
366 «List.importedName»<«String.importedName»> properties = «Lists.importedName».newArrayList(«allProperties.propsAsArgs»);
367 if (!properties.contains(defaultValue)) {
368 throw new «IllegalArgumentException.importedName»("invalid default parameter");
371 return new «genTO.name»(
372 «FOR prop : allProperties SEPARATOR ","»
373 properties.get(i++).equals(defaultValue) ? «Boolean.importedName».TRUE : null
378 def protected propsAsArgs(Iterable<GeneratedProperty> properties) '''
379 «FOR prop : properties SEPARATOR ","»
385 * Template method which generates JAVA class declaration.
387 * @param isInnerClass boolean value which specify if generated class is|isn't inner
388 * @return string with class declaration in JAVA format
390 def protected generateClassDeclaration(boolean isInnerClass) '''
394 ELSEIF (type.abstract)»«
398 ENDIF»class «type.name»«
399 IF (genTO.superType !== null)»«
400 " extends "»«genTO.superType.importedName»«
402 «IF (!type.implements.empty)»«
404 FOR type : type.implements SEPARATOR ", "»«
411 * Template method which generates JAVA enum type.
413 * @return string with inner enum source code in JAVA format
415 def protected enumDeclarations() '''
417 «FOR e : enums SEPARATOR "\n"»
418 «new EnumTemplate(javaType.getEnclosedType(e.identifier), e).generateAsInnerClass»
423 def protected suidDeclaration() '''
424 «IF genTO.SUID !== null»
425 private static final long serialVersionUID = «genTO.SUID.value»L;
429 def protected annotationDeclaration() '''
430 «IF genTO.getAnnotations !== null»
431 «FOR e : genTO.getAnnotations»
438 * Template method which generates JAVA constants.
440 * @return string with constants in JAVA format
442 def protected constantsDeclarations() '''
445 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
446 «val cValue = c.value as Map<String, String>»
447 public static final «List.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
448 FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»);
449 «IF cValue.size == 1»
450 private static final «Pattern.importedName» «Constants.MEMBER_PATTERN_LIST» = «Pattern.importedName».compile(«TypeConstants.PATTERN_CONSTANT_NAME».get(0));
451 private static final String «Constants.MEMBER_REGEX_LIST» = "«cValue.values.get(0).escapeJava»";
453 private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST» = «CodeHelpers.importedName».compilePatterns(«TypeConstants.PATTERN_CONSTANT_NAME»);
454 private static final String[] «Constants.MEMBER_REGEX_LIST» = { «
455 FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
465 * Template method which generates JAVA class attributes.
467 * @return string with the class attributes in JAVA format
469 def protected generateFields() '''
470 «IF !properties.empty»
472 private«IF isReadOnly(f)» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
477 protected def isReadOnly(GeneratedProperty field) {
478 return field.readOnly
482 * Template method which generates the method <code>hashCode()</code>.
484 * @return string with the <code>hashCode()</code> method definition in JAVA format
486 def protected generateHashCode() {
487 val size = genTO.hashCodeIdentifiers.size
492 @«Override.importedName»
493 public int hashCode() {
495 «hashCodeResult(genTO.hashCodeIdentifiers)»
498 return «CodeHelpers.importedName».wrapperHashCode(«genTO.hashCodeIdentifiers.get(0).fieldName»);
505 * Template method which generates the method <code>equals()</code>.
507 * @return string with the <code>equals()</code> method definition in JAVA format
509 def private generateEquals() '''
510 «IF !genTO.equalsIdentifiers.empty»
511 @«Override.importedName»
512 public final boolean equals(java.lang.Object obj) {
516 if (!(obj instanceof «type.name»)) {
519 final «type.name» other = («type.name») obj;
520 «FOR property : genTO.equalsIdentifiers»
521 «val fieldName = property.fieldName»
522 if (!«property.importedUtilClass».equals(«fieldName», other.«fieldName»)) {
531 def GeneratedProperty getPropByName(String name) {
532 for (GeneratedProperty prop : allProperties) {
533 if (prop.name.equals(name)) {