c459c12b6d1d738aab647433efec54cf95847112
[mdsal.git] / binding2 / mdsal-binding2-java-api-generator / src / main / java / org / opendaylight / mdsal / binding2 / java / api / generator / renderers / ClassRenderer.java
1 /*
2  * Copyright (c) 2016 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.binding2.java.api.generator.renderers;
10
11 import static org.opendaylight.mdsal.binding2.generator.util.Binding2Mapping.MEMBER_PATTERN_LIST;
12 import static org.opendaylight.mdsal.binding2.generator.util.Binding2Mapping.PATTERN_CONSTANT_NAME;
13 import static org.opendaylight.mdsal.binding2.java.api.generator.util.TextTemplateUtil.fieldName;
14 import static org.opendaylight.mdsal.binding2.java.api.generator.util.TextTemplateUtil.setterMethod;
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.binding2.java.api.generator.rangeGenerators.AbstractRangeGenerator;
34 import org.opendaylight.mdsal.binding2.java.api.generator.rangeGenerators.LengthGenerator;
35 import org.opendaylight.mdsal.binding2.model.api.Constant;
36 import org.opendaylight.mdsal.binding2.model.api.Enumeration;
37 import org.opendaylight.mdsal.binding2.model.api.GeneratedProperty;
38 import org.opendaylight.mdsal.binding2.model.api.GeneratedTransferObject;
39 import org.opendaylight.mdsal.binding2.model.api.GeneratedType;
40 import org.opendaylight.mdsal.binding2.model.api.Restrictions;
41 import org.opendaylight.mdsal.binding2.model.api.Type;
42 import org.opendaylight.mdsal.binding2.txt.classTemplate;
43 import org.opendaylight.mdsal.binding2.txt.classTemplateConstructors;
44 import org.opendaylight.mdsal.binding2.txt.classTemplateInitBlock;
45 import org.opendaylight.mdsal.binding2.txt.classTemplateRestrictions;
46 import org.opendaylight.mdsal.binding2.txt.classTemplateUnionConstr;
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.getLengthConstraints
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("genTypeSuper", importedName(genTO.getSuperType()));
131         importedNames.put("type", importedName(getType()));
132         importedNames.put("arrays", importedName(Arrays.class));
133         importedNames.put("objects", importedName(Objects.class));
134         importedNames.put("string", importedName(String.class));
135         importedNames.put("byte", importedName(Byte.class));
136         importedNames.put("short", importedName(Short.class));
137         importedNames.put("integer", importedName(Integer.class));
138         importedNames.put("long", importedName(Long.class));
139         importedNames.put("stringBuilder", importedName(StringBuilder.class));
140         importedNames.put("list", importedName(List.class));
141         importedNames.put("lists", importedName(Lists.class));
142         importedNames.put("illegalArgumentException", importedName(IllegalArgumentException.class));
143         importedNames.put("boolean", importedName(Boolean.class));
144
145         final List<String> implementsListBuilder = new LinkedList<>();
146         if (!getType().getImplements().isEmpty()) {
147             for (Type impl : getType().getImplements()) {
148                 implementsListBuilder.add((importedName(impl)));
149             }
150         }
151         final String implementsList = String.join(", ", implementsListBuilder);
152
153         final List<String> classTemplateBuilder = new LinkedList<>();
154         if (!enclosedGeneratedTypes.isEmpty()) {
155             for (GeneratedType innerClass : enclosedGeneratedTypes) {
156                 if (innerClass instanceof GeneratedTransferObject) {
157                     classTemplateBuilder.add(new ClassRenderer((GeneratedTransferObject) innerClass).generateAsInnerClass());
158                 }
159             }
160         }
161         final String innerClasses = String.join("\n", classTemplateBuilder);
162
163         final List<String> enumList = new LinkedList<>();
164         if (!enums.isEmpty()) {
165             for (Enumeration enumeration : enums) {
166                 enumList.add(new EnumRenderer(enumeration).body());
167             }
168         }
169         final String enumerations = String.join("\n", enumList);
170
171         final StringBuilder sb1 = new StringBuilder();
172         final String initBlock = classTemplateInitBlock.render(importedName(Pattern.class)).body();
173         if (!consts.isEmpty()) {
174             for (Constant constant : consts) {
175                 if (PATTERN_CONSTANT_NAME.equals(constant.getName())) {
176                     if (constant.getValue() instanceof List<?>) {
177                         sb1.append("private static final ")
178                             .append(importedName(Pattern.class))
179                             .append("[] ")
180                             .append(MEMBER_PATTERN_LIST)
181                             .append(";\npublic static final ")
182                             .append(importedName(List.class))
183                             .append("<String> ")
184                             .append(PATTERN_CONSTANT_NAME)
185                             .append(" = ")
186                             .append(importedName(ImmutableList.class))
187                             .append(".of(");
188                         final List<String> constantList = new LinkedList<>();
189                         for (Object item : (List) constant.getValue()) {
190                             if (item instanceof String) {
191                                 constantList.add("\"" + item + "\"");
192                             }
193                         }
194                         sb1.append(String.join(", ", constantList));
195                         sb1.append(");")
196                                 .append(initBlock);
197                     }
198                 } else {
199                     sb1.append(emitConstant(constant));
200                 }
201             }
202         }
203         final String constants = sb1.toString();
204
205         if (genTO.getSuperType() != null) {
206             importedNames.put("superType", importedName(genTO.getSuperType()));
207         }
208
209         for (GeneratedProperty property : properties) {
210             importedNames.put(property.getReturnType().toString(), importedName(property.getReturnType()));
211         }
212
213         final String constructors = generateConstructors();
214
215         final StringBuilder lengthRangeCheckerBuilder = new StringBuilder();
216         if (restrictions != null) {
217             if (restrictions.getLengthConstraints() != null && !restrictions.getLengthConstraints().isEmpty()) {
218                 lengthRangeCheckerBuilder.append(LengthGenerator.generateLengthChecker("_value", findProperty(genTO,
219                         "value").getReturnType(), restrictions.getLengthConstraints()))
220                         .append("\n");
221             }
222             if (restrictions.getRangeConstraints() != null && !restrictions.getRangeConstraints().isEmpty()) {
223                 lengthRangeCheckerBuilder.append(rangeGenerator.generateRangeChecker("_value", restrictions
224                         .getRangeConstraints()))
225                         .append("\n");
226             }
227         }
228         final String lengthRangeChecker = lengthRangeCheckerBuilder.toString();
229
230         final StringBuilder sb2 = new StringBuilder();
231         if (!properties.isEmpty()) {
232             for (GeneratedProperty property : properties) {
233                 final String isFinal = property.isReadOnly() ? " final " : " ";
234                 sb2.append("private")
235                         .append(isFinal)
236                         .append(importedName(property.getReturnType()))
237                         .append(' ')
238                         .append(fieldName(property))
239                         .append(';');
240             }
241         }
242         final String fields = sb2.toString();
243         importedNames.put("baseEncoding", importedName(BaseEncoding.class));
244         importedNames.put("defProp", importedName(((GeneratedProperty)((List) allProperties).get(0)).getReturnType()));
245
246         final StringBuilder sb3 = new StringBuilder();
247         for (GeneratedProperty property : properties) {
248             sb3.append(getterMethod(property));
249             if (!property.isReadOnly()) {
250                 sb3.append(setterMethod(property, getType().getName(), importedName(property
251                         .getReturnType())));
252             }
253         }
254         final String propertyMethod = sb3.toString();
255
256         return classTemplate.render(getType(), genTO, importedNames, implementsList, innerClasses, enumerations,
257                 constants, constructors, lengthRangeChecker, fields, (List) allProperties, propertyMethod,
258                 isInnerClass).body();
259     }
260
261     protected String generateConstructors() {
262         importedNames.put("constructorProperties", importedName(ConstructorProperties.class));
263         importedNames.put("preconditions", importedName(Preconditions.class));
264
265         final StringBuilder sb1 = new StringBuilder();
266         for (GeneratedProperty allProperty : allProperties) {
267             sb1.append(classTemplateRestrictions.render(getType(), fieldName(allProperty), allProperty
268                     .getReturnType(), rangeGenerator).body());
269         }
270         final String genRestrictions = sb1.toString();
271
272         final StringBuilder sb2 = new StringBuilder();
273         if (genTO.isUnionType()) {
274             for (GeneratedProperty allProperty : allProperties) {
275                 final List other = new ArrayList<>(properties);
276                 if (other.remove(allProperty)) {
277                     sb2.append(classTemplateUnionConstr.render(getType(), parentProperties, allProperty,
278                         other, importedName(allProperty.getReturnType()), genRestrictions).body());
279                 }
280             }
281         }
282         final String unionConstructor = sb2.toString();
283
284         final String argumentsDeclaration = asArgumentsDeclaration(allProperties);
285         return classTemplateConstructors.render(genTO, (List) allProperties, properties, parentProperties,
286                 importedNames, argumentsDeclaration, unionConstructor, genRestrictions).body();
287     }
288
289     /**
290      * Selects from input list of properties only those which have read only
291      * attribute set to true.
292      *
293      * @param properties
294      *            list of properties of generated transfer object
295      * @return subset of <code>properties</code> which have read only attribute
296      *         set to true
297      */
298     private List<GeneratedProperty> resolveReadOnlyPropertiesFromTO(final List<GeneratedProperty> properties) {
299         return new ArrayList(Collections2.transform(properties, GeneratedProperty::isReadOnly));
300     }
301
302     /**
303      * Returns the list of the read only properties of all extending generated
304      * transfer object from <code>genTO</code> to highest parent generated
305      * transfer object
306      *
307      * @param genTO
308      *            generated transfer object for which is the list of read only
309      *            properties generated
310      * @return list of all read only properties from actual to highest parent
311      *         generated transfer object. In case when extension exists the
312      *         method is recursive called.
313      */
314     private List<GeneratedProperty> getPropertiesOfAllParents(final GeneratedTransferObject genTO) {
315         final List<GeneratedProperty> propertiesOfAllParents = new ArrayList<>();
316         if (genTO.getSuperType() != null) {
317             final List<GeneratedProperty> allPropertiesOfTO = genTO.getSuperType().getProperties();
318             List<GeneratedProperty> readOnlyPropertiesOfTO = resolveReadOnlyPropertiesFromTO(allPropertiesOfTO);
319             propertiesOfAllParents.addAll(readOnlyPropertiesOfTO);
320             propertiesOfAllParents.addAll(getPropertiesOfAllParents(genTO.getSuperType()));
321         }
322         return propertiesOfAllParents;
323     }
324 }