Code generator prototype - Binding Specification v2
[mdsal.git] / binding2 / mdsal-binding2-java-api-generator / src / main / java / org / opendaylight / mdsal / binding / javav2 / java / api / generator / renderers / ClassRenderer.java
1 /*
2  * Copyright (c) 2017 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.mdsal.binding.javav2.java.api.generator.renderers;
10
11 import static org.opendaylight.mdsal.binding.javav2.java.api.generator.util.TextTemplateUtil.fieldName;
12 import static org.opendaylight.mdsal.binding.javav2.java.api.generator.util.TextTemplateUtil.setterMethod;
13 import static org.opendaylight.mdsal.binding.javav2.util.BindingMapping.MEMBER_PATTERN_LIST;
14 import static org.opendaylight.mdsal.binding.javav2.util.BindingMapping.PATTERN_CONSTANT_NAME;
15
16 import com.google.common.base.Preconditions;
17 import com.google.common.collect.Collections2;
18 import com.google.common.collect.ImmutableList;
19 import com.google.common.collect.Lists;
20 import com.google.common.io.BaseEncoding;
21 import java.beans.ConstructorProperties;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.Comparator;
27 import java.util.HashMap;
28 import java.util.LinkedList;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Objects;
32 import java.util.regex.Pattern;
33 import org.opendaylight.mdsal.binding.javav2.java.api.generator.rangeGenerators.AbstractRangeGenerator;
34 import org.opendaylight.mdsal.binding.javav2.java.api.generator.rangeGenerators.LengthGenerator;
35 import org.opendaylight.mdsal.binding.javav2.java.api.generator.txt.classTemplate;
36 import org.opendaylight.mdsal.binding.javav2.java.api.generator.txt.classTemplateConstructors;
37 import org.opendaylight.mdsal.binding.javav2.java.api.generator.txt.classTemplateInitBlock;
38 import org.opendaylight.mdsal.binding.javav2.java.api.generator.txt.classTemplateRestrictions;
39 import org.opendaylight.mdsal.binding.javav2.java.api.generator.txt.classTemplateUnionConstr;
40 import org.opendaylight.mdsal.binding.javav2.model.api.Constant;
41 import org.opendaylight.mdsal.binding.javav2.model.api.Enumeration;
42 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedProperty;
43 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedTransferObject;
44 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedType;
45 import org.opendaylight.mdsal.binding.javav2.model.api.Restrictions;
46 import org.opendaylight.mdsal.binding.javav2.model.api.Type;
47
48 public class ClassRenderer extends BaseRenderer {
49     protected final GeneratedTransferObject genTO;
50     protected final Restrictions restrictions;
51     private final List<GeneratedProperty> properties;
52     private final List<GeneratedProperty> finalProperties;
53     private final List<GeneratedProperty> parentProperties;
54     private final List<Enumeration> enums;
55     private final List<Constant> consts;
56     private final List<GeneratedType> enclosedGeneratedTypes;
57     private final List<GeneratedProperty> allProperties;
58     private final Map<String, String> importedNames = new HashMap<>();
59     private final AbstractRangeGenerator<?> rangeGenerator;
60
61     public ClassRenderer(final GeneratedTransferObject genType) {
62         super(genType);
63         genTO = genType;
64         properties = ImmutableList.copyOf(genTO.getProperties());
65         finalProperties = ImmutableList.copyOf(resolveReadOnlyPropertiesFromTO(genTO.getProperties()));
66         parentProperties = ImmutableList.copyOf(getPropertiesOfAllParents(genTO));
67         enums = ImmutableList.copyOf(genTO.getEnumerations());
68         consts = ImmutableList.copyOf(genTO.getConstantDefinitions());
69         enclosedGeneratedTypes = ImmutableList.copyOf(genTO.getEnclosedTypes());
70         restrictions = genTO.getRestrictions();
71
72         final List<GeneratedProperty> sorted = new ArrayList<>();
73         sorted.addAll(properties);
74         sorted.addAll(parentProperties);
75         final Comparator<GeneratedProperty> function = (GeneratedProperty p1, GeneratedProperty p2) -> {
76             final String name = p1.getName();
77             final String name1 = p2.getName();
78             return name.compareTo(name1);
79         };
80         Collections.sort(sorted, function);
81         allProperties = ImmutableList.copyOf(sorted);
82
83         if (restrictions != null && (restrictions.getRangeConstraints() != null && !restrictions.getRangeConstraints()
84                 .isEmpty())) {
85             rangeGenerator = AbstractRangeGenerator.forType(findProperty(genType, "value").getReturnType());
86             Preconditions.checkNotNull(rangeGenerator);
87         } else {
88             rangeGenerator = null;
89         }
90     }
91
92     protected List<GeneratedProperty> getProperties() {
93         return properties;
94     }
95
96     protected List<GeneratedProperty> getFinalProperties() {
97         return finalProperties;
98     }
99
100     protected List<GeneratedProperty> getParentProperties() {
101         return parentProperties;
102     }
103
104     protected List<Enumeration> getEnums() {
105         return enums;
106     }
107
108     protected List<Constant> getConsts() {
109         return consts;
110     }
111
112     protected List<GeneratedType> getEnclosedGeneratedTypes() {
113         return enclosedGeneratedTypes;
114     }
115
116     protected Collection<GeneratedProperty> getAllProperties() {
117         return allProperties;
118     }
119
120     protected String generateAsInnerClass() {
121         return generateBody(true);
122     }
123
124     @Override
125     protected String body() {
126         return generateBody(false);
127     }
128
129     protected String generateBody(final boolean isInnerClass) {
130         importedNames.put("type", importedName(getType()));
131         importedNames.put("arrays", importedName(Arrays.class));
132         importedNames.put("objects", importedName(Objects.class));
133         importedNames.put("string", importedName(String.class));
134         importedNames.put("byte", importedName(Byte.class));
135         importedNames.put("short", importedName(Short.class));
136         importedNames.put("integer", importedName(Integer.class));
137         importedNames.put("long", importedName(Long.class));
138         importedNames.put("stringBuilder", importedName(StringBuilder.class));
139         importedNames.put("list", importedName(List.class));
140         importedNames.put("lists", importedName(Lists.class));
141         importedNames.put("illegalArgumentException", importedName(IllegalArgumentException.class));
142         importedNames.put("boolean", importedName(Boolean.class));
143
144         final List<String> implementsListBuilder = new LinkedList<>();
145         if (!getType().getImplements().isEmpty()) {
146             for (Type impl : getType().getImplements()) {
147                 implementsListBuilder.add((importedName(impl)));
148             }
149         }
150         final String implementsList = String.join(", ", implementsListBuilder);
151
152         final List<String> classTemplateBuilder = new LinkedList<>();
153         if (!enclosedGeneratedTypes.isEmpty()) {
154             for (GeneratedType innerClass : enclosedGeneratedTypes) {
155                 if (innerClass instanceof GeneratedTransferObject) {
156                     classTemplateBuilder.add(new ClassRenderer((GeneratedTransferObject) innerClass).generateAsInnerClass());
157                 }
158             }
159         }
160         final String innerClasses = String.join("\n", classTemplateBuilder);
161
162         final List<String> enumList = new LinkedList<>();
163         if (!enums.isEmpty()) {
164             for (Enumeration enumeration : enums) {
165                 enumList.add(new EnumRenderer(enumeration).body());
166             }
167         }
168         final String enumerations = String.join("\n", enumList);
169
170         final StringBuilder sb1 = new StringBuilder();
171         final String initBlock = classTemplateInitBlock.render(importedName(Pattern.class)).body();
172         if (!consts.isEmpty()) {
173             for (Constant constant : consts) {
174                 if (PATTERN_CONSTANT_NAME.equals(constant.getName())) {
175                     if (constant.getValue() instanceof List<?>) {
176                         sb1.append("private static final ")
177                             .append(importedName(Pattern.class))
178                             .append("[] ")
179                             .append(MEMBER_PATTERN_LIST)
180                             .append(";\npublic static final ")
181                             .append(importedName(List.class))
182                             .append("<String> ")
183                             .append(PATTERN_CONSTANT_NAME)
184                             .append(" = ")
185                             .append(importedName(ImmutableList.class))
186                             .append(".of(");
187                         final List<String> constantList = new LinkedList<>();
188                         for (Object item : (List) constant.getValue()) {
189                             if (item instanceof String) {
190                                 constantList.add("\"" + item + "\"");
191                             }
192                         }
193                         sb1.append(String.join(", ", constantList));
194                         sb1.append(");")
195                                 .append(initBlock);
196                     }
197                 } else {
198                     sb1.append(emitConstant(constant));
199                 }
200             }
201         }
202         final String constants = sb1.toString();
203
204         if (genTO.getSuperType() != null) {
205             importedNames.put("superType", importedName(genTO.getSuperType()));
206         }
207
208         for (GeneratedProperty property : properties) {
209             importedNames.put(property.getReturnType().toString(), importedName(property.getReturnType()));
210         }
211
212         final String constructors = generateConstructors();
213
214         final StringBuilder lengthRangeCheckerBuilder = new StringBuilder();
215         if (restrictions != null) {
216             if (restrictions.getLengthConstraints() != null && !restrictions.getLengthConstraints().isEmpty()) {
217                 lengthRangeCheckerBuilder.append(LengthGenerator.generateLengthChecker("_value", findProperty(genTO,
218                         "value").getReturnType(), restrictions.getLengthConstraints()))
219                         .append("\n");
220             }
221             if (restrictions.getRangeConstraints() != null && !restrictions.getRangeConstraints().isEmpty()) {
222                 lengthRangeCheckerBuilder.append(rangeGenerator.generateRangeChecker("_value", restrictions
223                         .getRangeConstraints()))
224                         .append("\n");
225             }
226         }
227         final String lengthRangeChecker = lengthRangeCheckerBuilder.toString();
228
229         final StringBuilder sb2 = new StringBuilder();
230         if (!properties.isEmpty()) {
231             for (GeneratedProperty property : properties) {
232                 final String isFinal = property.isReadOnly() ? " final " : " ";
233                 sb2.append("private")
234                         .append(isFinal)
235                         .append(importedName(property.getReturnType()))
236                         .append(' ')
237                         .append(fieldName(property))
238                         .append(';');
239             }
240         }
241         final String fields = sb2.toString();
242         importedNames.put("baseEncoding", importedName(BaseEncoding.class));
243         importedNames.put("defProp", importedName(((GeneratedProperty)((List) allProperties).get(0)).getReturnType()));
244
245         final StringBuilder sb3 = new StringBuilder();
246         for (GeneratedProperty property : properties) {
247             sb3.append(getterMethod(property));
248             if (!property.isReadOnly()) {
249                 sb3.append(setterMethod(property, getType().getName(), importedName(property
250                         .getReturnType())));
251             }
252         }
253         final String propertyMethod = sb3.toString();
254
255         return classTemplate.render(getType(), genTO, importedNames, implementsList, innerClasses, enumerations,
256                 constants, constructors, lengthRangeChecker, fields, allProperties, propertyMethod,
257                 isInnerClass).body();
258     }
259
260     protected String generateConstructors() {
261         importedNames.put("constructorProperties", importedName(ConstructorProperties.class));
262         importedNames.put("preconditions", importedName(Preconditions.class));
263
264         final StringBuilder sb1 = new StringBuilder();
265         for (GeneratedProperty allProperty : allProperties) {
266             sb1.append(classTemplateRestrictions.render(getType(), fieldName(allProperty), allProperty
267                     .getReturnType(), rangeGenerator).body());
268         }
269         final String genRestrictions = sb1.toString();
270
271         final StringBuilder sb2 = new StringBuilder();
272         if (genTO.isUnionType()) {
273             for (GeneratedProperty allProperty : allProperties) {
274                 final List other = new ArrayList<>(properties);
275                 if (other.remove(allProperty)) {
276                     sb2.append(classTemplateUnionConstr.render(getType(), parentProperties, allProperty,
277                         other, importedName(allProperty.getReturnType()), genRestrictions).body());
278                 }
279             }
280         }
281         final String unionConstructor = sb2.toString();
282
283         final String argumentsDeclaration = asArgumentsDeclaration(allProperties);
284         return classTemplateConstructors.render(genTO, allProperties, properties, parentProperties,
285                 importedNames, argumentsDeclaration, unionConstructor, genRestrictions).body();
286     }
287
288     /**
289      * Selects from input list of properties only those which have read only
290      * attribute set to true.
291      *
292      * @param properties
293      *            list of properties of generated transfer object
294      * @return subset of <code>properties</code> which have read only attribute
295      *         set to true
296      */
297     private List<GeneratedProperty> resolveReadOnlyPropertiesFromTO(final List<GeneratedProperty> properties) {
298         return new ArrayList(Collections2.transform(properties, GeneratedProperty::isReadOnly));
299     }
300
301     /**
302      * Returns the list of the read only properties of all extending generated
303      * transfer object from <code>genTO</code> to highest parent generated
304      * transfer object
305      *
306      * @param genTO
307      *            generated transfer object for which is the list of read only
308      *            properties generated
309      * @return list of all read only properties from actual to highest parent
310      *         generated transfer object. In case when extension exists the
311      *         method is recursive called.
312      */
313     private List<GeneratedProperty> getPropertiesOfAllParents(final GeneratedTransferObject genTO) {
314         final List<GeneratedProperty> propertiesOfAllParents = new ArrayList<>();
315         if (genTO.getSuperType() != null) {
316             final List<GeneratedProperty> allPropertiesOfTO = genTO.getSuperType().getProperties();
317             List<GeneratedProperty> readOnlyPropertiesOfTO = resolveReadOnlyPropertiesFromTO(allPropertiesOfTO);
318             propertiesOfAllParents.addAll(readOnlyPropertiesOfTO);
319             propertiesOfAllParents.addAll(getPropertiesOfAllParents(genTO.getSuperType()));
320         }
321         return propertiesOfAllParents;
322     }
323 }