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.base.Preconditions
11 import com.google.common.collect.ImmutableList
12 import com.google.common.collect.Lists
13 import com.google.common.collect.Range
14 import com.google.common.io.BaseEncoding
15 import java.beans.ConstructorProperties
16 import java.math.BigDecimal
17 import java.math.BigInteger
18 import java.util.ArrayList
19 import java.util.Arrays
20 import java.util.Collections
22 import java.util.regex.Pattern
23 import org.opendaylight.yangtools.binding.generator.util.TypeConstants
24 import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType
25 import org.opendaylight.yangtools.sal.binding.model.api.Constant
26 import org.opendaylight.yangtools.sal.binding.model.api.Enumeration
27 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
28 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
29 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
30 import org.opendaylight.yangtools.sal.binding.model.api.Restrictions
31 import org.opendaylight.yangtools.sal.binding.model.api.Type
32 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition
33 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint
36 * Template for generating JAVA class.
38 class ClassTemplate extends BaseTemplate {
40 protected val List<GeneratedProperty> properties
41 protected val List<GeneratedProperty> finalProperties
42 protected val List<GeneratedProperty> parentProperties
43 protected val Iterable<GeneratedProperty> allProperties;
44 protected val Restrictions restrictions
47 * List of enumeration which are generated as JAVA enum type.
49 protected val List<Enumeration> enums
52 * List of constant instances which are generated as JAVA public static final attributes.
54 protected val List<Constant> consts
57 * List of generated types which are enclosed inside <code>genType</code>
59 protected val List<GeneratedType> enclosedGeneratedTypes;
61 protected val GeneratedTransferObject genTO;
63 private 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) {
73 this.properties = genType.properties
74 this.finalProperties = GeneratorUtil.resolveReadOnlyPropertiesFromTO(genTO.properties)
75 this.parentProperties = GeneratorUtil.getPropertiesOfAllParents(genTO)
76 this.restrictions = genType.restrictions
78 var List<GeneratedProperty> sorted = new ArrayList<GeneratedProperty>();
79 sorted.addAll(properties);
80 sorted.addAll(parentProperties);
81 Collections.sort(sorted, [p1, p2|
82 p1.name.compareTo(p2.name)
85 this.allProperties = sorted
86 this.enums = genType.enumerations
87 this.consts = genType.constantDefinitions
88 this.enclosedGeneratedTypes = genType.enclosedTypes
90 if (restrictions != null && !restrictions.rangeConstraints.nullOrEmpty) {
91 rangeGenerator = AbstractRangeGenerator.forType(findProperty(genType, "value").returnType)
92 Preconditions.checkNotNull(rangeGenerator)
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 «generateClassDeclaration(isInnerClass)» {
121 «innerClassesDeclarations»
123 «constantsDeclarations»
126 «IF restrictions != null»
127 «IF !restrictions.lengthConstraints.nullOrEmpty»
128 «LengthGenerator.generateLengthChecker("_value", findProperty(genTO, "value").returnType, restrictions.lengthConstraints)»
130 «IF !restrictions.rangeConstraints.nullOrEmpty»
131 «rangeGenerator.generateRangeChecker("_value", restrictions.rangeConstraints)»
139 «FOR field : properties SEPARATOR "\n"»
146 «IF (genTO.isTypedef() && genTO.getBaseType instanceof BitsTypeDefinition)»
147 «generateGetValueForBitsTypeDef»
154 «generateToString(genTO.toStringIdentifiers)»
156 «generateLengthMethod()»
158 «generateRangeMethod()»
165 * Template method which generates the method <code>getValue()</code> for typedef,
166 * which base type is BitsDefinition.
168 * @return string with the <code>getValue()</code> method definition in JAVA format
170 def protected generateGetValueForBitsTypeDef() '''
172 public boolean[] getValue() {
173 return new boolean[]{
174 «FOR property: genTO.properties SEPARATOR ','»
182 def private generateLengthMethod() '''
183 «IF restrictions != null && !(restrictions.lengthConstraints.empty)»
184 «val numberClass = restrictions.lengthConstraints.iterator.next.min.class»
186 * @deprecated This method is slated for removal in a future release. See BUG-1485 for details.
189 public static «List.importedName»<«Range.importedName»<«numberClass.importedNumber»>> length() {
190 «List.importedName»<«Range.importedName»<«numberClass.importedName»>> ret = new java.util.ArrayList<>(«restrictions.lengthConstraints.size»);
191 «FOR r : restrictions.lengthConstraints»
192 ret.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», «numericValue(numberClass, r.max)»));
200 private def rangeBody(List<RangeConstraint> restrictions, Class<? extends Number> numberClass) '''
201 «List.importedName»<«Range.importedName»<«numberClass.importedName»>> ret = new java.util.ArrayList<>(«restrictions.size»);
202 «FOR r : restrictions»
203 ret.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», «numericValue(numberClass, r.max)»));
208 def private generateRangeMethod() '''
209 «IF restrictions != null && !(restrictions.rangeConstraints.empty)»
210 «val returnType = allProperties.iterator.next.returnType»
212 * @deprecated This method is slated for removal in a future release. See BUG-1485 for details.
215 public static «List.importedName»<«Range.importedName»<«returnType.importedNumber»>> range() {
216 «IF returnType.fullyQualifiedName.equals(BigDecimal.canonicalName)»
217 «rangeBody(restrictions.rangeConstraints, BigDecimal)»
219 «rangeBody(restrictions.rangeConstraints, BigInteger)»
227 * Template method which generates inner classes inside this interface.
229 * @return string with the source code for inner classes in JAVA format
231 def protected innerClassesDeclarations() '''
232 «IF !enclosedGeneratedTypes.empty»
233 «FOR innerClass : enclosedGeneratedTypes SEPARATOR "\n"»
234 «IF (innerClass instanceof GeneratedTransferObject)»
235 «val classTemplate = new ClassTemplate(innerClass)»
236 «classTemplate.generateAsInnerClass»
243 def protected constructors() '''
245 «genUnionConstructor»
247 «allValuesConstructor»
249 «IF !allProperties.empty»
252 «IF properties.empty && !parentProperties.empty »
257 def protected allValuesConstructor() '''
258 «IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
259 @«ConstructorProperties.importedName»("value")
261 public «type.name»(«allProperties.asArgumentsDeclaration») {
262 «IF false == parentProperties.empty»
263 super(«parentProperties.asArguments»);
265 «FOR p : allProperties»
266 «generateRestrictions(type, p.fieldName.toString, p.returnType)»
270 * If we have patterns, we need to apply them to the value field. This is a sad
271 * consequence of how this code is structured.
273 IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
275 «Preconditions.importedName».checkNotNull(_value, "Supplied value may not be null");
278 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME && c.value instanceof List<?>»
279 for (Pattern p : patterns) {
280 «Preconditions.importedName».checkArgument(p.matcher(_value).matches(), "Supplied value \"%s\" does not match any of the permitted patterns %s", _value, «TypeConstants.PATTERN_CONSTANT_NAME»);
287 «IF p.returnType.importedName.contains("[]")»
288 «IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name
290 this.«p.fieldName» = «p.fieldName».clone();
292 this.«p.fieldName» = «p.fieldName» == null ? null : «p.fieldName».clone();
295 this.«p.fieldName» = «p.fieldName»;
302 def protected genUnionConstructor() '''
303 «FOR p : allProperties»
304 «val List<GeneratedProperty> other = new ArrayList(properties)»
306 «genConstructor(p, other)»
312 def protected genConstructor(GeneratedProperty property, GeneratedProperty... other) '''
313 public «type.name»(«property.returnType.importedName + " " + property.name») {
314 «IF false == parentProperties.empty»
315 super(«parentProperties.asArguments»);
318 «generateRestrictions(type, property.fieldName.toString, property.returnType)»
320 this.«property.fieldName» = «property.name»;
322 this.«p.fieldName» = null;
327 def private static paramValue(Type returnType, String paramName) {
328 if (returnType instanceof ConcreteType) {
331 return paramName + ".getValue()"
335 def private generateRestrictions(Type type, String paramName, Type returnType) '''
336 «val restrictions = type.getRestrictions»
337 «IF restrictions !== null»
338 «IF !restrictions.lengthConstraints.empty || !restrictions.rangeConstraints.empty»
339 if («paramName» != null) {
340 «IF !restrictions.lengthConstraints.empty»
341 «LengthGenerator.generateLengthCheckerCall(paramName, paramValue(returnType, paramName))»
343 «IF !restrictions.rangeConstraints.empty»
344 «rangeGenerator.generateRangeCheckerCall(paramName, paramValue(returnType, paramName))»
351 def protected copyConstructor() '''
353 * Creates a copy from Source Object.
355 * @param source Source object
357 public «type.name»(«type.name» source) {
358 «IF false == parentProperties.empty»
362 this.«p.fieldName» = source.«p.fieldName»;
367 def protected parentConstructor() '''
369 * Creates a new instance from «genTO.superType.importedName»
371 * @param source Source object
373 public «type.name»(«genTO.superType.importedName» source) {
378 def protected defaultInstance() '''
379 «IF genTO.typedef && !allProperties.empty && !genTO.unionType»
380 «val prop = allProperties.get(0)»
381 «IF !("org.opendaylight.yangtools.yang.binding.InstanceIdentifier".equals(prop.returnType.fullyQualifiedName))»
382 public static «genTO.name» getDefaultInstance(String defaultValue) {
383 «IF "byte[]".equals(prop.returnType.name)»
384 «BaseEncoding.importedName» baseEncoding = «BaseEncoding.importedName».base64();
385 return new «genTO.name»(baseEncoding.decode(defaultValue));
386 «ELSEIF "java.lang.String".equals(prop.returnType.fullyQualifiedName)»
387 return new «genTO.name»(defaultValue);
388 «ELSEIF allProperties.size > 1»
390 «ELSEIF "java.lang.Boolean".equals(prop.returnType.fullyQualifiedName)»
391 return new «genTO.name»(Boolean.valueOf(defaultValue));
392 «ELSEIF "java.lang.Byte".equals(prop.returnType.fullyQualifiedName)»
393 return new «genTO.name»(Byte.valueOf(defaultValue));
394 «ELSEIF "java.lang.Short".equals(prop.returnType.fullyQualifiedName)»
395 return new «genTO.name»(Short.valueOf(defaultValue));
396 «ELSEIF "java.lang.Integer".equals(prop.returnType.fullyQualifiedName)»
397 return new «genTO.name»(Integer.valueOf(defaultValue));
398 «ELSEIF "java.lang.Long".equals(prop.returnType.fullyQualifiedName)»
399 return new «genTO.name»(Long.valueOf(defaultValue));
401 return new «genTO.name»(new «prop.returnType.importedName»(defaultValue));
408 def protected bitsArgs() '''
409 «List.importedName»<«String.importedName»> properties = «Lists.importedName».newArrayList(«allProperties.propsAsArgs»);
410 if (!properties.contains(defaultValue)) {
411 throw new «IllegalArgumentException.importedName»("invalid default parameter");
414 return new «genTO.name»(
415 «FOR prop : allProperties SEPARATOR ","»
416 properties.get(i++).equals(defaultValue) ? «Boolean.importedName».TRUE : null
421 def protected propsAsArgs(Iterable<GeneratedProperty> properties) '''
422 «FOR prop : properties SEPARATOR ","»
428 * Template method which generates JAVA class declaration.
430 * @param isInnerClass boolean value which specify if generated class is|isn't inner
431 * @return string with class declaration in JAVA format
433 def protected generateClassDeclaration(boolean isInnerClass) '''
437 ELSEIF (type.abstract)»«
441 ENDIF»class «type.name»«
442 IF (genTO.superType != null)»«
443 " extends "»«genTO.superType.importedName»«
445 «IF (!type.implements.empty)»«
447 FOR type : type.implements SEPARATOR ", "»«
454 * Template method which generates JAVA enum type.
456 * @return string with inner enum source code in JAVA format
458 def protected enumDeclarations() '''
460 «FOR e : enums SEPARATOR "\n"»
461 «val enumTemplate = new EnumTemplate(e)»
462 «enumTemplate.generateAsInnerClass»
467 def protected suidDeclaration() '''
468 «IF genTO.SUID != null»
469 private static final long serialVersionUID = «genTO.SUID.value»L;
474 * Template method which generates JAVA constants.
476 * @return string with constants in JAVA format
478 def protected constantsDeclarations() '''
481 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
482 «val cValue = c.value»
483 «IF cValue instanceof List<?>»
484 private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST»;
485 public static final «List.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
486 FOR v : cValue SEPARATOR ", "»«
487 IF v instanceof String»"«
492 «generateStaticInicializationBlock»
495 public static final «c.type.importedName» «c.name» = «c.value»;
502 * Template method which generates JAVA static initialization block.
504 * @return string with static initialization block in JAVA format
506 def protected generateStaticInicializationBlock() '''
508 final «Pattern.importedName» a[] = new «Pattern.importedName»[«TypeConstants.PATTERN_CONSTANT_NAME».size()];
510 for (String regEx : «TypeConstants.PATTERN_CONSTANT_NAME») {
511 a[i++] = Pattern.compile(regEx);
514 «Constants.MEMBER_PATTERN_LIST» = a;
519 * Template method which generates JAVA class attributes.
521 * @return string with the class attributes in JAVA format
523 def protected generateFields() '''
524 «IF !properties.empty»
526 private«IF f.readOnly» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
532 * Template method which generates the method <code>hashCode()</code>.
534 * @return string with the <code>hashCode()</code> method definition in JAVA format
536 def protected generateHashCode() '''
537 «IF !genTO.hashCodeIdentifiers.empty»
539 public int hashCode() {
540 final int prime = 31;
542 «FOR property : genTO.hashCodeIdentifiers»
543 «IF property.returnType.name.contains("[")»
544 result = prime * result + ((«property.fieldName» == null) ? 0 : «Arrays.importedName».hashCode(«property.fieldName»));
546 result = prime * result + ((«property.fieldName» == null) ? 0 : «property.fieldName».hashCode());
555 * Template method which generates the method <code>equals()</code>.
557 * @return string with the <code>equals()</code> method definition in JAVA format
559 def protected generateEquals() '''
560 «IF !genTO.equalsIdentifiers.empty»
562 public boolean equals(java.lang.Object obj) {
569 if (getClass() != obj.getClass()) {
572 «type.name» other = («type.name») obj;
573 «FOR property : genTO.equalsIdentifiers»
574 «val fieldName = property.fieldName»
575 if («fieldName» == null) {
576 if (other.«fieldName» != null) {
579 «IF property.returnType.name.contains("[")»
580 } else if(!«Arrays.importedName».equals(«fieldName», other.«fieldName»)) {
582 } else if(!«fieldName».equals(other.«fieldName»)) {
592 def GeneratedProperty getPropByName(String name) {
593 for (GeneratedProperty prop : allProperties) {
594 if (prop.name.equals(name)) {