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 com.google.common.base.Preconditions
32 * Template for generating JAVA class.
34 class ClassTemplate extends BaseTemplate {
36 protected val List<GeneratedProperty> properties
37 protected val List<GeneratedProperty> finalProperties
38 protected val List<GeneratedProperty> parentProperties
39 protected val Iterable<GeneratedProperty> allProperties;
40 protected val Restrictions restrictions
43 * List of enumeration which are generated as JAVA enum type.
45 protected val List<Enumeration> enums
48 * List of constant instances which are generated as JAVA public static final attributes.
50 protected val List<Constant> consts
53 * List of generated types which are enclosed inside <code>genType</code>
55 protected val List<GeneratedType> enclosedGeneratedTypes;
57 protected val GeneratedTransferObject genTO;
60 * Creates instance of this class with concrete <code>genType</code>.
62 * @param genType generated transfer object which will be transformed to JAVA class source code
64 new(GeneratedTransferObject genType) {
67 this.properties = genType.properties
68 this.finalProperties = GeneratorUtil.resolveReadOnlyPropertiesFromTO(genTO.properties)
69 this.parentProperties = GeneratorUtil.getPropertiesOfAllParents(genTO)
70 this.restrictions = genType.restrictions
72 var List<GeneratedProperty> sorted = new ArrayList<GeneratedProperty>();
73 sorted.addAll(properties);
74 sorted.addAll(parentProperties);
75 Collections.sort(sorted, [p1, p2|
76 p1.name.compareTo(p2.name)
79 this.allProperties = sorted
80 this.enums = genType.enumerations
81 this.consts = genType.constantDefinitions
82 this.enclosedGeneratedTypes = genType.enclosedTypes
86 * Generates JAVA class source code (class body only).
88 * @return string with JAVA class body source code
90 def CharSequence generateAsInnerClass() {
91 return generateBody(true)
94 override protected body() {
99 * Template method which generates class body.
101 * @param isInnerClass boolean value which specify if generated class is|isn't inner
102 * @return string with class source code in JAVA format
104 def protected generateBody(boolean isInnerClass) '''
105 «wrapToDocumentation(formatDataForJavaDoc(type))»
106 «generateClassDeclaration(isInnerClass)» {
108 «innerClassesDeclarations»
110 «constantsDeclarations»
113 «IF restrictions != null && (!restrictions.rangeConstraints.nullOrEmpty ||
114 !restrictions.lengthConstraints.nullOrEmpty)»
115 «generateConstraints»
122 «FOR field : properties SEPARATOR "\n"»
133 «generateToString(genTO.toStringIdentifiers)»
135 «generateLengthMethod("length", "_length")»
137 «generateRangeMethod("range", "_range")»
142 def private generateLengthMethod(String methodName, String varName) '''
143 «IF restrictions != null && !(restrictions.lengthConstraints.empty)»
144 «val numberClass = restrictions.lengthConstraints.iterator.next.min.class»
145 public static «List.importedName»<«Range.importedName»<«numberClass.importedNumber»>> «methodName»() {
151 def private generateRangeMethod(String methodName, String varName) '''
152 «IF restrictions != null && !(restrictions.rangeConstraints.empty)»
153 «val returnType = allProperties.iterator.next.returnType»
154 public static «List.importedName»<«Range.importedName»<«returnType.importedNumber»>> «methodName»() {
161 * Template method which generates inner classes inside this interface.
163 * @return string with the source code for inner classes in JAVA format
165 def protected innerClassesDeclarations() '''
166 «IF !enclosedGeneratedTypes.empty»
167 «FOR innerClass : enclosedGeneratedTypes SEPARATOR "\n"»
168 «IF (innerClass instanceof GeneratedTransferObject)»
169 «val classTemplate = new ClassTemplate(innerClass as GeneratedTransferObject)»
170 «classTemplate.generateAsInnerClass»
177 def protected constructors() '''
179 «genUnionConstructor»
181 «allValuesConstructor»
183 «IF !allProperties.empty»
186 «IF properties.empty && !parentProperties.empty »
191 def private generateConstraints() '''
193 «IF !restrictions.rangeConstraints.nullOrEmpty»
194 «generateRangeConstraints»
196 «IF !restrictions.lengthConstraints.nullOrEmpty»
197 «generateLengthConstraints»
202 private def generateRangeConstraints() '''
203 «IF !allProperties.nullOrEmpty»
204 «val returnType = allProperties.iterator.next.returnType»
205 «IF returnType.fullyQualifiedName.equals(BigDecimal.canonicalName)»
206 «rangeBody(restrictions, BigDecimal, genTO.importedName, "_range")»
208 «rangeBody(restrictions, BigInteger, genTO.importedName, "_range")»
213 private def rangeBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
214 «ImmutableList.importedName».Builder<«Range.importedName»<«numberClass.importedName»>> builder = «ImmutableList.importedName».builder();
215 «FOR r : restrictions.rangeConstraints»
216 builder.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», «numericValue(numberClass, r.max)»));
218 «varName» = builder.build();
221 private def lengthBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
222 «ImmutableList.importedName».Builder<«Range.importedName»<«numberClass.importedName»>> builder = «ImmutableList.importedName».builder();
223 «FOR r : restrictions.lengthConstraints»
224 builder.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», «numericValue(numberClass, r.max)»));
226 «varName» = builder.build();
229 private def generateLengthConstraints() '''
230 «IF restrictions != null && !(restrictions.lengthConstraints.empty)»
231 «val numberClass = restrictions.lengthConstraints.iterator.next.min.class»
232 «IF numberClass.equals(typeof(BigDecimal))»
233 «lengthBody(restrictions, numberClass, genTO.importedName, "_length")»
235 «lengthBody(restrictions, typeof(BigInteger), genTO.importedName, "_length")»
240 def protected allValuesConstructor() '''
241 «IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
242 @«ConstructorProperties.importedName»("value")
244 public «type.name»(«allProperties.asArgumentsDeclaration») {
245 «IF false == parentProperties.empty»
246 super(«parentProperties.asArguments»);
248 «FOR p : allProperties»
249 «generateRestrictions(type, p.fieldName.toString, p.returnType)»
253 * If we have patterns, we need to apply them to the value field. This is a sad
254 * consequence of how this code is structured.
256 IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
258 «Preconditions.importedName».checkNotNull(_value, "Supplied value may not be null");
261 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME && c.value instanceof List<?>»
262 boolean valid = false;
263 for (Pattern p : patterns) {
264 if (p.matcher(_value).matches()) {
270 «Preconditions.importedName».checkArgument(valid, "Supplied value \"%s\" does not match any of the permitted patterns %s", _value, «TypeConstants.PATTERN_CONSTANT_NAME»);
276 this.«p.fieldName» = «p.fieldName»;
282 def protected genUnionConstructor() '''
283 «FOR p : allProperties»
284 «val List<GeneratedProperty> other = new ArrayList(properties)»
286 «genConstructor(p, other)»
292 def protected genConstructor(GeneratedProperty property, GeneratedProperty... other) '''
293 public «type.name»(«property.returnType.importedName + " " + property.name») {
294 «IF false == parentProperties.empty»
295 super(«parentProperties.asArguments»);
298 «generateRestrictions(type, property.fieldName.toString, property.returnType)»
300 this.«property.fieldName» = «property.name»;
302 this.«p.fieldName» = null;
307 def protected copyConstructor() '''
309 * Creates a copy from Source Object.
311 * @param source Source object
313 public «type.name»(«type.name» source) {
314 «IF false == parentProperties.empty»
318 this.«p.fieldName» = source.«p.fieldName»;
323 def protected parentConstructor() '''
325 * Creates a new instance from «genTO.superType.importedName»
327 * @param source Source object
329 public «type.name»(«genTO.superType.importedName» source) {
334 def protected defaultInstance() '''
335 «IF genTO.typedef && !allProperties.empty && !genTO.unionType»
336 «val prop = allProperties.get(0)»
337 «IF !("org.opendaylight.yangtools.yang.binding.InstanceIdentifier".equals(prop.returnType.fullyQualifiedName))»
338 public static «genTO.name» getDefaultInstance(String defaultValue) {
339 «IF "byte[]".equals(prop.returnType.name)»
340 «BaseEncoding.importedName» baseEncoding = «BaseEncoding.importedName».base64();
341 return new «genTO.name»(baseEncoding.decode(defaultValue));
342 «ELSEIF "java.lang.String".equals(prop.returnType.fullyQualifiedName)»
343 return new «genTO.name»(defaultValue);
344 «ELSEIF allProperties.size > 1»
346 «ELSEIF "java.lang.Boolean".equals(prop.returnType.fullyQualifiedName)»
347 return new «genTO.name»(Boolean.valueOf(defaultValue));
348 «ELSEIF "java.lang.Byte".equals(prop.returnType.fullyQualifiedName)»
349 return new «genTO.name»(Byte.valueOf(defaultValue));
350 «ELSEIF "java.lang.Short".equals(prop.returnType.fullyQualifiedName)»
351 return new «genTO.name»(Short.valueOf(defaultValue));
352 «ELSEIF "java.lang.Integer".equals(prop.returnType.fullyQualifiedName)»
353 return new «genTO.name»(Integer.valueOf(defaultValue));
354 «ELSEIF "java.lang.Long".equals(prop.returnType.fullyQualifiedName)»
355 return new «genTO.name»(Long.valueOf(defaultValue));
357 return new «genTO.name»(new «prop.returnType.importedName»(defaultValue));
364 def protected bitsArgs() '''
365 «List.importedName»<«String.importedName»> properties = «Lists.importedName».newArrayList(«allProperties.propsAsArgs»);
366 if (!properties.contains(defaultValue)) {
367 throw new «IllegalArgumentException.importedName»("invalid default parameter");
370 return new «genTO.name»(
371 «FOR prop : allProperties SEPARATOR ","»
372 properties.get(i++).equals(defaultValue) ? «Boolean.importedName».TRUE : null
377 def protected propsAsArgs(Iterable<GeneratedProperty> properties) '''
378 «FOR prop : properties SEPARATOR ","»
384 * Template method which generates JAVA class declaration.
386 * @param isInnerClass boolean value which specify if generated class is|isn't inner
387 * @return string with class declaration in JAVA format
389 def protected generateClassDeclaration(boolean isInnerClass) '''
393 ELSEIF (type.abstract)»«
397 ENDIF»class «type.name»«
398 IF (genTO.superType != null)»«
399 " extends "»«genTO.superType.importedName»«
401 «IF (!type.implements.empty)»«
403 FOR type : type.implements SEPARATOR ", "»«
410 * Template method which generates JAVA enum type.
412 * @return string with inner enum source code in JAVA format
414 def protected enumDeclarations() '''
416 «FOR e : enums SEPARATOR "\n"»
417 «val enumTemplate = new EnumTemplate(e)»
418 «enumTemplate.generateAsInnerClass»
423 def protected suidDeclaration() '''
424 «IF genTO.SUID != null»
425 private static final long serialVersionUID = «genTO.SUID.value»L;
430 * Template method which generates JAVA constants.
432 * @return string with constants in JAVA format
434 def protected constantsDeclarations() '''
437 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
438 «val cValue = c.value»
439 «IF cValue instanceof List<?>»
440 «val cValues = cValue as List<?>»
441 private static final «List.importedName»<«Pattern.importedName»> «Constants.MEMBER_PATTERN_LIST»;
442 public static final «List.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
443 FOR v : cValues SEPARATOR ", "»«
444 IF v instanceof String»"«
449 «generateStaticInicializationBlock»
452 public static final «c.type.importedName» «c.name» = «c.value»;
459 * Template method which generates JAVA static initialization block.
461 * @return string with static initialization block in JAVA format
463 def protected generateStaticInicializationBlock() '''
465 final «List.importedName»<«Pattern.importedName»> l = new «ArrayList.importedName»<«Pattern.importedName»>();
466 for (String regEx : «TypeConstants.PATTERN_CONSTANT_NAME») {
467 l.add(Pattern.compile(regEx));
470 «Constants.MEMBER_PATTERN_LIST» = «ImmutableList.importedName».copyOf(l);
475 * Template method which generates JAVA class attributes.
477 * @return string with the class attributes in JAVA format
479 def protected generateFields() '''
480 «IF restrictions != null»
481 «val prop = getPropByName("value")»
483 «IF !(restrictions.lengthConstraints.empty)»
484 private static final «List.importedName»<«Range.importedName»<«prop.returnType.importedNumber»>> _length;
486 «IF !(restrictions.rangeConstraints.empty)»
487 private static final «List.importedName»<«Range.importedName»<«prop.returnType.importedNumber»>> _range;
491 «IF !properties.empty»
493 «IF f.readOnly»final«ENDIF» private «f.returnType.importedName» «f.fieldName»;
499 * Template method which generates the method <code>hashCode()</code>.
501 * @return string with the <code>hashCode()</code> method definition in JAVA format
503 def protected generateHashCode() '''
504 «IF !genTO.hashCodeIdentifiers.empty»
506 public int hashCode() {
507 final int prime = 31;
509 «FOR property : genTO.hashCodeIdentifiers»
510 «IF property.returnType.name.contains("[")»
511 result = prime * result + ((«property.fieldName» == null) ? 0 : «Arrays.importedName».hashCode(«property.fieldName»));
513 result = prime * result + ((«property.fieldName» == null) ? 0 : «property.fieldName».hashCode());
522 * Template method which generates the method <code>equals()</code>.
524 * @return string with the <code>equals()</code> method definition in JAVA format
526 def protected generateEquals() '''
527 «IF !genTO.equalsIdentifiers.empty»
529 public boolean equals(java.lang.Object obj) {
536 if (getClass() != obj.getClass()) {
539 «type.name» other = («type.name») obj;
540 «FOR property : genTO.equalsIdentifiers»
541 «val fieldName = property.fieldName»
542 if («fieldName» == null) {
543 if (other.«fieldName» != null) {
546 «IF property.returnType.name.contains("[")»
547 } else if(!«Arrays.importedName».equals(«fieldName», other.«fieldName»)) {
549 } else if(!«fieldName».equals(other.«fieldName»)) {
559 def GeneratedProperty getPropByName(String name) {
560 for (GeneratedProperty prop : allProperties) {
561 if (prop.name.equals(name)) {