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, restrictions, 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, Restrictions restrictions, String paramName, boolean isNestedType) '''
203 «val clazz = restrictions.rangeConstraints.iterator.next.min.class»
204 if («paramName» != null) {
205 «printRangeConstraint(returnType, clazz, paramName, isNestedType)»
206 boolean isValidRange = false;
207 for («Range.importedName»<«clazz.importedNumber»> r : «IF isNestedType»«returnType.importedName».«ENDIF»range()) {
208 if (r.contains(_constraint)) {
213 throw new IllegalArgumentException(String.format("Invalid range: %s, expected: %s.", «paramName», «IF isNestedType»«returnType.importedName».«ENDIF»range()));
219 * Print length constraint.
220 * This should always be a BigInteger (only string and binary can have length restriction)
222 def printLengthConstraint(Type returnType, Class<? extends Number> clazz, String paramName, boolean isNestedType, boolean isArray) '''
223 «clazz.importedNumber» _constraint = «clazz.importedNumber».valueOf(«paramName»«IF isNestedType».getValue()«ENDIF».length«IF !isArray»()«ENDIF»);
226 def printRangeConstraint(Type returnType, Class<? extends Number> clazz, String paramName, boolean isNestedType) '''
227 «IF clazz.canonicalName.equals(BigDecimal.canonicalName)»
228 «clazz.importedNumber» _constraint = new «clazz.importedNumber»(«paramName»«IF isNestedType».getValue()«ENDIF».toString());
231 «val propReturnType = findProperty(returnType as GeneratedTransferObject, "value").returnType»
232 «IF propReturnType.fullyQualifiedName.equals(BigInteger.canonicalName)»
233 «clazz.importedNumber» _constraint = «paramName».getValue();
235 «clazz.importedNumber» _constraint = «clazz.importedNumber».valueOf(«paramName».getValue());
238 «IF returnType.fullyQualifiedName.equals(BigInteger.canonicalName)»
239 «clazz.importedNumber» _constraint = «paramName»;
241 «clazz.importedNumber» _constraint = «clazz.importedNumber».valueOf(«paramName»);
247 def protected generateToString(Collection<GeneratedProperty> properties) '''
248 «IF !properties.empty»
250 public «String.importedName» toString() {
251 «StringBuilder.importedName» builder = new «StringBuilder.importedName»("«type.name» [");
252 boolean first = true;
254 «FOR property : properties»
255 if («property.fieldName» != null) {
259 builder.append(", ");
261 builder.append("«property.fieldName»=");
262 «IF property.returnType.name.contains("[")»
263 builder.append(«Arrays.importedName».toString(«property.fieldName»));
265 builder.append(«property.fieldName»);
269 return builder.append(']').toString();
274 def GeneratedProperty getPropByName(GeneratedType gt, String name) {
275 for (GeneratedProperty prop : gt.properties) {
276 if (prop.name.equals(name)) {
283 def getRestrictions(Type type) {
284 var Restrictions restrictions = null
285 if (type instanceof ConcreteType) {
286 restrictions = (type as ConcreteType).restrictions
287 } else if (type instanceof GeneratedTransferObject) {
288 restrictions = (type as GeneratedTransferObject).restrictions
293 def boolean isArrayType(GeneratedTransferObject type) {
295 val GeneratedProperty value = findProperty(type, "value")
296 if (value != null && value.returnType.name.contains("[")) {
302 def String toQuote(Object obj) {
303 return "\"" + obj.toString + "\"";
307 * Template method which generates method parameters with their types from <code>parameters</code>.
310 * list of parameter instances which are transformed to the method parameters
311 * @return string with the list of the method parameters with their types in JAVA format
313 def protected generateParameters(List<MethodSignature.Parameter> parameters) '''«
314 IF !parameters.empty»«
315 FOR parameter : parameters SEPARATOR ", "»«
316 parameter.type.importedName» «parameter.name»«
321 def protected generateLengthMethod(String methodName, Type type, String className, String varName) '''
322 «val Restrictions restrictions = type.restrictions»
323 «IF restrictions != null && !(restrictions.lengthConstraints.empty)»
324 «val numberClass = restrictions.lengthConstraints.iterator.next.min.class»
325 public static «List.importedName»<«Range.importedName»<«numberClass.importedNumber»>> «methodName»() {
326 «IF numberClass.equals(typeof(BigDecimal))»
327 «lengthMethodBody(restrictions, numberClass, className, varName)»
329 «lengthMethodBody(restrictions, typeof(BigInteger), className, varName)»
335 def private lengthMethodBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
336 if («varName» == null) {
337 synchronized («className».class) {
338 if («varName» == null) {
339 «ImmutableList.importedName».Builder<«Range.importedName»<«numberClass.importedName»>> builder = «ImmutableList.importedName».builder();
340 «FOR r : restrictions.lengthConstraints»
341 builder.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», «numericValue(numberClass, r.max)»));
343 «varName» = builder.build();
350 def protected generateRangeMethod(String methodName, Type type, String className, String varName) '''
351 «val Restrictions restrictions = type.restrictions»
352 «IF restrictions != null && !(restrictions.rangeConstraints.empty)»
353 «val numberClass = restrictions.rangeConstraints.iterator.next.min.class»
354 public static «List.importedName»<«Range.importedName»<«numberClass.importedNumber»>> «methodName»() {
355 «IF numberClass.equals(typeof(BigDecimal))»
356 «rangeMethodBody(restrictions, numberClass, className, varName)»
358 «rangeMethodBody(restrictions, typeof(BigInteger), className, varName)»
364 def private rangeMethodBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
365 if («varName» == null) {
366 synchronized («className».class) {
367 if («varName» == null) {
368 «ImmutableList.importedName».Builder<«Range.importedName»<«numberClass.importedName»>> builder = «ImmutableList.importedName».builder();
369 «FOR r : restrictions.rangeConstraints»
370 builder.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», «numericValue(numberClass, r.max)»));
372 «varName» = builder.build();
379 def protected String importedNumber(Class<? extends Number> clazz) {
380 if (clazz.equals(typeof(BigDecimal))) {
381 return BigDecimal.importedName
383 return BigInteger.importedName
386 def private String numericValue(Class<? extends Number> clazz, Object numberValue) {
387 val number = clazz.importedName;
388 val value = numberValue.toString
389 if (clazz.equals(typeof(BigInteger)) || clazz.equals(typeof(BigDecimal))) {
390 if (value.equals("0")) {
391 return number + ".ZERO"
392 } else if (value.equals("1")) {
393 return number + ".ONE"
394 } else if (value.equals("10")) {
395 return number + ".TEN"
398 val Long longVal = Long.valueOf(value)
399 return number + ".valueOf(" + longVal + "L)"
400 } catch (NumberFormatException e) {
401 if (clazz.equals(typeof(BigDecimal))) {
403 val Double doubleVal = Double.valueOf(value);
404 return number + ".valueOf(" + doubleVal + ")"
405 } catch (NumberFormatException e2) {
411 return "new " + number + "(\"" + value + "\")"
414 def private GeneratedProperty findProperty(GeneratedTransferObject gto, String name) {
415 val props = gto.properties
417 if (prop.name.equals(name)) {
421 val GeneratedTransferObject parent = gto.superType
422 if (parent != null) {
423 return findProperty(parent, name)