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 org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
11 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
13 import org.opendaylight.yangtools.sal.binding.model.api.Type
14 import org.opendaylight.yangtools.binding.generator.util.Types
15 import com.google.common.base.Splitter
16 import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature
17 import com.google.common.collect.Range
19 import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType
20 import org.opendaylight.yangtools.sal.binding.model.api.Restrictions
21 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
22 import java.util.Collection
23 import java.util.Arrays
24 import java.util.HashMap
25 import com.google.common.collect.ImmutableList
26 import java.math.BigInteger
27 import java.math.BigDecimal
29 abstract class BaseTemplate {
30 protected val GeneratedType type;
31 protected val Map<String, String> importMap;
32 static val paragraphSplitter = Splitter.on("\n\n").omitEmptyStrings();
34 new(GeneratedType _type) {
36 throw new IllegalArgumentException("Generated type reference cannot be NULL!")
39 this.importMap = new HashMap<String,String>()
42 def packageDefinition() '''package «type.packageName»;'''
44 protected def getFullyQualifiedName() {
45 return type.fullyQualifiedName
48 final public def generate() {
58 protected def imports() '''
60 «FOR entry : importMap.entrySet»
61 «IF entry.value != fullyQualifiedName»
62 import «entry.value».«entry.key»;
69 protected abstract def CharSequence body();
72 final protected def fieldName(GeneratedProperty property) '''_«property.name»'''
74 final protected def propertyNameFromGetter(MethodSignature getter) {
76 if (getter.name.startsWith("is")) {
78 } else if (getter.name.startsWith("get")) {
81 throw new IllegalArgumentException("Not a getter")
83 return getter.name.substring(prefix).toFirstLower;
87 * Template method which generates the getter method for <code>field</code>
90 * generated property with data about field which is generated as the getter method
91 * @return string with the getter method source code in JAVA format
93 final protected def getterMethod(GeneratedProperty field) {
95 public «field.returnType.importedName» «field.getterMethodName»() {
96 return «field.fieldName»;
101 final protected def getterMethodName(GeneratedProperty field) {
102 val prefix = if(field.returnType.equals(Types.BOOLEAN)) "is" else "get"
103 return '''«prefix»«field.name.toFirstUpper»'''
107 * Template method which generates the setter method for <code>field</code>
110 * generated property with data about field which is generated as the setter method
111 * @return string with the setter method source code in JAVA format
113 final protected def setterMethod(GeneratedProperty field) '''
114 «val returnType = field.returnType.importedName»
115 public «type.name» set«field.name.toFirstUpper»(«returnType» value) {
116 this.«field.fieldName» = value;
121 final protected def importedName(Type intype) {
122 GeneratorUtil.putTypeIntoImports(type, intype, importMap);
123 GeneratorUtil.getExplicitType(type, intype, importMap)
126 final protected def importedName(Class<?> cls) {
127 importedName(Types.typeForClass(cls))
131 * Template method which generates method parameters with their types from <code>parameters</code>.
134 * group of generated property instances which are transformed to the method parameters
135 * @return string with the list of the method parameters with their types in JAVA format
137 def final protected asArgumentsDeclaration(Iterable<GeneratedProperty> parameters) '''«IF !parameters.empty»«FOR parameter : parameters SEPARATOR ", "»«parameter.
138 returnType.importedName» «parameter.fieldName»«ENDFOR»«ENDIF»'''
141 * Template method which generates sequence of the names of the class attributes from <code>parameters</code>.
144 * group of generated property instances which are transformed to the sequence of parameter names
145 * @return string with the list of the parameter names of the <code>parameters</code>
147 def final protected asArguments(Iterable<GeneratedProperty> parameters) '''«IF !parameters.empty»«FOR parameter : parameters SEPARATOR ", "»«parameter.
148 fieldName»«ENDFOR»«ENDIF»'''
151 * Template method which generates JAVA comments.
153 * @param comment string with the comment for whole JAVA class
154 * @return string with comment in JAVA format
156 def protected CharSequence asJavadoc(String comment) {
157 if(comment == null) return '';
159 if (txt.contains("*/")) {
160 txt = txt.replace("*/", "*/")
162 val paragraphs = paragraphSplitter.split(txt)
166 «FOR p : paragraphs SEPARATOR "<p>"»
173 def generateRestrictions(Type type, String paramName, Type returnType) '''
174 «val restrictions = type.getRestrictions»
175 «IF restrictions !== null»
176 «val boolean isNestedType = !(returnType instanceof ConcreteType)»
177 «IF !restrictions.lengthConstraints.empty»
178 «generateLengthRestriction(returnType, restrictions, paramName, isNestedType)»
180 «IF !restrictions.rangeConstraints.empty»
181 «generateRangeRestriction(returnType, paramName, isNestedType)»
186 def private generateLengthRestriction(Type returnType, Restrictions restrictions, String paramName, boolean isNestedType) '''
187 «val clazz = restrictions.lengthConstraints.iterator.next.min.class»
188 if («paramName» != null) {
189 «printLengthConstraint(returnType, clazz, paramName, isNestedType, returnType.name.contains("["))»
190 boolean isValidLength = false;
191 for («Range.importedName»<«clazz.importedNumber»> r : «IF isNestedType»«returnType.importedName».«ENDIF»length()) {
192 if (r.contains(_constraint)) {
193 isValidLength = true;
196 if (!isValidLength) {
197 throw new IllegalArgumentException(String.format("Invalid length: %s, expected: %s.", «paramName», «IF isNestedType»«returnType.importedName».«ENDIF»length()));
202 def private generateRangeRestriction(Type returnType, String paramName, boolean isNestedType) '''
203 if («paramName» != null) {
204 «printRangeConstraint(returnType, paramName, isNestedType)»
205 boolean isValidRange = false;
206 for («Range.importedName»<«returnType.importedNumber»> r : «IF isNestedType»«returnType.importedName».«ENDIF»range()) {
207 if (r.contains(_constraint)) {
212 throw new IllegalArgumentException(String.format("Invalid range: %s, expected: %s.", «paramName», «IF isNestedType»«returnType.importedName».«ENDIF»range()));
218 * Print length constraint.
219 * This should always be a BigInteger (only string and binary can have length restriction)
221 def printLengthConstraint(Type returnType, Class<? extends Number> clazz, String paramName, boolean isNestedType, boolean isArray) '''
222 «clazz.importedNumber» _constraint = «clazz.importedNumber».valueOf(«paramName»«IF isNestedType».getValue()«ENDIF».length«IF !isArray»()«ENDIF»);
225 def printRangeConstraint(Type returnType, String paramName, boolean isNestedType) '''
226 «IF BigDecimal.canonicalName.equals(returnType.fullyQualifiedName)»
227 «BigDecimal.importedName» _constraint = new «BigDecimal.importedName»(«paramName»«IF isNestedType».getValue()«ENDIF».toString());
230 «val propReturnType = findProperty(returnType as GeneratedTransferObject, "value").returnType»
231 «IF propReturnType.fullyQualifiedName.equals(BigInteger.canonicalName)»
232 «BigInteger.importedName» _constraint = «paramName».getValue();
234 «BigInteger.importedName» _constraint = «BigInteger.importedName».valueOf(«paramName».getValue());
237 «IF returnType.fullyQualifiedName.equals(BigInteger.canonicalName)»
238 «BigInteger.importedName» _constraint = «paramName»;
240 «BigInteger.importedName» _constraint = «BigInteger.importedName».valueOf(«paramName»);
246 def protected generateToString(Collection<GeneratedProperty> properties) '''
247 «IF !properties.empty»
249 public «String.importedName» toString() {
250 «StringBuilder.importedName» builder = new «StringBuilder.importedName»("«type.name» [");
251 boolean first = true;
253 «FOR property : properties»
254 if («property.fieldName» != null) {
258 builder.append(", ");
260 builder.append("«property.fieldName»=");
261 «IF property.returnType.name.contains("[")»
262 builder.append(«Arrays.importedName».toString(«property.fieldName»));
264 builder.append(«property.fieldName»);
268 return builder.append(']').toString();
273 def getRestrictions(Type type) {
274 var Restrictions restrictions = null
275 if (type instanceof ConcreteType) {
276 restrictions = (type as ConcreteType).restrictions
277 } else if (type instanceof GeneratedTransferObject) {
278 restrictions = (type as GeneratedTransferObject).restrictions
283 def boolean isArrayType(GeneratedTransferObject type) {
285 val GeneratedProperty value = findProperty(type, "value")
286 if (value != null && value.returnType.name.contains("[")) {
292 def String toQuote(Object obj) {
293 return "\"" + obj.toString + "\"";
297 * Template method which generates method parameters with their types from <code>parameters</code>.
300 * list of parameter instances which are transformed to the method parameters
301 * @return string with the list of the method parameters with their types in JAVA format
303 def protected generateParameters(List<MethodSignature.Parameter> parameters) '''«
304 IF !parameters.empty»«
305 FOR parameter : parameters SEPARATOR ", "»«
306 parameter.type.importedName» «parameter.name»«
311 def protected generateLengthMethod(String methodName, Type type, String className, String varName) '''
312 «val Restrictions restrictions = type.restrictions»
313 «IF restrictions != null && !(restrictions.lengthConstraints.empty)»
314 «val numberClass = restrictions.lengthConstraints.iterator.next.min.class»
315 public static «List.importedName»<«Range.importedName»<«numberClass.importedNumber»>> «methodName»() {
316 «IF numberClass.equals(typeof(BigDecimal))»
317 «lengthMethodBody(restrictions, numberClass, className, varName)»
319 «lengthMethodBody(restrictions, typeof(BigInteger), className, varName)»
325 def private lengthMethodBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
326 if («varName» == null) {
327 synchronized («className».class) {
328 if («varName» == null) {
329 «ImmutableList.importedName».Builder<«Range.importedName»<«numberClass.importedName»>> builder = «ImmutableList.importedName».builder();
330 «FOR r : restrictions.lengthConstraints»
331 builder.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», «numericValue(numberClass, r.max)»));
333 «varName» = builder.build();
340 def protected generateRangeMethod(String methodName, Restrictions restrictions, Type returnType, String className, String varName) '''
341 «IF restrictions != null && !(restrictions.rangeConstraints.empty)»
342 «val number = returnType.importedNumber»
343 public static «List.importedName»<«Range.importedName»<«number»>> «methodName»() {
344 «IF returnType.fullyQualifiedName.equals(BigDecimal.canonicalName)»
345 «rangeMethodBody(restrictions, BigDecimal, className, varName)»
347 «rangeMethodBody(restrictions, BigInteger, className, varName)»
353 def protected generateRangeMethod(String methodName, Restrictions restrictions, String className, String varName, Iterable<GeneratedProperty> properties) '''
354 «IF restrictions != null && !(restrictions.rangeConstraints.empty)»
355 «val returnType = properties.iterator.next.returnType»
356 public static «List.importedName»<«Range.importedName»<«returnType.importedNumber»>> «methodName»() {
357 «IF returnType.fullyQualifiedName.equals(BigDecimal.canonicalName)»
358 «rangeMethodBody(restrictions, BigDecimal, className, varName)»
360 «rangeMethodBody(restrictions, BigInteger, className, varName)»
366 def private rangeMethodBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
367 if («varName» == null) {
368 synchronized («className».class) {
369 if («varName» == null) {
370 «ImmutableList.importedName».Builder<«Range.importedName»<«numberClass.importedName»>> builder = «ImmutableList.importedName».builder();
371 «FOR r : restrictions.rangeConstraints»
372 builder.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», «numericValue(numberClass, r.max)»));
374 «varName» = builder.build();
381 def protected String importedNumber(Class<? extends Number> clazz) {
382 if (clazz.equals(typeof(BigDecimal))) {
383 return BigDecimal.importedName
385 return BigInteger.importedName
388 def protected String importedNumber(Type clazz) {
389 if (clazz.fullyQualifiedName.equals(BigDecimal.canonicalName)) {
390 return BigDecimal.importedName
392 return BigInteger.importedName
395 def private String numericValue(Class<? extends Number> clazz, Object numberValue) {
396 val number = clazz.importedName;
397 val value = numberValue.toString
398 if (clazz.equals(typeof(BigInteger)) || clazz.equals(typeof(BigDecimal))) {
399 if (value.equals("0")) {
400 return number + ".ZERO"
401 } else if (value.equals("1")) {
402 return number + ".ONE"
403 } else if (value.equals("10")) {
404 return number + ".TEN"
407 val Long longVal = Long.valueOf(value)
408 return number + ".valueOf(" + longVal + "L)"
409 } catch (NumberFormatException e) {
410 if (clazz.equals(typeof(BigDecimal))) {
412 val Double doubleVal = Double.valueOf(value);
413 return number + ".valueOf(" + doubleVal + ")"
414 } catch (NumberFormatException e2) {
420 return "new " + number + "(\"" + value + "\")"
423 def private GeneratedProperty findProperty(GeneratedTransferObject gto, String name) {
424 val props = gto.properties
426 if (prop.name.equals(name)) {
430 val GeneratedTransferObject parent = gto.superType
431 if (parent != null) {
432 return findProperty(parent, name)