2 * Copyright (c) 2017 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
9 package org.opendaylight.mdsal.binding.javav2.java.api.generator.renderers;
11 import static org.opendaylight.mdsal.binding.javav2.java.api.generator.util.TextTemplateUtil.DOT;
12 import static org.opendaylight.mdsal.binding.javav2.java.api.generator.util.TextTemplateUtil.getPropertyList;
13 import static org.opendaylight.mdsal.binding.javav2.java.api.generator.util.TextTemplateUtil.toFirstLower;
15 import com.google.common.base.Preconditions;
16 import com.google.common.base.Strings;
17 import com.google.common.collect.ClassToInstanceMap;
18 import com.google.common.collect.Collections2;
19 import com.google.common.collect.ImmutableSortedSet;
20 import java.lang.reflect.Method;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.Comparator;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.LinkedHashSet;
28 import java.util.List;
30 import java.util.Objects;
32 import org.opendaylight.mdsal.binding.javav2.generator.util.ReferencedTypeImpl;
33 import org.opendaylight.mdsal.binding.javav2.generator.util.Types;
34 import org.opendaylight.mdsal.binding.javav2.generator.util.generated.type.builder.GeneratedTOBuilderImpl;
35 import org.opendaylight.mdsal.binding.javav2.java.api.generator.txt.builderConstructorHelperTemplate;
36 import org.opendaylight.mdsal.binding.javav2.java.api.generator.txt.builderTemplate;
37 import org.opendaylight.mdsal.binding.javav2.java.api.generator.util.AlphabeticallyTypeMemberComparator;
38 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedProperty;
39 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedTransferObject;
40 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedType;
41 import org.opendaylight.mdsal.binding.javav2.model.api.MethodSignature;
42 import org.opendaylight.mdsal.binding.javav2.model.api.ParameterizedType;
43 import org.opendaylight.mdsal.binding.javav2.model.api.Type;
44 import org.opendaylight.mdsal.binding.javav2.spec.base.Instantiable;
45 import org.opendaylight.mdsal.binding.javav2.spec.base.Item;
46 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
47 import org.opendaylight.mdsal.binding.javav2.spec.structural.Augmentable;
48 import org.opendaylight.mdsal.binding.javav2.spec.structural.Augmentation;
49 import org.opendaylight.mdsal.binding.javav2.spec.structural.AugmentationHolder;
50 import org.opendaylight.yangtools.concepts.Builder;
51 import org.opendaylight.yangtools.concepts.Identifiable;
53 public class BuilderRenderer extends BaseRenderer {
56 * Set of class attributes (fields) which are derived from the getter methods names
58 private final Set<GeneratedProperty> properties;
61 * Set of name from properties
63 private final Map<GeneratedProperty, String> importedNamesForProperties = new HashMap<>();
66 * list of all imported names for template
68 private final Map<String, String> importedNames = new HashMap<>();
71 * Generated property is set if among methods is found one with the name GET_AUGMENTATION_METHOD_NAME
73 private GeneratedProperty augmentField;
75 public BuilderRenderer(final GeneratedType type) {
77 this.properties = propertiesFromMethods(createMethods());
78 putToImportMap(Builder.class.getSimpleName(), Builder.class.getPackage().getName());
82 * Creates set of generated property instances from getter <code>methods</code>.
84 * @param methods set of method signature instances which should be transformed to list of properties
85 * @return set of generated property instances which represents the getter <code>methods</code>
87 private Set<GeneratedProperty> propertiesFromMethods(final Collection<MethodSignature> methods) {
88 if (methods == null || methods.isEmpty()) {
89 return Collections.emptySet();
91 final Set<GeneratedProperty> result = new LinkedHashSet<>();
92 for (MethodSignature method : methods) {
93 final GeneratedProperty createdField = propertyFromGetter(method);
94 if (createdField != null) {
95 result.add(createdField);
96 importedNamesForProperties.put(createdField, importedName(createdField.getReturnType()));
103 * Creates generated property instance from the getter <code>method</code> name and return type.
105 * @param method method signature from which is the method name and return type obtained
106 * @return generated property instance for the getter <code>method</code>
107 * @throws IllegalArgumentException
108 * <li>if the <code>method</code> equals <code>null</code></li>
109 * <li>if the name of the <code>method</code> equals <code>null</code></li>
110 * <li>if the name of the <code>method</code> is empty</li>
111 * <li>if the return type of the <code>method</code> equals <code>null</code></li>
113 private GeneratedProperty propertyFromGetter(final MethodSignature method) {
114 Preconditions.checkArgument(method != null, "Method cannot be NULL");
115 Preconditions.checkArgument(!Strings.isNullOrEmpty(method.getName()), "Method name cannot be NULL or empty");
116 Preconditions.checkArgument(method.getReturnType() != null, "Method return type reference cannot be NULL");
117 final String prefix = Types.BOOLEAN.equals(method.getReturnType()) ? "is" : "get";
118 if (method.getName().startsWith(prefix)) {
119 final String fieldName = toFirstLower(method.getName().substring(prefix.length()));
120 final GeneratedTOBuilderImpl tmpGenTO = new GeneratedTOBuilderImpl("foo", "foo");
121 tmpGenTO.addProperty(fieldName)
122 .setReturnType(method.getReturnType());
123 return tmpGenTO.toInstance().getProperties().get(0);
129 * Returns set of method signature instances which contains all the methods of the <code>genType</code>
130 * and all the methods of the implemented interfaces.
132 * @returns set of method signature instances
134 private Set<MethodSignature> createMethods() {
135 final Set<MethodSignature> methods = new LinkedHashSet<>();
136 methods.addAll(getType().getMethodDefinitions());
137 collectImplementedMethods(methods, getType().getImplements());
138 final Set<MethodSignature> sortedMethods = ImmutableSortedSet.orderedBy(
139 new AlphabeticallyTypeMemberComparator<MethodSignature>())
142 return sortedMethods;
146 * Adds to the <code>methods</code> set all the methods of the <code>implementedIfcs</code>
147 * and recursively their implemented interfaces.
149 * @param methods set of method signatures
150 * @param implementedIfcs list of implemented interfaces
152 private void collectImplementedMethods(final Set<MethodSignature> methods, List<Type> implementedIfcs) {
153 if (implementedIfcs != null && !implementedIfcs.isEmpty()) {
154 for (Type implementedIfc : implementedIfcs) {
155 if ((implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))) {
156 final GeneratedType ifc = (GeneratedType) implementedIfc;
157 methods.addAll(ifc.getMethodDefinitions());
158 collectImplementedMethods(methods, ifc.getImplements());
159 } else if (Augmentable.class.getName().equals(implementedIfc.getFullyQualifiedName())) {
160 for (Method method : Augmentable.class.getMethods()) {
161 if ("getAugmentation".equals(method.getName())) {
162 final String fullyQualifiedName = method.getReturnType().getName();
163 final String aPackage = getPackage(fullyQualifiedName);
164 final String name = getName(fullyQualifiedName);
165 final GeneratedTOBuilderImpl generatedTOBuilder = new GeneratedTOBuilderImpl(aPackage, name);
166 final ReferencedTypeImpl referencedType = new ReferencedTypeImpl(aPackage, name, true);
167 final ReferencedTypeImpl generic = new ReferencedTypeImpl(getType().getPackageName(),
168 getType().getName(), true);
169 final ParameterizedType parametrizedReturnType = Types.parameterizedTypeFor(referencedType, generic);
170 generatedTOBuilder.addMethod(method.getName()).setReturnType(parametrizedReturnType);
171 augmentField = propertyFromGetter(generatedTOBuilder.toInstance().getMethodDefinitions().get(0));
172 importedNames.put("map", importedName(Map.class));
173 importedNames.put("hashMap", importedName(HashMap.class));
174 importedNames.put("class", importedName(Class.class));
175 // To do This is for third party, is it needed ?
176 importedNames.put("augmentationHolder", importedName(AugmentationHolder.class));
177 importedNames.put("collections", importedName(Collections.class));
178 importedNames.put("augmentFieldReturnType", importedName(augmentField.getReturnType()));
187 * Returns the name of the package from <code>fullyQualifiedName</code>.
189 * @param fullyQualifiedName string with fully qualified type name (package + type)
190 * @return string with the package name
192 private String getPackage(final String fullyQualifiedName) {
193 final int lastDotIndex = fullyQualifiedName.lastIndexOf(DOT);
194 return (lastDotIndex == -1) ? "" : fullyQualifiedName.substring(0, lastDotIndex);
198 * Returns the name of tye type from <code>fullyQualifiedName</code>
200 * @param fullyQualifiedName string with fully qualified type name (package + type)
201 * @return string with the name of the type
203 private String getName(final String fullyQualifiedName) {
204 final int lastDotIndex = fullyQualifiedName.lastIndexOf(DOT);
205 return (lastDotIndex == -1) ? fullyQualifiedName : fullyQualifiedName.substring(lastDotIndex + 1);
208 public static Set<Type> getAllIfcs(final Type type) {
209 final Set<Type> baseIfcs = new HashSet<>();
210 if (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject)) {
211 for (Type impl : ((GeneratedType)type).getImplements()) {
212 if (impl instanceof GeneratedType && !(((GeneratedType)impl).getMethodDefinitions().isEmpty())) {
215 baseIfcs.addAll(getAllIfcs(impl));
222 * Method is used to find out if given type implements any interface from uses.
224 public static boolean hasImplementsFromUses(GeneratedType type) {
225 for (Type impl : getAllIfcs(type)) {
226 if ((impl instanceof GeneratedType) && !(((GeneratedType)impl).getMethodDefinitions().isEmpty())) {
233 public static Set<String> toListOfNames(final Set<Type> types) {
234 final Set<String> names = new HashSet<>();
235 for (Type currentType : types) {
236 names.add(currentType.getFullyQualifiedName());
242 protected String body() {
243 String parentTypeForBuilderName;
244 importedNames.put("genType", importedName(getType()));
245 importedNames.put("objects", importedName(Objects.class));
246 importedNames.put("object", importedName(Object.class));
247 importedNames.put("string", importedName(String.class));
248 importedNames.put("stringBuilder", importedName(StringBuilder.class));
249 importedNames.put("treeNode", importedName(TreeNode.class));
250 importedNames.put("instantiable", importedName(Instantiable.class));
251 importedNames.put("item", importedName(Item.class));
252 if (getType().getParentType() != null) {
253 importedNames.put("parent", importedName(getType().getParentType()));
254 parentTypeForBuilderName = getType().getParentType().getName();
256 importedNames.put("parentTypeForBuilder", importedName(getType().getParentTypeForBuilder()));
257 parentTypeForBuilderName = getType().getParentTypeForBuilder().getName();
259 importedNames.put("augmentation", importedName(Augmentation.class));
260 importedNames.put("classInstMap", importedName(ClassToInstanceMap.class));
262 // list for generate copy constructor
263 final String copyConstructorHelper = generateListForCopyConstructor();
264 List<String> getterMethods = new ArrayList<>(Collections2.transform(properties, this::getterMethod));
266 return builderTemplate.render(getType(), properties, importedNames, importedNamesForProperties, augmentField,
267 copyConstructorHelper, getterMethods, parentTypeForBuilderName)
271 private String generateListForCopyConstructor() {
272 final List allProps = new ArrayList<>(properties);
273 final boolean isList = implementsIfc(getType(), Types.parameterizedTypeFor(Types.typeForClass(Identifiable.class),
275 final Type keyType = getKey(getType());
276 if (isList && keyType != null) {
277 final List<GeneratedProperty> keyProps = ((GeneratedTransferObject) keyType).getProperties();
278 final Comparator<GeneratedProperty> function = (GeneratedProperty p1, GeneratedProperty p2) -> {
279 String name2 = p1.getName();
280 String name3 = p2.getName();
281 return name2.compareTo(name3);
283 Collections.sort(keyProps, function);
284 for (GeneratedProperty keyProp : keyProps) {
285 removeProperty(allProps, keyProp.getName());
287 removeProperty(allProps, "key");
288 importedNames.put("keyTypeConstructor", importedName(keyType));
289 return builderConstructorHelperTemplate.render(allProps, keyProps, importedNames, getPropertyList(allProps))
292 return builderConstructorHelperTemplate.render(allProps, null, importedNames, null).body();
295 private Type getKey(final GeneratedType genType) {
296 for (MethodSignature methodSignature : genType.getMethodDefinitions()) {
297 if ("getKey".equals(methodSignature.getName())) {
298 return methodSignature.getReturnType();
304 private boolean implementsIfc(final GeneratedType type, final Type impl) {
305 return type.getImplements().contains(impl);
308 private void removeProperty(final Collection<GeneratedProperty> properties, final String name) {
309 for (final GeneratedProperty property : properties) {
310 if (name.equals(property.getName())) {
311 properties.remove(property);