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»
168 public static «List.importedName»<«Range.importedName»<«numberClass.importedNumber»>> «methodName»() {
174 def private generateRangeMethod(String methodName, String varName) '''
175 «IF restrictions != null && !(restrictions.rangeConstraints.empty)»
176 «val returnType = allProperties.iterator.next.returnType»
177 public static «List.importedName»<«Range.importedName»<«returnType.importedNumber»>> «methodName»() {
184 * Template method which generates inner classes inside this interface.
186 * @return string with the source code for inner classes in JAVA format
188 def protected innerClassesDeclarations() '''
189 «IF !enclosedGeneratedTypes.empty»
190 «FOR innerClass : enclosedGeneratedTypes SEPARATOR "\n"»
191 «IF (innerClass instanceof GeneratedTransferObject)»
192 «val classTemplate = new ClassTemplate(innerClass)»
193 «classTemplate.generateAsInnerClass»
200 def protected constructors() '''
202 «genUnionConstructor»
204 «allValuesConstructor»
206 «IF !allProperties.empty»
209 «IF properties.empty && !parentProperties.empty »
214 def private generateConstraints() '''
216 «IF !restrictions.rangeConstraints.nullOrEmpty»
217 «generateRangeConstraints»
219 «IF !restrictions.lengthConstraints.nullOrEmpty»
220 «generateLengthConstraints»
225 private def generateRangeConstraints() '''
226 «IF !allProperties.nullOrEmpty»
227 «val returnType = allProperties.iterator.next.returnType»
228 «IF returnType.fullyQualifiedName.equals(BigDecimal.canonicalName)»
229 «rangeBody(restrictions, BigDecimal, genTO.importedName, "_range")»
231 «rangeBody(restrictions, BigInteger, genTO.importedName, "_range")»
236 private def rangeBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
237 «ImmutableList.importedName».Builder<«Range.importedName»<«numberClass.importedName»>> builder = «ImmutableList.importedName».builder();
238 «FOR r : restrictions.rangeConstraints»
239 builder.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», «numericValue(numberClass, r.max)»));
241 «varName» = builder.build();
244 private def lengthBody(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.lengthConstraints»
247 builder.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», «numericValue(numberClass, r.max)»));
249 «varName» = builder.build();
252 private def generateLengthConstraints() '''
253 «IF restrictions != null && !(restrictions.lengthConstraints.empty)»
254 «val numberClass = restrictions.lengthConstraints.iterator.next.min.class»
255 «IF numberClass.equals(typeof(BigDecimal))»
256 «lengthBody(restrictions, numberClass, genTO.importedName, "_length")»
258 «lengthBody(restrictions, typeof(BigInteger), genTO.importedName, "_length")»
263 def protected allValuesConstructor() '''
264 «IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
265 @«ConstructorProperties.importedName»("value")
267 public «type.name»(«allProperties.asArgumentsDeclaration») {
268 «IF false == parentProperties.empty»
269 super(«parentProperties.asArguments»);
271 «FOR p : allProperties»
272 «generateRestrictions(type, p.fieldName.toString, p.returnType)»
276 * If we have patterns, we need to apply them to the value field. This is a sad
277 * consequence of how this code is structured.
279 IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
281 «Preconditions.importedName».checkNotNull(_value, "Supplied value may not be null");
284 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME && c.value instanceof List<?>»
285 boolean valid = false;
286 for (Pattern p : patterns) {
287 if (p.matcher(_value).matches()) {
293 «Preconditions.importedName».checkArgument(valid, "Supplied value \"%s\" does not match any of the permitted patterns %s", _value, «TypeConstants.PATTERN_CONSTANT_NAME»);
299 «IF p.returnType.importedName.contains("[]")»
300 this.«p.fieldName» = «p.fieldName» == null ? null : «p.fieldName».clone();
302 this.«p.fieldName» = «p.fieldName»;
309 def protected genUnionConstructor() '''
310 «FOR p : allProperties»
311 «val List<GeneratedProperty> other = new ArrayList(properties)»
313 «genConstructor(p, other)»
319 def protected genConstructor(GeneratedProperty property, GeneratedProperty... other) '''
320 public «type.name»(«property.returnType.importedName + " " + property.name») {
321 «IF false == parentProperties.empty»
322 super(«parentProperties.asArguments»);
325 «generateRestrictions(type, property.fieldName.toString, property.returnType)»
327 this.«property.fieldName» = «property.name»;
329 this.«p.fieldName» = null;
334 def protected copyConstructor() '''
336 * Creates a copy from Source Object.
338 * @param source Source object
340 public «type.name»(«type.name» source) {
341 «IF false == parentProperties.empty»
345 this.«p.fieldName» = source.«p.fieldName»;
350 def protected parentConstructor() '''
352 * Creates a new instance from «genTO.superType.importedName»
354 * @param source Source object
356 public «type.name»(«genTO.superType.importedName» source) {
361 def protected defaultInstance() '''
362 «IF genTO.typedef && !allProperties.empty && !genTO.unionType»
363 «val prop = allProperties.get(0)»
364 «IF !("org.opendaylight.yangtools.yang.binding.InstanceIdentifier".equals(prop.returnType.fullyQualifiedName))»
365 public static «genTO.name» getDefaultInstance(String defaultValue) {
366 «IF "byte[]".equals(prop.returnType.name)»
367 «BaseEncoding.importedName» baseEncoding = «BaseEncoding.importedName».base64();
368 return new «genTO.name»(baseEncoding.decode(defaultValue));
369 «ELSEIF "java.lang.String".equals(prop.returnType.fullyQualifiedName)»
370 return new «genTO.name»(defaultValue);
371 «ELSEIF allProperties.size > 1»
373 «ELSEIF "java.lang.Boolean".equals(prop.returnType.fullyQualifiedName)»
374 return new «genTO.name»(Boolean.valueOf(defaultValue));
375 «ELSEIF "java.lang.Byte".equals(prop.returnType.fullyQualifiedName)»
376 return new «genTO.name»(Byte.valueOf(defaultValue));
377 «ELSEIF "java.lang.Short".equals(prop.returnType.fullyQualifiedName)»
378 return new «genTO.name»(Short.valueOf(defaultValue));
379 «ELSEIF "java.lang.Integer".equals(prop.returnType.fullyQualifiedName)»
380 return new «genTO.name»(Integer.valueOf(defaultValue));
381 «ELSEIF "java.lang.Long".equals(prop.returnType.fullyQualifiedName)»
382 return new «genTO.name»(Long.valueOf(defaultValue));
384 return new «genTO.name»(new «prop.returnType.importedName»(defaultValue));
391 def protected bitsArgs() '''
392 «List.importedName»<«String.importedName»> properties = «Lists.importedName».newArrayList(«allProperties.propsAsArgs»);
393 if (!properties.contains(defaultValue)) {
394 throw new «IllegalArgumentException.importedName»("invalid default parameter");
397 return new «genTO.name»(
398 «FOR prop : allProperties SEPARATOR ","»
399 properties.get(i++).equals(defaultValue) ? «Boolean.importedName».TRUE : null
404 def protected propsAsArgs(Iterable<GeneratedProperty> properties) '''
405 «FOR prop : properties SEPARATOR ","»
411 * Template method which generates JAVA class declaration.
413 * @param isInnerClass boolean value which specify if generated class is|isn't inner
414 * @return string with class declaration in JAVA format
416 def protected generateClassDeclaration(boolean isInnerClass) '''
420 ELSEIF (type.abstract)»«
424 ENDIF»class «type.name»«
425 IF (genTO.superType != null)»«
426 " extends "»«genTO.superType.importedName»«
428 «IF (!type.implements.empty)»«
430 FOR type : type.implements SEPARATOR ", "»«
437 * Template method which generates JAVA enum type.
439 * @return string with inner enum source code in JAVA format
441 def protected enumDeclarations() '''
443 «FOR e : enums SEPARATOR "\n"»
444 «val enumTemplate = new EnumTemplate(e)»
445 «enumTemplate.generateAsInnerClass»
450 def protected suidDeclaration() '''
451 «IF genTO.SUID != null»
452 private static final long serialVersionUID = «genTO.SUID.value»L;
457 * Template method which generates JAVA constants.
459 * @return string with constants in JAVA format
461 def protected constantsDeclarations() '''
464 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
465 «val cValue = c.value»
466 «IF cValue instanceof List<?>»
467 private static final «List.importedName»<«Pattern.importedName»> «Constants.MEMBER_PATTERN_LIST»;
468 public static final «List.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
469 FOR v : cValue SEPARATOR ", "»«
470 IF v instanceof String»"«
475 «generateStaticInicializationBlock»
478 public static final «c.type.importedName» «c.name» = «c.value»;
485 * Template method which generates JAVA static initialization block.
487 * @return string with static initialization block in JAVA format
489 def protected generateStaticInicializationBlock() '''
491 final «List.importedName»<«Pattern.importedName»> l = new «ArrayList.importedName»<«Pattern.importedName»>();
492 for (String regEx : «TypeConstants.PATTERN_CONSTANT_NAME») {
493 l.add(Pattern.compile(regEx));
496 «Constants.MEMBER_PATTERN_LIST» = «ImmutableList.importedName».copyOf(l);
501 * Template method which generates JAVA class attributes.
503 * @return string with the class attributes in JAVA format
505 def protected generateFields() '''
506 «IF restrictions != null»
507 «val prop = getPropByName("value")»
509 «IF !(restrictions.lengthConstraints.empty)»
510 private static final «List.importedName»<«Range.importedName»<«prop.returnType.importedNumber»>> _length;
512 «IF !(restrictions.rangeConstraints.empty)»
513 private static final «List.importedName»<«Range.importedName»<«prop.returnType.importedNumber»>> _range;
517 «IF !properties.empty»
519 private«IF f.readOnly» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
525 * Template method which generates the method <code>hashCode()</code>.
527 * @return string with the <code>hashCode()</code> method definition in JAVA format
529 def protected generateHashCode() '''
530 «IF !genTO.hashCodeIdentifiers.empty»
532 public int hashCode() {
533 final int prime = 31;
535 «FOR property : genTO.hashCodeIdentifiers»
536 «IF property.returnType.name.contains("[")»
537 result = prime * result + ((«property.fieldName» == null) ? 0 : «Arrays.importedName».hashCode(«property.fieldName»));
539 result = prime * result + ((«property.fieldName» == null) ? 0 : «property.fieldName».hashCode());
548 * Template method which generates the method <code>equals()</code>.
550 * @return string with the <code>equals()</code> method definition in JAVA format
552 def protected generateEquals() '''
553 «IF !genTO.equalsIdentifiers.empty»
555 public boolean equals(java.lang.Object obj) {
562 if (getClass() != obj.getClass()) {
565 «type.name» other = («type.name») obj;
566 «FOR property : genTO.equalsIdentifiers»
567 «val fieldName = property.fieldName»
568 if («fieldName» == null) {
569 if (other.«fieldName» != null) {
572 «IF property.returnType.name.contains("[")»
573 } else if(!«Arrays.importedName».equals(«fieldName», other.«fieldName»)) {
575 } else if(!«fieldName».equals(other.«fieldName»)) {
585 def GeneratedProperty getPropByName(String name) {
586 for (GeneratedProperty prop : allProperties) {
587 if (prop.name.equals(name)) {