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.mdsal.binding.java.api.generator
10 import static java.util.Objects.requireNonNull
11 import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.BINARY_TYPE
12 import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.BOOLEAN_TYPE
13 import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.DECIMAL64_TYPE
14 import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.EMPTY_TYPE
15 import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.INSTANCE_IDENTIFIER
16 import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.INT16_TYPE
17 import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.INT32_TYPE
18 import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.INT64_TYPE
19 import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.INT8_TYPE
20 import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.STRING_TYPE
21 import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.UINT16_TYPE
22 import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.UINT32_TYPE
23 import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.UINT64_TYPE
24 import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.UINT8_TYPE
25 import static org.opendaylight.mdsal.binding.model.ri.BindingTypes.SCALAR_TYPE_OBJECT
26 import static org.opendaylight.mdsal.binding.model.ri.Types.STRING;
27 import static extension org.apache.commons.text.StringEscapeUtils.escapeJava
28 import static extension org.opendaylight.mdsal.binding.model.ri.BindingTypes.isBitsType
30 import com.google.common.base.Preconditions
31 import com.google.common.collect.ImmutableList
32 import com.google.common.collect.Lists
33 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
34 import java.util.ArrayList
35 import java.util.Base64;
36 import java.util.Collection
37 import java.util.Comparator
41 import javax.management.ConstructorParameters
42 import org.opendaylight.mdsal.binding.model.api.ConcreteType
43 import org.opendaylight.mdsal.binding.model.api.Constant
44 import org.opendaylight.mdsal.binding.model.api.Enumeration
45 import org.opendaylight.mdsal.binding.model.api.GeneratedProperty
46 import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject
47 import org.opendaylight.mdsal.binding.model.api.Restrictions
48 import org.opendaylight.mdsal.binding.model.api.Type
49 import org.opendaylight.mdsal.binding.model.ri.TypeConstants
50 import org.opendaylight.mdsal.binding.model.ri.Types
51 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping
52 import org.opendaylight.yangtools.yang.common.Empty
55 * Template for generating JAVA class.
57 class ClassTemplate extends BaseTemplate {
58 static val Comparator<GeneratedProperty> PROP_COMPARATOR = Comparator.comparing([prop | prop.name])
59 static val VALUEOF_TYPES = Set.of(
71 protected val List<GeneratedProperty> properties
72 protected val List<GeneratedProperty> finalProperties
73 protected val List<GeneratedProperty> parentProperties
74 protected val List<GeneratedProperty> allProperties
75 protected val Restrictions restrictions
78 * List of enumeration which are generated as JAVA enum type.
80 protected val List<Enumeration> enums
83 * List of constant instances which are generated as JAVA public static final attributes.
85 protected val List<Constant> consts
87 protected val GeneratedTransferObject genTO
89 val AbstractRangeGenerator<?> rangeGenerator
92 * Creates instance of this class with concrete <code>genType</code>.
94 * @param genType generated transfer object which will be transformed to JAVA class source code
96 new(GeneratedTransferObject genType) {
97 this(new TopLevelJavaGeneratedType(genType), genType)
101 * Creates instance of this class with concrete <code>genType</code>.
103 * @param genType generated transfer object which will be transformed to JAVA class source code
105 new(AbstractJavaGeneratedType javaType, GeneratedTransferObject genType) {
106 super(javaType, genType)
108 this.properties = genType.properties
109 this.finalProperties = GeneratorUtil.resolveReadOnlyPropertiesFromTO(genTO.properties)
110 this.parentProperties = GeneratorUtil.getPropertiesOfAllParents(genTO)
111 this.restrictions = genType.restrictions
113 val sorted = new ArrayList();
114 sorted.addAll(properties);
115 sorted.addAll(parentProperties);
116 sorted.sort(PROP_COMPARATOR);
118 this.allProperties = sorted
119 this.enums = genType.enumerations
120 this.consts = genType.constantDefinitions
122 if (restrictions !== null && restrictions.rangeConstraint.present) {
123 rangeGenerator = requireNonNull(AbstractRangeGenerator.forType(TypeUtils.encapsulatedValueType(genType)))
125 rangeGenerator = null
130 * Generates JAVA class source code (class body only).
132 * @return string with JAVA class body source code
134 def CharSequence generateAsInnerClass() {
135 return generateBody(true)
138 override protected body() {
143 * Template method which generates class body.
145 * @param isInnerClass boolean value which specify if generated class is|isn't inner
146 * @return string with class source code in JAVA format
148 def protected generateBody(boolean isInnerClass) '''
149 «type.formatDataForJavaDoc.wrapToDocumentation»
150 «annotationDeclaration»
152 «generatedAnnotation»
154 «generateClassDeclaration(isInnerClass)» {
156 «innerClassesDeclarations»
158 «constantsDeclarations»
161 «IF restrictions !== null»
162 «IF restrictions.lengthConstraint.present»
163 «LengthGenerator.generateLengthChecker("_value", TypeUtils.encapsulatedValueType(genTO),
164 restrictions.lengthConstraint.get, this)»
166 «IF restrictions.rangeConstraint.present»
167 «rangeGenerator.generateRangeChecker("_value", restrictions.rangeConstraint.get, this)»
177 «IF genTO.isBitsType»
178 «generateGetValueForBitsTypeDef»
185 «generateToString(genTO.toStringIdentifiers)»
190 def protected propertyMethods() {
191 if (properties.empty) {
194 isScalarTypeObject ? scalarTypeObjectValue(properties.get(0)) : defaultProperties
197 def private isScalarTypeObject() {
198 for (impl : genTO.implements) {
199 if (SCALAR_TYPE_OBJECT.identifier.equals(impl.identifier)) {
206 def private defaultProperties() '''
207 «FOR field : properties SEPARATOR "\n"»
215 def private scalarTypeObjectValue(GeneratedProperty field) '''
216 @«OVERRIDE.importedName»
217 public «field.returnType.importedName» «BindingMapping.SCALAR_TYPE_OBJECT_GET_VALUE_NAME»() {
218 return «field.fieldName»«field.cloneCall»;
223 * Template method which generates the method <code>getValue()</code> for typedef,
224 * which base type is BitsDefinition.
226 * @return string with the <code>getValue()</code> method definition in JAVA format
228 def protected generateGetValueForBitsTypeDef() '''
230 public boolean[] getValue() {
231 return new boolean[]{
232 «FOR property: genTO.properties SEPARATOR ','»
240 * Template method which generates inner classes inside this interface.
242 * @return string with the source code for inner classes in JAVA format
244 def protected innerClassesDeclarations() '''
245 «IF !type.enclosedTypes.empty»
246 «FOR innerClass : type.enclosedTypes SEPARATOR "\n"»
247 «generateInnerClass(innerClass)»
252 def protected constructors() '''
254 «genUnionConstructor»
255 «ELSEIF genTO.typedef && allProperties.size == 1 && allProperties.get(0).name.equals(TypeConstants.VALUE_PROP)»
258 «allValuesConstructor»
261 «IF !allProperties.empty»
264 «IF properties.empty && !parentProperties.empty »
269 def allValuesConstructor() '''
270 public «type.name»(«allProperties.asArgumentsDeclaration») {
271 «IF !parentProperties.empty»
272 super(«parentProperties.asArguments»);
274 «FOR p : allProperties»
275 «generateRestrictions(type, p.fieldName, p.returnType)»
279 «val fieldName = p.fieldName»
280 «IF p.returnType.name.endsWith("[]")»
281 this.«fieldName» = «fieldName» == null ? null : «fieldName».clone();
283 this.«fieldName» = «fieldName»;
289 def private typedefConstructor() '''
290 @«ConstructorParameters.importedName»("«TypeConstants.VALUE_PROP»")
291 public «type.name»(«allProperties.asArgumentsDeclaration») {
292 «IF !parentProperties.empty»
293 super(«parentProperties.asArguments»);
295 «FOR p : allProperties»
296 «generateRestrictions(type, p.fieldName, p.returnType)»
299 * If we have patterns, we need to apply them to the value field. This is a sad consequence of how this code is
302 «CODEHELPERS.importedName».requireValue(_value);
303 «genPatternEnforcer("_value")»
306 «val fieldName = p.fieldName»
307 this.«fieldName» = «fieldName»«p.cloneCall»;
312 def protected genUnionConstructor() '''
313 «FOR p : allProperties»
314 «val List<GeneratedProperty> other = new ArrayList(properties)»
316 «genConstructor(p, other)»
321 def protected genConstructor(GeneratedProperty property, Iterable<GeneratedProperty> other) '''
322 public «type.name»(«property.returnType.importedName + " " + property.name») {
323 «IF !parentProperties.empty»
324 super(«parentProperties.asArguments»);
327 «val fieldName = property.fieldName»
328 «generateRestrictions(type, fieldName, property.returnType)»
330 this.«fieldName» = «property.name»;
332 this.«p.fieldName» = null;
337 def private genPatternEnforcer(String ref) '''
339 «IF TypeConstants.PATTERN_CONSTANT_NAME.equals(c.name)»
340 «CODEHELPERS.importedName».checkPattern(«ref», «Constants.MEMBER_PATTERN_LIST», «Constants.MEMBER_REGEX_LIST»);
345 def private static paramValue(Type returnType, String paramName) {
346 if (returnType instanceof ConcreteType) {
349 return paramName + ".getValue()"
353 def generateRestrictions(Type type, String paramName, Type returnType) '''
354 «val restrictions = type.restrictions»
355 «IF restrictions !== null»
356 «IF restrictions.lengthConstraint.present || restrictions.rangeConstraint.present»
357 if («paramName» != null) {
358 «IF restrictions.lengthConstraint.present»
359 «LengthGenerator.generateLengthCheckerCall(paramName, paramValue(returnType, paramName))»
361 «IF restrictions.rangeConstraint.present»
362 «rangeGenerator.generateRangeCheckerCall(paramName, paramValue(returnType, paramName))»
369 def protected copyConstructor() '''
371 * Creates a copy from Source Object.
373 * @param source Source object
375 public «type.name»(«type.name» source) {
376 «IF !parentProperties.empty»
380 «val fieldName = p.fieldName»
381 this.«fieldName» = source.«fieldName»;
386 def protected parentConstructor() '''
388 * Creates a new instance from «genTO.superType.importedName»
390 * @param source Source object
392 public «type.name»(«genTO.superType.importedName» source) {
394 «genPatternEnforcer("getValue()")»
398 def protected defaultInstance() '''
399 «IF genTO.typedef && !allProperties.empty && !genTO.unionType»
400 «val prop = allProperties.get(0)»
401 «val propType = prop.returnType»
402 «IF !(INSTANCE_IDENTIFIER.identifier.equals(propType.identifier))»
403 public static «genTO.name» getDefaultInstance(final String defaultValue) {
404 «IF allProperties.size > 1»
406 «ELSEIF VALUEOF_TYPES.contains(propType)»
407 return new «genTO.name»(«propType.importedName».valueOf(defaultValue));
408 «ELSEIF STRING_TYPE.equals(propType)»
409 return new «genTO.name»(defaultValue);
410 «ELSEIF BINARY_TYPE.equals(propType)»
411 return new «genTO.name»(«Base64.importedName».getDecoder().decode(defaultValue));
412 «ELSEIF EMPTY_TYPE.equals(propType)»
413 «Preconditions.importedName».checkArgument(defaultValue.isEmpty(), "Invalid value %s", defaultValue);
414 return new «genTO.name»(«Empty.importedName».value());
416 return new «genTO.name»(new «propType.importedName»(defaultValue));
423 @SuppressFBWarnings(value = "DLS_DEAD_LOCAL_STORE", justification = "FOR with SEPARATOR, not needing for value")
424 def protected bitsArgs() '''
425 «JU_LIST.importedName»<«STRING.importedName»> properties = «Lists.importedName».newArrayList(«allProperties.propsAsArgs»);
426 if (!properties.contains(defaultValue)) {
427 throw new «IAE.importedName»("invalid default parameter");
430 return new «genTO.name»(
431 «FOR prop : allProperties SEPARATOR ","»
432 properties.get(i++).equals(defaultValue) ? true : false
437 def protected propsAsArgs(Iterable<GeneratedProperty> properties) '''
438 «FOR prop : properties SEPARATOR ","»
444 * Template method which generates JAVA class declaration.
446 * @param isInnerClass boolean value which specify if generated class is|isn't inner
447 * @return string with class declaration in JAVA format
449 def protected generateClassDeclaration(boolean isInnerClass) '''
453 ELSEIF (type.abstract)»«
457 ENDIF»class «type.name»«
458 IF (genTO.superType !== null)»«
459 " extends "»«genTO.superType.importedName»«
461 «IF (!type.implements.empty)»«
463 FOR type : type.implements SEPARATOR ", "»«
470 * Template method which generates JAVA enum type.
472 * @return string with inner enum source code in JAVA format
474 def protected enumDeclarations() '''
476 «FOR e : enums SEPARATOR "\n"»
477 «new EnumTemplate(javaType.getEnclosedType(e.identifier), e).generateAsInnerClass»
482 def protected suidDeclaration() '''
483 «IF genTO.SUID !== null»
484 private static final long serialVersionUID = «genTO.SUID.value»L;
488 def protected annotationDeclaration() '''
489 «IF genTO.getAnnotations !== null»
490 «FOR e : genTO.getAnnotations»
497 * Template method which generates JAVA constants.
499 * @return string with constants in JAVA format
501 def protected constantsDeclarations() '''
504 «IF TypeConstants.PATTERN_CONSTANT_NAME.equals(c.name)»
505 «val cValue = c.value as Map<String, String>»
506 «val jurPatternRef = JUR_PATTERN.importedName»
507 public static final «JU_LIST.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
508 FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»);
509 «IF cValue.size == 1»
510 private static final «jurPatternRef» «Constants.MEMBER_PATTERN_LIST» = «jurPatternRef».compile(«TypeConstants.PATTERN_CONSTANT_NAME».get(0));
511 private static final String «Constants.MEMBER_REGEX_LIST» = "«cValue.values.iterator.next.escapeJava»";
513 private static final «jurPatternRef»[] «Constants.MEMBER_PATTERN_LIST» = «CODEHELPERS.importedName».compilePatterns(«TypeConstants.PATTERN_CONSTANT_NAME»);
514 private static final String[] «Constants.MEMBER_REGEX_LIST» = { «
515 FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
525 * Template method which generates JAVA class attributes.
527 * @return string with the class attributes in JAVA format
529 def protected generateFields() '''
530 «IF !properties.empty»
532 private«IF isReadOnly(f)» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
537 protected def isReadOnly(GeneratedProperty field) {
538 return field.readOnly
542 * Template method which generates the method <code>hashCode()</code>.
544 * @return string with the <code>hashCode()</code> method definition in JAVA format
546 def protected generateHashCode() {
547 val size = genTO.hashCodeIdentifiers.size
552 @«OVERRIDE.importedName»
553 public int hashCode() {
555 final int prime = 31;
557 «FOR property : genTO.hashCodeIdentifiers»
558 result = prime * result + «property.importedUtilClass».hashCode(«property.fieldName»);
562 return «CODEHELPERS.importedName».wrapperHashCode(«genTO.hashCodeIdentifiers.get(0).fieldName»);
569 * Template method which generates the method <code>equals()</code>.
571 * @return string with the <code>equals()</code> method definition in JAVA format
573 def private generateEquals() '''
574 «IF !genTO.equalsIdentifiers.empty»
575 @«OVERRIDE.importedName»
576 public final boolean equals(«OBJECT.importedName» obj) {
577 return this == obj || obj instanceof «type.name» other
578 «FOR property : genTO.equalsIdentifiers»
579 «val fieldName = property.fieldName»
580 «val type = property.returnType»
581 «IF type.equals(Types.primitiveBooleanType)»
582 && «fieldName» == other.«fieldName»«
584 && «type.importedUtilClass».equals(«fieldName», other.«fieldName»)«
591 def private generateToString(Collection<GeneratedProperty> properties) '''
592 «IF !properties.empty»
593 @«OVERRIDE.importedName»
594 public «STRING.importedName» toString() {
595 final var helper = «MOREOBJECTS.importedName».toStringHelper(«type.importedName».class);
596 «FOR property : properties»
597 «CODEHELPERS.importedName».appendValue(helper, "«property.name»", «property.fieldName»);
599 return helper.toString();
604 def GeneratedProperty getPropByName(String name) {
605 for (GeneratedProperty prop : allProperties) {
606 if (prop.name.equals(name)) {