Bug 1411: BindingGeneratorImpl decomposition - getter name
[mdsal.git] / binding2 / mdsal-binding2-generator-impl / src / main / java / org / opendaylight / mdsal / binding / javav2 / generator / impl / AuxiliaryGenUtils.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.generator.impl;
10
11 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingGeneratorUtil.encodeAngleBrackets;
12 import static org.opendaylight.mdsal.binding.javav2.generator.util.Types.BOOLEAN;
13
14 import com.google.common.annotations.Beta;
15 import com.google.common.annotations.VisibleForTesting;
16 import com.google.common.base.Splitter;
17 import com.google.common.base.Strings;
18 import com.google.common.collect.Iterables;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.regex.Pattern;
22 import org.opendaylight.mdsal.binding.javav2.generator.impl.txt.yangTemplateForModule;
23 import org.opendaylight.mdsal.binding.javav2.generator.impl.txt.yangTemplateForNode;
24 import org.opendaylight.mdsal.binding.javav2.generator.impl.util.YangTextTemplate;
25 import org.opendaylight.mdsal.binding.javav2.generator.util.JavaIdentifier;
26 import org.opendaylight.mdsal.binding.javav2.generator.util.NonJavaCharsConverter;
27 import org.opendaylight.mdsal.binding.javav2.generator.util.Types;
28 import org.opendaylight.mdsal.binding.javav2.model.api.Constant;
29 import org.opendaylight.mdsal.binding.javav2.model.api.Type;
30 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.GeneratedTypeBuilder;
31 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.GeneratedTypeBuilderBase;
32 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.MethodSignatureBuilder;
33 import org.opendaylight.mdsal.binding.javav2.util.BindingMapping;
34 import org.opendaylight.yangtools.yang.common.QName;
35 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.Module;
38 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
39 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
40 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
41 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.Status;
43 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
44 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
45
46 /**
47  * Auxiliary util class for {@link GenHelperUtil} class
48  */
49 @Beta
50 final class AuxiliaryGenUtils {
51
52     private static final Splitter BSDOT_SPLITTER = Splitter.on("\\.");
53     private static final char NEW_LINE = '\n';
54     private static final Pattern UNICODE_CHAR_PATTERN = Pattern.compile("\\\\+u");
55
56     /**
57      * Constant with the concrete name of identifier.
58      */
59     private static final String AUGMENT_IDENTIFIER_NAME = "augment-identifier";
60
61     /**
62      * Constant with the concrete name of namespace.
63      */
64     private static final String YANG_EXT_NAMESPACE = "urn:opendaylight:yang:extension:yang-ext";
65
66     private AuxiliaryGenUtils() {
67         throw new UnsupportedOperationException("Util class");
68     }
69
70     static void annotateDeprecatedIfNecessary(final Status status, final GeneratedTypeBuilder builder) {
71         if (status == Status.DEPRECATED) {
72             builder.addAnnotation("", "Deprecated");
73         }
74     }
75
76     private static boolean hasBuilderClass(final SchemaNode schemaNode) {
77         if (schemaNode instanceof ContainerSchemaNode || schemaNode instanceof ListSchemaNode ||
78                 schemaNode instanceof RpcDefinition || schemaNode instanceof NotificationDefinition) {
79             return true;
80         }
81         return false;
82     }
83
84     static Constant qNameConstant(final GeneratedTypeBuilderBase<?> toBuilder, final String constantName,
85                                   final QName name) {
86         return toBuilder.addConstant(Types.typeForClass(QName.class), constantName, name);
87     }
88
89     /**
90      * Created a method signature builder as part of
91      * <code>interfaceBuilder</code>.
92      *
93      * The method signature builder is created for the getter method of
94      * <code>schemaNodeName</code>. Also <code>comment</code> and
95      * <code>returnType</code> information are added to the builder.
96      *
97      * @param interfaceBuilder
98      *            generated type builder for which the getter method should be
99      *            created
100      * @param schemaNodeName
101      *            string with schema node name. The name will be the part of the
102      *            getter method name.
103      * @param comment
104      *            string with comment for the getter method
105      * @param returnType
106      *            type which represents the return type of the getter method
107      * @param status
108      *            status from yang file, for deprecated annotation
109      * @return method signature builder which represents the getter method of
110      *         <code>interfaceBuilder</code>
111      */
112     static MethodSignatureBuilder constructGetter(final GeneratedTypeBuilder interfaceBuilder,
113                                                   final String schemaNodeName, final String comment, final Type returnType, final Status status) {
114
115         final MethodSignatureBuilder getMethod = interfaceBuilder
116                 .addMethod(getterMethodName(schemaNodeName, returnType));
117         if (status == Status.DEPRECATED) {
118             getMethod.addAnnotation("", "Deprecated");
119         }
120         getMethod.setComment(encodeAngleBrackets(comment));
121         getMethod.setReturnType(returnType);
122         return getMethod;
123     }
124
125     /**
126      * Creates the name of the getter method name from <code>localName</code>.
127      *
128      * @param localName
129      *            string with the name of the getter method
130      * @param returnType
131      *            return type
132      * @return string with the name of the getter method for
133      *         <code>methodName</code> in JAVA method format
134      */
135     private static String getterMethodName(final String localName, final Type returnType) {
136         final StringBuilder method = new StringBuilder();
137         if (BOOLEAN.equals(returnType)) {
138             method.append("is");
139         } else {
140             method.append("get");
141         }
142         final String name = BindingMapping.toFirstUpper(NonJavaCharsConverter.convertIdentifier(BindingMapping.getPropertyName
143                 (localName), JavaIdentifier.METHOD));
144         method.append(name);
145         return method.toString();
146     }
147
148     static String createDescription(final SchemaNode schemaNode, final String fullyQualifiedName,
149                                     final SchemaContext schemaContext, final boolean verboseClassComments) {
150         final StringBuilder sb = new StringBuilder();
151         final String nodeDescription = encodeAngleBrackets(schemaNode.getDescription());
152         final String formattedDescription = YangTextTemplate.formatToParagraph(nodeDescription, 0);
153
154         if (!Strings.isNullOrEmpty(formattedDescription)) {
155             sb.append(formattedDescription);
156             sb.append(NEW_LINE);
157         }
158
159         if (verboseClassComments) {
160             final Module module = SchemaContextUtil.findParentModule(schemaContext, schemaNode);
161             final StringBuilder linkToBuilderClass = new StringBuilder();
162             final String[] namespace = Iterables.toArray(BSDOT_SPLITTER.split(fullyQualifiedName), String.class);
163             final String className = namespace[namespace.length - 1];
164
165             if (hasBuilderClass(schemaNode)) {
166                 linkToBuilderClass.append(className);
167                 linkToBuilderClass.append("Builder");
168             }
169
170             sb.append("<p>");
171             sb.append("This class represents the following YANG schema fragment defined in module <b>");
172             sb.append(module.getName());
173             sb.append("</b>");
174             sb.append(NEW_LINE);
175             sb.append("<pre>");
176             sb.append(NEW_LINE);
177             sb.append(encodeAngleBrackets(yangTemplateForNode.render(schemaNode).body()));
178             sb.append("</pre>");
179             sb.append(NEW_LINE);
180             sb.append("The schema path to identify an instance is");
181             sb.append(NEW_LINE);
182             sb.append("<i>");
183             sb.append(YangTextTemplate.formatSchemaPath(module.getName(), schemaNode.getPath().getPathFromRoot()));
184             sb.append("</i>");
185             sb.append(NEW_LINE);
186
187             if (hasBuilderClass(schemaNode)) {
188                 sb.append(NEW_LINE);
189                 sb.append("<p>To create instances of this class use " + "{@link " + linkToBuilderClass + "}.");
190                 sb.append(NEW_LINE);
191                 sb.append("@see ");
192                 sb.append(linkToBuilderClass);
193                 sb.append(NEW_LINE);
194                 if (schemaNode instanceof ListSchemaNode) {
195                     final List<QName> keyDef = ((ListSchemaNode)schemaNode).getKeyDefinition();
196                     if (keyDef != null && !keyDef.isEmpty()) {
197                         sb.append("@see ");
198                         sb.append(className);
199                         sb.append("Key");
200                     }
201                     sb.append(NEW_LINE);
202                 }
203             }
204         }
205
206         return replaceAllIllegalChars(sb);
207     }
208
209     static String createDescription(final Module module, final boolean verboseClassComments) {
210         final StringBuilder sb = new StringBuilder();
211         final String moduleDescription = encodeAngleBrackets(module.getDescription());
212         final String formattedDescription = YangTextTemplate.formatToParagraph(moduleDescription, 0);
213
214         if (!Strings.isNullOrEmpty(formattedDescription)) {
215             sb.append(formattedDescription);
216             sb.append(NEW_LINE);
217         }
218
219         if (verboseClassComments) {
220             sb.append("<p>");
221             sb.append("This class represents the following YANG schema fragment defined in module <b>");
222             sb.append(module.getName());
223             sb.append("</b>");
224             sb.append(NEW_LINE);
225             sb.append("<pre>");
226             sb.append(NEW_LINE);
227             sb.append(encodeAngleBrackets(yangTemplateForModule.render(module).body()));
228             sb.append("</pre>");
229         }
230
231         return replaceAllIllegalChars(sb);
232     }
233
234     /**
235      * Returns first unique name for the augment generated type builder. The
236      * generated type builder name for augment consists from name of augmented
237      * node and serial number of its augmentation.
238      *
239      * @param builders
240      *            map of builders which were created in the package to which the
241      *            augmentation belongs
242      * @param genTypeName
243      *            string with name of augmented node
244      * @return string with unique name for augmentation builder
245      */
246     static String augGenTypeName(final Map<String, GeneratedTypeBuilder> builders, final String genTypeName) {
247         int index = 1;
248         if (builders != null) {
249             while (builders.containsKey(genTypeName + index)) {
250                 index = index + 1;
251             }
252         }
253         return genTypeName + index;
254     }
255
256     /**
257      * @param unknownSchemaNodes unknows schema nodes
258      * @return nodeParameter of UnknownSchemaNode
259      */
260     static String getAugmentIdentifier(final List<UnknownSchemaNode> unknownSchemaNodes) {
261         for (final UnknownSchemaNode unknownSchemaNode : unknownSchemaNodes) {
262             final QName nodeType = unknownSchemaNode.getNodeType();
263             if (AUGMENT_IDENTIFIER_NAME.equals(nodeType.getLocalName())
264                     && YANG_EXT_NAMESPACE.equals(nodeType.getNamespace().toString())) {
265                 return unknownSchemaNode.getNodeParameter();
266             }
267         }
268         return null;
269     }
270
271     @VisibleForTesting
272     public static String replaceAllIllegalChars(final StringBuilder stringBuilder){
273         final String ret = UNICODE_CHAR_PATTERN.matcher(stringBuilder).replaceAll("\\\\\\\\u");
274         return ret.isEmpty() ? "" : ret;
275     }
276 }