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
33 * Template for generating JAVA class.
35 class ClassTemplate extends BaseTemplate {
37 protected val List<GeneratedProperty> properties
38 protected val List<GeneratedProperty> finalProperties
39 protected val List<GeneratedProperty> parentProperties
40 protected val Iterable<GeneratedProperty> allProperties;
41 protected val Restrictions restrictions
44 * List of enumeration which are generated as JAVA enum type.
46 protected val List<Enumeration> enums
49 * List of constant instances which are generated as JAVA public static final attributes.
51 protected val List<Constant> consts
54 * List of generated types which are enclosed inside <code>genType</code>
56 protected val List<GeneratedType> enclosedGeneratedTypes;
58 protected val GeneratedTransferObject genTO;
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) {
68 this.properties = genType.properties
69 this.finalProperties = GeneratorUtil.resolveReadOnlyPropertiesFromTO(genTO.properties)
70 this.parentProperties = GeneratorUtil.getPropertiesOfAllParents(genTO)
71 this.restrictions = genType.restrictions
73 var List<GeneratedProperty> sorted = new ArrayList<GeneratedProperty>();
74 sorted.addAll(properties);
75 sorted.addAll(parentProperties);
76 Collections.sort(sorted, [p1, p2|
77 p1.name.compareTo(p2.name)
80 this.allProperties = sorted
81 this.enums = genType.enumerations
82 this.consts = genType.constantDefinitions
83 this.enclosedGeneratedTypes = genType.enclosedTypes
87 * Generates JAVA class source code (class body only).
89 * @return string with JAVA class body source code
91 def CharSequence generateAsInnerClass() {
92 return generateBody(true)
95 override protected body() {
100 * Template method which generates class body.
102 * @param isInnerClass boolean value which specify if generated class is|isn't inner
103 * @return string with class source code in JAVA format
105 def protected generateBody(boolean isInnerClass) '''
106 «wrapToDocumentation(formatDataForJavaDoc(type))»
107 «generateClassDeclaration(isInnerClass)» {
109 «innerClassesDeclarations»
111 «constantsDeclarations»
114 «IF restrictions != null && (!restrictions.rangeConstraints.nullOrEmpty ||
115 !restrictions.lengthConstraints.nullOrEmpty)»
116 «generateConstraints»
123 «FOR field : properties SEPARATOR "\n"»
130 «IF (genTO.isTypedef() && genTO.getBaseType instanceof BitsTypeDefinition)»
131 «generateGetValueForBitsTypeDef»
138 «generateToString(genTO.toStringIdentifiers)»
140 «generateLengthMethod("length", "_length")»
142 «generateRangeMethod("range", "_range")»
149 * Template method which generates the method <code>getValue()</code> for typedef,
150 * which base type is BitsDefinition.
152 * @return string with the <code>getValue()</code> method definition in JAVA format
154 def protected generateGetValueForBitsTypeDef() '''
156 public boolean[] getValue() {
157 return new boolean[]{
158 «FOR property: genTO.properties SEPARATOR ','»
165 def private generateLengthMethod(String methodName, String varName) '''
166 «IF restrictions != null && !(restrictions.lengthConstraints.empty)»
167 «val numberClass = restrictions.lengthConstraints.iterator.next.min.class»
169 * @deprecated This method is slated for removal in a future release. See BUG-1485 for details.
172 public static «List.importedName»<«Range.importedName»<«numberClass.importedNumber»>> «methodName»() {
178 def private generateRangeMethod(String methodName, String varName) '''
179 «IF restrictions != null && !(restrictions.rangeConstraints.empty)»
180 «val returnType = allProperties.iterator.next.returnType»
182 * @deprecated This method is slated for removal in a future release. See BUG-1485 for details.
185 public static «List.importedName»<«Range.importedName»<«returnType.importedNumber»>> «methodName»() {
192 * Template method which generates inner classes inside this interface.
194 * @return string with the source code for inner classes in JAVA format
196 def protected innerClassesDeclarations() '''
197 «IF !enclosedGeneratedTypes.empty»
198 «FOR innerClass : enclosedGeneratedTypes SEPARATOR "\n"»
199 «IF (innerClass instanceof GeneratedTransferObject)»
200 «val classTemplate = new ClassTemplate(innerClass)»
201 «classTemplate.generateAsInnerClass»
208 def protected constructors() '''
210 «genUnionConstructor»
212 «allValuesConstructor»
214 «IF !allProperties.empty»
217 «IF properties.empty && !parentProperties.empty »
222 def private generateConstraints() '''
224 «IF !restrictions.rangeConstraints.nullOrEmpty»
225 «generateRangeConstraints»
227 «IF !restrictions.lengthConstraints.nullOrEmpty»
228 «generateLengthConstraints»
233 private def generateRangeConstraints() '''
234 «IF !allProperties.nullOrEmpty»
235 «val returnType = allProperties.iterator.next.returnType»
236 «IF returnType.fullyQualifiedName.equals(BigDecimal.canonicalName)»
237 «rangeBody(restrictions, BigDecimal, genTO.importedName, "_range")»
239 «rangeBody(restrictions, BigInteger, genTO.importedName, "_range")»
244 private def rangeBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
245 «ImmutableList.importedName».Builder<«Range.importedName»<«numberClass.importedName»>> builder = «ImmutableList.importedName».builder();
246 «FOR r : restrictions.rangeConstraints»
247 builder.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», «numericValue(numberClass, r.max)»));
249 «varName» = builder.build();
252 private def lengthBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
253 «ImmutableList.importedName».Builder<«Range.importedName»<«numberClass.importedName»>> builder = «ImmutableList.importedName».builder();
254 «FOR r : restrictions.lengthConstraints»
255 builder.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», «numericValue(numberClass, r.max)»));
257 «varName» = builder.build();
260 private def generateLengthConstraints() '''
261 «IF restrictions != null && !(restrictions.lengthConstraints.empty)»
262 «val numberClass = restrictions.lengthConstraints.iterator.next.min.class»
263 «IF numberClass.equals(typeof(BigDecimal))»
264 «lengthBody(restrictions, numberClass, genTO.importedName, "_length")»
266 «lengthBody(restrictions, typeof(BigInteger), genTO.importedName, "_length")»
271 def protected allValuesConstructor() '''
272 «IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
273 @«ConstructorProperties.importedName»("value")
275 public «type.name»(«allProperties.asArgumentsDeclaration») {
276 «IF false == parentProperties.empty»
277 super(«parentProperties.asArguments»);
279 «FOR p : allProperties»
280 «generateRestrictions(type, p.fieldName.toString, p.returnType)»
284 * If we have patterns, we need to apply them to the value field. This is a sad
285 * consequence of how this code is structured.
287 IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
289 «Preconditions.importedName».checkNotNull(_value, "Supplied value may not be null");
292 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME && c.value instanceof List<?>»
293 boolean valid = false;
294 for (Pattern p : patterns) {
295 if (p.matcher(_value).matches()) {
301 «Preconditions.importedName».checkArgument(valid, "Supplied value \"%s\" does not match any of the permitted patterns %s", _value, «TypeConstants.PATTERN_CONSTANT_NAME»);
307 «IF p.returnType.importedName.contains("[]")»
308 this.«p.fieldName» = «p.fieldName» == null ? null : «p.fieldName».clone();
310 this.«p.fieldName» = «p.fieldName»;
317 def protected genUnionConstructor() '''
318 «FOR p : allProperties»
319 «val List<GeneratedProperty> other = new ArrayList(properties)»
321 «genConstructor(p, other)»
327 def protected genConstructor(GeneratedProperty property, GeneratedProperty... other) '''
328 public «type.name»(«property.returnType.importedName + " " + property.name») {
329 «IF false == parentProperties.empty»
330 super(«parentProperties.asArguments»);
333 «generateRestrictions(type, property.fieldName.toString, property.returnType)»
335 this.«property.fieldName» = «property.name»;
337 this.«p.fieldName» = null;
342 def protected copyConstructor() '''
344 * Creates a copy from Source Object.
346 * @param source Source object
348 public «type.name»(«type.name» source) {
349 «IF false == parentProperties.empty»
353 this.«p.fieldName» = source.«p.fieldName»;
358 def protected parentConstructor() '''
360 * Creates a new instance from «genTO.superType.importedName»
362 * @param source Source object
364 public «type.name»(«genTO.superType.importedName» source) {
369 def protected defaultInstance() '''
370 «IF genTO.typedef && !allProperties.empty && !genTO.unionType»
371 «val prop = allProperties.get(0)»
372 «IF !("org.opendaylight.yangtools.yang.binding.InstanceIdentifier".equals(prop.returnType.fullyQualifiedName))»
373 public static «genTO.name» getDefaultInstance(String defaultValue) {
374 «IF "byte[]".equals(prop.returnType.name)»
375 «BaseEncoding.importedName» baseEncoding = «BaseEncoding.importedName».base64();
376 return new «genTO.name»(baseEncoding.decode(defaultValue));
377 «ELSEIF "java.lang.String".equals(prop.returnType.fullyQualifiedName)»
378 return new «genTO.name»(defaultValue);
379 «ELSEIF allProperties.size > 1»
381 «ELSEIF "java.lang.Boolean".equals(prop.returnType.fullyQualifiedName)»
382 return new «genTO.name»(Boolean.valueOf(defaultValue));
383 «ELSEIF "java.lang.Byte".equals(prop.returnType.fullyQualifiedName)»
384 return new «genTO.name»(Byte.valueOf(defaultValue));
385 «ELSEIF "java.lang.Short".equals(prop.returnType.fullyQualifiedName)»
386 return new «genTO.name»(Short.valueOf(defaultValue));
387 «ELSEIF "java.lang.Integer".equals(prop.returnType.fullyQualifiedName)»
388 return new «genTO.name»(Integer.valueOf(defaultValue));
389 «ELSEIF "java.lang.Long".equals(prop.returnType.fullyQualifiedName)»
390 return new «genTO.name»(Long.valueOf(defaultValue));
392 return new «genTO.name»(new «prop.returnType.importedName»(defaultValue));
399 def protected bitsArgs() '''
400 «List.importedName»<«String.importedName»> properties = «Lists.importedName».newArrayList(«allProperties.propsAsArgs»);
401 if (!properties.contains(defaultValue)) {
402 throw new «IllegalArgumentException.importedName»("invalid default parameter");
405 return new «genTO.name»(
406 «FOR prop : allProperties SEPARATOR ","»
407 properties.get(i++).equals(defaultValue) ? «Boolean.importedName».TRUE : null
412 def protected propsAsArgs(Iterable<GeneratedProperty> properties) '''
413 «FOR prop : properties SEPARATOR ","»
419 * Template method which generates JAVA class declaration.
421 * @param isInnerClass boolean value which specify if generated class is|isn't inner
422 * @return string with class declaration in JAVA format
424 def protected generateClassDeclaration(boolean isInnerClass) '''
428 ELSEIF (type.abstract)»«
432 ENDIF»class «type.name»«
433 IF (genTO.superType != null)»«
434 " extends "»«genTO.superType.importedName»«
436 «IF (!type.implements.empty)»«
438 FOR type : type.implements SEPARATOR ", "»«
445 * Template method which generates JAVA enum type.
447 * @return string with inner enum source code in JAVA format
449 def protected enumDeclarations() '''
451 «FOR e : enums SEPARATOR "\n"»
452 «val enumTemplate = new EnumTemplate(e)»
453 «enumTemplate.generateAsInnerClass»
458 def protected suidDeclaration() '''
459 «IF genTO.SUID != null»
460 private static final long serialVersionUID = «genTO.SUID.value»L;
465 * Template method which generates JAVA constants.
467 * @return string with constants in JAVA format
469 def protected constantsDeclarations() '''
472 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
473 «val cValue = c.value»
474 «IF cValue instanceof List<?>»
475 private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST»;
476 public static final «List.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
477 FOR v : cValue SEPARATOR ", "»«
478 IF v instanceof String»"«
483 «generateStaticInicializationBlock»
486 public static final «c.type.importedName» «c.name» = «c.value»;
493 * Template method which generates JAVA static initialization block.
495 * @return string with static initialization block in JAVA format
497 def protected generateStaticInicializationBlock() '''
499 final «Pattern.importedName» a[] = new «Pattern.importedName»[«TypeConstants.PATTERN_CONSTANT_NAME».size()];
501 for (String regEx : «TypeConstants.PATTERN_CONSTANT_NAME») {
502 a[i++] = Pattern.compile(regEx);
505 «Constants.MEMBER_PATTERN_LIST» = a;
510 * Template method which generates JAVA class attributes.
512 * @return string with the class attributes in JAVA format
514 def protected generateFields() '''
515 «IF restrictions != null»
516 «val prop = getPropByName("value")»
518 «IF !(restrictions.lengthConstraints.empty)»
519 private static final «List.importedName»<«Range.importedName»<«prop.returnType.importedNumber»>> _length;
521 «IF !(restrictions.rangeConstraints.empty)»
522 private static final «List.importedName»<«Range.importedName»<«prop.returnType.importedNumber»>> _range;
526 «IF !properties.empty»
528 private«IF f.readOnly» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
534 * Template method which generates the method <code>hashCode()</code>.
536 * @return string with the <code>hashCode()</code> method definition in JAVA format
538 def protected generateHashCode() '''
539 «IF !genTO.hashCodeIdentifiers.empty»
541 public int hashCode() {
542 final int prime = 31;
544 «FOR property : genTO.hashCodeIdentifiers»
545 «IF property.returnType.name.contains("[")»
546 result = prime * result + ((«property.fieldName» == null) ? 0 : «Arrays.importedName».hashCode(«property.fieldName»));
548 result = prime * result + ((«property.fieldName» == null) ? 0 : «property.fieldName».hashCode());
557 * Template method which generates the method <code>equals()</code>.
559 * @return string with the <code>equals()</code> method definition in JAVA format
561 def protected generateEquals() '''
562 «IF !genTO.equalsIdentifiers.empty»
564 public boolean equals(java.lang.Object obj) {
571 if (getClass() != obj.getClass()) {
574 «type.name» other = («type.name») obj;
575 «FOR property : genTO.equalsIdentifiers»
576 «val fieldName = property.fieldName»
577 if («fieldName» == null) {
578 if (other.«fieldName» != null) {
581 «IF property.returnType.name.contains("[")»
582 } else if(!«Arrays.importedName».equals(«fieldName», other.«fieldName»)) {
584 } else if(!«fieldName».equals(other.«fieldName»)) {
594 def GeneratedProperty getPropByName(String name) {
595 for (GeneratedProperty prop : allProperties) {
596 if (prop.name.equals(name)) {