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.generator.util.Types.typeForClass;
12 import static org.opendaylight.mdsal.binding.javav2.java.api.generator.util.TextTemplateUtil.DOT;
13 import static org.opendaylight.mdsal.binding.javav2.java.api.generator.util.TextTemplateUtil.getPropertyList;
14 import static org.opendaylight.mdsal.binding.javav2.java.api.generator.util.TextTemplateUtil.toFirstLower;
16 import com.google.common.base.Preconditions;
17 import com.google.common.base.Strings;
18 import com.google.common.collect.ClassToInstanceMap;
19 import com.google.common.collect.Collections2;
20 import com.google.common.collect.ImmutableList;
21 import com.google.common.collect.ImmutableSortedSet;
22 import java.lang.reflect.Method;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.LinkedHashSet;
30 import java.util.List;
32 import java.util.Objects;
34 import java.util.regex.Pattern;
35 import org.opendaylight.mdsal.binding.javav2.generator.util.BindingTypes;
36 import org.opendaylight.mdsal.binding.javav2.generator.util.ReferencedTypeImpl;
37 import org.opendaylight.mdsal.binding.javav2.generator.util.Types;
38 import org.opendaylight.mdsal.binding.javav2.generator.util.generated.type.builder.GeneratedTOBuilderImpl;
39 import org.opendaylight.mdsal.binding.javav2.java.api.generator.txt.builderConstructorHelperTemplate;
40 import org.opendaylight.mdsal.binding.javav2.java.api.generator.txt.builderTemplate;
41 import org.opendaylight.mdsal.binding.javav2.java.api.generator.txt.constantsTemplate;
42 import org.opendaylight.mdsal.binding.javav2.java.api.generator.util.AlphabeticallyTypeMemberComparator;
43 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedProperty;
44 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedTransferObject;
45 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedType;
46 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedTypeForBuilder;
47 import org.opendaylight.mdsal.binding.javav2.model.api.MethodSignature;
48 import org.opendaylight.mdsal.binding.javav2.model.api.ParameterizedType;
49 import org.opendaylight.mdsal.binding.javav2.model.api.Type;
50 import org.opendaylight.mdsal.binding.javav2.spec.base.IdentifiableItem;
51 import org.opendaylight.mdsal.binding.javav2.spec.base.Instantiable;
52 import org.opendaylight.mdsal.binding.javav2.spec.base.Item;
53 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
54 import org.opendaylight.mdsal.binding.javav2.spec.runtime.CodeHelpers;
55 import org.opendaylight.mdsal.binding.javav2.spec.structural.Augmentable;
56 import org.opendaylight.mdsal.binding.javav2.spec.structural.Augmentation;
57 import org.opendaylight.mdsal.binding.javav2.spec.structural.AugmentationHolder;
58 import org.opendaylight.yangtools.concepts.Builder;
59 import org.opendaylight.yangtools.concepts.Identifiable;
60 import org.opendaylight.yangtools.yang.common.QName;
62 public class BuilderRenderer extends BaseRenderer {
65 * Set of class attributes (fields) which are derived from the getter methods names
67 private final Set<GeneratedProperty> properties;
70 * Set of name from properties
72 private final Map<GeneratedProperty, String> importedNamesForProperties = new HashMap<>();
75 * Generated property is set if among methods is found one with the name GET_AUGMENTATION_METHOD_NAME
77 private GeneratedProperty augmentField;
79 boolean instantiable = false;
81 public BuilderRenderer(final GeneratedType type) {
83 this.properties = propertiesFromMethods(createMethods());
84 putToImportMap(Builder.class.getSimpleName(), Builder.class.getPackage().getName());
85 putToImportMap(type.getName(), type.getPackageName());
89 protected String packageDefinition() {
90 final StringBuilder sb = new StringBuilder();
92 .append(((GeneratedTypeForBuilder)getType()).getPackageNameForBuilder())
98 protected boolean hasSamePackage(final String importedTypePackageName) {
99 return ((GeneratedTypeForBuilder)getType()).getPackageNameForBuilder()
100 .equals(importedTypePackageName);
104 * Creates set of generated property instances from getter <code>methods</code>.
106 * @param methods set of method signature instances which should be transformed to list of properties
107 * @return set of generated property instances which represents the getter <code>methods</code>
109 private Set<GeneratedProperty> propertiesFromMethods(final Collection<MethodSignature> methods) {
110 if (methods == null || methods.isEmpty()) {
111 return Collections.emptySet();
113 final Set<GeneratedProperty> result = new LinkedHashSet<>();
114 for (MethodSignature method : methods) {
115 final GeneratedProperty createdField = propertyFromGetter(method);
116 if (createdField != null) {
117 result.add(createdField);
118 importedNamesForProperties.put(createdField, importedName(createdField.getReturnType()));
119 if (createdField.getReturnType().equals(typeForClass(List.class))) {
120 getImportedNames().put("arrayList", importedName(ArrayList.class));
128 * Creates generated property instance from the getter <code>method</code> name and return type.
130 * @param method method signature from which is the method name and return type obtained
131 * @return generated property instance for the getter <code>method</code>
132 * @throws IllegalArgumentException
133 * <li>if the <code>method</code> equals <code>null</code></li>
134 * <li>if the name of the <code>method</code> equals <code>null</code></li>
135 * <li>if the name of the <code>method</code> is empty</li>
136 * <li>if the return type of the <code>method</code> equals <code>null</code></li>
138 private GeneratedProperty propertyFromGetter(final MethodSignature method) {
139 Preconditions.checkArgument(method != null, "Method cannot be NULL");
140 Preconditions.checkArgument(!Strings.isNullOrEmpty(method.getName()),
141 "Method name cannot be NULL or empty");
142 Preconditions.checkArgument(method.getReturnType() != null,
143 "Method return type reference cannot be NULL");
144 final String prefix = Types.BOOLEAN.equals(method.getReturnType()) ? "is" : "get";
145 if (method.getName().startsWith(prefix)) {
146 final String fieldName = toFirstLower(method.getName().substring(prefix.length()));
147 final GeneratedTOBuilderImpl tmpGenTO =
148 new GeneratedTOBuilderImpl("foo", "foo", true);
149 tmpGenTO.addProperty(fieldName).setReturnType(method.getReturnType());
150 return tmpGenTO.toInstance().getProperties().get(0);
156 * Returns set of method signature instances which contains all the methods of the <code>genType</code>
157 * and all the methods of the implemented interfaces.
159 * @returns set of method signature instances
161 private Set<MethodSignature> createMethods() {
162 final Set<MethodSignature> methods = new LinkedHashSet<>();
163 methods.addAll(getType().getMethodDefinitions());
164 collectImplementedMethods(methods, getType().getImplements());
165 final Set<MethodSignature> sortedMethods = ImmutableSortedSet.orderedBy(
166 new AlphabeticallyTypeMemberComparator<MethodSignature>())
169 return sortedMethods;
173 * Adds to the <code>methods</code> set all the methods of the <code>implementedIfcs</code>
174 * and recursively their implemented interfaces.
176 * @param methods set of method signatures
177 * @param implementedIfcs list of implemented interfaces
179 private void collectImplementedMethods(final Set<MethodSignature> methods, List<Type> implementedIfcs) {
180 if (implementedIfcs != null && !implementedIfcs.isEmpty()) {
181 for (Type implementedIfc : implementedIfcs) {
182 if ((implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))) {
183 final GeneratedType ifc = (GeneratedType) implementedIfc;
184 if (implementedIfc instanceof GeneratedTypeForBuilder) {
185 methods.addAll(ifc.getMethodDefinitions());
187 collectImplementedMethods(methods, ifc.getImplements());
188 } else if (Augmentable.class.getName().equals(implementedIfc.getFullyQualifiedName())) {
189 for (Method method : Augmentable.class.getMethods()) {
190 if ("getAugmentation".equals(method.getName())) {
191 final String fullyQualifiedName = method.getReturnType().getName();
192 final String aPackage = getPackage(fullyQualifiedName);
193 final String name = getName(fullyQualifiedName);
194 final GeneratedTOBuilderImpl generatedTOBuilder = new GeneratedTOBuilderImpl(aPackage,
196 final ReferencedTypeImpl referencedType = new ReferencedTypeImpl(aPackage, name,
198 final ReferencedTypeImpl generic = new ReferencedTypeImpl(getType().getPackageName(),
199 getType().getName(), true, null);
200 final ParameterizedType parametrizedReturnType =
201 Types.parameterizedTypeFor(referencedType, generic);
202 generatedTOBuilder.addMethod(method.getName()).setReturnType(parametrizedReturnType);
203 augmentField = propertyFromGetter(generatedTOBuilder.toInstance().getMethodDefinitions()
205 getImportedNames().put("map", importedName(Map.class));
206 getImportedNames().put("hashMap", importedName(HashMap.class));
207 getImportedNames().put("class", importedName(Class.class));
208 // To do This is for third party, is it needed ?
209 getImportedNames().put("augmentationHolder", importedName(AugmentationHolder.class));
210 getImportedNames().put("collections", importedName(Collections.class));
211 getImportedNames().put("augmentFieldReturnType", importedName(augmentField.getReturnType()));
214 } else if (Instantiable.class.getName().equals(implementedIfc.getFullyQualifiedName())) {
215 getImportedNames().put("class", importedName(Class.class));
223 * Returns the name of the package from <code>fullyQualifiedName</code>.
225 * @param fullyQualifiedName string with fully qualified type name (package + type)
226 * @return string with the package name
228 private String getPackage(final String fullyQualifiedName) {
229 final int lastDotIndex = fullyQualifiedName.lastIndexOf(DOT);
230 return (lastDotIndex == -1) ? "" : fullyQualifiedName.substring(0, lastDotIndex);
234 * Returns the name of tye type from <code>fullyQualifiedName</code>
236 * @param fullyQualifiedName string with fully qualified type name (package + type)
237 * @return string with the name of the type
239 private String getName(final String fullyQualifiedName) {
240 final int lastDotIndex = fullyQualifiedName.lastIndexOf(DOT);
241 return (lastDotIndex == -1) ? fullyQualifiedName : fullyQualifiedName.substring(lastDotIndex + 1);
244 public static Set<Type> getAllIfcs(final Type type) {
245 final Set<Type> baseIfcs = new HashSet<>();
246 if (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject)) {
247 for (Type impl : ((GeneratedType)type).getImplements()) {
248 if (impl instanceof GeneratedType && !(((GeneratedType)impl).getMethodDefinitions().isEmpty())) {
251 baseIfcs.addAll(getAllIfcs(impl));
258 * Method is used to find out if given type implements any interface from uses.
260 * @param type type to inspect
261 * @return has implements from uses-stmt true/false
263 public static boolean hasImplementsFromUses(GeneratedType type) {
264 for (Type impl : getAllIfcs(type)) {
265 if ((impl instanceof GeneratedType) && !(((GeneratedType)impl).getMethodDefinitions().isEmpty())) {
272 public static Set<String> toListOfNames(final Set<Type> types) {
273 final Set<String> names = new HashSet<>();
274 for (Type currentType : types) {
275 names.add(currentType.getFullyQualifiedName());
281 protected String body() {
282 final String parentTypeForBuilderName;
283 getImportedNames().put("genType", importedName(getType()));
284 getImportedNames().put("objects", importedName(Objects.class));
285 getImportedNames().put("object", importedName(Object.class));
286 getImportedNames().put("string", importedName(String.class));
287 getImportedNames().put("arrays", importedName(Arrays.class));
288 getImportedNames().put("stringBuilder", importedName(StringBuilder.class));
289 getImportedNames().put("treeNode", importedName(TreeNode.class));
290 getImportedNames().put("instantiable", importedName(Instantiable.class));
291 getImportedNames().put("item", importedName(Item.class));
292 getImportedNames().put("identifiableItem", importedName(IdentifiableItem.class));
293 getImportedNames().put("qname", importedName(QName.class));
294 getImportedNames().put("codeHelpers", importedName(CodeHelpers.class));
295 getImportedNames().put("list", importedName(List.class));
296 getImportedNames().put("immutableList", importedName(ImmutableList.class));
297 getImportedNames().put("pattern", importedName(Pattern.class));
299 if (getType().getParentType() != null) {
300 getImportedNames().put("parent", importedName(getType().getParentType()));
301 parentTypeForBuilderName = getType().getParentType().getFullyQualifiedName();
302 } else if (getType().getParentTypeForBuilder() != null) {
303 getImportedNames().put("parentTypeForBuilder", importedName(getType().getParentTypeForBuilder()));
304 parentTypeForBuilderName = getType().getParentTypeForBuilder().getFullyQualifiedName();
306 parentTypeForBuilderName = null;
309 boolean childTreeNode = false;
310 boolean childTreeNodeIdent = false;
311 String keyTypeName = null;
312 if (getType().getImplements().contains(BindingTypes.TREE_CHILD_NODE)) {
313 childTreeNode = true;
314 if (getType().getImplements().contains(BindingTypes.IDENTIFIABLE)) {
315 childTreeNodeIdent = true;
316 final ParameterizedType pType = (ParameterizedType) getType().getImplements().get(getType()
317 .getImplements().indexOf(BindingTypes.IDENTIFIABLE));
318 keyTypeName = pType.getActualTypeArguments()[0].getName();
322 getImportedNames().put("augmentation", importedName(Augmentation.class));
323 getImportedNames().put("classInstMap", importedName(ClassToInstanceMap.class));
325 final String constants = constantsTemplate.render(getType(), getImportedNames(), this::importedName, false).body();
327 // list for generate copy constructor
328 final String copyConstructorHelper = generateListForCopyConstructor();
329 List<String> getterMethods = new ArrayList<>(Collections2.transform(properties, this::getterMethod));
331 return builderTemplate.render(getType(), properties, getImportedNames(), importedNamesForProperties, augmentField,
332 copyConstructorHelper, getterMethods, parentTypeForBuilderName, childTreeNode, childTreeNodeIdent,
333 keyTypeName, instantiable, constants).body();
336 private String generateListForCopyConstructor() {
337 final List allProps = new ArrayList<>(properties);
338 final boolean isList = implementsIfc(getType(),
339 Types.parameterizedTypeFor(typeForClass(Identifiable.class), getType()));
340 final Type keyType = getKey(getType());
341 if (isList && keyType != null) {
342 final List<GeneratedProperty> keyProps = ((GeneratedTransferObject) keyType).getProperties();
343 for (GeneratedProperty keyProp : keyProps) {
344 removeProperty(allProps, keyProp.getName());
346 removeProperty(allProps, "key");
347 getImportedNames().put("keyTypeConstructor", importedName(keyType));
348 return builderConstructorHelperTemplate.render(allProps, keyProps, getImportedNames(), getPropertyList(keyProps))
351 return builderConstructorHelperTemplate.render(allProps, null, getImportedNames(), null).body();
354 private Type getKey(final GeneratedType genType) {
355 for (MethodSignature methodSignature : genType.getMethodDefinitions()) {
356 if ("getKey".equals(methodSignature.getName())) {
357 return methodSignature.getReturnType();
363 private boolean implementsIfc(final GeneratedType type, final Type impl) {
364 return type.getImplements().contains(impl);
367 private void removeProperty(final Collection<GeneratedProperty> properties, final String name) {
368 for (final GeneratedProperty property : properties) {
369 if (name.equals(property.getName())) {
370 properties.remove(property);