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 as GeneratedTransferObject)»
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 this.«p.fieldName» = «p.fieldName»;
305 def protected genUnionConstructor() '''
306 «FOR p : allProperties»
307 «val List<GeneratedProperty> other = new ArrayList(properties)»
309 «genConstructor(p, other)»
315 def protected genConstructor(GeneratedProperty property, GeneratedProperty... other) '''
316 public «type.name»(«property.returnType.importedName + " " + property.name») {
317 «IF false == parentProperties.empty»
318 super(«parentProperties.asArguments»);
321 «generateRestrictions(type, property.fieldName.toString, property.returnType)»
323 this.«property.fieldName» = «property.name»;
325 this.«p.fieldName» = null;
330 def protected copyConstructor() '''
332 * Creates a copy from Source Object.
334 * @param source Source object
336 public «type.name»(«type.name» source) {
337 «IF false == parentProperties.empty»
341 this.«p.fieldName» = source.«p.fieldName»;
346 def protected parentConstructor() '''
348 * Creates a new instance from «genTO.superType.importedName»
350 * @param source Source object
352 public «type.name»(«genTO.superType.importedName» source) {
357 def protected defaultInstance() '''
358 «IF genTO.typedef && !allProperties.empty && !genTO.unionType»
359 «val prop = allProperties.get(0)»
360 «IF !("org.opendaylight.yangtools.yang.binding.InstanceIdentifier".equals(prop.returnType.fullyQualifiedName))»
361 public static «genTO.name» getDefaultInstance(String defaultValue) {
362 «IF "byte[]".equals(prop.returnType.name)»
363 «BaseEncoding.importedName» baseEncoding = «BaseEncoding.importedName».base64();
364 return new «genTO.name»(baseEncoding.decode(defaultValue));
365 «ELSEIF "java.lang.String".equals(prop.returnType.fullyQualifiedName)»
366 return new «genTO.name»(defaultValue);
367 «ELSEIF allProperties.size > 1»
369 «ELSEIF "java.lang.Boolean".equals(prop.returnType.fullyQualifiedName)»
370 return new «genTO.name»(Boolean.valueOf(defaultValue));
371 «ELSEIF "java.lang.Byte".equals(prop.returnType.fullyQualifiedName)»
372 return new «genTO.name»(Byte.valueOf(defaultValue));
373 «ELSEIF "java.lang.Short".equals(prop.returnType.fullyQualifiedName)»
374 return new «genTO.name»(Short.valueOf(defaultValue));
375 «ELSEIF "java.lang.Integer".equals(prop.returnType.fullyQualifiedName)»
376 return new «genTO.name»(Integer.valueOf(defaultValue));
377 «ELSEIF "java.lang.Long".equals(prop.returnType.fullyQualifiedName)»
378 return new «genTO.name»(Long.valueOf(defaultValue));
380 return new «genTO.name»(new «prop.returnType.importedName»(defaultValue));
387 def protected bitsArgs() '''
388 «List.importedName»<«String.importedName»> properties = «Lists.importedName».newArrayList(«allProperties.propsAsArgs»);
389 if (!properties.contains(defaultValue)) {
390 throw new «IllegalArgumentException.importedName»("invalid default parameter");
393 return new «genTO.name»(
394 «FOR prop : allProperties SEPARATOR ","»
395 properties.get(i++).equals(defaultValue) ? «Boolean.importedName».TRUE : null
400 def protected propsAsArgs(Iterable<GeneratedProperty> properties) '''
401 «FOR prop : properties SEPARATOR ","»
407 * Template method which generates JAVA class declaration.
409 * @param isInnerClass boolean value which specify if generated class is|isn't inner
410 * @return string with class declaration in JAVA format
412 def protected generateClassDeclaration(boolean isInnerClass) '''
416 ELSEIF (type.abstract)»«
420 ENDIF»class «type.name»«
421 IF (genTO.superType != null)»«
422 " extends "»«genTO.superType.importedName»«
424 «IF (!type.implements.empty)»«
426 FOR type : type.implements SEPARATOR ", "»«
433 * Template method which generates JAVA enum type.
435 * @return string with inner enum source code in JAVA format
437 def protected enumDeclarations() '''
439 «FOR e : enums SEPARATOR "\n"»
440 «val enumTemplate = new EnumTemplate(e)»
441 «enumTemplate.generateAsInnerClass»
446 def protected suidDeclaration() '''
447 «IF genTO.SUID != null»
448 private static final long serialVersionUID = «genTO.SUID.value»L;
453 * Template method which generates JAVA constants.
455 * @return string with constants in JAVA format
457 def protected constantsDeclarations() '''
460 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
461 «val cValue = c.value»
462 «IF cValue instanceof List<?>»
463 «val cValues = cValue as List<?>»
464 private static final «List.importedName»<«Pattern.importedName»> «Constants.MEMBER_PATTERN_LIST»;
465 public static final «List.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
466 FOR v : cValues SEPARATOR ", "»«
467 IF v instanceof String»"«
472 «generateStaticInicializationBlock»
475 public static final «c.type.importedName» «c.name» = «c.value»;
482 * Template method which generates JAVA static initialization block.
484 * @return string with static initialization block in JAVA format
486 def protected generateStaticInicializationBlock() '''
488 final «List.importedName»<«Pattern.importedName»> l = new «ArrayList.importedName»<«Pattern.importedName»>();
489 for (String regEx : «TypeConstants.PATTERN_CONSTANT_NAME») {
490 l.add(Pattern.compile(regEx));
493 «Constants.MEMBER_PATTERN_LIST» = «ImmutableList.importedName».copyOf(l);
498 * Template method which generates JAVA class attributes.
500 * @return string with the class attributes in JAVA format
502 def protected generateFields() '''
503 «IF restrictions != null»
504 «val prop = getPropByName("value")»
506 «IF !(restrictions.lengthConstraints.empty)»
507 private static final «List.importedName»<«Range.importedName»<«prop.returnType.importedNumber»>> _length;
509 «IF !(restrictions.rangeConstraints.empty)»
510 private static final «List.importedName»<«Range.importedName»<«prop.returnType.importedNumber»>> _range;
514 «IF !properties.empty»
516 «IF f.readOnly»final«ENDIF» private «f.returnType.importedName» «f.fieldName»;
522 * Template method which generates the method <code>hashCode()</code>.
524 * @return string with the <code>hashCode()</code> method definition in JAVA format
526 def protected generateHashCode() '''
527 «IF !genTO.hashCodeIdentifiers.empty»
529 public int hashCode() {
530 final int prime = 31;
532 «FOR property : genTO.hashCodeIdentifiers»
533 «IF property.returnType.name.contains("[")»
534 result = prime * result + ((«property.fieldName» == null) ? 0 : «Arrays.importedName».hashCode(«property.fieldName»));
536 result = prime * result + ((«property.fieldName» == null) ? 0 : «property.fieldName».hashCode());
545 * Template method which generates the method <code>equals()</code>.
547 * @return string with the <code>equals()</code> method definition in JAVA format
549 def protected generateEquals() '''
550 «IF !genTO.equalsIdentifiers.empty»
552 public boolean equals(java.lang.Object obj) {
559 if (getClass() != obj.getClass()) {
562 «type.name» other = («type.name») obj;
563 «FOR property : genTO.equalsIdentifiers»
564 «val fieldName = property.fieldName»
565 if («fieldName» == null) {
566 if (other.«fieldName» != null) {
569 «IF property.returnType.name.contains("[")»
570 } else if(!«Arrays.importedName».equals(«fieldName», other.«fieldName»)) {
572 } else if(!«fieldName».equals(other.«fieldName»)) {
582 def GeneratedProperty getPropByName(String name) {
583 for (GeneratedProperty prop : allProperties) {
584 if (prop.name.equals(name)) {