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 org.opendaylight.mdsal.binding.javav2.generator.impl.AuxiliaryGenUtils.annotateDeprecatedIfNecessary;
12 import static org.opendaylight.mdsal.binding.javav2.generator.impl.AuxiliaryGenUtils.augGenTypeName;
13 import static org.opendaylight.mdsal.binding.javav2.generator.impl.AuxiliaryGenUtils.constructGetter;
14 import static org.opendaylight.mdsal.binding.javav2.generator.impl.AuxiliaryGenUtils.createDescription;
15 import static org.opendaylight.mdsal.binding.javav2.generator.impl.AuxiliaryGenUtils.getAugmentIdentifier;
16 import static org.opendaylight.mdsal.binding.javav2.generator.impl.AuxiliaryGenUtils.qNameConstant;
17 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingGeneratorUtil.packageNameForGeneratedType;
18 import com.google.common.annotations.Beta;
19 import com.google.common.base.Preconditions;
20 import java.util.HashMap;
21 import java.util.List;
23 import org.opendaylight.mdsal.binding.javav2.generator.util.BindingTypes;
24 import org.opendaylight.mdsal.binding.javav2.generator.util.JavaIdentifier;
25 import org.opendaylight.mdsal.binding.javav2.generator.util.NonJavaCharsConverter;
26 import org.opendaylight.mdsal.binding.javav2.generator.util.Types;
27 import org.opendaylight.mdsal.binding.javav2.generator.util.generated.type.builder.GeneratedTypeBuilderImpl;
28 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedType;
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.spec.base.TreeNode;
32 import org.opendaylight.mdsal.binding.javav2.spec.runtime.BindingNamespaceType;
33 import org.opendaylight.mdsal.binding.javav2.spec.structural.Augmentable;
34 import org.opendaylight.mdsal.binding.javav2.util.BindingMapping;
35 import org.opendaylight.yangtools.yang.common.QName;
36 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
37 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
39 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
41 import org.opendaylight.yangtools.yang.model.api.Module;
42 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
43 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
45 import org.opendaylight.yangtools.yang.model.api.UsesNode;
46 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
49 * Helper util class used for generation of types in Binding spec v2.
52 final class GenHelperUtil {
54 private GenHelperUtil() {
55 throw new UnsupportedOperationException("Util class");
59 * Create GeneratedTypeBuilder object from module argument.
62 * Module object from which builder will be created
64 * @param verboseClassComments
66 * @return <code>GeneratedTypeBuilder</code> which is internal
67 * representation of the module
68 * @throws IllegalArgumentException
71 static GeneratedTypeBuilder moduleToDataType(final Module module, final Map<Module, ModuleContext> genCtx, final boolean verboseClassComments) {
72 Preconditions.checkArgument(module != null, "Module reference cannot be NULL.");
74 final GeneratedTypeBuilder moduleDataTypeBuilder = moduleTypeBuilder(module, "Data", verboseClassComments);
75 addImplementedInterfaceFromUses(module, moduleDataTypeBuilder, genCtx);
76 moduleDataTypeBuilder.addImplementsType(BindingTypes.TREE_ROOT);
77 moduleDataTypeBuilder.addComment(module.getDescription());
78 moduleDataTypeBuilder.setDescription(createDescription(module, verboseClassComments));
79 moduleDataTypeBuilder.setReference(module.getReference());
80 return moduleDataTypeBuilder;
84 * Generates type builder for <code>module</code>.
87 * Module which is source of package name for generated type
90 * string which is added to the module class name representation
92 * @param verboseClassComments
93 * @return instance of GeneratedTypeBuilder which represents
94 * <code>module</code>.
95 * @throws IllegalArgumentException
96 * if <code>module</code> is null
98 private static GeneratedTypeBuilder moduleTypeBuilder(final Module module, final String postfix, final boolean
99 verboseClassComments) {
100 Preconditions.checkArgument(module != null, "Module reference cannot be NULL.");
101 final String packageName = BindingMapping.getRootPackageName(module);
102 final String moduleName = BindingMapping.getClassName(NonJavaCharsConverter.convertIdentifier(module.getName
103 (), JavaIdentifier.CLASS)) + postfix;
105 final GeneratedTypeBuilderImpl moduleBuilder = new GeneratedTypeBuilderImpl(packageName, moduleName);
106 moduleBuilder.setDescription(createDescription(module, verboseClassComments));
107 moduleBuilder.setReference(module.getReference());
108 moduleBuilder.setModuleName(moduleName);
110 return moduleBuilder;
114 * Adds the implemented types to type builder.
116 * The method passes through the list of <i>uses</i> in
117 * {@code dataNodeContainer}. For every <i>use</i> is obtained corresponding
118 * generated type from all groupings
119 * allGroupings} which is added as <i>implements type</i> to
120 * <code>builder</code>
122 * @param dataNodeContainer
123 * element which contains the list of used YANG groupings
125 * builder to which are added implemented types according to
126 * <code>dataNodeContainer</code>
127 * @param genCtx generated context
128 * @return generated type builder with all implemented types
130 private static GeneratedTypeBuilder addImplementedInterfaceFromUses(final DataNodeContainer dataNodeContainer,
131 final GeneratedTypeBuilder builder, final Map<Module, ModuleContext> genCtx) {
132 for (final UsesNode usesNode : dataNodeContainer.getUses()) {
133 final GeneratedType genType = findGroupingByPath(usesNode.getGroupingPath(), genCtx).toInstance();
134 if (genType == null) {
135 throw new IllegalStateException("Grouping " + usesNode.getGroupingPath() + "is not resolved for "
136 + builder.getName());
138 builder.addImplementsType(genType);
143 static GeneratedTypeBuilder findGroupingByPath(final SchemaPath path, final Map<Module, ModuleContext> genCtx) {
144 for (final ModuleContext ctx : genCtx.values()) {
145 final GeneratedTypeBuilder result = ctx.getGrouping(path);
146 if (result != null) {
154 * Adds the methods to <code>typeBuilder</code> which represent subnodes of
155 * node for which <code>typeBuilder</code> was created.
157 * The subnodes aren't mapped to the methods if they are part of grouping or
158 * augment (in this case are already part of them).
162 * @param basePackageName
163 * string contains the module package name
165 * generated type builder which represents any node. The subnodes
166 * of this node are added to the <code>typeBuilder</code> as
167 * methods. The subnode can be of type leaf, leaf-list, list,
172 * set of data schema nodes which are the children of the node
173 * for which <code>typeBuilder</code> was created
174 * @return generated type builder which is the same builder as input
175 * parameter. The getter methods (representing child nodes) could be
178 static GeneratedTypeBuilder resolveDataSchemaNodes(final Module module, final String basePackageName,
179 final GeneratedTypeBuilder parent, final GeneratedTypeBuilder childOf,
180 final Iterable<DataSchemaNode> schemaNodes, final Map<Module, ModuleContext> genCtx,
181 final SchemaContext schemaContext, final boolean verboseClassComments,
182 final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders) {
184 if (schemaNodes != null && parent != null) {
185 for (final DataSchemaNode schemaNode : schemaNodes) {
186 if (!schemaNode.isAugmenting() && !schemaNode.isAddedByUses()) {
187 addSchemaNodeToBuilderAsMethod(basePackageName, schemaNode, parent, childOf, module, genCtx,
188 schemaContext, verboseClassComments, genTypeBuilders);
195 static GeneratedTypeBuilder addDefaultInterfaceDefinition(final String packageName, final SchemaNode
196 schemaNode, final Module module, final Map<Module, ModuleContext> genCtx, final SchemaContext schemaContext,
197 final boolean verboseClassComments, final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders) {
198 return addDefaultInterfaceDefinition(packageName, schemaNode, null, module, genCtx, schemaContext,
199 verboseClassComments, genTypeBuilders);
202 static Map<Module, ModuleContext> processUsesAugments(final SchemaContext schemaContext, final
203 DataNodeContainer node, final Module module, Map<Module, ModuleContext> genCtx, final Map<String,
204 Map<String, GeneratedTypeBuilder>> genTypeBuilders, final boolean verboseClassComments) {
205 final String basePackageName = BindingMapping.getRootPackageName(module);
206 for (final UsesNode usesNode : node.getUses()) {
207 for (final AugmentationSchema augment : usesNode.getAugmentations()) {
208 genCtx = AugmentToGenType.usesAugmentationToGenTypes(schemaContext, basePackageName, augment, module,
210 node, genCtx, genTypeBuilders, verboseClassComments);
211 genCtx = processUsesAugments(schemaContext, augment, module, genCtx, genTypeBuilders, verboseClassComments);
217 static GeneratedTypeBuilder findChildNodeByPath(final SchemaPath path, final Map<Module, ModuleContext> genCtx) {
218 for (final ModuleContext ctx : genCtx.values()) {
219 final GeneratedTypeBuilder result = ctx.getChildNode(path);
220 if (result != null) {
227 static GeneratedTypeBuilder findCaseByPath(final SchemaPath path, final Map<Module, ModuleContext> genCtx) {
228 for (final ModuleContext ctx : genCtx.values()) {
229 final GeneratedTypeBuilder result = ctx.getCase(path);
230 if (result != null) {
238 * Returns a generated type builder for an augmentation.
240 * The name of the type builder is equal to the name of augmented node with
241 * serial number as suffix.
245 * @param augmentPackageName
246 * string with contains the package name to which the augment
248 * @param basePackageName
249 * string with the package name to which the augmented node
251 * @param targetTypeRef
254 * augmentation schema which contains data about the child nodes
255 * and uses of augment
256 * @return generated type builder for augment in genCtx
258 static Map<Module, ModuleContext> addRawAugmentGenTypeDefinition(final Module module, final String augmentPackageName,
259 final String basePackageName, final Type targetTypeRef, final AugmentationSchema augSchema,
260 final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders, final Map<Module, ModuleContext> genCtx) {
262 Map<String, GeneratedTypeBuilder> augmentBuilders = genTypeBuilders.get(augmentPackageName);
263 if (augmentBuilders == null) {
264 augmentBuilders = new HashMap<>();
265 genTypeBuilders.put(augmentPackageName, augmentBuilders);
267 final String augIdentifier = getAugmentIdentifier(augSchema.getUnknownSchemaNodes());
270 if (augIdentifier != null) {
271 augTypeName = BindingMapping.getClassName(augIdentifier);
273 augTypeName = augGenTypeName(augmentBuilders, targetTypeRef.getName());
276 GeneratedTypeBuilder augTypeBuilder = new GeneratedTypeBuilderImpl(augmentPackageName, augTypeName);
278 augTypeBuilder.addImplementsType(BindingTypes.TREE_NODE);
279 augTypeBuilder.addImplementsType(Types.augmentationTypeFor(targetTypeRef));
280 annotateDeprecatedIfNecessary(augSchema.getStatus(), augTypeBuilder);
281 augTypeBuilder = addImplementedInterfaceFromUses(augSchema, augTypeBuilder, genCtx);
283 augTypeBuilder = augSchemaNodeToMethods(module, basePackageName, augTypeBuilder, augTypeBuilder, augSchema
285 augmentBuilders.put(augTypeName, augTypeBuilder);
287 if(!augSchema.getChildNodes().isEmpty()) {
288 genCtx.get(module).addTypeToAugmentation(augTypeBuilder, augSchema);
291 genCtx.get(module).addAugmentType(augTypeBuilder);
296 * Adds the methods to <code>typeBuilder</code> what represents subnodes of
297 * node for which <code>typeBuilder</code> was created.
301 * @param basePackageName
302 * string contains the module package name
304 * generated type builder which represents any node. The subnodes
305 * of this node are added to the <code>typeBuilder</code> as
306 * methods. The subnode can be of type leaf, leaf-list, list,
311 * set of data schema nodes which are the children of the node
312 * for which <code>typeBuilder</code> was created
313 * @return generated type builder which is the same object as the input
314 * parameter <code>typeBuilder</code>. The getter method could be
317 private static GeneratedTypeBuilder augSchemaNodeToMethods(final Module module, final String basePackageName,
318 final GeneratedTypeBuilder typeBuilder, final GeneratedTypeBuilder childOf,
319 final Iterable<DataSchemaNode> schemaNodes) {
320 if (schemaNodes != null && typeBuilder != null) {
321 for (final DataSchemaNode schemaNode : schemaNodes) {
322 if (!schemaNode.isAugmenting()) {
323 //TODO: design decomposition and implement it
324 //addSchemaNodeToBuilderAsMethod(basePackageName, schemaNode, typeBuilder, childOf, module);
332 * Instantiates generated type builder with <code>packageName</code> and
333 * <code>schemaNode</code>.
335 * The new builder always implements
336 * {@link TreeNode TreeNode}.<br>
337 * If <code>schemaNode</code> is instance of GroupingDefinition it also
338 * implements {@link Augmentable
340 * If <code>schemaNode</code> is instance of
341 * {@link org.opendaylight.yangtools.yang.model.api.DataNodeContainer
342 * DataNodeContainer} it can also implement nodes which are specified in
346 * string with the name of the package to which
347 * <code>schemaNode</code> belongs.
349 * schema node for which is created generated type builder
351 * parent type (can be null)
352 * @param schemaContext schema context
353 * @return generated type builder <code>schemaNode</code>
355 private static GeneratedTypeBuilder addDefaultInterfaceDefinition(final String packageName, final SchemaNode
356 schemaNode, final Type parent, final Module module, final Map<Module, ModuleContext> genCtx,
357 final SchemaContext schemaContext, final boolean verboseClassComments, final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders) {
359 GeneratedTypeBuilder it = addRawInterfaceDefinition(packageName, schemaNode, schemaContext, "",
360 verboseClassComments, genTypeBuilders);
361 if (parent == null) {
362 it.addImplementsType(BindingTypes.TREE_NODE);
364 it.addImplementsType(BindingTypes.treeChildNode(parent));
366 if (!(schemaNode instanceof GroupingDefinition)) {
367 it.addImplementsType(BindingTypes.augmentable(it));
370 if (schemaNode instanceof DataNodeContainer) {
371 //TODO: design decomposition and implement it
372 //groupingsToGenTypes(module, ((DataNodeContainer) schemaNode).getGroupings());
373 it = addImplementedInterfaceFromUses((DataNodeContainer) schemaNode, it, genCtx);
380 * Returns reference to generated type builder for specified
381 * <code>schemaNode</code> with <code>packageName</code>.
383 * Firstly the generated type builder is searched in
384 * {@link BindingGeneratorImpl#genTypeBuilders genTypeBuilders}. If it isn't
385 * found it is created and added to <code>genTypeBuilders</code>.
388 * string with the package name to which returning generated type
391 * schema node which provide data about the schema node name
392 * @param schemaContext
394 * return type name prefix
395 * @return generated type builder for <code>schemaNode</code>
396 * @throws IllegalArgumentException
398 * <li>if <code>schemaNode</code> is null</li>
399 * <li>if <code>packageName</code> is null</li>
400 * <li>if QName of schema node is null</li>
401 * <li>if schemaNode name is null</li>
405 private static GeneratedTypeBuilder addRawInterfaceDefinition(final String packageName, final SchemaNode schemaNode,
406 final SchemaContext schemaContext, final String prefix, final boolean verboseClassComments,
407 final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders) {
409 Preconditions.checkArgument(schemaNode != null, "Data Schema Node cannot be NULL.");
410 Preconditions.checkArgument(packageName != null, "Package Name for Generated Type cannot be NULL.");
411 final String schemaNodeName = schemaNode.getQName().getLocalName();
412 Preconditions.checkArgument(schemaNodeName != null, "Local Name of QName for Data Schema Node cannot be NULL.");
415 if (prefix == null) {
416 genTypeName = BindingMapping
417 .getClassName(NonJavaCharsConverter.normalizeClassIdentifier(packageName, schemaNodeName));
419 genTypeName = prefix + BindingMapping
420 .getClassName(NonJavaCharsConverter.normalizeClassIdentifier(packageName, schemaNodeName));
423 final GeneratedTypeBuilderImpl newType = new GeneratedTypeBuilderImpl(packageName, genTypeName);
424 final Module module = SchemaContextUtil.findParentModule(schemaContext, schemaNode);
425 qNameConstant(newType, BindingMapping.QNAME_STATIC_FIELD_NAME, schemaNode.getQName());
426 newType.addComment(schemaNode.getDescription());
427 newType.setDescription(createDescription(schemaNode, newType.getFullyQualifiedName(), schemaContext, verboseClassComments));
428 newType.setReference(schemaNode.getReference());
429 newType.setSchemaPath((List<QName>) schemaNode.getPath().getPathFromRoot());
430 newType.setModuleName(module.getName());
432 if (!genTypeBuilders.containsKey(packageName)) {
433 final Map<String, GeneratedTypeBuilder> builders = new HashMap<>();
434 builders.put(genTypeName, newType);
435 genTypeBuilders.put(packageName, builders);
437 final Map<String, GeneratedTypeBuilder> builders = genTypeBuilders.get(packageName);
438 if (!builders.containsKey(genTypeName)) {
439 builders.put(genTypeName, newType);
446 private static void addSchemaNodeToBuilderAsMethod(final String basePackageName, final DataSchemaNode node,
447 final GeneratedTypeBuilder typeBuilder, final GeneratedTypeBuilder childOf, final Module module,
448 final Map<Module, ModuleContext> genCtx, final SchemaContext schemaContext, final boolean verboseClassComments,
449 final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders) {
450 //TODO: implement rest of schema nodes GTO building
451 if (node != null && typeBuilder != null) {
452 if (node instanceof ContainerSchemaNode) {
453 containerToGenType(module, basePackageName, typeBuilder, childOf, (ContainerSchemaNode) node,
454 schemaContext, verboseClassComments, genCtx, genTypeBuilders);
460 private static void containerToGenType(final Module module, final String basePackageName,
461 final GeneratedTypeBuilder parent, final GeneratedTypeBuilder childOf, final ContainerSchemaNode node,
462 final SchemaContext schemaContext, final boolean verboseClassComments, final Map<Module, ModuleContext> genCtx,
463 final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders) {
465 final GeneratedTypeBuilder genType = processDataSchemaNode(module, basePackageName, childOf, node,
466 schemaContext, verboseClassComments, genCtx, genTypeBuilders);
467 if (genType != null) {
468 constructGetter(parent, node.getQName().getLocalName(), node.getDescription(), genType, node.getStatus());
469 resolveDataSchemaNodes(module, basePackageName, genType, genType, node.getChildNodes(), genCtx,
470 schemaContext, verboseClassComments, genTypeBuilders);
474 private static GeneratedTypeBuilder processDataSchemaNode(final Module module, final String basePackageName,
475 final GeneratedTypeBuilder childOf, final DataSchemaNode node, final SchemaContext schemaContext,
476 final boolean verboseClassComments, final Map<Module, ModuleContext> genCtx, final Map<String, Map<String,
477 GeneratedTypeBuilder>> genTypeBuilders) {
479 if (node.isAugmenting() || node.isAddedByUses()) {
482 final String packageName = packageNameForGeneratedType(basePackageName, node.getPath(), BindingNamespaceType.Data);
483 final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(packageName, node, childOf, module,
484 genCtx, schemaContext, verboseClassComments, genTypeBuilders);
485 genType.addComment(node.getDescription());
486 annotateDeprecatedIfNecessary(node.getStatus(), genType);
487 genType.setDescription(createDescription(node, genType.getFullyQualifiedName(), schemaContext, verboseClassComments));
488 genType.setModuleName(module.getName());
489 genType.setReference(node.getReference());
490 genType.setSchemaPath((List) node.getPath().getPathFromRoot());
491 if (node instanceof DataNodeContainer) {
492 genCtx.get(module).addChildNodeType(node, genType);
493 //TODO: implement groupings to GTO building first
494 // groupingsToGenTypes(module, ((DataNodeContainer) node).getGroupings());
495 processUsesAugments(schemaContext, (DataNodeContainer) node, module, genCtx, genTypeBuilders,
496 verboseClassComments);