*/
package org.opendaylight.yangtools.sal.java.api.generator
-import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
-import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
-import java.util.Map
-import org.opendaylight.yangtools.sal.binding.model.api.Type
-import org.opendaylight.yangtools.binding.generator.util.Types
+import com.google.common.base.CharMatcher
import com.google.common.base.Splitter
-import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature
-import com.google.common.collect.Range
-import java.util.ArrayList
+import java.math.BigDecimal
+import java.math.BigInteger
+import java.util.Arrays
+import java.util.Collection
+import java.util.HashMap
import java.util.List
+import java.util.Map
+import java.util.StringTokenizer
+import java.util.regex.Pattern
+import org.opendaylight.yangtools.binding.generator.util.Types
import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType
-import org.opendaylight.yangtools.sal.binding.model.api.Restrictions
+import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
-import java.util.Collection
-import java.util.Arrays
+import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
+import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature
+import org.opendaylight.yangtools.sal.binding.model.api.Restrictions
+import org.opendaylight.yangtools.sal.binding.model.api.Type
abstract class BaseTemplate {
-
protected val GeneratedType type;
protected val Map<String, String> importMap;
- static val paragraphSplitter = Splitter.on("\n\n").omitEmptyStrings();
+
+ private static final char NEW_LINE = '\n'
+ private static final CharMatcher NL_MATCHER = CharMatcher.is(NEW_LINE)
+ private static final CharMatcher TAB_MATCHER = CharMatcher.is('\t')
+ private static final Pattern SPACES_PATTERN = Pattern.compile(" +")
+ private static final Splitter NL_SPLITTER = Splitter.on(NL_MATCHER)
new(GeneratedType _type) {
if (_type == null) {
throw new IllegalArgumentException("Generated type reference cannot be NULL!")
}
this.type = _type;
- this.importMap = GeneratorUtil.createImports(type)
+ this.importMap = new HashMap<String,String>()
}
def packageDefinition() '''package «type.packageName»;'''
- protected def getFullyQualifiedName() {
- return type.fullyQualifiedName
- }
-
final public def generate() {
val _body = body()
'''
«packageDefinition»
«imports»
-
+
«_body»
'''.toString
}
- protected def imports() '''
+ protected def imports() '''
«IF !importMap.empty»
«FOR entry : importMap.entrySet»
- «IF entry.value != fullyQualifiedName»
+ «IF !hasSamePackage(entry.value)»
import «entry.value».«entry.key»;
«ENDIF»
«ENDFOR»
«ENDIF»
-
+
'''
+ /**
+ * Checks if packages of generated type and imported type is the same
+ *
+ * @param importedTypePackageNam
+ * the package name of imported type
+ * @return true if the packages are the same false otherwise
+ */
+ final private def boolean hasSamePackage(String importedTypePackageName) {
+ return type.packageName.equals(importedTypePackageName);
+ }
+
protected abstract def CharSequence body();
// Helper patterns
/**
* Template method which generates the getter method for <code>field</code>
- *
- * @param field
+ *
+ * @param field
* generated property with data about field which is generated as the getter method
- * @return string with the getter method source code in JAVA format
+ * @return string with the getter method source code in JAVA format
*/
final protected def getterMethod(GeneratedProperty field) {
'''
public «field.returnType.importedName» «field.getterMethodName»() {
+ «IF field.returnType.importedName.contains("[]")»
+ return «field.fieldName» == null ? null : «field.fieldName».clone();
+ «ELSE»
return «field.fieldName»;
+ «ENDIF»
}
'''
}
/**
* Template method which generates the setter method for <code>field</code>
- *
- * @param field
+ *
+ * @param field
* generated property with data about field which is generated as the setter method
- * @return string with the setter method source code in JAVA format
+ * @return string with the setter method source code in JAVA format
*/
final protected def setterMethod(GeneratedProperty field) '''
«val returnType = field.returnType.importedName»
/**
* Template method which generates method parameters with their types from <code>parameters</code>.
- *
+ *
* @param parameters
* group of generated property instances which are transformed to the method parameters
* @return string with the list of the method parameters with their types in JAVA format
/**
* Template method which generates sequence of the names of the class attributes from <code>parameters</code>.
- *
- * @param parameters
+ *
+ * @param parameters
* group of generated property instances which are transformed to the sequence of parameter names
- * @return string with the list of the parameter names of the <code>parameters</code>
+ * @return string with the list of the parameter names of the <code>parameters</code>
*/
def final protected asArguments(Iterable<GeneratedProperty> parameters) '''«IF !parameters.empty»«FOR parameter : parameters SEPARATOR ", "»«parameter.
fieldName»«ENDFOR»«ENDIF»'''
/**
* Template method which generates JAVA comments.
- *
+ *
* @param comment string with the comment for whole JAVA class
* @return string with comment in JAVA format
*/
def protected CharSequence asJavadoc(String comment) {
- if(comment == null) return '';
+ if(comment == null) return ''
var txt = comment
- if (txt.contains("*/")) {
- txt = txt.replace("*/", "*/")
- }
- val paragraphs = paragraphSplitter.split(txt)
+
+ txt = comment.trim
+ txt = formatToParagraph(txt)
return '''
- /**
- «FOR p : paragraphs SEPARATOR "<p>"»
- «p»
- «ENDFOR»
- **/
+ «wrapToDocumentation(txt)»
'''
}
- def generateRestrictions(Type type, String paramName, Type returnType) '''
- «val boolean isArray = returnType.name.contains("[")»
- «processRestrictions(type, paramName, returnType, isArray)»
- '''
+ def String wrapToDocumentation(String text) {
+ if (text.empty)
+ return ""
- def generateRestrictions(GeneratedProperty field, String paramName) '''
- «val Type type = field.returnType»
- «IF type instanceof ConcreteType»
- «processRestrictions(type, paramName, field.returnType, type.name.contains("["))»
- «ELSEIF type instanceof GeneratedTransferObject»
- «processRestrictions(type, paramName, field.returnType, isArrayType(type as GeneratedTransferObject))»
- «ENDIF»
- '''
+ val StringBuilder sb = new StringBuilder("/**")
+ sb.append(NEW_LINE)
+ for (String t : NL_SPLITTER.split(text)) {
+ sb.append(" *")
+ if (!t.isEmpty()) {
+ sb.append(' ');
+ sb.append(t)
+ }
+ sb.append(NEW_LINE)
+ }
+ sb.append(" */")
- private def processRestrictions(Type type, String paramName, Type returnType, boolean isArray) '''
- «val restrictions = type.getRestrictions»
- «IF restrictions !== null»
- «IF !restrictions.lengthConstraints.empty»
- «generateLengthRestriction(type, restrictions, paramName, isArray,
- !(returnType instanceof ConcreteType))»
- «ENDIF»
- «IF !restrictions.rangeConstraints.empty &&
- ("java.lang".equals(returnType.packageName) || "java.math".equals(returnType.packageName))»
- «generateRangeRestriction(type, returnType, restrictions, paramName,
- !(returnType instanceof ConcreteType))»
+ return sb.toString
+ }
+
+ def protected String formatDataForJavaDoc(GeneratedType type) {
+ val typeDescription = type.getDescription().encodeJavadocSymbols;
+
+ return '''
+ «IF !typeDescription.nullOrEmpty»
+ «typeDescription»
«ENDIF»
- «ENDIF»
- '''
+ '''.toString
+ }
- def generateLengthRestriction(Type type, Restrictions restrictions, String paramName, boolean isArray,
- boolean isNestedType) '''
- if («paramName» != null) {
- boolean isValidLength = false;
- «List.importedName»<«Range.importedName»<«Integer.importedName»>> lengthConstraints = new «ArrayList.
- importedName»<>();
- «FOR r : restrictions.lengthConstraints»
- lengthConstraints.add(«Range.importedName».closed(«r.min», «r.max»));
- «ENDFOR»
- for («Range.importedName»<«Integer.importedName»> r : lengthConstraints) {
- «IF isArray»
- «IF isNestedType»
- if (r.contains(«paramName».getValue().length)) {
- «ELSE»
- if (r.contains(«paramName».length)) {
- «ENDIF»
- «ELSE»
- «IF isNestedType»
- if (r.contains(«paramName».getValue().length())) {
- «ELSE»
- if (r.contains(«paramName».length())) {
- «ENDIF»
- «ENDIF»
- isValidLength = true;
- }
- }
- if (!isValidLength) {
- throw new IllegalArgumentException(String.format("Invalid length: {}, expected: {}.", «paramName», lengthConstraints));
- }
+ private static final CharMatcher AMP_MATCHER = CharMatcher.is('&');
+ private static final CharMatcher GT_MATCHER = CharMatcher.is('>');
+ private static final CharMatcher LT_MATCHER = CharMatcher.is('<');
+
+ def encodeJavadocSymbols(String description) {
+ if (description.nullOrEmpty) {
+ return description;
}
- '''
- def generateRangeRestriction(Type type, Type returnType, Restrictions restrictions, String paramName,
- boolean isNestedType) '''
- «val javaType = Class.forName(returnType.fullyQualifiedName)»
- if («paramName» != null) {
- boolean isValidRange = false;
- «List.importedName»<«Range.importedName»<«javaType.importedName»>> rangeConstraints = new «ArrayList.
- importedName»<>();
- «FOR r : restrictions.rangeConstraints»
- rangeConstraints.add(«Range.importedName».closed(new «javaType.importedName»(«r.min.toQuote»), new «javaType.
- importedName»(«r.max.toQuote»)));
- «ENDFOR»
- for («Range.importedName»<«javaType.importedName»> r : rangeConstraints) {
- «IF isNestedType»
- if (r.contains(«paramName».getValue())) {
- «ELSE»
- if (r.contains(«paramName»)) {
- «ENDIF»
- isValidRange = true;
+ var ret = description.replace("*/", "*/")
+
+ // FIXME: Use Guava's HtmlEscapers once we have it available
+ ret = AMP_MATCHER.replaceFrom(ret, "&");
+ ret = GT_MATCHER.replaceFrom(ret, ">");
+ ret = LT_MATCHER.replaceFrom(ret, "<");
+ return ret;
+ }
+
+ def protected String formatDataForJavaDoc(GeneratedType type, String additionalComment) {
+ val StringBuilder typeDescription = new StringBuilder();
+ if (!type.description.nullOrEmpty) {
+ typeDescription.append(type.description)
+ typeDescription.append(NEW_LINE)
+ typeDescription.append(NEW_LINE)
+ typeDescription.append(NEW_LINE)
+ typeDescription.append(additionalComment)
+ } else {
+ typeDescription.append(additionalComment)
+ }
+
+ return '''
+ «typeDescription.toString»
+ '''.toString
+ }
+
+ def asLink(String text) {
+ val StringBuilder sb = new StringBuilder()
+ var tempText = text
+ var char lastChar = ' '
+ var boolean badEnding = false
+
+ if (text.endsWith('.') || text.endsWith(':') || text.endsWith(',')) {
+ tempText = text.substring(0, text.length - 1)
+ lastChar = text.charAt(text.length - 1)
+ badEnding = true
+ }
+ sb.append("<a href = \"")
+ sb.append(tempText)
+ sb.append("\">")
+ sb.append(tempText)
+ sb.append("</a>")
+
+ if(badEnding)
+ sb.append(lastChar)
+
+ return sb.toString
+ }
+
+ protected def formatToParagraph(String text) {
+ if(text == null || text.isEmpty)
+ return text
+
+ var formattedText = text
+ val StringBuilder sb = new StringBuilder();
+ var StringBuilder lineBuilder = new StringBuilder();
+ var boolean isFirstElementOnNewLineEmptyChar = false;
+
+ formattedText = formattedText.encodeJavadocSymbols
+ formattedText = NL_MATCHER.removeFrom(formattedText)
+ formattedText = TAB_MATCHER.removeFrom(formattedText)
+ formattedText = SPACES_PATTERN.matcher(formattedText).replaceAll(" ")
+
+ val StringTokenizer tokenizer = new StringTokenizer(formattedText, " ", true);
+
+ while(tokenizer.hasMoreElements) {
+ val nextElement = tokenizer.nextElement.toString
+
+ if(lineBuilder.length + nextElement.length > 80) {
+ if (lineBuilder.charAt(lineBuilder.length - 1) == ' ') {
+ lineBuilder.setLength(0)
+ lineBuilder.append(lineBuilder.substring(0, lineBuilder.length - 1))
+ }
+ if (lineBuilder.charAt(0) == ' ') {
+ lineBuilder.setLength(0)
+ lineBuilder.append(lineBuilder.substring(1))
+ }
+
+ sb.append(lineBuilder);
+ lineBuilder.setLength(0)
+ sb.append(NEW_LINE)
+
+ if(nextElement.toString == ' ') {
+ isFirstElementOnNewLineEmptyChar = !isFirstElementOnNewLineEmptyChar;
}
}
- if (!isValidRange) {
- throw new IllegalArgumentException(String.format("Invalid range: %s, expected: %s.", «paramName», rangeConstraints));
+
+ if(isFirstElementOnNewLineEmptyChar) {
+ isFirstElementOnNewLineEmptyChar = !isFirstElementOnNewLineEmptyChar
+ }
+
+ else {
+ lineBuilder.append(nextElement)
}
}
- '''
+ sb.append(lineBuilder)
+ sb.append(NEW_LINE)
+
+ return sb.toString
+ }
def protected generateToString(Collection<GeneratedProperty> properties) '''
«IF !properties.empty»
@Override
- public String toString() {
- StringBuilder builder = new StringBuilder("«type.name» [");
+ public «String.importedName» toString() {
+ «StringBuilder.importedName» builder = new «StringBuilder.importedName»(«type.importedName».class.getSimpleName()).append(" [");
boolean first = true;
- «FOR i : 0..<properties.size»
- if («properties.get(i).fieldName» != null) {
+ «FOR property : properties»
+ if («property.fieldName» != null) {
if (first) {
first = false;
} else {
builder.append(", ");
}
- builder.append("«properties.get(i).fieldName»=");
- «IF properties.get(i).returnType.name.contains("[")»
- builder.append(«Arrays.importedName».toString(«properties.get(i).fieldName»));
+ builder.append("«property.fieldName»=");
+ «IF property.returnType.name.contains("[")»
+ builder.append(«Arrays.importedName».toString(«property.fieldName»));
«ELSE»
- builder.append(«properties.get(i).fieldName»);
+ builder.append(«property.fieldName»);
«ENDIF»
}
«ENDFOR»
«ENDIF»
'''
- def GeneratedProperty getPropByName(GeneratedType gt, String name) {
- for (GeneratedProperty prop : gt.properties) {
- if (prop.name.equals(name)) {
- return prop;
- }
- }
- return null;
- }
-
def getRestrictions(Type type) {
var Restrictions restrictions = null
if (type instanceof ConcreteType) {
- restrictions = (type as ConcreteType).restrictions
+ restrictions = type.restrictions
} else if (type instanceof GeneratedTransferObject) {
- restrictions = (type as GeneratedTransferObject).restrictions
+ restrictions = type.restrictions
}
return restrictions
}
- def boolean isArrayType(GeneratedTransferObject type) {
- var isArray = false
- val GeneratedTransferObject superType = type.findSuperType
- val GeneratedProperty value = superType.getPropByName("value")
- if (value != null && value.returnType.name.contains("[")) {
- isArray = true
- }
- return isArray
- }
-
- def GeneratedTransferObject findSuperType(GeneratedTransferObject gto) {
- var GeneratedTransferObject base = gto
- var GeneratedTransferObject superType = base.superType
- while (superType !== null) {
- base = superType
- superType = base.superType
- }
- return base;
- }
-
- def String toQuote(Object obj) {
- return "\"" + obj.toString + "\"";
- }
-
/**
* Template method which generates method parameters with their types from <code>parameters</code>.
- *
+ *
* @param parameters
* list of parameter instances which are transformed to the method parameters
* @return string with the list of the method parameters with their types in JAVA format
ENDIF
»'''
+ @Deprecated
+ def protected String importedNumber(Class<? extends Number> clazz) {
+ if (clazz.equals(typeof(BigDecimal))) {
+ return BigDecimal.importedName
+ }
+ return BigInteger.importedName
+ }
+
+ @Deprecated
+ def protected String importedNumber(Type clazz) {
+ if (clazz.fullyQualifiedName.equals(BigDecimal.canonicalName)) {
+ return BigDecimal.importedName
+ }
+ return BigInteger.importedName
+ }
+
+ @Deprecated
+ def protected String numericValue(Class<? extends Number> clazz, Object numberValue) {
+ val number = clazz.importedName;
+ val value = numberValue.toString
+ if (clazz.equals(typeof(BigInteger)) || clazz.equals(typeof(BigDecimal))) {
+ if (value.equals("0")) {
+ return number + ".ZERO"
+ } else if (value.equals("1")) {
+ return number + ".ONE"
+ } else if (value.equals("10")) {
+ return number + ".TEN"
+ } else {
+ try {
+ val Long longVal = Long.valueOf(value)
+ return number + ".valueOf(" + longVal + "L)"
+ } catch (NumberFormatException e) {
+ if (clazz.equals(typeof(BigDecimal))) {
+ try {
+ val Double doubleVal = Double.valueOf(value);
+ return number + ".valueOf(" + doubleVal + ")"
+ } catch (NumberFormatException e2) {
+ }
+ }
+ }
+ }
+ }
+ return "new " + number + "(\"" + value + "\")"
+ }
+
+ def protected GeneratedProperty findProperty(GeneratedTransferObject gto, String name) {
+ val props = gto.properties
+ for (prop : props) {
+ if (prop.name.equals(name)) {
+ return prop
+ }
+ }
+ val GeneratedTransferObject parent = gto.superType
+ if (parent != null) {
+ return findProperty(parent, name)
+ }
+ return null
+ }
+
}