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 final public def generate() {
60 protected def imports() '''
62 «FOR entry : importMap.entrySet»
63 «IF !hasSamePackage(entry.value)»
64 import «entry.value».«entry.key»;
72 * Checks if packages of generated type and imported type is the same
74 * @param importedTypePackageNam
75 * the package name of imported type
76 * @return true if the packages are the same false otherwise
78 final private def boolean hasSamePackage(String importedTypePackageName) {
79 return type.packageName.equals(importedTypePackageName);
82 protected abstract def CharSequence body();
85 final protected def fieldName(GeneratedProperty property) '''_«property.name»'''
87 final protected def propertyNameFromGetter(MethodSignature getter) {
89 if (getter.name.startsWith("is")) {
91 } else if (getter.name.startsWith("get")) {
94 throw new IllegalArgumentException("Not a getter")
96 return getter.name.substring(prefix).toFirstLower;
100 * Template method which generates the getter method for <code>field</code>
103 * generated property with data about field which is generated as the getter method
104 * @return string with the getter method source code in JAVA format
106 final protected def getterMethod(GeneratedProperty field) {
108 public «field.returnType.importedName» «field.getterMethodName»() {
109 «IF field.returnType.importedName.contains("[]")»
110 return «field.fieldName» == null ? null : «field.fieldName».clone();
112 return «field.fieldName»;
118 final protected def getterMethodName(GeneratedProperty field) {
119 val prefix = if(field.returnType.equals(Types.BOOLEAN)) "is" else "get"
120 return '''«prefix»«field.name.toFirstUpper»'''
124 * Template method which generates the setter method for <code>field</code>
127 * generated property with data about field which is generated as the setter method
128 * @return string with the setter method source code in JAVA format
130 final protected def setterMethod(GeneratedProperty field) '''
131 «val returnType = field.returnType.importedName»
132 public «type.name» set«field.name.toFirstUpper»(«returnType» value) {
133 this.«field.fieldName» = value;
138 final protected def importedName(Type intype) {
139 GeneratorUtil.putTypeIntoImports(type, intype, importMap);
140 GeneratorUtil.getExplicitType(type, intype, importMap)
143 final protected def importedName(Class<?> cls) {
144 importedName(Types.typeForClass(cls))
148 * Template method which generates method parameters with their types from <code>parameters</code>.
151 * group of generated property instances which are transformed to the method parameters
152 * @return string with the list of the method parameters with their types in JAVA format
154 def final protected asArgumentsDeclaration(Iterable<GeneratedProperty> parameters) '''«IF !parameters.empty»«FOR parameter : parameters SEPARATOR ", "»«parameter.
155 returnType.importedName» «parameter.fieldName»«ENDFOR»«ENDIF»'''
158 * Template method which generates sequence of the names of the class attributes from <code>parameters</code>.
161 * group of generated property instances which are transformed to the sequence of parameter names
162 * @return string with the list of the parameter names of the <code>parameters</code>
164 def final protected asArguments(Iterable<GeneratedProperty> parameters) '''«IF !parameters.empty»«FOR parameter : parameters SEPARATOR ", "»«parameter.
165 fieldName»«ENDFOR»«ENDIF»'''
168 * Template method which generates JAVA comments.
170 * @param comment string with the comment for whole JAVA class
171 * @return string with comment in JAVA format
173 def protected CharSequence asJavadoc(String comment) {
174 if(comment == null) return ''
178 txt = formatToParagraph(txt)
181 «wrapToDocumentation(txt)»
185 def String wrapToDocumentation(String text) {
189 val StringBuilder sb = new StringBuilder("/**")
192 for (String t : NL_SPLITTER.split(text)) {
205 def protected String formatDataForJavaDoc(GeneratedType type) {
206 val typeDescription = type.getDescription().encodeJavadocSymbols;
209 «IF !typeDescription.nullOrEmpty»
215 private static final CharMatcher AMP_MATCHER = CharMatcher.is('&');
216 private static final CharMatcher GT_MATCHER = CharMatcher.is('>');
217 private static final CharMatcher LT_MATCHER = CharMatcher.is('<');
219 def encodeJavadocSymbols(String description) {
220 if (description.nullOrEmpty) {
224 var ret = description.replace("*/", "*/")
226 // FIXME: Use Guava's HtmlEscapers once we have it available
227 ret = AMP_MATCHER.replaceFrom(ret, "&");
228 ret = GT_MATCHER.replaceFrom(ret, ">");
229 ret = LT_MATCHER.replaceFrom(ret, "<");
233 def protected String formatDataForJavaDoc(GeneratedType type, String additionalComment) {
234 val StringBuilder typeDescription = new StringBuilder();
235 if (!type.description.nullOrEmpty) {
236 typeDescription.append(type.description)
237 typeDescription.append(NEW_LINE)
238 typeDescription.append(NEW_LINE)
239 typeDescription.append(NEW_LINE)
240 typeDescription.append(additionalComment)
242 typeDescription.append(additionalComment)
246 «typeDescription.toString»
250 def asLink(String text) {
251 val StringBuilder sb = new StringBuilder()
253 var char lastChar = ' '
254 var boolean badEnding = false
256 if (text.endsWith('.') || text.endsWith(':') || text.endsWith(',')) {
257 tempText = text.substring(0, text.length - 1)
258 lastChar = text.charAt(text.length - 1)
261 sb.append("<a href = \"")
273 protected def formatToParagraph(String text) {
274 if(text == null || text.isEmpty)
277 var formattedText = text
278 val StringBuilder sb = new StringBuilder();
279 var StringBuilder lineBuilder = new StringBuilder();
280 var boolean isFirstElementOnNewLineEmptyChar = false;
282 formattedText = formattedText.encodeJavadocSymbols
283 formattedText = NL_MATCHER.removeFrom(formattedText)
284 formattedText = TAB_MATCHER.removeFrom(formattedText)
285 formattedText = SPACES_PATTERN.matcher(formattedText).replaceAll(" ")
287 val StringTokenizer tokenizer = new StringTokenizer(formattedText, " ", true);
289 while(tokenizer.hasMoreElements) {
290 val nextElement = tokenizer.nextElement.toString
292 if(lineBuilder.length + nextElement.length > 80) {
293 if (lineBuilder.charAt(lineBuilder.length - 1) == ' ') {
294 lineBuilder.setLength(0)
295 lineBuilder.append(lineBuilder.substring(0, lineBuilder.length - 1))
297 if (lineBuilder.charAt(0) == ' ') {
298 lineBuilder.setLength(0)
299 lineBuilder.append(lineBuilder.substring(1))
302 sb.append(lineBuilder);
303 lineBuilder.setLength(0)
306 if(nextElement.toString == ' ') {
307 isFirstElementOnNewLineEmptyChar = !isFirstElementOnNewLineEmptyChar;
311 if(isFirstElementOnNewLineEmptyChar) {
312 isFirstElementOnNewLineEmptyChar = !isFirstElementOnNewLineEmptyChar
316 lineBuilder.append(nextElement)
319 sb.append(lineBuilder)
325 def protected generateToString(Collection<GeneratedProperty> properties) '''
326 «IF !properties.empty»
328 public «String.importedName» toString() {
329 «StringBuilder.importedName» builder = new «StringBuilder.importedName»(«type.importedName».class.getSimpleName()).append(" [");
330 boolean first = true;
332 «FOR property : properties»
333 if («property.fieldName» != null) {
337 builder.append(", ");
339 builder.append("«property.fieldName»=");
340 «IF property.returnType.name.contains("[")»
341 builder.append(«Arrays.importedName».toString(«property.fieldName»));
343 builder.append(«property.fieldName»);
347 return builder.append(']').toString();
352 def getRestrictions(Type type) {
353 var Restrictions restrictions = null
354 if (type instanceof ConcreteType) {
355 restrictions = type.restrictions
356 } else if (type instanceof GeneratedTransferObject) {
357 restrictions = type.restrictions
363 * Template method which generates method parameters with their types from <code>parameters</code>.
366 * list of parameter instances which are transformed to the method parameters
367 * @return string with the list of the method parameters with their types in JAVA format
369 def protected generateParameters(List<MethodSignature.Parameter> parameters) '''«
370 IF !parameters.empty»«
371 FOR parameter : parameters SEPARATOR ", "»«
372 parameter.type.importedName» «parameter.name»«
378 def protected String importedNumber(Class<? extends Number> clazz) {
379 if (clazz.equals(typeof(BigDecimal))) {
380 return BigDecimal.importedName
382 return BigInteger.importedName
386 def protected String importedNumber(Type clazz) {
387 if (clazz.fullyQualifiedName.equals(BigDecimal.canonicalName)) {
388 return BigDecimal.importedName
390 return BigInteger.importedName
394 def protected String numericValue(Class<? extends Number> clazz, Object numberValue) {
395 val number = clazz.importedName;
396 val value = numberValue.toString
397 if (clazz.equals(typeof(BigInteger)) || clazz.equals(typeof(BigDecimal))) {
398 if (value.equals("0")) {
399 return number + ".ZERO"
400 } else if (value.equals("1")) {
401 return number + ".ONE"
402 } else if (value.equals("10")) {
403 return number + ".TEN"
406 val Long longVal = Long.valueOf(value)
407 return number + ".valueOf(" + longVal + "L)"
408 } catch (NumberFormatException e) {
409 if (clazz.equals(typeof(BigDecimal))) {
411 val Double doubleVal = Double.valueOf(value);
412 return number + ".valueOf(" + doubleVal + ")"
413 } catch (NumberFormatException e2) {
419 return "new " + number + "(\"" + value + "\")"
422 def protected GeneratedProperty findProperty(GeneratedTransferObject gto, String name) {
423 val props = gto.properties
425 if (prop.name.equals(name)) {
429 val GeneratedTransferObject parent = gto.superType
430 if (parent != null) {
431 return findProperty(parent, name)