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.yangtools.sal.java.api.generator
10 import com.google.common.collect.ImmutableList
11 import com.google.common.collect.Lists
12 import com.google.common.collect.Range
13 import com.google.common.io.BaseEncoding
14 import java.beans.ConstructorProperties
15 import java.math.BigDecimal
16 import java.math.BigInteger
17 import java.util.ArrayList
18 import java.util.Arrays
19 import java.util.Collections
21 import java.util.regex.Pattern
22 import org.opendaylight.yangtools.binding.generator.util.TypeConstants
23 import org.opendaylight.yangtools.sal.binding.model.api.Constant
24 import org.opendaylight.yangtools.sal.binding.model.api.Enumeration
25 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
26 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
27 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
28 import org.opendaylight.yangtools.sal.binding.model.api.Restrictions
29 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition
30 import com.google.common.base.Preconditions
31 import org.opendaylight.yangtools.sal.binding.model.api.Type
32 import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType
35 * Template for generating JAVA class.
37 class ClassTemplate extends BaseTemplate {
39 protected val List<GeneratedProperty> properties
40 protected val List<GeneratedProperty> finalProperties
41 protected val List<GeneratedProperty> parentProperties
42 protected val Iterable<GeneratedProperty> allProperties;
43 protected val Restrictions restrictions
46 * List of enumeration which are generated as JAVA enum type.
48 protected val List<Enumeration> enums
51 * List of constant instances which are generated as JAVA public static final attributes.
53 protected val List<Constant> consts
56 * List of generated types which are enclosed inside <code>genType</code>
58 protected val List<GeneratedType> enclosedGeneratedTypes;
60 protected val GeneratedTransferObject genTO;
63 * Creates instance of this class with concrete <code>genType</code>.
65 * @param genType generated transfer object which will be transformed to JAVA class source code
67 new(GeneratedTransferObject genType) {
70 this.properties = genType.properties
71 this.finalProperties = GeneratorUtil.resolveReadOnlyPropertiesFromTO(genTO.properties)
72 this.parentProperties = GeneratorUtil.getPropertiesOfAllParents(genTO)
73 this.restrictions = genType.restrictions
75 var List<GeneratedProperty> sorted = new ArrayList<GeneratedProperty>();
76 sorted.addAll(properties);
77 sorted.addAll(parentProperties);
78 Collections.sort(sorted, [p1, p2|
79 p1.name.compareTo(p2.name)
82 this.allProperties = sorted
83 this.enums = genType.enumerations
84 this.consts = genType.constantDefinitions
85 this.enclosedGeneratedTypes = genType.enclosedTypes
89 * Generates JAVA class source code (class body only).
91 * @return string with JAVA class body source code
93 def CharSequence generateAsInnerClass() {
94 return generateBody(true)
97 override protected body() {
102 * Template method which generates class body.
104 * @param isInnerClass boolean value which specify if generated class is|isn't inner
105 * @return string with class source code in JAVA format
107 def protected generateBody(boolean isInnerClass) '''
108 «wrapToDocumentation(formatDataForJavaDoc(type))»
109 «generateClassDeclaration(isInnerClass)» {
111 «innerClassesDeclarations»
113 «constantsDeclarations»
116 «IF restrictions != null && (!restrictions.rangeConstraints.nullOrEmpty ||
117 !restrictions.lengthConstraints.nullOrEmpty)»
118 «generateConstraints»
125 «FOR field : properties SEPARATOR "\n"»
132 «IF (genTO.isTypedef() && genTO.getBaseType instanceof BitsTypeDefinition)»
133 «generateGetValueForBitsTypeDef»
140 «generateToString(genTO.toStringIdentifiers)»
142 «generateLengthMethod("length", "_length")»
144 «generateRangeMethod("range", "_range")»
151 * Template method which generates the method <code>getValue()</code> for typedef,
152 * which base type is BitsDefinition.
154 * @return string with the <code>getValue()</code> method definition in JAVA format
156 def protected generateGetValueForBitsTypeDef() '''
158 public boolean[] getValue() {
159 return new boolean[]{
160 «FOR property: genTO.properties SEPARATOR ','»
167 def private generateLengthMethod(String methodName, String varName) '''
168 «IF restrictions != null && !(restrictions.lengthConstraints.empty)»
169 «val numberClass = restrictions.lengthConstraints.iterator.next.min.class»
171 * @deprecated This method is slated for removal in a future release. See BUG-1485 for details.
174 public static «List.importedName»<«Range.importedName»<«numberClass.importedNumber»>> «methodName»() {
180 def private generateRangeMethod(String methodName, String varName) '''
181 «IF restrictions != null && !(restrictions.rangeConstraints.empty)»
182 «val returnType = allProperties.iterator.next.returnType»
184 * @deprecated This method is slated for removal in a future release. See BUG-1485 for details.
187 public static «List.importedName»<«Range.importedName»<«returnType.importedNumber»>> «methodName»() {
194 * Template method which generates inner classes inside this interface.
196 * @return string with the source code for inner classes in JAVA format
198 def protected innerClassesDeclarations() '''
199 «IF !enclosedGeneratedTypes.empty»
200 «FOR innerClass : enclosedGeneratedTypes SEPARATOR "\n"»
201 «IF (innerClass instanceof GeneratedTransferObject)»
202 «val classTemplate = new ClassTemplate(innerClass)»
203 «classTemplate.generateAsInnerClass»
210 def protected constructors() '''
212 «genUnionConstructor»
214 «allValuesConstructor»
216 «IF !allProperties.empty»
219 «IF properties.empty && !parentProperties.empty »
224 def private generateConstraints() '''
226 «IF !restrictions.rangeConstraints.nullOrEmpty»
227 «generateRangeConstraints»
229 «IF !restrictions.lengthConstraints.nullOrEmpty»
230 «generateLengthConstraints»
235 private def generateRangeConstraints() '''
236 «IF !allProperties.nullOrEmpty»
237 «val returnType = allProperties.iterator.next.returnType»
238 «IF returnType.fullyQualifiedName.equals(BigDecimal.canonicalName)»
239 «rangeBody(restrictions, BigDecimal, genTO.importedName, "_range")»
241 «rangeBody(restrictions, BigInteger, genTO.importedName, "_range")»
246 private def rangeBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
247 «ImmutableList.importedName».Builder<«Range.importedName»<«numberClass.importedName»>> builder = «ImmutableList.importedName».builder();
248 «FOR r : restrictions.rangeConstraints»
249 builder.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», «numericValue(numberClass, r.max)»));
251 «varName» = builder.build();
254 private def lengthBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
255 «ImmutableList.importedName».Builder<«Range.importedName»<«numberClass.importedName»>> builder = «ImmutableList.importedName».builder();
256 «FOR r : restrictions.lengthConstraints»
257 builder.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», «numericValue(numberClass, r.max)»));
259 «varName» = builder.build();
262 private def generateLengthConstraints() '''
263 «IF restrictions != null && !(restrictions.lengthConstraints.empty)»
264 «val numberClass = restrictions.lengthConstraints.iterator.next.min.class»
265 «IF numberClass.equals(typeof(BigDecimal))»
266 «lengthBody(restrictions, numberClass, genTO.importedName, "_length")»
268 «lengthBody(restrictions, typeof(BigInteger), genTO.importedName, "_length")»
273 def protected allValuesConstructor() '''
274 «IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
275 @«ConstructorProperties.importedName»("value")
277 public «type.name»(«allProperties.asArgumentsDeclaration») {
278 «IF false == parentProperties.empty»
279 super(«parentProperties.asArguments»);
281 «FOR p : allProperties»
282 «generateRestrictions(type, p.fieldName.toString, p.returnType)»
286 * If we have patterns, we need to apply them to the value field. This is a sad
287 * consequence of how this code is structured.
289 IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
291 «Preconditions.importedName».checkNotNull(_value, "Supplied value may not be null");
294 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME && c.value instanceof List<?>»
295 boolean valid = false;
296 for (Pattern p : patterns) {
297 if (p.matcher(_value).matches()) {
303 «Preconditions.importedName».checkArgument(valid, "Supplied value \"%s\" does not match any of the permitted patterns %s", _value, «TypeConstants.PATTERN_CONSTANT_NAME»);
309 «IF p.returnType.importedName.contains("[]")»
310 this.«p.fieldName» = «p.fieldName» == null ? null : «p.fieldName».clone();
312 this.«p.fieldName» = «p.fieldName»;
319 def protected genUnionConstructor() '''
320 «FOR p : allProperties»
321 «val List<GeneratedProperty> other = new ArrayList(properties)»
323 «genConstructor(p, other)»
329 def protected genConstructor(GeneratedProperty property, GeneratedProperty... other) '''
330 public «type.name»(«property.returnType.importedName + " " + property.name») {
331 «IF false == parentProperties.empty»
332 super(«parentProperties.asArguments»);
335 «generateRestrictions(type, property.fieldName.toString, property.returnType)»
337 this.«property.fieldName» = «property.name»;
339 this.«p.fieldName» = null;
344 def private generateRestrictions(Type type, String paramName, Type returnType) '''
345 «val restrictions = type.getRestrictions»
346 «IF restrictions !== null»
347 «val boolean isNestedType = !(returnType instanceof ConcreteType)»
348 «IF !restrictions.lengthConstraints.empty»
349 «generateLengthRestriction(returnType, restrictions, paramName, isNestedType)»
351 «IF !restrictions.rangeConstraints.empty»
352 «generateRangeRestriction(returnType, paramName, isNestedType)»
357 def private generateLengthRestriction(Type returnType, Restrictions restrictions, String paramName, boolean isNestedType) '''
358 «val clazz = restrictions.lengthConstraints.iterator.next.min.class»
359 if («paramName» != null) {
360 «printLengthConstraint(returnType, clazz, paramName, isNestedType, returnType.name.contains("["))»
361 boolean isValidLength = false;
362 for («Range.importedName»<«clazz.importedNumber»> r : «IF isNestedType»«returnType.importedName».«ENDIF»length()) {
363 if (r.contains(_constraint)) {
364 isValidLength = true;
367 if (!isValidLength) {
368 throw new IllegalArgumentException(String.format("Invalid length: %s, expected: %s.", «paramName», «IF isNestedType»«returnType.importedName».«ENDIF»length()));
373 def private generateRangeRestriction(Type returnType, String paramName, boolean isNestedType) '''
374 if («paramName» != null) {
375 «printRangeConstraint(returnType, paramName, isNestedType)»
376 boolean isValidRange = false;
377 for («Range.importedName»<«returnType.importedNumber»> r : «IF isNestedType»«returnType.importedName».«ENDIF»range()) {
378 if (r.contains(_constraint)) {
383 throw new IllegalArgumentException(String.format("Invalid range: %s, expected: %s.", «paramName», «IF isNestedType»«returnType.importedName».«ENDIF»range()));
388 def protected copyConstructor() '''
390 * Creates a copy from Source Object.
392 * @param source Source object
394 public «type.name»(«type.name» source) {
395 «IF false == parentProperties.empty»
399 this.«p.fieldName» = source.«p.fieldName»;
404 def protected parentConstructor() '''
406 * Creates a new instance from «genTO.superType.importedName»
408 * @param source Source object
410 public «type.name»(«genTO.superType.importedName» source) {
415 def protected defaultInstance() '''
416 «IF genTO.typedef && !allProperties.empty && !genTO.unionType»
417 «val prop = allProperties.get(0)»
418 «IF !("org.opendaylight.yangtools.yang.binding.InstanceIdentifier".equals(prop.returnType.fullyQualifiedName))»
419 public static «genTO.name» getDefaultInstance(String defaultValue) {
420 «IF "byte[]".equals(prop.returnType.name)»
421 «BaseEncoding.importedName» baseEncoding = «BaseEncoding.importedName».base64();
422 return new «genTO.name»(baseEncoding.decode(defaultValue));
423 «ELSEIF "java.lang.String".equals(prop.returnType.fullyQualifiedName)»
424 return new «genTO.name»(defaultValue);
425 «ELSEIF allProperties.size > 1»
427 «ELSEIF "java.lang.Boolean".equals(prop.returnType.fullyQualifiedName)»
428 return new «genTO.name»(Boolean.valueOf(defaultValue));
429 «ELSEIF "java.lang.Byte".equals(prop.returnType.fullyQualifiedName)»
430 return new «genTO.name»(Byte.valueOf(defaultValue));
431 «ELSEIF "java.lang.Short".equals(prop.returnType.fullyQualifiedName)»
432 return new «genTO.name»(Short.valueOf(defaultValue));
433 «ELSEIF "java.lang.Integer".equals(prop.returnType.fullyQualifiedName)»
434 return new «genTO.name»(Integer.valueOf(defaultValue));
435 «ELSEIF "java.lang.Long".equals(prop.returnType.fullyQualifiedName)»
436 return new «genTO.name»(Long.valueOf(defaultValue));
438 return new «genTO.name»(new «prop.returnType.importedName»(defaultValue));
445 def protected bitsArgs() '''
446 «List.importedName»<«String.importedName»> properties = «Lists.importedName».newArrayList(«allProperties.propsAsArgs»);
447 if (!properties.contains(defaultValue)) {
448 throw new «IllegalArgumentException.importedName»("invalid default parameter");
451 return new «genTO.name»(
452 «FOR prop : allProperties SEPARATOR ","»
453 properties.get(i++).equals(defaultValue) ? «Boolean.importedName».TRUE : null
458 def protected propsAsArgs(Iterable<GeneratedProperty> properties) '''
459 «FOR prop : properties SEPARATOR ","»
465 * Template method which generates JAVA class declaration.
467 * @param isInnerClass boolean value which specify if generated class is|isn't inner
468 * @return string with class declaration in JAVA format
470 def protected generateClassDeclaration(boolean isInnerClass) '''
474 ELSEIF (type.abstract)»«
478 ENDIF»class «type.name»«
479 IF (genTO.superType != null)»«
480 " extends "»«genTO.superType.importedName»«
482 «IF (!type.implements.empty)»«
484 FOR type : type.implements SEPARATOR ", "»«
491 * Template method which generates JAVA enum type.
493 * @return string with inner enum source code in JAVA format
495 def protected enumDeclarations() '''
497 «FOR e : enums SEPARATOR "\n"»
498 «val enumTemplate = new EnumTemplate(e)»
499 «enumTemplate.generateAsInnerClass»
504 def protected suidDeclaration() '''
505 «IF genTO.SUID != null»
506 private static final long serialVersionUID = «genTO.SUID.value»L;
511 * Template method which generates JAVA constants.
513 * @return string with constants in JAVA format
515 def protected constantsDeclarations() '''
518 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
519 «val cValue = c.value»
520 «IF cValue instanceof List<?>»
521 private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST»;
522 public static final «List.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
523 FOR v : cValue SEPARATOR ", "»«
524 IF v instanceof String»"«
529 «generateStaticInicializationBlock»
532 public static final «c.type.importedName» «c.name» = «c.value»;
539 * Template method which generates JAVA static initialization block.
541 * @return string with static initialization block in JAVA format
543 def protected generateStaticInicializationBlock() '''
545 final «Pattern.importedName» a[] = new «Pattern.importedName»[«TypeConstants.PATTERN_CONSTANT_NAME».size()];
547 for (String regEx : «TypeConstants.PATTERN_CONSTANT_NAME») {
548 a[i++] = Pattern.compile(regEx);
551 «Constants.MEMBER_PATTERN_LIST» = a;
556 * Template method which generates JAVA class attributes.
558 * @return string with the class attributes in JAVA format
560 def protected generateFields() '''
561 «IF restrictions != null»
562 «val prop = getPropByName("value")»
564 «IF !(restrictions.lengthConstraints.empty)»
565 private static final «List.importedName»<«Range.importedName»<«prop.returnType.importedNumber»>> _length;
567 «IF !(restrictions.rangeConstraints.empty)»
568 private static final «List.importedName»<«Range.importedName»<«prop.returnType.importedNumber»>> _range;
572 «IF !properties.empty»
574 private«IF f.readOnly» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
580 * Template method which generates the method <code>hashCode()</code>.
582 * @return string with the <code>hashCode()</code> method definition in JAVA format
584 def protected generateHashCode() '''
585 «IF !genTO.hashCodeIdentifiers.empty»
587 public int hashCode() {
588 final int prime = 31;
590 «FOR property : genTO.hashCodeIdentifiers»
591 «IF property.returnType.name.contains("[")»
592 result = prime * result + ((«property.fieldName» == null) ? 0 : «Arrays.importedName».hashCode(«property.fieldName»));
594 result = prime * result + ((«property.fieldName» == null) ? 0 : «property.fieldName».hashCode());
603 * Template method which generates the method <code>equals()</code>.
605 * @return string with the <code>equals()</code> method definition in JAVA format
607 def protected generateEquals() '''
608 «IF !genTO.equalsIdentifiers.empty»
610 public boolean equals(java.lang.Object obj) {
617 if (getClass() != obj.getClass()) {
620 «type.name» other = («type.name») obj;
621 «FOR property : genTO.equalsIdentifiers»
622 «val fieldName = property.fieldName»
623 if («fieldName» == null) {
624 if (other.«fieldName» != null) {
627 «IF property.returnType.name.contains("[")»
628 } else if(!«Arrays.importedName».equals(«fieldName», other.«fieldName»)) {
630 } else if(!«fieldName».equals(other.«fieldName»)) {
640 def GeneratedProperty getPropByName(String name) {
641 for (GeneratedProperty prop : allProperties) {
642 if (prop.name.equals(name)) {