*/
package org.opendaylight.mdsal.binding.java.api.generator
+import static java.util.Objects.requireNonNull
+import static org.opendaylight.mdsal.binding.model.util.Types.BOOLEAN;
+import static org.opendaylight.mdsal.binding.model.util.Types.BYTE_ARRAY;
+import static org.opendaylight.mdsal.binding.model.util.Types.STRING;
+import static extension org.apache.commons.text.StringEscapeUtils.escapeJava
+
import com.google.common.base.Preconditions
import com.google.common.collect.ImmutableList
import com.google.common.collect.Lists
-import com.google.common.io.BaseEncoding
import java.beans.ConstructorProperties
import java.util.ArrayList
-import java.util.Arrays
-import java.util.Collections
+import java.util.Base64;
+import java.util.Comparator
import java.util.List
-import java.util.Objects
+import java.util.Map
import java.util.regex.Pattern
-import org.opendaylight.mdsal.binding.generator.util.TypeConstants
+import javax.management.ConstructorParameters
+import org.gaul.modernizer_maven_annotations.SuppressModernizer
import org.opendaylight.mdsal.binding.model.api.ConcreteType
import org.opendaylight.mdsal.binding.model.api.Constant
import org.opendaylight.mdsal.binding.model.api.Enumeration
import org.opendaylight.mdsal.binding.model.api.GeneratedProperty
import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject
-import org.opendaylight.mdsal.binding.model.api.GeneratedType
import org.opendaylight.mdsal.binding.model.api.Restrictions
import org.opendaylight.mdsal.binding.model.api.Type
+import org.opendaylight.mdsal.binding.model.util.TypeConstants
+import org.opendaylight.yangtools.yang.binding.CodeHelpers
+import org.opendaylight.yangtools.yang.common.Empty
+import org.opendaylight.yangtools.yang.common.Uint16
+import org.opendaylight.yangtools.yang.common.Uint32
+import org.opendaylight.yangtools.yang.common.Uint64
+import org.opendaylight.yangtools.yang.common.Uint8
import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition
/**
* Template for generating JAVA class.
*/
+@SuppressModernizer
class ClassTemplate extends BaseTemplate {
+ static val Comparator<GeneratedProperty> PROP_COMPARATOR = Comparator.comparing([prop | prop.name])
protected val List<GeneratedProperty> properties
protected val List<GeneratedProperty> finalProperties
protected val List<GeneratedProperty> parentProperties
- protected val Iterable<GeneratedProperty> allProperties;
+ protected val List<GeneratedProperty> allProperties
protected val Restrictions restrictions
/**
*/
protected val List<Constant> consts
- /**
- * List of generated types which are enclosed inside <code>genType</code>
- */
- protected val List<GeneratedType> enclosedGeneratedTypes;
+ protected val GeneratedTransferObject genTO
- protected val GeneratedTransferObject genTO;
-
- private val AbstractRangeGenerator<?> rangeGenerator
+ val AbstractRangeGenerator<?> rangeGenerator
/**
* Creates instance of this class with concrete <code>genType</code>.
* @param genType generated transfer object which will be transformed to JAVA class source code
*/
new(GeneratedTransferObject genType) {
- super(genType)
+ this(new TopLevelJavaGeneratedType(genType), genType)
+ }
+
+ /**
+ * Creates instance of this class with concrete <code>genType</code>.
+ *
+ * @param genType generated transfer object which will be transformed to JAVA class source code
+ */
+ new(AbstractJavaGeneratedType javaType, GeneratedTransferObject genType) {
+ super(javaType, genType)
this.genTO = genType
this.properties = genType.properties
this.finalProperties = GeneratorUtil.resolveReadOnlyPropertiesFromTO(genTO.properties)
this.parentProperties = GeneratorUtil.getPropertiesOfAllParents(genTO)
this.restrictions = genType.restrictions
- var List<GeneratedProperty> sorted = new ArrayList<GeneratedProperty>();
+ val sorted = new ArrayList();
sorted.addAll(properties);
sorted.addAll(parentProperties);
- Collections.sort(sorted, [p1, p2|
- p1.name.compareTo(p2.name)
- ]);
+ sorted.sort(PROP_COMPARATOR);
this.allProperties = sorted
this.enums = genType.enumerations
this.consts = genType.constantDefinitions
- this.enclosedGeneratedTypes = genType.enclosedTypes
- if (restrictions !== null && !restrictions.rangeConstraints.nullOrEmpty) {
- rangeGenerator = AbstractRangeGenerator.forType(findProperty(genType, "value").returnType)
- Preconditions.checkNotNull(rangeGenerator)
+ if (restrictions !== null && restrictions.rangeConstraint.present) {
+ rangeGenerator = requireNonNull(AbstractRangeGenerator.forType(findProperty(genType, "value").returnType))
} else {
rangeGenerator = null
}
«generateFields»
«IF restrictions !== null»
- «IF !restrictions.lengthConstraints.nullOrEmpty»
- «LengthGenerator.generateLengthChecker("_value", findProperty(genTO, "value").returnType, restrictions.lengthConstraints)»
+ «IF restrictions.lengthConstraint.present»
+ «LengthGenerator.generateLengthChecker("_value", findProperty(genTO, "value").returnType, restrictions.lengthConstraint.get, this)»
«ENDIF»
- «IF !restrictions.rangeConstraints.nullOrEmpty»
- «rangeGenerator.generateRangeChecker("_value", restrictions.rangeConstraints)»
+ «IF restrictions.rangeConstraint.present»
+ «rangeGenerator.generateRangeChecker("_value", restrictions.rangeConstraint.get, this)»
«ENDIF»
«ENDIF»
* @return string with the source code for inner classes in JAVA format
*/
def protected innerClassesDeclarations() '''
- «IF !enclosedGeneratedTypes.empty»
- «FOR innerClass : enclosedGeneratedTypes SEPARATOR "\n"»
- «IF (innerClass instanceof GeneratedTransferObject)»
- «val classTemplate = new ClassTemplate(innerClass)»
- «classTemplate.generateAsInnerClass»
-
- «ENDIF»
+ «IF !type.enclosedTypes.empty»
+ «FOR innerClass : type.enclosedTypes SEPARATOR "\n"»
+ «generateInnerClass(innerClass)»
«ENDFOR»
«ENDIF»
'''
def protected constructors() '''
«IF genTO.unionType»
«genUnionConstructor»
+ «ELSEIF genTO.typedef && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
+ «typedefConstructor»
+ «legacyConstructor»
«ELSE»
«allValuesConstructor»
+ «legacyConstructor»
«ENDIF»
+
«IF !allProperties.empty»
«copyConstructor»
«ENDIF»
«ENDIF»
'''
- def protected allValuesConstructor() '''
- «IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
- @«ConstructorProperties.importedName»("value")
- «ENDIF»
+ def private allValuesConstructor() '''
public «type.name»(«allProperties.asArgumentsDeclaration») {
«IF false == parentProperties.empty»
super(«parentProperties.asArguments»);
«ENDIF»
«FOR p : allProperties»
- «generateRestrictions(type, p.fieldName.toString, p.returnType)»
+ «generateRestrictions(type, p.fieldName, p.returnType)»
«ENDFOR»
- «/*
- * If we have patterns, we need to apply them to the value field. This is a sad
- * consequence of how this code is structured.
- */
- IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
-
- «Preconditions.importedName».checkNotNull(_value, "Supplied value may not be null");
+ «FOR p : properties»
+ «val fieldName = p.fieldName»
+ «IF p.returnType.name.endsWith("[]")»
+ this.«fieldName» = «fieldName» == null ? null : «fieldName».clone();
+ «ELSE»
+ this.«fieldName» = «fieldName»;
+ «ENDIF»
+ «ENDFOR»
+ }
+ '''
- «FOR c : consts»
- «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME && c.value instanceof List<?>»
- for (Pattern p : patterns) {
- «Preconditions.importedName».checkArgument(p.matcher(_value).matches(), "Supplied value \"%s\" does not match required pattern \"%s\"", _value, p);
- }
- «ENDIF»
- «ENDFOR»
+ def private typedefConstructor() '''
+ @«ConstructorParameters.importedName»("value")
+ @«ConstructorProperties.importedName»("value")
+ public «type.name»(«allProperties.asArgumentsDeclaration») {
+ «IF false == parentProperties.empty»
+ super(«parentProperties.asArguments»);
«ENDIF»
+ «FOR p : allProperties»
+ «generateRestrictions(type, p.fieldName, p.returnType)»
+ «ENDFOR»
+ «/*
+ * If we have patterns, we need to apply them to the value field. This is a sad consequence of how this code is
+ * structured.
+ */»
+ «CodeHelpers.importedName».requireValue(_value);
+ «genPatternEnforcer("_value")»
«FOR p : properties»
- «IF p.returnType.importedName.contains("[]")»
- «IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name
- .equals("value")»
- this.«p.fieldName» = «p.fieldName».clone();
- «ELSE»
- this.«p.fieldName» = «p.fieldName» == null ? null : «p.fieldName».clone();
- «ENDIF»
+ «val fieldName = p.fieldName»
+ «IF p.returnType.name.endsWith("[]")»
+ this.«fieldName» = «fieldName».clone();
«ELSE»
- this.«p.fieldName» = «p.fieldName»;
+ this.«fieldName» = «fieldName»;
«ENDIF»
«ENDFOR»
}
-
'''
+ def private legacyConstructor() {
+ if (!hasUintProperties) {
+ return ""
+ }
+
+ val compatUint = CodeHelpers.importedName + ".compatUint("
+ return '''
+
+ /**
+ * Utility migration constructor.
+ *
+ «FOR prop : allProperties»
+ * @param «prop.fieldName» «prop.name»«IF prop.isUintType» in legacy Java type«ENDIF»
+ «ENDFOR»
+ * @deprecated Use {#link «type.name»(«FOR prop : allProperties SEPARATOR ", "»«prop.returnType.importedJavadocName»«ENDFOR»)} instead.
+ */
+ @Deprecated(forRemoval = true)
+ public «type.getName»(«FOR prop : allProperties SEPARATOR ", "»«prop.legacyType.importedName» «prop.fieldName»«ENDFOR») {
+ this(«FOR prop : allProperties SEPARATOR ", "»«IF prop.isUintType»«compatUint»«prop.fieldName»)«ELSE»«prop.fieldName»«ENDIF»«ENDFOR»);
+ }
+ '''
+ }
+
def protected genUnionConstructor() '''
«FOR p : allProperties»
«val List<GeneratedProperty> other = new ArrayList(properties)»
«genConstructor(p, other)»
«ENDIF»
«ENDFOR»
-
'''
- def protected genConstructor(GeneratedProperty property, GeneratedProperty... other) '''
+ def protected genConstructor(GeneratedProperty property, Iterable<GeneratedProperty> other) '''
public «type.name»(«property.returnType.importedName + " " + property.name») {
«IF false == parentProperties.empty»
super(«parentProperties.asArguments»);
«ENDIF»
- «generateRestrictions(type, property.fieldName.toString, property.returnType)»
+ «val fieldName = property.fieldName»
+ «generateRestrictions(type, fieldName, property.returnType)»
- this.«property.fieldName» = «property.name»;
+ this.«fieldName» = «property.name»;
«FOR p : other»
this.«p.fieldName» = null;
«ENDFOR»
}
'''
+ def private genPatternEnforcer(String ref) '''
+ «FOR c : consts»
+ «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
+ «CodeHelpers.importedName».checkPattern(«ref», «Constants.MEMBER_PATTERN_LIST», «Constants.MEMBER_REGEX_LIST»);
+ «ENDIF»
+ «ENDFOR»
+ '''
+
def private static paramValue(Type returnType, String paramName) {
if (returnType instanceof ConcreteType) {
return paramName
}
def private generateRestrictions(Type type, String paramName, Type returnType) '''
- «val restrictions = type.getRestrictions»
+ «val restrictions = type.restrictions»
«IF restrictions !== null»
- «IF !restrictions.lengthConstraints.empty || !restrictions.rangeConstraints.empty»
+ «IF restrictions.lengthConstraint.present || restrictions.rangeConstraint.present»
if («paramName» != null) {
- «IF !restrictions.lengthConstraints.empty»
+ «IF restrictions.lengthConstraint.present»
«LengthGenerator.generateLengthCheckerCall(paramName, paramValue(returnType, paramName))»
«ENDIF»
- «IF !restrictions.rangeConstraints.empty»
+ «IF restrictions.rangeConstraint.present»
«rangeGenerator.generateRangeCheckerCall(paramName, paramValue(returnType, paramName))»
«ENDIF»
- }
+ }
«ENDIF»
«ENDIF»
'''
super(source);
«ENDIF»
«FOR p : properties»
- this.«p.fieldName» = source.«p.fieldName»;
+ «val fieldName = p.fieldName»
+ this.«fieldName» = source.«fieldName»;
«ENDFOR»
}
'''
* @param source Source object
*/
public «type.name»(«genTO.superType.importedName» source) {
- super(source);
+ super(source);
+ «genPatternEnforcer("getValue()")»
}
'''
«val prop = allProperties.get(0)»
«IF !("org.opendaylight.yangtools.yang.binding.InstanceIdentifier".equals(prop.returnType.fullyQualifiedName))»
public static «genTO.name» getDefaultInstance(String defaultValue) {
- «IF "byte[]".equals(prop.returnType.name)»
- «BaseEncoding.importedName» baseEncoding = «BaseEncoding.importedName».base64();
- return new «genTO.name»(baseEncoding.decode(defaultValue));
- «ELSEIF "java.lang.String".equals(prop.returnType.fullyQualifiedName)»
+ «IF BYTE_ARRAY.equals(prop.returnType)»
+ return new «genTO.name»(«Base64.importedName».getDecoder().decode(defaultValue));
+ «ELSEIF STRING.equals(prop.returnType)»
return new «genTO.name»(defaultValue);
+ «ELSEIF Constants.EMPTY.equals(prop.returnType)»
+ «Preconditions.importedName».checkArgument(defaultValue.isEmpty(), "Invalid value %s", defaultValue);
+ return new «genTO.name»(«Empty.importedName».getInstance());
«ELSEIF allProperties.size > 1»
«bitsArgs»
- «ELSEIF "java.lang.Boolean".equals(prop.returnType.fullyQualifiedName)»
+ «ELSEIF BOOLEAN.equals(prop.returnType)»
return new «genTO.name»(«Boolean.importedName».valueOf(defaultValue));
«ELSEIF "java.lang.Byte".equals(prop.returnType.fullyQualifiedName)»
return new «genTO.name»(«Byte.importedName».valueOf(defaultValue));
return new «genTO.name»(«Integer.importedName».valueOf(defaultValue));
«ELSEIF "java.lang.Long".equals(prop.returnType.fullyQualifiedName)»
return new «genTO.name»(«Long.importedName».valueOf(defaultValue));
+ «ELSEIF "org.opendaylight.yangtools.yang.common.Uint8".equals(prop.returnType.fullyQualifiedName)»
+ return new «genTO.name»(«Uint8.importedName».valueOf(defaultValue));
+ «ELSEIF "org.opendaylight.yangtools.yang.common.Uint16".equals(prop.returnType.fullyQualifiedName)»
+ return new «genTO.name»(«Uint16.importedName».valueOf(defaultValue));
+ «ELSEIF "org.opendaylight.yangtools.yang.common.Uint32".equals(prop.returnType.fullyQualifiedName)»
+ return new «genTO.name»(«Uint32.importedName».valueOf(defaultValue));
+ «ELSEIF "org.opendaylight.yangtools.yang.common.Uint64".equals(prop.returnType.fullyQualifiedName)»
+ return new «genTO.name»(«Uint64.importedName».valueOf(defaultValue));
«ELSE»
return new «genTO.name»(new «prop.returnType.importedName»(defaultValue));
«ENDIF»
def protected enumDeclarations() '''
«IF !enums.empty»
«FOR e : enums SEPARATOR "\n"»
- «val enumTemplate = new EnumTemplate(e)»
- «enumTemplate.generateAsInnerClass»
+ «new EnumTemplate(javaType.getEnclosedType(e.identifier), e).generateAsInnerClass»
«ENDFOR»
«ENDIF»
'''
«IF !consts.empty»
«FOR c : consts»
«IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
- «val cValue = c.value»
- «IF cValue instanceof List<?>»
- private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST»;
- public static final «List.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
- FOR v : cValue SEPARATOR ", "»«
- IF v instanceof String»"«
- v»"«
- ENDIF»«
- ENDFOR»);
-
- «generateStaticInitializationBlock»
+ «val cValue = c.value as Map<String, String>»
+ public static final «List.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
+ FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»);
+ «IF cValue.size == 1»
+ private static final «Pattern.importedName» «Constants.MEMBER_PATTERN_LIST» = «Pattern.importedName».compile(«TypeConstants.PATTERN_CONSTANT_NAME».get(0));
+ private static final String «Constants.MEMBER_REGEX_LIST» = "«cValue.values.iterator.next.escapeJava»";
+ «ELSE»
+ private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST» = «CodeHelpers.importedName».compilePatterns(«TypeConstants.PATTERN_CONSTANT_NAME»);
+ private static final String[] «Constants.MEMBER_REGEX_LIST» = { «
+ FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
«ENDIF»
«ELSE»
«emitConstant(c)»
«ENDIF»
'''
- /**
- * Template method which generates JAVA static initialization block.
- *
- * @return string with static initialization block in JAVA format
- */
- def protected generateStaticInitializationBlock() '''
- static {
- final «Pattern.importedName» a[] = new «Pattern.importedName»[«TypeConstants.PATTERN_CONSTANT_NAME».size()];
- int i = 0;
- for (String regEx : «TypeConstants.PATTERN_CONSTANT_NAME») {
- a[i++] = Pattern.compile(regEx);
- }
-
- «Constants.MEMBER_PATTERN_LIST» = a;
- }
- '''
-
/**
* Template method which generates JAVA class attributes.
*
*
* @return string with the <code>hashCode()</code> method definition in JAVA format
*/
- def protected generateHashCode() '''
- «IF !genTO.hashCodeIdentifiers.empty»
- @Override
+ def protected generateHashCode() {
+ val size = genTO.hashCodeIdentifiers.size
+ if (size == 0) {
+ return ""
+ }
+ return '''
+ @«OVERRIDE.importedName»
public int hashCode() {
- final int prime = 31;
- int result = 1;
- «FOR property : genTO.hashCodeIdentifiers»
- «IF property.returnType.name.contains("[")»
- result = prime * result + «Arrays.importedName».hashCode(«property.fieldName»);
- «ELSE»
- result = prime * result + «Objects.importedName».hashCode(«property.fieldName»);
- «ENDIF»
- «ENDFOR»
- return result;
+ «IF size != 1»
+ «hashCodeResult(genTO.hashCodeIdentifiers)»
+ return result;
+ «ELSE»
+ return «CodeHelpers.importedName».wrapperHashCode(«genTO.hashCodeIdentifiers.get(0).fieldName»);
+ «ENDIF»
}
- «ENDIF»
- '''
+ '''
+ }
/**
* Template method which generates the method <code>equals()</code>.
*
* @return string with the <code>equals()</code> method definition in JAVA format
*/
- def protected generateEquals() '''
+ def private generateEquals() '''
«IF !genTO.equalsIdentifiers.empty»
- @Override
- public boolean equals(java.lang.Object obj) {
+ @«OVERRIDE.importedName»
+ public final boolean equals(java.lang.Object obj) {
if (this == obj) {
return true;
}
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
+ if (!(obj instanceof «type.name»)) {
return false;
}
- «type.name» other = («type.name») obj;
+ final «type.name» other = («type.name») obj;
«FOR property : genTO.equalsIdentifiers»
«val fieldName = property.fieldName»
- «IF property.returnType.name.contains("[")»
- if (!«Arrays.importedName».equals(«fieldName», other.«fieldName»)) {
- «ELSE»
- if (!«Objects.importedName».equals(«fieldName», other.«fieldName»)) {
- «ENDIF»
+ if (!«property.importedUtilClass».equals(«fieldName», other.«fieldName»)) {
return false;
}
«ENDFOR»
return prop;
}
}
- return null;
+ return null
}
+ def private hasUintProperties() {
+ for (GeneratedProperty prop : allProperties) {
+ if (prop.isUintType) {
+ return true
+ }
+ }
+ return false
+ }
+
+ def private static isUintType(GeneratedProperty prop) {
+ UINT_TYPES.containsKey(prop.returnType)
+ }
+
+ def private static legacyType(GeneratedProperty prop) {
+ val type = prop.returnType
+ val uint = UINT_TYPES.get(type)
+ if (uint !== null) {
+ return uint
+ }
+ return type
+ }
}