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