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 com.google.common.annotations.Beta;
12 import com.google.common.base.Optional;
13 import com.google.common.base.Preconditions;
14 import java.util.ArrayList;
15 import java.util.Collections;
16 import java.util.Comparator;
17 import java.util.Iterator;
18 import java.util.List;
20 import java.util.Map.Entry;
22 import java.util.stream.Collectors;
23 import org.opendaylight.mdsal.binding.javav2.generator.spi.TypeProvider;
24 import org.opendaylight.mdsal.binding.javav2.generator.util.BindingGeneratorUtil;
25 import org.opendaylight.mdsal.binding.javav2.model.api.Type;
26 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.GeneratedTypeBuilder;
27 import org.opendaylight.mdsal.binding.javav2.util.BindingMapping;
28 import org.opendaylight.yangtools.yang.common.QName;
29 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
30 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
31 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
33 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
36 import org.opendaylight.yangtools.yang.model.api.Module;
37 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
38 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
39 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
41 import org.opendaylight.yangtools.yang.model.api.UsesNode;
42 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
45 final class AugmentToGenType {
48 * Comparator based on augment target path.
50 private static final Comparator<AugmentationSchema> AUGMENT_COMP = (o1, o2) -> {
51 final Iterator<QName> thisIt = o1.getTargetPath().getPathFromRoot().iterator();
52 final Iterator<QName> otherIt = o2.getTargetPath().getPathFromRoot().iterator();
54 while (thisIt.hasNext()) {
55 if (!otherIt.hasNext()) {
59 final int comp = thisIt.next().compareTo(otherIt.next());
65 return otherIt.hasNext() ? -1 : 0;
68 private AugmentToGenType() {
69 throw new UnsupportedOperationException("Utility class");
73 * Converts all <b>augmentation</b> of the module to the list
74 * <code>Type</code> objects.
77 * module from which is obtained list of all augmentation objects
78 * to iterate over them
79 * @param schemaContext actual schema context
80 * @param typeProvider actual type provider instance
81 * @param genCtx generated input context
82 * @param genTypeBuilders auxiliary type builders map
83 * @param verboseClassComments verbosity switch
85 * @throws IllegalArgumentException
87 * <li>if the module is null</li>
88 * <li>if the name of module is null</li>
90 * @throws IllegalStateException
91 * if set of augmentations from module is null
93 static Map<Module, ModuleContext> generate(final Module module, final SchemaContext schemaContext,
94 final TypeProvider typeProvider, final Map<Module, ModuleContext> genCtx,
95 Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders, final boolean verboseClassComments) {
97 Preconditions.checkArgument(module != null, "Module reference cannot be NULL.");
98 Preconditions.checkArgument(module.getName() != null, "Module name cannot be NULL.");
99 Preconditions.checkState(module.getAugmentations() != null, "Augmentations Set cannot be NULL.");
101 final String basePackageName = BindingMapping.getRootPackageName(module);
102 final List<AugmentationSchema> augmentations = resolveAugmentations(module);
103 Map<Module, ModuleContext> resultCtx = genCtx;
105 //let's group augments by target path
106 Map<SchemaPath, List<AugmentationSchema>> augmentationsGrouped =
107 augmentations.stream().collect(Collectors.groupingBy(AugmentationSchema::getTargetPath));
109 //process child nodes of grouped augment entries
110 for (Entry<SchemaPath, List<AugmentationSchema>> schemaPathAugmentListEntry : augmentationsGrouped.entrySet()) {
111 resultCtx = augmentationToGenTypes(basePackageName, schemaPathAugmentListEntry, module, schemaContext,
112 verboseClassComments, resultCtx, genTypeBuilders, typeProvider);
114 for (AugmentationSchema augSchema : schemaPathAugmentListEntry.getValue()) {
115 GenHelperUtil.processUsesAugments(schemaContext, augSchema, module, genCtx,
116 genTypeBuilders, verboseClassComments, typeProvider);
125 * Returns list of <code>AugmentationSchema</code> objects. The objects are
126 * sorted according to the length of their target path from the shortest to
130 * module from which is obtained list of all augmentation objects
131 * @return list of sorted <code>AugmentationSchema</code> objects obtained
132 * from <code>module</code>
133 * @throws IllegalArgumentException
135 * @throws IllegalStateException
136 * if set of module augmentations is null
138 private static List<AugmentationSchema> resolveAugmentations(final Module module) {
139 Preconditions.checkArgument(module != null, "Module reference cannot be NULL.");
140 Preconditions.checkState(module.getAugmentations() != null, "Augmentations Set cannot be NULL.");
142 final Set<AugmentationSchema> augmentations = module.getAugmentations();
143 final List<AugmentationSchema> sortedAugmentations = new ArrayList<>(augmentations);
144 Collections.sort(sortedAugmentations, AUGMENT_COMP);
146 return sortedAugmentations;
150 * Converts <code>augSchema</code> to list of <code>Type</code> which
151 * contains generated type for augmentation. In addition there are also
152 * generated types for all containers, list and choices which are child of
153 * <code>augSchema</code> node or a generated types for cases are added if
154 * augmented node is choice.
156 * @param augmentPackageName
157 * string with the name of the package to which the augmentation
159 * @param schemaPathAugmentListEntry
160 * list of AugmentationSchema nodes grouped by target path
161 * @param module current module
162 * @param schemaContext actual schema context
163 * @param verboseClassComments verbosity switch
164 * @param genCtx generated input context
165 * @param genTypeBuilders auxiliary type builders map
166 * @param typeProvider actual type provider instance
167 * @throws IllegalArgumentException
168 * if <code>augmentPackageName</code> equals null
169 * @throws IllegalStateException
170 * if augment target path is null
171 * @return generated context
173 private static Map<Module, ModuleContext> augmentationToGenTypes(final String basePackageName,
174 final Entry<SchemaPath, List<AugmentationSchema>> schemaPathAugmentListEntry, final Module module,
175 final SchemaContext schemaContext, final boolean verboseClassComments,
176 Map<Module, ModuleContext> genCtx, Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders,
177 final TypeProvider typeProvider) {
179 final SchemaPath targetPath = schemaPathAugmentListEntry.getKey();
180 Preconditions.checkArgument(basePackageName != null, "Package Name cannot be NULL.");
181 Preconditions.checkState(targetPath != null,
182 "Augmentation Schema does not contain Target Path (Target Path is NULL).");
184 SchemaNode targetSchemaNode;
186 targetSchemaNode = SchemaContextUtil.findDataSchemaNode(schemaContext, targetPath);
187 if (targetSchemaNode instanceof DataSchemaNode && ((DataSchemaNode) targetSchemaNode).isAddedByUses()) {
188 if (targetSchemaNode instanceof DerivableSchemaNode) {
189 targetSchemaNode = ((DerivableSchemaNode) targetSchemaNode).getOriginal().orNull();
191 if (targetSchemaNode == null) {
192 throw new IllegalStateException("Failed to find target node from grouping in augmentation " +
193 schemaPathAugmentListEntry.getValue().get(0)
194 + " in module " + module.getName());
197 if (targetSchemaNode == null) {
198 throw new IllegalArgumentException("augment target not found: " + targetPath);
201 //TODO: loose this assignment afterwards #2 done
202 Map<Module, ModuleContext> generatedCtx = genCtx;
204 GeneratedTypeBuilder targetTypeBuilder = GenHelperUtil.findChildNodeByPath(targetSchemaNode.getPath(),
206 if (targetTypeBuilder == null) {
207 targetTypeBuilder = GenHelperUtil.findCaseByPath(targetSchemaNode.getPath(), generatedCtx);
209 if (targetTypeBuilder == null) {
210 throw new NullPointerException("Target type not yet generated: " + targetSchemaNode);
213 final String augmentNamespacePackageName =
214 BindingGeneratorUtil.packageNameForAugmentedGeneratedType(basePackageName, targetPath);
216 if (!(targetSchemaNode instanceof ChoiceSchemaNode)) {
217 generatedCtx = GenHelperUtil.addRawAugmentGenTypeDefinition(module, augmentNamespacePackageName,
218 targetTypeBuilder.toInstance(), schemaPathAugmentListEntry.getValue(), genTypeBuilders, generatedCtx,
219 schemaContext, verboseClassComments, typeProvider);
221 //TODO: #3 implement augmented choice cases scenario
226 static Map<Module, ModuleContext> usesAugmentationToGenTypes(final SchemaContext schemaContext,
227 final String augmentPackageName, final List<AugmentationSchema> schemaPathAugmentListEntry, final Module module,
228 final UsesNode usesNode, final DataNodeContainer usesNodeParent, Map<Module, ModuleContext> genCtx,
229 Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders, final boolean verboseClassComments,
230 final TypeProvider typeProvider) {
232 Preconditions.checkArgument(augmentPackageName != null, "Package Name cannot be NULL.");
234 final SchemaPath targetPath = schemaPathAugmentListEntry.get(0).getTargetPath();
235 final SchemaNode targetSchemaNode = findOriginalTargetFromGrouping(schemaContext, targetPath, usesNode);
236 if (targetSchemaNode == null) {
237 throw new IllegalArgumentException("augment target not found: " + targetPath);
240 GeneratedTypeBuilder targetTypeBuilder = GenHelperUtil.findChildNodeByPath(targetSchemaNode.getPath(),
242 if (targetTypeBuilder == null) {
243 targetTypeBuilder = GenHelperUtil.findCaseByPath(targetSchemaNode.getPath(), genCtx);
245 if (targetTypeBuilder == null) {
246 throw new NullPointerException("Target type not yet generated: " + targetSchemaNode);
249 if (!(targetSchemaNode instanceof ChoiceSchemaNode)) {
250 String packageName = augmentPackageName;
251 if (usesNodeParent instanceof SchemaNode) {
252 packageName = BindingGeneratorUtil.packageNameForAugmentedGeneratedType(augmentPackageName,
253 ((SchemaNode) usesNodeParent).getPath());
254 } else if (usesNodeParent instanceof AugmentationSchema) {
255 Type parentTypeBuilder = genCtx.get(module).getTargetToAugmentation()
256 .get(((AugmentationSchema) usesNodeParent).getTargetPath());
257 packageName = parentTypeBuilder.getFullyQualifiedName();
259 genCtx = GenHelperUtil.addRawAugmentGenTypeDefinition(module, packageName,
260 targetTypeBuilder.toInstance(), schemaPathAugmentListEntry, genTypeBuilders, genCtx,
261 schemaContext, verboseClassComments, typeProvider);
264 genCtx = generateTypesFromAugmentedChoiceCases(schemaContext, module, augmentPackageName,
265 targetTypeBuilder.toInstance(), (ChoiceSchemaNode) targetSchemaNode,
266 schemaPathAugmentListEntry.get(0).getChildNodes(),
267 usesNodeParent, genCtx, verboseClassComments, genTypeBuilders, typeProvider);
272 //TODO: delete this method eventually when uses-augments & augmented choice cases are implemented
274 public static Map<Module, ModuleContext> usesAugmentationToGenTypes(final SchemaContext schemaContext, final String
275 augmentPackageName, final AugmentationSchema augSchema, final Module module, final UsesNode usesNode, final DataNodeContainer
276 usesNodeParent, Map<Module, ModuleContext> genCtx,
277 Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders,
278 final boolean verboseClassComments, final TypeProvider typeProvider) {
280 Preconditions.checkArgument(augmentPackageName != null, "Package Name cannot be NULL.");
281 Preconditions.checkArgument(augSchema != null, "Augmentation Schema cannot be NULL.");
282 Preconditions.checkState(augSchema.getTargetPath() != null,
283 "Augmentation Schema does not contain Target Path (Target Path is NULL).");
285 final SchemaPath targetPath = augSchema.getTargetPath();
286 final SchemaNode targetSchemaNode = findOriginalTargetFromGrouping(schemaContext, targetPath, usesNode);
287 if (targetSchemaNode == null) {
288 throw new IllegalArgumentException("augment target not found: " + targetPath);
291 GeneratedTypeBuilder targetTypeBuilder = GenHelperUtil.findChildNodeByPath(targetSchemaNode.getPath(),
293 if (targetTypeBuilder == null) {
294 targetTypeBuilder = GenHelperUtil.findCaseByPath(targetSchemaNode.getPath(), genCtx);
296 if (targetTypeBuilder == null) {
297 throw new NullPointerException("Target type not yet generated: " + targetSchemaNode);
300 if (!(targetSchemaNode instanceof ChoiceSchemaNode)) {
301 String packageName = augmentPackageName;
302 if (usesNodeParent instanceof SchemaNode) {
303 packageName = BindingGeneratorUtil.packageNameForAugmentedGeneratedType(augmentPackageName,
304 ((SchemaNode) usesNodeParent).getPath());
305 } else if (usesNodeParent instanceof AugmentationSchema) {
306 Type parentTypeBuilder = genCtx.get(module).getTypeToAugmentation().inverse().get(usesNodeParent);
307 packageName = BindingGeneratorUtil.packageNameForAugmentedGeneratedType(
308 parentTypeBuilder.getPackageName(), (AugmentationSchema)usesNodeParent);
310 genCtx = GenHelperUtil.addRawAugmentGenTypeDefinition(module, packageName, augmentPackageName,
311 targetTypeBuilder.toInstance(), augSchema, genTypeBuilders, genCtx, schemaContext,
312 verboseClassComments, typeProvider);
315 genCtx = generateTypesFromAugmentedChoiceCases(schemaContext, module, augmentPackageName,
316 targetTypeBuilder.toInstance(), (ChoiceSchemaNode) targetSchemaNode, augSchema.getChildNodes(),
317 usesNodeParent, genCtx, verboseClassComments, genTypeBuilders, typeProvider);
323 * Convenient method to find node added by uses statement.
324 * @param schemaContext
327 * @param parentUsesNode
328 * parent of uses node
329 * @return node from its original location in grouping
331 private static DataSchemaNode findOriginalTargetFromGrouping(final SchemaContext schemaContext, final SchemaPath targetPath,
332 final UsesNode parentUsesNode) {
333 SchemaNode targetGrouping = null;
334 QName current = parentUsesNode.getGroupingPath().getPathFromRoot().iterator().next();
335 Module module = schemaContext.findModuleByNamespaceAndRevision(current.getNamespace(), current.getRevision());
336 if (module == null) {
337 throw new IllegalArgumentException("Fialed to find module for grouping in: " + parentUsesNode);
339 for (GroupingDefinition group : module.getGroupings()) {
340 if (group.getQName().equals(current)) {
341 targetGrouping = group;
347 if (targetGrouping == null) {
348 throw new IllegalArgumentException("Failed to generate code for augment in " + parentUsesNode);
351 final GroupingDefinition grouping = (GroupingDefinition) targetGrouping;
352 SchemaNode result = grouping;
353 for (final QName node : targetPath.getPathFromRoot()) {
354 if (result instanceof DataNodeContainer) {
355 final QName resultNode = QName.create(result.getQName().getModule(), node.getLocalName());
356 result = ((DataNodeContainer) result).getDataChildByName(resultNode);
357 } else if (result instanceof ChoiceSchemaNode) {
358 result = ((ChoiceSchemaNode) result).getCaseNodeByName(node.getLocalName());
361 if (result == null) {
365 if (result instanceof DerivableSchemaNode) {
366 DerivableSchemaNode castedResult = (DerivableSchemaNode) result;
367 Optional<? extends SchemaNode> originalNode = castedResult
369 if (castedResult.isAddedByUses() && originalNode.isPresent()) {
370 result = originalNode.get();
374 if (result instanceof DataSchemaNode) {
375 DataSchemaNode resultDataSchemaNode = (DataSchemaNode) result;
376 if (resultDataSchemaNode.isAddedByUses()) {
377 // The original node is required, but we have only the copy of
378 // the original node.
379 // Maybe this indicates a bug in Yang parser.
380 throw new IllegalStateException(
381 "Failed to generate code for augment in "
384 return resultDataSchemaNode;
387 throw new IllegalStateException(
388 "Target node of uses-augment statement must be DataSchemaNode. Failed to generate code for augment in "
394 * Generates list of generated types for all the cases of a choice which are
395 * added to the choice through the augment.
397 * @param schemaContext
400 * @param basePackageName
401 * string contains name of package to which augment belongs. If
402 * an augmented choice is from an other package (pcg1) than an
403 * augmenting choice (pcg2) then case's of the augmenting choice
404 * will belong to pcg2.
406 * Type which represents target choice
408 * node which represents target choice
409 * @param augmentedNodes
410 * set of choice case nodes for which is checked if are/aren't
411 * added to choice through augmentation
412 * @return list of generated types which represents augmented cases of
413 * choice <code>refChoiceType</code>
414 * @throws IllegalArgumentException
416 * <li>if <code>basePackageName</code> is null</li>
417 * <li>if <code>targetType</code> is null</li>
418 * <li>if <code>augmentedNodes</code> is null</li>
421 private static Map<Module, ModuleContext> generateTypesFromAugmentedChoiceCases(final SchemaContext schemaContext, final Module module,
422 final String basePackageName, final Type targetType, final ChoiceSchemaNode targetNode,
423 final Iterable<DataSchemaNode> augmentedNodes, final DataNodeContainer usesNodeParent,
424 Map<Module, ModuleContext> genCtx, final boolean verboseClassComments,
425 Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders, final TypeProvider typeProvider) {
426 Preconditions.checkArgument(basePackageName != null, "Base Package Name cannot be NULL.");
427 Preconditions.checkArgument(targetType != null, "Referenced Choice Type cannot be NULL.");
428 Preconditions.checkArgument(augmentedNodes != null, "Set of Choice Case Nodes cannot be NULL.");
430 for (final DataSchemaNode caseNode : augmentedNodes) {
431 if (caseNode != null) {
432 final String packageName = BindingGeneratorUtil.packageNameForGeneratedType(basePackageName,
433 caseNode.getPath(), null);
434 final GeneratedTypeBuilder caseTypeBuilder = GenHelperUtil.addDefaultInterfaceDefinition(packageName,
435 caseNode, module, genCtx, schemaContext, verboseClassComments, genTypeBuilders, typeProvider);
436 caseTypeBuilder.addImplementsType(targetType);
439 final SchemaPath nodeSp = targetNode.getPath();
440 parent = SchemaContextUtil.findDataSchemaNode(schemaContext, nodeSp.getParent());
442 GeneratedTypeBuilder childOfType = null;
443 if (parent instanceof Module) {
444 childOfType = genCtx.get(parent).getModuleNode();
445 } else if (parent instanceof ChoiceCaseNode) {
446 childOfType = GenHelperUtil.findCaseByPath(parent.getPath(), genCtx);
447 } else if (parent instanceof DataSchemaNode || parent instanceof NotificationDefinition) {
448 childOfType = GenHelperUtil.findChildNodeByPath(parent.getPath(), genCtx);
449 } else if (parent instanceof GroupingDefinition) {
450 childOfType = GenHelperUtil.findGroupingByPath(parent.getPath(), genCtx);
453 if (childOfType == null) {
454 throw new IllegalArgumentException("Failed to find parent type of choice " + targetNode);
457 ChoiceCaseNode node = null;
458 final String caseLocalName = caseNode.getQName().getLocalName();
459 if (caseNode instanceof ChoiceCaseNode) {
460 node = (ChoiceCaseNode) caseNode;
461 } else if (targetNode.getCaseNodeByName(caseLocalName) == null) {
462 final String targetNodeLocalName = targetNode.getQName().getLocalName();
463 for (DataSchemaNode dataSchemaNode : usesNodeParent.getChildNodes()) {
464 if (dataSchemaNode instanceof ChoiceSchemaNode && targetNodeLocalName.equals(dataSchemaNode.getQName
465 ().getLocalName())) {
466 node = ((ChoiceSchemaNode) dataSchemaNode).getCaseNodeByName(caseLocalName);
471 node = targetNode.getCaseNodeByName(caseLocalName);
473 final Iterable<DataSchemaNode> childNodes = node.getChildNodes();
474 if (childNodes != null) {
475 GenHelperUtil.resolveDataSchemaNodes(module, basePackageName, caseTypeBuilder, childOfType,
476 childNodes, genCtx, schemaContext, verboseClassComments, genTypeBuilders, typeProvider);
478 genCtx.get(module).addCaseType(caseNode.getPath(), caseTypeBuilder);
479 genCtx.get(module).addChoiceToCaseMapping(targetType, caseTypeBuilder, node);