Switch to Objects.requireNonNull
[mdsal.git] / binding2 / mdsal-binding2-java-api-generator / src / main / java / org / opendaylight / mdsal / binding / javav2 / java / api / generator / renderers / InterfaceRenderer.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 java.util.Objects.requireNonNull;
12
13 import java.util.AbstractMap.SimpleEntry;
14 import java.util.ArrayDeque;
15 import java.util.ArrayList;
16 import java.util.Deque;
17 import java.util.List;
18 import java.util.Map.Entry;
19 import java.util.Optional;
20 import org.opendaylight.mdsal.binding.javav2.generator.util.BindingTypes;
21 import org.opendaylight.mdsal.binding.javav2.generator.util.JavaIdentifier;
22 import org.opendaylight.mdsal.binding.javav2.generator.util.JavaIdentifierNormalizer;
23 import org.opendaylight.mdsal.binding.javav2.java.api.generator.txt.constantsTemplate;
24 import org.opendaylight.mdsal.binding.javav2.java.api.generator.txt.enumTemplate;
25 import org.opendaylight.mdsal.binding.javav2.java.api.generator.txt.interfaceTemplate;
26 import org.opendaylight.mdsal.binding.javav2.java.api.generator.util.TextTemplateUtil;
27 import org.opendaylight.mdsal.binding.javav2.model.api.AnnotationType;
28 import org.opendaylight.mdsal.binding.javav2.model.api.Enumeration;
29 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedTransferObject;
30 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedType;
31 import org.opendaylight.mdsal.binding.javav2.model.api.MethodSignature;
32 import org.opendaylight.mdsal.binding.javav2.model.api.Type;
33 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.GeneratedTypeBuilder;
34 import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier;
35 import org.opendaylight.mdsal.binding.javav2.spec.runtime.BindingNamespaceType;
36 import org.opendaylight.yangtools.yang.common.QName;
37
38 public class InterfaceRenderer extends BaseRenderer {
39
40     private static final char NEW_LINE = '\n';
41
42     /**
43      * Creates the instance of this class which is used for generating the interface file source
44      * code from <code>type</code>.
45      * @param type generated type
46      */
47     public InterfaceRenderer(final GeneratedType type) {
48         super(type);
49         requireNonNull(type, "Generated type reference cannot be NULL!");
50     }
51
52     @Override
53     protected String body() {
54         // mainAnnotations string with annotations for whole interface
55         final String mainAnnotations = generateAnnotations(getType().getAnnotations());
56         // StringBuilder string with the declaration of methods source code in JAVA format
57         final StringBuilder sb1 = new StringBuilder();
58         for (MethodSignature method : getType().getMethodDefinitions()) {
59             if (isAccessor(method)) {
60                 sb1.append(TextTemplateUtil.asJavadoc(method.getComment()));
61             } else {
62                 sb1.append(TextTemplateUtil.getJavaDocForInterface(method));
63             }
64             sb1.append(generateAnnotations(method.getAnnotations()))
65                 .append(importedName(method.getReturnType()))
66                 .append(' ')
67                 .append(method.getName())
68                 .append('(')
69                 .append(generateParameters(method.getParameters()))
70                 .append(");")
71                 .append(NEW_LINE);
72         }
73         final String methodList = sb1.toString();
74
75         // enums string with rendered enums from template
76         final StringBuilder sb2 = new StringBuilder();
77         for (Enumeration enumeration : getType().getEnumerations()) {
78             final String importedName = importedName(String.class);
79             final String enumBody = enumTemplate.render(enumeration, importedName).body();
80             sb2.append(enumBody);
81         }
82         final String enums = sb2.toString();
83
84         final String generatedImports = generateImports(getType().getImplements());
85
86         getImportedNames().put("qname", importedName(QName.class));
87         final String generatedConstants = constantsTemplate.render(getType(), getImportedNames(),
88             this::importedName, true).body();
89
90         final Entry<String, String> identifier = generateInstanceIdentifier();
91
92         final List<String> innerClasses = new ArrayList<>(getType().getEnclosedTypes().size());
93         for (GeneratedType innerClass : getType().getEnclosedTypes()) {
94             if (innerClass instanceof GeneratedTransferObject) {
95                 if (((GeneratedTransferObject) innerClass).isUnionType()) {
96                     final UnionRenderer unionRenderer = new UnionRenderer((GeneratedTransferObject) innerClass);
97                     innerClasses.add(unionRenderer.generateAsInnerClass());
98                     this.putAllToImportMap(unionRenderer.getImportMap());
99                 } else {
100                     final ClassRenderer classRenderer = new ClassRenderer((GeneratedTransferObject) innerClass);
101                     innerClasses.add(classRenderer.generateAsInnerClass());
102                     this.putAllToImportMap(classRenderer.getImportMap());
103                 }
104             }
105         }
106         final String generatedInnerClasses = String.join("\n", innerClasses);
107
108         return interfaceTemplate.render(getType(), enums, mainAnnotations, methodList, generatedImports,
109                 generatedConstants, generatedInnerClasses, identifier.getKey(), identifier.getValue()).body();
110     }
111
112     private static boolean isAccessor(final MethodSignature maybeGetter) {
113         return maybeGetter.getName().startsWith("is") || maybeGetter.getName().startsWith("get");
114     }
115
116     /**
117      * Return string of annotations.
118      * @param annotationTypeList list of annotations
119      * @return String of annotations in format:
120      *     "@"annotation
121      *     (parameterName1=ParameterSingleValue1,...)
122      *
123      */
124     private String generateAnnotations(final List<AnnotationType> annotationTypeList) {
125         final StringBuilder sb1 = new StringBuilder();
126         for (AnnotationType annotationType : annotationTypeList) {
127             sb1.append('@').append(importedName(annotationType));
128             if (!annotationType.getParameters().isEmpty()) {
129                 sb1.append('(');
130             }
131             final List<String> parameterList = new ArrayList<>(annotationType.getParameters().size());
132             for (AnnotationType.Parameter parameter : annotationType.getParameters()) {
133                 final StringBuilder sb2 = new StringBuilder();
134                 sb2.append(parameter.getName()).append('=').append(parameter.getSingleValue());
135                 parameterList.add(sb2.toString());
136             }
137             sb1.append(String.join(",", parameterList));
138             if (!annotationType.getParameters().isEmpty()) {
139                 sb1.append(')');
140             }
141             sb1.append(NEW_LINE);
142         }
143         return sb1.toString();
144     }
145
146     /**
147      * Generate default method getInstanceIdentifier.
148      * @return  string pair of instance identifier and key parameters
149      */
150     private Entry<String, String> generateInstanceIdentifier() {
151         //Only tree data nodes need to generate the method.
152         if (null == getType().getBindingNamespaceType()
153                 || !BindingNamespaceType.isTreeData(getType().getBindingNamespaceType())
154                 || !getType().getImplements().contains(BindingTypes.TREE_CHILD_NODE)) {
155             return new SimpleEntry<>(null, null);
156         }
157
158         final Deque<GeneratedType> dataPath = new ArrayDeque<>();
159         GeneratedType type = getType();
160         GeneratedTypeBuilder parentTypeBuilder;
161
162         while (type != null) {
163             dataPath.push(type);
164             importedName(type);
165             parentTypeBuilder = (GeneratedTypeBuilder) type.getParentTypeForBuilder();
166             type = parentTypeBuilder != null ? parentTypeBuilder.toInstance() : null;
167         }
168
169         dataPath.pop();
170
171         final StringBuilder iiBuidler = new StringBuilder();
172         type = dataPath.pop();
173         iiBuidler.append("InstanceIdentifier.builder(").append(type.getName()).append(".class)");
174         importedName(InstanceIdentifier.class);
175         final List<String> keys = new ArrayList<>();
176         while (dataPath.peek() != null) {
177             type = dataPath.pop();
178             if (type.getImplements().contains(BindingTypes.AUGMENTATION)) {
179                 iiBuidler.append(".augmentation(").append(type.getName()).append(".class)");
180             } else {
181                 Optional<MethodSignature> method = type.getMethodDefinitions().stream().filter(m ->
182                     m.getName().equals("getIdentifier")).findFirst();
183                 if (method.isPresent()) {
184                     importedName(method.get().getReturnType());
185                     final String keyName = method.get().getReturnType().getName();
186                     final String normalizedKeyName = JavaIdentifierNormalizer.normalizeSpecificIdentifier(keyName,
187                         JavaIdentifier.METHOD);
188                     keys.add(new StringBuilder().append("final ").append(keyName).append(" _")
189                         .append(normalizedKeyName).toString());
190                     iiBuidler.append(".child(").append(type.getFullyQualifiedName()).append(".class, _")
191                         .append(normalizedKeyName).append(")");
192                 } else {
193                     iiBuidler.append(".child(").append(type.getFullyQualifiedName()).append(".class)");
194                 }
195             }
196         }
197         iiBuidler.append(".build()");
198         return new SimpleEntry<>(iiBuidler.toString(), String.join(", ", keys));
199     }
200
201
202     /**
203      * Return list of parameters.
204      * @param parameters list of parameters
205      * @return list of parameters separated with ","
206      */
207     private String generateImports(final List<Type> parameters) {
208         final List<String> strings = new ArrayList<>(parameters.size());
209         for (Type parameter : parameters) {
210             strings.add(importedName(parameter));
211         }
212
213         return String.join(", ", strings);
214     }
215 }