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.base.CharMatcher
11 import com.google.common.base.Splitter
12 import java.math.BigDecimal
13 import java.math.BigInteger
14 import java.util.Arrays
15 import java.util.Collection
16 import java.util.HashMap
19 import java.util.StringTokenizer
20 import java.util.regex.Pattern
21 import org.opendaylight.yangtools.binding.generator.util.Types
22 import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType
23 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
24 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
25 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
26 import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature
27 import org.opendaylight.yangtools.sal.binding.model.api.Restrictions
28 import org.opendaylight.yangtools.sal.binding.model.api.Type
30 abstract class BaseTemplate {
31 protected val GeneratedType type;
32 protected val Map<String, String> importMap;
34 private static final char NEW_LINE = '\n'
35 private static final CharMatcher NL_MATCHER = CharMatcher.is(NEW_LINE)
36 private static final CharMatcher TAB_MATCHER = CharMatcher.is('\t')
37 private static final Pattern SPACES_PATTERN = Pattern.compile(" +")
38 private static final Splitter NL_SPLITTER = Splitter.on(NL_MATCHER)
40 new(GeneratedType _type) {
42 throw new IllegalArgumentException("Generated type reference cannot be NULL!")
45 this.importMap = new HashMap<String,String>()
48 def packageDefinition() '''package «type.packageName»;'''
50 protected def getFullyQualifiedName() {
51 return type.fullyQualifiedName
54 final public def generate() {
64 protected def imports() '''
66 «FOR entry : importMap.entrySet»
67 «IF !hasSamePackage(entry.value)»
68 import «entry.value».«entry.key»;
76 * Checks if packages of generated type and imported type is the same
78 * @param importedTypePackageNam
79 * the package name of imported type
80 * @return true if the packages are the same false otherwise
82 final private def boolean hasSamePackage(String importedTypePackageName) {
83 return type.packageName.equals(importedTypePackageName);
86 protected abstract def CharSequence body();
89 final protected def fieldName(GeneratedProperty property) '''_«property.name»'''
91 final protected def propertyNameFromGetter(MethodSignature getter) {
93 if (getter.name.startsWith("is")) {
95 } else if (getter.name.startsWith("get")) {
98 throw new IllegalArgumentException("Not a getter")
100 return getter.name.substring(prefix).toFirstLower;
104 * Template method which generates the getter method for <code>field</code>
107 * generated property with data about field which is generated as the getter method
108 * @return string with the getter method source code in JAVA format
110 final protected def getterMethod(GeneratedProperty field) {
112 public «field.returnType.importedName» «field.getterMethodName»() {
113 «IF field.returnType.importedName.contains("[]")»
114 return «field.fieldName» == null ? null : «field.fieldName».clone();
116 return «field.fieldName»;
122 final protected def getterMethodName(GeneratedProperty field) {
123 val prefix = if(field.returnType.equals(Types.BOOLEAN)) "is" else "get"
124 return '''«prefix»«field.name.toFirstUpper»'''
128 * Template method which generates the setter method for <code>field</code>
131 * generated property with data about field which is generated as the setter method
132 * @return string with the setter method source code in JAVA format
134 final protected def setterMethod(GeneratedProperty field) '''
135 «val returnType = field.returnType.importedName»
136 public «type.name» set«field.name.toFirstUpper»(«returnType» value) {
137 this.«field.fieldName» = value;
142 final protected def importedName(Type intype) {
143 GeneratorUtil.putTypeIntoImports(type, intype, importMap);
144 GeneratorUtil.getExplicitType(type, intype, importMap)
147 final protected def importedName(Class<?> cls) {
148 importedName(Types.typeForClass(cls))
152 * Template method which generates method parameters with their types from <code>parameters</code>.
155 * group of generated property instances which are transformed to the method parameters
156 * @return string with the list of the method parameters with their types in JAVA format
158 def final protected asArgumentsDeclaration(Iterable<GeneratedProperty> parameters) '''«IF !parameters.empty»«FOR parameter : parameters SEPARATOR ", "»«parameter.
159 returnType.importedName» «parameter.fieldName»«ENDFOR»«ENDIF»'''
162 * Template method which generates sequence of the names of the class attributes from <code>parameters</code>.
165 * group of generated property instances which are transformed to the sequence of parameter names
166 * @return string with the list of the parameter names of the <code>parameters</code>
168 def final protected asArguments(Iterable<GeneratedProperty> parameters) '''«IF !parameters.empty»«FOR parameter : parameters SEPARATOR ", "»«parameter.
169 fieldName»«ENDFOR»«ENDIF»'''
172 * Template method which generates JAVA comments.
174 * @param comment string with the comment for whole JAVA class
175 * @return string with comment in JAVA format
177 def protected CharSequence asJavadoc(String comment) {
178 if(comment == null) return ''
182 txt = formatToParagraph(txt)
185 «wrapToDocumentation(txt)»
189 def String wrapToDocumentation(String text) {
193 val StringBuilder sb = new StringBuilder("/**")
196 for (String t : NL_SPLITTER.split(text)) {
209 def protected String formatDataForJavaDoc(GeneratedType type) {
210 val typeDescription = type.getDescription().encodeJavadocSymbols;
213 «IF !typeDescription.nullOrEmpty»
219 private static final CharMatcher AMP_MATCHER = CharMatcher.is('&');
220 private static final CharMatcher GT_MATCHER = CharMatcher.is('>');
221 private static final CharMatcher LT_MATCHER = CharMatcher.is('<');
223 def encodeJavadocSymbols(String description) {
224 if (description.nullOrEmpty) {
228 var ret = description.replace("*/", "*/")
230 // FIXME: Use Guava's HtmlEscapers once we have it available
231 ret = AMP_MATCHER.replaceFrom(ret, "&");
232 ret = GT_MATCHER.replaceFrom(ret, ">");
233 ret = LT_MATCHER.replaceFrom(ret, "<");
237 def protected String formatDataForJavaDoc(GeneratedType type, String additionalComment) {
238 val StringBuilder typeDescription = new StringBuilder();
239 if (!type.description.nullOrEmpty) {
240 typeDescription.append(type.description)
241 typeDescription.append(NEW_LINE)
242 typeDescription.append(NEW_LINE)
243 typeDescription.append(NEW_LINE)
244 typeDescription.append(additionalComment)
246 typeDescription.append(additionalComment)
250 «typeDescription.toString»
254 def asLink(String text) {
255 val StringBuilder sb = new StringBuilder()
257 var char lastChar = ' '
258 var boolean badEnding = false
260 if (text.endsWith('.') || text.endsWith(':') || text.endsWith(',')) {
261 tempText = text.substring(0, text.length - 1)
262 lastChar = text.charAt(text.length - 1)
265 sb.append("<a href = \"")
277 protected def formatToParagraph(String text) {
278 if(text == null || text.isEmpty)
281 var formattedText = text
282 val StringBuilder sb = new StringBuilder();
283 var StringBuilder lineBuilder = new StringBuilder();
284 var boolean isFirstElementOnNewLineEmptyChar = false;
286 formattedText = formattedText.encodeJavadocSymbols
287 formattedText = NL_MATCHER.removeFrom(formattedText)
288 formattedText = TAB_MATCHER.removeFrom(formattedText)
289 formattedText = SPACES_PATTERN.matcher(formattedText).replaceAll(" ")
291 val StringTokenizer tokenizer = new StringTokenizer(formattedText, " ", true);
293 while(tokenizer.hasMoreElements) {
294 val nextElement = tokenizer.nextElement.toString
296 if(lineBuilder.length + nextElement.length > 80) {
297 if (lineBuilder.charAt(lineBuilder.length - 1) == ' ') {
298 lineBuilder.setLength(0)
299 lineBuilder.append(lineBuilder.substring(0, lineBuilder.length - 1))
301 if (lineBuilder.charAt(0) == ' ') {
302 lineBuilder.setLength(0)
303 lineBuilder.append(lineBuilder.substring(1))
306 sb.append(lineBuilder);
307 lineBuilder.setLength(0)
310 if(nextElement.toString == ' ') {
311 isFirstElementOnNewLineEmptyChar = !isFirstElementOnNewLineEmptyChar;
315 if(isFirstElementOnNewLineEmptyChar) {
316 isFirstElementOnNewLineEmptyChar = !isFirstElementOnNewLineEmptyChar
320 lineBuilder.append(nextElement)
323 sb.append(lineBuilder)
330 * Print length constraint.
331 * This should always be a BigInteger (only string and binary can have length restriction)
333 def printLengthConstraint(Type returnType, Class<? extends Number> clazz, String paramName, boolean isNestedType, boolean isArray) '''
334 «clazz.importedNumber» _constraint = «clazz.importedNumber».valueOf(«paramName»«IF isNestedType».getValue()«ENDIF».length«IF !isArray»()«ENDIF»);
337 def printRangeConstraint(Type returnType, String paramName, boolean isNestedType) '''
338 «IF BigDecimal.canonicalName.equals(returnType.fullyQualifiedName)»
339 «BigDecimal.importedName» _constraint = new «BigDecimal.importedName»(«paramName»«IF isNestedType».getValue()«ENDIF».toString());
342 «val propReturnType = findProperty(returnType as GeneratedTransferObject, "value").returnType»
343 «IF propReturnType.fullyQualifiedName.equals(BigInteger.canonicalName)»
344 «BigInteger.importedName» _constraint = «paramName».getValue();
346 «BigInteger.importedName» _constraint = «BigInteger.importedName».valueOf(«paramName».getValue());
349 «IF returnType.fullyQualifiedName.equals(BigInteger.canonicalName)»
350 «BigInteger.importedName» _constraint = «paramName»;
352 «BigInteger.importedName» _constraint = «BigInteger.importedName».valueOf(«paramName»);
358 def protected generateToString(Collection<GeneratedProperty> properties) '''
359 «IF !properties.empty»
361 public «String.importedName» toString() {
362 «StringBuilder.importedName» builder = new «StringBuilder.importedName»(«type.importedName».class.getSimpleName()).append(" [");
363 boolean first = true;
365 «FOR property : properties»
366 if («property.fieldName» != null) {
370 builder.append(", ");
372 builder.append("«property.fieldName»=");
373 «IF property.returnType.name.contains("[")»
374 builder.append(«Arrays.importedName».toString(«property.fieldName»));
376 builder.append(«property.fieldName»);
380 return builder.append(']').toString();
385 def getRestrictions(Type type) {
386 var Restrictions restrictions = null
387 if (type instanceof ConcreteType) {
388 restrictions = type.restrictions
389 } else if (type instanceof GeneratedTransferObject) {
390 restrictions = type.restrictions
395 def boolean isArrayType(GeneratedTransferObject type) {
397 val GeneratedProperty value = findProperty(type, "value")
398 if (value != null && value.returnType.name.contains("[")) {
404 def String toQuote(Object obj) {
405 return "\"" + obj.toString + "\"";
409 * Template method which generates method parameters with their types from <code>parameters</code>.
412 * list of parameter instances which are transformed to the method parameters
413 * @return string with the list of the method parameters with their types in JAVA format
415 def protected generateParameters(List<MethodSignature.Parameter> parameters) '''«
416 IF !parameters.empty»«
417 FOR parameter : parameters SEPARATOR ", "»«
418 parameter.type.importedName» «parameter.name»«
423 def protected String importedNumber(Class<? extends Number> clazz) {
424 if (clazz.equals(typeof(BigDecimal))) {
425 return BigDecimal.importedName
427 return BigInteger.importedName
430 def protected String importedNumber(Type clazz) {
431 if (clazz.fullyQualifiedName.equals(BigDecimal.canonicalName)) {
432 return BigDecimal.importedName
434 return BigInteger.importedName
437 def protected String numericValue(Class<? extends Number> clazz, Object numberValue) {
438 val number = clazz.importedName;
439 val value = numberValue.toString
440 if (clazz.equals(typeof(BigInteger)) || clazz.equals(typeof(BigDecimal))) {
441 if (value.equals("0")) {
442 return number + ".ZERO"
443 } else if (value.equals("1")) {
444 return number + ".ONE"
445 } else if (value.equals("10")) {
446 return number + ".TEN"
449 val Long longVal = Long.valueOf(value)
450 return number + ".valueOf(" + longVal + "L)"
451 } catch (NumberFormatException e) {
452 if (clazz.equals(typeof(BigDecimal))) {
454 val Double doubleVal = Double.valueOf(value);
455 return number + ".valueOf(" + doubleVal + ")"
456 } catch (NumberFormatException e2) {
462 return "new " + number + "(\"" + value + "\")"
465 def private GeneratedProperty findProperty(GeneratedTransferObject gto, String name) {
466 val props = gto.properties
468 if (prop.name.equals(name)) {
472 val GeneratedTransferObject parent = gto.superType
473 if (parent != null) {
474 return findProperty(parent, name)