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 this.«p.fieldName» = «p.fieldName» == null ? null : «p.fieldName».clone();
290 this.«p.fieldName» = «p.fieldName»;
297 def protected genUnionConstructor() '''
298 «FOR p : allProperties»
299 «val List<GeneratedProperty> other = new ArrayList(properties)»
301 «genConstructor(p, other)»
307 def protected genConstructor(GeneratedProperty property, GeneratedProperty... other) '''
308 public «type.name»(«property.returnType.importedName + " " + property.name») {
309 «IF false == parentProperties.empty»
310 super(«parentProperties.asArguments»);
313 «generateRestrictions(type, property.fieldName.toString, property.returnType)»
315 this.«property.fieldName» = «property.name»;
317 this.«p.fieldName» = null;
322 def private static paramValue(Type returnType, String paramName) {
323 if (returnType instanceof ConcreteType) {
326 return paramName + ".getValue()"
330 def private generateRestrictions(Type type, String paramName, Type returnType) '''
331 «val restrictions = type.getRestrictions»
332 «IF restrictions !== null»
333 «IF !restrictions.lengthConstraints.empty || !restrictions.rangeConstraints.empty»
334 if («paramName» != null) {
335 «IF !restrictions.lengthConstraints.empty»
336 «LengthGenerator.generateLengthCheckerCall(paramName, paramValue(returnType, paramName))»
338 «IF !restrictions.rangeConstraints.empty»
339 «rangeGenerator.generateRangeCheckerCall(paramName, paramValue(returnType, paramName))»
346 def protected copyConstructor() '''
348 * Creates a copy from Source Object.
350 * @param source Source object
352 public «type.name»(«type.name» source) {
353 «IF false == parentProperties.empty»
357 this.«p.fieldName» = source.«p.fieldName»;
362 def protected parentConstructor() '''
364 * Creates a new instance from «genTO.superType.importedName»
366 * @param source Source object
368 public «type.name»(«genTO.superType.importedName» source) {
373 def protected defaultInstance() '''
374 «IF genTO.typedef && !allProperties.empty && !genTO.unionType»
375 «val prop = allProperties.get(0)»
376 «IF !("org.opendaylight.yangtools.yang.binding.InstanceIdentifier".equals(prop.returnType.fullyQualifiedName))»
377 public static «genTO.name» getDefaultInstance(String defaultValue) {
378 «IF "byte[]".equals(prop.returnType.name)»
379 «BaseEncoding.importedName» baseEncoding = «BaseEncoding.importedName».base64();
380 return new «genTO.name»(baseEncoding.decode(defaultValue));
381 «ELSEIF "java.lang.String".equals(prop.returnType.fullyQualifiedName)»
382 return new «genTO.name»(defaultValue);
383 «ELSEIF allProperties.size > 1»
385 «ELSEIF "java.lang.Boolean".equals(prop.returnType.fullyQualifiedName)»
386 return new «genTO.name»(Boolean.valueOf(defaultValue));
387 «ELSEIF "java.lang.Byte".equals(prop.returnType.fullyQualifiedName)»
388 return new «genTO.name»(Byte.valueOf(defaultValue));
389 «ELSEIF "java.lang.Short".equals(prop.returnType.fullyQualifiedName)»
390 return new «genTO.name»(Short.valueOf(defaultValue));
391 «ELSEIF "java.lang.Integer".equals(prop.returnType.fullyQualifiedName)»
392 return new «genTO.name»(Integer.valueOf(defaultValue));
393 «ELSEIF "java.lang.Long".equals(prop.returnType.fullyQualifiedName)»
394 return new «genTO.name»(Long.valueOf(defaultValue));
396 return new «genTO.name»(new «prop.returnType.importedName»(defaultValue));
403 def protected bitsArgs() '''
404 «List.importedName»<«String.importedName»> properties = «Lists.importedName».newArrayList(«allProperties.propsAsArgs»);
405 if (!properties.contains(defaultValue)) {
406 throw new «IllegalArgumentException.importedName»("invalid default parameter");
409 return new «genTO.name»(
410 «FOR prop : allProperties SEPARATOR ","»
411 properties.get(i++).equals(defaultValue) ? «Boolean.importedName».TRUE : null
416 def protected propsAsArgs(Iterable<GeneratedProperty> properties) '''
417 «FOR prop : properties SEPARATOR ","»
423 * Template method which generates JAVA class declaration.
425 * @param isInnerClass boolean value which specify if generated class is|isn't inner
426 * @return string with class declaration in JAVA format
428 def protected generateClassDeclaration(boolean isInnerClass) '''
432 ELSEIF (type.abstract)»«
436 ENDIF»class «type.name»«
437 IF (genTO.superType != null)»«
438 " extends "»«genTO.superType.importedName»«
440 «IF (!type.implements.empty)»«
442 FOR type : type.implements SEPARATOR ", "»«
449 * Template method which generates JAVA enum type.
451 * @return string with inner enum source code in JAVA format
453 def protected enumDeclarations() '''
455 «FOR e : enums SEPARATOR "\n"»
456 «val enumTemplate = new EnumTemplate(e)»
457 «enumTemplate.generateAsInnerClass»
462 def protected suidDeclaration() '''
463 «IF genTO.SUID != null»
464 private static final long serialVersionUID = «genTO.SUID.value»L;
469 * Template method which generates JAVA constants.
471 * @return string with constants in JAVA format
473 def protected constantsDeclarations() '''
476 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
477 «val cValue = c.value»
478 «IF cValue instanceof List<?>»
479 private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST»;
480 public static final «List.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
481 FOR v : cValue SEPARATOR ", "»«
482 IF v instanceof String»"«
487 «generateStaticInicializationBlock»
490 public static final «c.type.importedName» «c.name» = «c.value»;
497 * Template method which generates JAVA static initialization block.
499 * @return string with static initialization block in JAVA format
501 def protected generateStaticInicializationBlock() '''
503 final «Pattern.importedName» a[] = new «Pattern.importedName»[«TypeConstants.PATTERN_CONSTANT_NAME».size()];
505 for (String regEx : «TypeConstants.PATTERN_CONSTANT_NAME») {
506 a[i++] = Pattern.compile(regEx);
509 «Constants.MEMBER_PATTERN_LIST» = a;
514 * Template method which generates JAVA class attributes.
516 * @return string with the class attributes in JAVA format
518 def protected generateFields() '''
519 «IF !properties.empty»
521 private«IF f.readOnly» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
527 * Template method which generates the method <code>hashCode()</code>.
529 * @return string with the <code>hashCode()</code> method definition in JAVA format
531 def protected generateHashCode() '''
532 «IF !genTO.hashCodeIdentifiers.empty»
534 public int hashCode() {
535 final int prime = 31;
537 «FOR property : genTO.hashCodeIdentifiers»
538 «IF property.returnType.name.contains("[")»
539 result = prime * result + ((«property.fieldName» == null) ? 0 : «Arrays.importedName».hashCode(«property.fieldName»));
541 result = prime * result + ((«property.fieldName» == null) ? 0 : «property.fieldName».hashCode());
550 * Template method which generates the method <code>equals()</code>.
552 * @return string with the <code>equals()</code> method definition in JAVA format
554 def protected generateEquals() '''
555 «IF !genTO.equalsIdentifiers.empty»
557 public boolean equals(java.lang.Object obj) {
564 if (getClass() != obj.getClass()) {
567 «type.name» other = («type.name») obj;
568 «FOR property : genTO.equalsIdentifiers»
569 «val fieldName = property.fieldName»
570 if («fieldName» == null) {
571 if (other.«fieldName» != null) {
574 «IF property.returnType.name.contains("[")»
575 } else if(!«Arrays.importedName».equals(«fieldName», other.«fieldName»)) {
577 } else if(!«fieldName».equals(other.«fieldName»)) {
587 def GeneratedProperty getPropByName(String name) {
588 for (GeneratedProperty prop : allProperties) {
589 if (prop.name.equals(name)) {