2 * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.mdsal.binding.javav2.generator.impl;
11 import static com.google.common.base.Preconditions.checkArgument;
12 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingGeneratorUtil.encodeAngleBrackets;
13 import static org.opendaylight.mdsal.binding.javav2.generator.util.Types.BOOLEAN;
15 import com.google.common.annotations.Beta;
16 import com.google.common.annotations.VisibleForTesting;
17 import com.google.common.base.Splitter;
18 import java.util.Collection;
19 import java.util.List;
21 import java.util.regex.Pattern;
22 import org.opendaylight.mdsal.binding.javav2.generator.context.ModuleContext;
23 import org.opendaylight.mdsal.binding.javav2.generator.spi.TypeProvider;
24 import org.opendaylight.mdsal.binding.javav2.generator.util.JavaIdentifier;
25 import org.opendaylight.mdsal.binding.javav2.generator.util.JavaIdentifierNormalizer;
26 import org.opendaylight.mdsal.binding.javav2.generator.util.TypeComments;
27 import org.opendaylight.mdsal.binding.javav2.generator.util.Types;
28 import org.opendaylight.mdsal.binding.javav2.generator.util.generated.type.builder.GeneratedTOBuilderImpl;
29 import org.opendaylight.mdsal.binding.javav2.generator.util.generated.type.builder.GeneratedTypeBuilderImpl;
30 import org.opendaylight.mdsal.binding.javav2.generator.yang.types.TypeProviderImpl;
31 import org.opendaylight.mdsal.binding.javav2.model.api.Constant;
32 import org.opendaylight.mdsal.binding.javav2.model.api.Type;
33 import org.opendaylight.mdsal.binding.javav2.model.api.YangSourceDefinition;
34 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.EnumBuilder;
35 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.GeneratedPropertyBuilder;
36 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.GeneratedTOBuilder;
37 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.GeneratedTypeBuilder;
38 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.GeneratedTypeBuilderBase;
39 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.MethodSignatureBuilder;
40 import org.opendaylight.mdsal.binding.javav2.spec.runtime.BindingNamespaceType;
41 import org.opendaylight.yangtools.yang.common.QName;
42 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
43 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
45 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
46 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
47 import org.opendaylight.yangtools.yang.model.api.Module;
48 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
49 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
50 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
51 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
52 import org.opendaylight.yangtools.yang.model.api.Status;
53 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
54 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
55 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
56 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
57 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
60 * Auxiliary util class for {@link GenHelperUtil} class
63 final class AuxiliaryGenUtils {
65 private static final Splitter BSDOT_SPLITTER = Splitter.on(".");
66 private static final char NEW_LINE = '\n';
67 private static final Pattern UNICODE_CHAR_PATTERN = Pattern.compile("\\\\+u");
70 * Constant with the concrete name of identifier.
72 private static final String AUGMENT_IDENTIFIER_NAME = "augment-identifier";
75 * Constant with the concrete name of namespace.
77 private static final String YANG_EXT_NAMESPACE = "urn:opendaylight:yang:extension:yang-ext";
79 private AuxiliaryGenUtils() {
80 throw new UnsupportedOperationException("Util class");
83 static void annotateDeprecatedIfNecessary(final Status status, final GeneratedTypeBuilder builder) {
84 if (status == Status.DEPRECATED) {
85 builder.addAnnotation("", "Deprecated");
89 static Constant qNameConstant(final GeneratedTypeBuilderBase<?> toBuilder, final String constantName,
91 return toBuilder.addConstant(Types.typeForClass(QName.class), constantName, name);
95 * Created a method signature builder as part of
96 * <code>interfaceBuilder</code>.
98 * The method signature builder is created for the getter method of
99 * <code>schemaNodeName</code>. Also <code>comment</code> and
100 * <code>returnType</code> information are added to the builder.
102 * @param interfaceBuilder
103 * generated type builder for which the getter method should be
105 * @param schemaNodeName
106 * string with schema node name. The name will be the part of the
107 * getter method name.
109 * string with comment for the getter method
111 * type which represents the return type of the getter method
113 * status from yang file, for deprecated annotation
114 * @return method signature builder which represents the getter method of
115 * <code>interfaceBuilder</code>
117 static MethodSignatureBuilder constructGetter(final GeneratedTypeBuilder interfaceBuilder,
118 final String schemaNodeName, final String comment, final Type returnType, final Status status) {
120 final MethodSignatureBuilder getMethod = interfaceBuilder
121 .addMethod(getterMethodName(schemaNodeName, returnType));
122 if (status == Status.DEPRECATED) {
123 getMethod.addAnnotation("", "Deprecated");
125 getMethod.setComment(encodeAngleBrackets(comment));
126 getMethod.setReturnType(returnType);
131 * Creates the name of the getter method name from <code>localName</code>.
134 * string with the name of the getter method
137 * @return string with the name of the getter method for
138 * <code>methodName</code> in JAVA method format
141 static String getterMethodName(final String localName, final Type returnType) {
142 final StringBuilder method = new StringBuilder();
143 if (BOOLEAN.equals(returnType)) {
146 method.append("get");
148 return method.append(JavaIdentifierNormalizer.normalizeSpecificIdentifier(localName, JavaIdentifier.CLASS))
152 public static boolean hasBuilderClass(final SchemaNode schemaNode, final BindingNamespaceType namespaceType) {
153 return (namespaceType.equals(BindingNamespaceType.Data)
154 && (schemaNode instanceof ContainerSchemaNode || schemaNode instanceof ListSchemaNode
155 || schemaNode instanceof RpcDefinition || schemaNode instanceof NotificationDefinition
156 || schemaNode instanceof CaseSchemaNode));
160 static boolean isNullOrEmpty(final Collection<?> list) {
161 return list == null || list.isEmpty();
165 * Returns first unique name for the augment generated type builder. The
166 * generated type builder name for augment consists from name of augmented
167 * node and serial number of its augmentation.
170 * map of builders which were created in the package to which the
171 * augmentation belongs
173 * string with name of augmented node
174 * @return string with unique name for augmentation builder
176 static String augGenTypeName(final Map<String, GeneratedTypeBuilder> builders, final String genTypeName) {
178 if (builders != null) {
179 while (builders.containsKey(genTypeName + index)) {
183 return genTypeName + index;
187 * @param unknownSchemaNodes unknown schema nodes
188 * @return nodeParameter of UnknownSchemaNode
190 static String getAugmentIdentifier(final List<UnknownSchemaNode> unknownSchemaNodes) {
191 for (final UnknownSchemaNode unknownSchemaNode : unknownSchemaNodes) {
192 final QName nodeType = unknownSchemaNode.getNodeType();
193 if (AUGMENT_IDENTIFIER_NAME.equals(nodeType.getLocalName())
194 && YANG_EXT_NAMESPACE.equals(nodeType.getNamespace().toString())) {
195 return unknownSchemaNode.getNodeParameter();
202 * Adds enumeration builder created from <code>enumTypeDef</code> to
203 * <code>typeBuilder</code>.
205 * Each <code>enumTypeDef</code> item is added to builder with its name and
209 * EnumTypeDefinition contains enum data
211 * string contains name which will be assigned to enumeration
214 * GeneratedTypeBuilder to which will be enum builder assigned
216 * Module in which type should be generated
217 * @return enumeration builder which contains data from
218 * <code>enumTypeDef</code>
220 static EnumBuilder resolveInnerEnumFromTypeDefinition(final EnumTypeDefinition enumTypeDef, final QName enumName,
221 final Map<Module, ModuleContext> genCtx, final GeneratedTypeBuilder typeBuilder, final Module module) {
222 if (enumTypeDef != null && typeBuilder != null && enumTypeDef.getQName().getLocalName() != null) {
223 final EnumBuilder enumBuilder = typeBuilder.addEnumeration(enumName.getLocalName(), genCtx.get(module));
224 final String enumTypedefDescription = encodeAngleBrackets(enumTypeDef.getDescription().orElse(null));
225 enumBuilder.setDescription(enumTypedefDescription);
226 enumBuilder.updateEnumPairsFromEnumTypeDef(enumTypeDef);
227 final ModuleContext ctx = genCtx.get(module);
228 ctx.addInnerTypedefType(enumTypeDef.getPath(), enumBuilder);
236 * Builds generated TO builders for <code>typeDef</code> of type
237 * {@link UnionTypeDefinition} or {@link BitsTypeDefinition} which are
238 * also added to <code>typeBuilder</code> as enclosing transfer object.
240 * If more then one generated TO builder is created for enclosing then all
241 * of the generated TO builders are added to <code>typeBuilder</code> as
242 * enclosing transfer objects.
245 * type definition which can be of type <code>UnionType</code> or
246 * <code>BitsTypeDefinition</code>
248 * generated type builder to which is added generated TO created
249 * from <code>typeDef</code>
251 * string with name for generated TO builder
252 * @param parentModule
254 * @return generated TO builder for <code>typeDef</code>
256 static GeneratedTOBuilder addTOToTypeBuilder(final TypeDefinition<?> typeDef, final GeneratedTypeBuilder
257 typeBuilder, final DataSchemaNode leaf, final Module parentModule, final TypeProvider typeProvider,
258 final SchemaContext schemaContext, ModuleContext context, final Map<Module, ModuleContext> genCtx) {
259 final String classNameFromLeaf = leaf.getQName().getLocalName();
260 GeneratedTOBuilder genTOBuilder = null;
261 final String packageName = typeBuilder.getFullyQualifiedName();
262 if (typeDef instanceof UnionTypeDefinition) {
263 genTOBuilder = ((TypeProviderImpl) typeProvider)
264 .provideGeneratedTOBuilderForUnionTypeDef(packageName, ((UnionTypeDefinition) typeDef),
265 classNameFromLeaf, leaf, schemaContext,
266 ((TypeProviderImpl) typeProvider).getGenTypeDefsContextMap(), context);
267 } else if (typeDef instanceof BitsTypeDefinition) {
268 genTOBuilder = (((TypeProviderImpl) typeProvider)).provideGeneratedTOBuilderForBitsTypeDefinition(
269 packageName, typeDef, classNameFromLeaf, parentModule.getName(), context);
271 if (genTOBuilder != null) {
272 typeBuilder.addEnclosingTransferObject(genTOBuilder);
273 genCtx.get(parentModule).addInnerTypedefType(typeDef.getPath(), genTOBuilder);
280 @SuppressWarnings({ "unchecked", "rawtypes" })
281 static Type createReturnTypeForUnion(final GeneratedTOBuilder genTOBuilder, final TypeDefinition<?> typeDef,
282 final GeneratedTypeBuilder typeBuilder, final Module parentModule, final TypeProvider typeProvider,
283 final boolean verboseClassComments) {
284 final GeneratedTOBuilderImpl returnType = (GeneratedTOBuilderImpl) genTOBuilder;
286 if (verboseClassComments) {
287 YangSourceDefinition.of(parentModule, typeDef).ifPresent(returnType::setYangSourceDefinition);
288 TypeComments.description(typeDef).ifPresent(returnType::addComment);
289 typeDef.getDescription().ifPresent(returnType::setDescription);
290 typeDef.getReference().ifPresent(returnType::setReference);
292 returnType.setSchemaPath((List) typeDef.getPath().getPathFromRoot());
293 returnType.setModuleName(parentModule.getName());
295 genTOBuilder.setTypedef(true);
296 genTOBuilder.setIsUnion(true);
297 TypeProviderImpl.addUnitsToGenTO(genTOBuilder, typeDef.getUnits().orElse(null));
299 return returnType.toInstance();
302 static boolean isInnerType(final LeafSchemaNode leaf, final TypeDefinition<?> type) {
303 return leaf.getPath().equals(type.getPath()) || leaf.getPath().equals(type.getPath().getParent());
308 * Generates for the <code>list</code> which contains any list keys special
309 * generated TO builder.
312 * string with package name to which the list belongs
314 * schema node of list
315 * @return generated TO builder which represents the keys of the
316 * <code>list</code> or empty TO builder if <code>list</code> is null or list of
317 * key definitions is null or empty.
319 static GeneratedTOBuilder resolveListKeyTOBuilder(final String packageName, final ListSchemaNode list,
320 ModuleContext context) {
321 GeneratedTOBuilder genTOBuilder = null;
322 if ((list.getKeyDefinition() != null) && (!list.getKeyDefinition().isEmpty())) {
323 // underscore used as separator for distinction of class name parts
324 final String genTOName =
325 new StringBuilder(list.getQName().getLocalName()).append('_').append(BindingNamespaceType.Key)
327 genTOBuilder = new GeneratedTOBuilderImpl(packageName, genTOName, context);
332 static GeneratedTypeBuilder resolveListKeyTypeBuilder(final String packageName, final ListSchemaNode list,
333 ModuleContext context) {
334 GeneratedTypeBuilder genTypeBuilder = null;
335 if ((list.getKeyDefinition() != null) && (!list.getKeyDefinition().isEmpty())) {
336 // underscore used as separator for distinction of class name parts
337 final String genTOName =
338 new StringBuilder(list.getQName().getLocalName()).append('_').append(BindingNamespaceType.Key)
340 genTypeBuilder = new GeneratedTypeBuilderImpl(packageName, genTOName, context);
342 return genTypeBuilder;
346 * Converts <code>leaf</code> schema node to property of generated TO
350 * generated TO builder to which is <code>leaf</code> added as
353 * leaf schema node which is added to <code>toBuilder</code> as
358 * boolean value which says if leaf property is|isn't read only
359 * @return boolean value
361 * <li>false - if <code>leaf</code>, <code>toBuilder</code> or leaf
362 * name equals null or if leaf is added by <i>uses</i>.</li>
363 * <li>true - other cases</li>
366 static boolean resolveLeafSchemaNodeAsProperty(final String nodeName, final GeneratedTOBuilder toBuilder, final LeafSchemaNode leaf,
367 final Type returnType, final boolean isReadOnly) {
369 if (returnType == null) {
372 final String leafName = leaf.getQName().getLocalName();
373 final String leafGetterName;
375 if ("key".equals(leafName.toLowerCase())) {
376 StringBuilder sb = new StringBuilder(leafName)
377 .append('_').append("RESERVED_WORD");
378 leafGetterName = sb.toString();
380 leafGetterName = leafName;
383 final String leafDesc = encodeAngleBrackets(leaf.getDescription().orElse(null));
384 final GeneratedPropertyBuilder propBuilder =
385 toBuilder.addProperty(JavaIdentifierNormalizer.normalizeSpecificIdentifier(leafGetterName, JavaIdentifier.METHOD));
386 propBuilder.setReadOnly(isReadOnly);
387 propBuilder.setReturnType(returnType);
388 propBuilder.setComment(leafDesc);
389 toBuilder.addEqualsIdentity(propBuilder);
390 toBuilder.addHashIdentity(propBuilder);
391 toBuilder.addToStringProperty(propBuilder);
395 static void checkModuleAndModuleName(final Module module) {
396 checkArgument(module != null, "Module reference cannot be NULL.");
397 checkArgument(module.getName() != null, "Module name cannot be NULL.");
401 public static String replaceAllIllegalChars(final StringBuilder stringBuilder){
402 final String ret = UNICODE_CHAR_PATTERN.matcher(stringBuilder).replaceAll("\\\\\\\\u");
403 return ret.isEmpty() ? "" : ret;