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
\r
10 import java.util.Arrays;
\r
11 import java.util.LinkedHashSet
\r
12 import java.util.List
\r
13 import java.util.Map
\r
14 import java.util.Set
\r
15 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl
\r
16 import org.opendaylight.yangtools.binding.generator.util.Types
\r
17 import org.opendaylight.yangtools.binding.generator.util.generated.type.builder.GeneratedTOBuilderImpl
\r
18 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
\r
19 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
\r
20 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
\r
21 import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature
\r
22 import org.opendaylight.yangtools.sal.binding.model.api.Type
\r
23 import org.opendaylight.yangtools.yang.binding.Augmentable
\r
24 import static org.opendaylight.yangtools.binding.generator.util.Types.*
\r
25 import java.util.HashMap
\r
26 import java.util.Collections
\rimport org.opendaylight.yangtools.yang.binding.DataObject
27 import java.util.ArrayList
28 import java.util.HashSet
29 import java.util.Collection
30 import org.opendaylight.yangtools.yang.binding.Identifiable
33 * Template for generating JAVA builder classes.
\r
36 class BuilderTemplate extends BaseTemplate {
\r
39 * Constant with the name of the concrete method.
\r
41 val static GET_AUGMENTATION_METHOD_NAME = "getAugmentation"
\r
44 * Constant with the suffix for builder classes.
\r
46 val static BUILDER = 'Builder'
\r
49 * Constant with suffix for the classes which are generated from the builder classes.
\r
51 val static IMPL = 'Impl'
\r
54 * Generated property is set if among methods is found one with the name GET_AUGMENTATION_METHOD_NAME
\r
56 var GeneratedProperty augmentField
\r
59 * Set of class attributes (fields) which are derived from the getter methods names
\r
61 val Set<GeneratedProperty> properties
\r
64 * Constructs new instance of this class.
\r
65 * @throws IllegalArgumentException if <code>genType</code> equals <code>null</code>
\r
67 new(GeneratedType genType) {
\r
69 this.properties = propertiesFromMethods(createMethods)
\r
73 * Returns set of method signature instances which contains all the methods of the <code>genType</code>
\r
74 * and all the methods of the implemented interfaces.
\r
76 * @returns set of method signature instances
\r
78 def private Set<MethodSignature> createMethods() {
\r
79 val Set<MethodSignature> methods = new LinkedHashSet
\r
80 methods.addAll(type.methodDefinitions)
\r
81 collectImplementedMethods(methods, type.implements)
\r
86 * Adds to the <code>methods</code> set all the methods of the <code>implementedIfcs</code>
\r
87 * and recursivelly their implemented interfaces.
\r
89 * @param methods set of method signatures
\r
90 * @param implementedIfcs list of implemented interfaces
\r
92 def private void collectImplementedMethods(Set<MethodSignature> methods, List<Type> implementedIfcs) {
\r
93 if (implementedIfcs == null || implementedIfcs.empty) {
\r
96 for (implementedIfc : implementedIfcs) {
\r
97 if ((implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))) {
\r
98 val ifc = implementedIfc as GeneratedType
\r
99 methods.addAll(ifc.methodDefinitions)
\r
100 collectImplementedMethods(methods, ifc.implements)
\r
101 } else if (implementedIfc.fullyQualifiedName == Augmentable.name) {
\r
102 for (m : Augmentable.methods) {
\r
103 if (m.name == GET_AUGMENTATION_METHOD_NAME) {
\r
104 val fullyQualifiedName = m.returnType.name
\r
105 val pkg = fullyQualifiedName.package
\r
106 val name = fullyQualifiedName.name
\r
107 val tmpGenTO = new GeneratedTOBuilderImpl(pkg, name)
\r
108 val refType = new ReferencedTypeImpl(pkg, name)
\r
109 val generic = new ReferencedTypeImpl(type.packageName, type.name)
\r
110 val parametrizedReturnType = Types.parameterizedTypeFor(refType, generic)
\r
111 tmpGenTO.addMethod(m.name).setReturnType(parametrizedReturnType)
\r
112 augmentField = tmpGenTO.toInstance.methodDefinitions.first.propertyFromGetter
\r
120 * Returns the first element of the list <code>elements</code>.
\r
122 * @param list of elements
\r
124 def private <E> first(List<E> elements) {
\r
129 * Returns the name of the package from <code>fullyQualifiedName</code>.
\r
131 * @param fullyQualifiedName string with fully qualified type name (package + type)
\r
132 * @return string with the package name
\r
134 def private String getPackage(String fullyQualifiedName) {
\r
135 val lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT)
\r
136 return if (lastDotIndex == -1) "" else fullyQualifiedName.substring(0, lastDotIndex)
\r
140 * Returns the name of tye type from <code>fullyQualifiedName</code>
\r
142 * @param fullyQualifiedName string with fully qualified type name (package + type)
\r
143 * @return string with the name of the type
\r
145 def private String getName(String fullyQualifiedName) {
\r
146 val lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT)
\r
147 return if (lastDotIndex == -1) fullyQualifiedName else fullyQualifiedName.substring(lastDotIndex + 1)
\r
151 * Creates set of generated property instances from getter <code>methods</code>.
\r
153 * @param set of method signature instances which should be transformed to list of properties
\r
154 * @return set of generated property instances which represents the getter <code>methods</code>
\r
156 def private propertiesFromMethods(Set<MethodSignature> methods) {
\r
157 if (methods == null || methods.isEmpty()) {
\r
158 return Collections.emptySet
\r
160 val Set<GeneratedProperty> result = new LinkedHashSet
\r
161 for (m : methods) {
\r
162 val createdField = m.propertyFromGetter
\r
163 if (createdField != null) {
\r
164 result.add(createdField)
\r
171 * Creates generated property instance from the getter <code>method</code> name and return type.
\r
173 * @param method method signature from which is the method name and return type obtained
\r
174 * @return generated property instance for the getter <code>method</code>
\r
175 * @throws IllegalArgumentException<ul>
\r
176 * <li>if the <code>method</code> equals <code>null</code></li>
\r
177 * <li>if the name of the <code>method</code> equals <code>null</code></li>
\r
178 * <li>if the name of the <code>method</code> is empty</li>
\r
179 * <li>if the return type of the <code>method</code> equals <code>null</code></li>
\r
182 def private GeneratedProperty propertyFromGetter(MethodSignature method) {
\r
183 if (method == null || method.name == null || method.name.empty || method.returnType == null) {
\r
184 throw new IllegalArgumentException("Method, method name, method return type reference cannot be NULL or empty!")
\r
186 var prefix = "get";
\r
187 if(BOOLEAN.equals(method.returnType)) {
\r
190 if (method.name.startsWith(prefix)) {
\r
191 val fieldName = method.getName().substring(prefix.length()).toFirstLower
\r
192 val tmpGenTO = new GeneratedTOBuilderImpl("foo", "foo")
\r
193 tmpGenTO.addProperty(fieldName).setReturnType(method.returnType)
\r
194 return tmpGenTO.toInstance.properties.first
\r
199 * Template method which generates JAVA class body for builder class and for IMPL class.
\r
201 * @return string with JAVA source code
\r
203 override body() '''
\r
205 public class «type.name»«BUILDER» {
\r
207 «generateFields(false)»
\r
209 «generateConstructorsFromIfcs(type)»
\r
211 «generateMethodFieldsFrom(type)»
\r
213 «generateGetters(false)»
\r
217 public «type.name» build() {
\r
218 return new «type.name»«IMPL»(this);
\r
221 private static final class «type.name»«IMPL» implements «type.name» {
\r
223 «implementedInterfaceGetter»
\r
225 «generateFields(true)»
\r
227 «generateConstructor»
\r
229 «generateGetters(true)»
\r
231 «generateHashCode()»
\r
235 «generateToString(properties)»
\r
242 * Generate default constructor and constructor for every implemented interface from uses statements.
\r
244 def private generateConstructorsFromIfcs(Type type) '''
\r
245 public «type.name»«BUILDER»() {
\r
247 «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»
\r
248 «val ifc = type as GeneratedType»
\r
249 «FOR impl : ifc.implements»
\r
250 «generateConstructorFromIfc(impl)»
\r
256 * Generate constructor with argument of given type.
\r
258 def private generateConstructorFromIfc(Type impl) '''
\r
259 «IF (impl instanceof GeneratedType)»
\r
260 «val implType = impl as GeneratedType»
\r
262 «IF !(implType.methodDefinitions.empty)»
\r
263 public «type.name»«BUILDER»(«implType.fullyQualifiedName» arg) {
\r
264 «printConstructorPropertySetter(implType)»
\r
267 «FOR implTypeImplement : implType.implements»
\r
268 «generateConstructorFromIfc(implTypeImplement)»
\r
273 def private printConstructorPropertySetter(Type implementedIfc) '''
\r
274 «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
\r
275 «val ifc = implementedIfc as GeneratedType»
\r
276 «FOR getter : ifc.methodDefinitions»
\r
277 this._«getter.propertyNameFromGetter» = arg.«getter.name»();
\r
279 «FOR impl : ifc.implements»
\r
280 «printConstructorPropertySetter(impl)»
\r
286 * Generate 'fieldsFrom' method to set builder properties based on type of given argument.
\r
288 def private generateMethodFieldsFrom(Type type) '''
\r
289 «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»
\r
290 «val ifc = type as GeneratedType»
\r
291 «IF ifc.hasImplementsFromUses»
\r
292 «val List<Type> done = ifc.getBaseIfcs»
\r
293 «generateMethodFieldsFromComment(ifc)»
\r
294 public void fieldsFrom(«DataObject.importedName» arg) {
\r
295 boolean isValidArg = false;
\r
296 «FOR impl : ifc.getAllIfcs»
\r
297 «generateIfCheck(impl, done)»
\r
300 throw new IllegalArgumentException(
\r
301 "expected one of: «ifc.getAllIfcs.toListOfNames» \n" +
\r
310 def private generateMethodFieldsFromComment(GeneratedType type) '''
\r
312 Set fields from given grouping argument. Valid argument is instance of one of following types:
\r
314 «FOR impl : type.getAllIfcs»
\r
315 * <li>«impl.fullyQualifiedName»</li>
\r
319 * @param arg grouping object
\r
320 * @throws IllegalArgumentException if given argument is none of valid types
\r
325 * Method is used to find out if given type implements any interface from uses.
\r
327 def boolean hasImplementsFromUses(GeneratedType type) {
\r
329 for (impl : type.getAllIfcs) {
\r
330 if ((impl instanceof GeneratedType) && !((impl as GeneratedType).methodDefinitions.empty)) {
\r
337 def private generateIfCheck(Type impl, List<Type> done) '''
\r
338 «IF (impl instanceof GeneratedType) && !((impl as GeneratedType).methodDefinitions.empty)»
\r
339 «val implType = impl as GeneratedType»
\r
340 if (arg instanceof «implType.fullyQualifiedName») {
\r
341 «printPropertySetter(implType)»
\r
347 def private printPropertySetter(Type implementedIfc) '''
\r
348 «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
\r
349 «val ifc = implementedIfc as GeneratedType»
\r
350 «FOR getter : ifc.methodDefinitions»
\r
351 this._«getter.propertyNameFromGetter» = ((«implementedIfc.fullyQualifiedName»)arg).«getter.name»();
\r
356 private def List<Type> getBaseIfcs(GeneratedType type) {
\r
357 val List<Type> baseIfcs = new ArrayList();
\r
358 for (ifc : type.implements) {
\r
359 if (ifc instanceof GeneratedType && !(ifc as GeneratedType).methodDefinitions.empty) {
\r
366 private def Set<Type> getAllIfcs(Type type) {
\r
367 val Set<Type> baseIfcs = new HashSet()
\r
368 if (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject)) {
\r
369 val ifc = type as GeneratedType
\r
370 for (impl : ifc.implements) {
\r
371 if (impl instanceof GeneratedType && !(impl as GeneratedType).methodDefinitions.empty) {
\r
374 baseIfcs.addAll(impl.getAllIfcs)
\r
380 private def List<String> toListOfNames(Collection<Type> types) {
\r
381 val List<String> names = new ArrayList
\r
382 for (type : types) {
\r
383 names.add(type.fullyQualifiedName)
\r
389 * Template method which generates class attributes.
\r
391 * @param boolean value which specify whether field is|isn't final
\r
392 * @return string with class attributes and their types
\r
394 def private generateFields(boolean _final) '''
\r
395 «IF !properties.empty»
\r
396 «FOR f : properties»
\r
397 private«IF _final» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
\r
400 «IF augmentField != null»
\r
401 private «Map.importedName»<Class<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> «augmentField.name» = new «HashMap.importedName»<>();
\r
406 * Template method which generates setter methods
\r
408 * @return string with the setter methods
\r
410 def private generateSetters() '''
\r
411 «FOR field : properties SEPARATOR '\n'»
\r
412 public «type.name»«BUILDER» set«field.name.toFirstUpper»(«field.returnType.importedName» value) {
\r
413 «generateRestrictions(field, "value")»
\r
415 this.«field.fieldName» = value;
\r
419 «IF augmentField != null»
\r
421 public «type.name»«BUILDER» add«augmentField.name.toFirstUpper»(Class<? extends «augmentField.returnType.importedName»> augmentationType, «augmentField.returnType.importedName» augmentation) {
\r
422 this.«augmentField.name».put(augmentationType, augmentation);
\r
429 * Template method which generate constructor for IMPL class.
\r
431 * @return string with IMPL class constructor
\r
433 def private generateConstructor() '''
\r
434 private «type.name»«IMPL»(«type.name»«BUILDER» builder) {
\r
435 «val allProps = new ArrayList(properties)»
\r
436 «val isList = implementsIfc(type, Types.parameterizedTypeFor(Types.typeForClass(Identifiable), type))»
\r
437 «val keyType = type.getKey»
\r
438 «IF isList && keyType != null»
\r
439 «val keyProps = new ArrayList((keyType as GeneratedTransferObject).properties)»
\r
440 «Collections.sort(keyProps,
\r
442 return p1.name.compareTo(p2.name)
\r
445 «FOR field : keyProps»
\r
446 «removeProperty(allProps, field.name)»
\r
448 «removeProperty(allProps, "key")»
\r
449 if (builder.getKey() == null) {
\r
450 this._key = new «keyType.importedName»(
\r
451 «FOR keyProp : keyProps SEPARATOR ", "»
\r
452 builder.«keyProp.getterMethodName»()
\r
455 «FOR field : keyProps»
\r
456 this.«field.fieldName» = builder.«field.getterMethodName»();
\r
459 this._key = builder.getKey();
\r
460 «FOR field : keyProps»
\r
461 this.«field.fieldName» = _key.«field.getterMethodName»();
\r
465 «FOR field : allProps»
\r
466 this.«field.fieldName» = builder.«field.getterMethodName»();
\r
468 «IF augmentField != null»
\r
469 this.«augmentField.name».putAll(builder.«augmentField.name»);
\r
474 private def boolean implementsIfc(GeneratedType type, Type impl) {
\r
475 for (Type ifc : type.implements) {
\r
476 if (ifc.equals(impl)) {
\r
483 private def Type getKey(GeneratedType type) {
\r
484 for (m : type.methodDefinitions) {
\r
485 if ("getKey".equals(m.name)) {
\r
486 return m.returnType;
\r
492 private def void removeProperty(Collection<GeneratedProperty> props, String name) {
\r
493 var GeneratedProperty toRemove = null
\r
495 if (p.name.equals(name)) {
\r
499 if (toRemove != null) {
\r
500 props.remove(toRemove);
\r
505 * Template method which generate getter methods for IMPL class.
\r
507 * @return string with getter methods
\r
509 def private generateGetters(boolean addOverride) '''
\r
510 «IF !properties.empty»
\r
511 «FOR field : properties SEPARATOR '\n'»
\r
512 «IF addOverride»@Override«ENDIF»
\r
513 «field.getterMethod»
\r
516 «IF augmentField != null»
\r
518 @SuppressWarnings("unchecked")
\r
519 «IF addOverride»@Override«ENDIF»
\r
520 public <E extends «augmentField.returnType.importedName»> E get«augmentField.name.toFirstUpper»(Class<E> augmentationType) {
\r
521 if (augmentationType == null) {
\r
522 throw new IllegalArgumentException("Augmentation Type reference cannot be NULL!");
\r
524 return (E) «augmentField.name».get(augmentationType);
\r
530 * Template method which generates the method <code>hashCode()</code>.
\r
532 * @return string with the <code>hashCode()</code> method definition in JAVA format
\r
534 def protected generateHashCode() '''
\r
535 «IF !properties.empty || augmentField != null»
\r
537 public int hashCode() {
\r
538 final int prime = 31;
\r
540 «FOR property : properties»
\r
541 «IF property.returnType.name.contains("[")»
\r
542 result = prime * result + ((«property.fieldName» == null) ? 0 : «Arrays.importedName».hashCode(«property.fieldName»));
\r
544 result = prime * result + ((«property.fieldName» == null) ? 0 : «property.fieldName».hashCode());
\r
547 «IF augmentField != null»
\r
548 result = prime * result + ((«augmentField.name» == null) ? 0 : «augmentField.name».hashCode());
\r
556 * Template method which generates the method <code>equals()</code>.
\r
558 * @return string with the <code>equals()</code> method definition in JAVA format
\r
560 def protected generateEquals() '''
\r
561 «IF !properties.empty || augmentField != null»
\r
563 public boolean equals(java.lang.Object obj) {
\r
570 if (getClass() != obj.getClass()) {
\r
573 «type.name»«IMPL» other = («type.name»«IMPL») obj;
\r
574 «FOR property : properties»
\r
575 «val fieldName = property.fieldName»
\r
576 if («fieldName» == null) {
\r
577 if (other.«fieldName» != null) {
\r
580 «IF property.returnType.name.contains("[")»
\r
581 } else if(!«Arrays.importedName».equals(«fieldName», other.«fieldName»)) {
\r
583 } else if(!«fieldName».equals(other.«fieldName»)) {
\r
588 «IF augmentField != null»
\r
589 «val fieldName = augmentField.name»
\r
590 if («fieldName» == null) {
\r
591 if (other.«fieldName» != null) {
\r
594 } else if(!«fieldName».equals(other.«fieldName»)) {
\r
603 def override generateToString(Collection<GeneratedProperty> properties) '''
\r
604 «IF !properties.empty»
\r
606 public String toString() {
\r
607 StringBuilder builder = new StringBuilder();
\r
608 builder.append("«type.name» [«properties.get(0).fieldName»=");
\r
609 «IF properties.get(0).returnType.name.contains("[")»
\r
610 builder.append(«Arrays.importedName».toString(«properties.get(0).fieldName»));
\r
612 builder.append(«properties.get(0).fieldName»);
\r
614 «FOR i : 1..<properties.size»
\r
615 builder.append(", «properties.get(i).fieldName»=");
\r
616 «IF properties.get(i).returnType.name.contains("[")»
\r
617 builder.append(«Arrays.importedName».toString(«properties.get(i).fieldName»));
\r
619 builder.append(«properties.get(i).fieldName»);
\r
622 «IF augmentField != null»
\r
623 builder.append(", «augmentField.name»=");
\r
624 builder.append(«augmentField.name».values());
\r
626 builder.append("]");
\r
627 return builder.toString();
\r
632 override protected getFullyQualifiedName() {
\r
633 '''«type.fullyQualifiedName»Builder'''.toString
\r
636 def implementedInterfaceGetter() '''
\r
637 public «Class.importedName»<«type.importedName»> getImplementedInterface() {
\r
638 return «type.importedName».class;
\r