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.spec.runtime.BindingNamespaceType;
28 import org.opendaylight.mdsal.binding.javav2.util.BindingMapping;
29 import org.opendaylight.yangtools.yang.common.QName;
30 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
31 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
32 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
34 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
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.SchemaContext;
40 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
42 import org.opendaylight.yangtools.yang.model.api.UsesNode;
43 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
46 final class AugmentToGenType {
49 * Comparator based on augment target path.
51 private static final Comparator<AugmentationSchema> AUGMENT_COMP = (o1, o2) -> {
52 final Iterator<QName> thisIt = o1.getTargetPath().getPathFromRoot().iterator();
53 final Iterator<QName> otherIt = o2.getTargetPath().getPathFromRoot().iterator();
55 while (thisIt.hasNext()) {
56 if (!otherIt.hasNext()) {
60 final int comp = thisIt.next().compareTo(otherIt.next());
66 return otherIt.hasNext() ? -1 : 0;
70 * Comparator based on augment target path.
72 private static final Comparator<Map.Entry<SchemaPath, List<AugmentationSchema>>> AUGMENTS_COMP = (o1, o2) -> {
73 final Iterator<QName> thisIt = o1.getKey().getPathFromRoot().iterator();
74 final Iterator<QName> otherIt = o2.getKey().getPathFromRoot().iterator();
76 while (thisIt.hasNext()) {
77 if (!otherIt.hasNext()) {
81 final int comp = thisIt.next().compareTo(otherIt.next());
87 return otherIt.hasNext() ? -1 : 0;
90 private AugmentToGenType() {
91 throw new UnsupportedOperationException("Utility class");
95 * Converts all <b>augmentation</b> of the module to the list
96 * <code>Type</code> objects.
99 * module from which is obtained list of all augmentation objects
100 * to iterate over them
101 * @param schemaContext actual schema context
102 * @param typeProvider actual type provider instance
103 * @param genCtx generated input context
104 * @param genTypeBuilders auxiliary type builders map
105 * @param verboseClassComments verbosity switch
107 * @throws IllegalArgumentException
109 * <li>if the module is null</li>
110 * <li>if the name of module is null</li>
112 * @throws IllegalStateException
113 * if set of augmentations from module is null
115 static Map<Module, ModuleContext> generate(final Module module, final SchemaContext schemaContext,
116 final TypeProvider typeProvider, final Map<Module, ModuleContext> genCtx,
117 Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders, final boolean verboseClassComments) {
119 Preconditions.checkArgument(module != null, "Module reference cannot be NULL.");
120 Preconditions.checkArgument(module.getName() != null, "Module name cannot be NULL.");
121 Preconditions.checkState(module.getAugmentations() != null, "Augmentations Set cannot be NULL.");
123 final String basePackageName = BindingMapping.getRootPackageName(module);
124 final List<AugmentationSchema> augmentations = resolveAugmentations(module);
125 Map<Module, ModuleContext> resultCtx = genCtx;
127 //let's group augments by target path
128 Map<SchemaPath, List<AugmentationSchema>> augmentationsGrouped =
129 augmentations.stream().collect(Collectors.groupingBy(AugmentationSchema::getTargetPath));
131 List<Map.Entry<SchemaPath, List<AugmentationSchema>>> sortedAugmentationsGrouped =
132 new ArrayList<>(augmentationsGrouped.entrySet());
133 Collections.sort(sortedAugmentationsGrouped, AUGMENTS_COMP);
135 //process child nodes of grouped augment entries
136 for (Map.Entry<SchemaPath, List<AugmentationSchema>> schemaPathAugmentListEntry : sortedAugmentationsGrouped) {
137 resultCtx = augmentationToGenTypes(basePackageName, schemaPathAugmentListEntry, module, schemaContext,
138 verboseClassComments, resultCtx, genTypeBuilders, typeProvider);
140 for (AugmentationSchema augSchema : schemaPathAugmentListEntry.getValue()) {
141 GenHelperUtil.processUsesAugments(schemaContext, augSchema, module, genCtx,
142 genTypeBuilders, verboseClassComments, typeProvider, BindingNamespaceType.Data);
151 * Returns list of <code>AugmentationSchema</code> objects. The objects are
152 * sorted according to the length of their target path from the shortest to
156 * module from which is obtained list of all augmentation objects
157 * @return list of sorted <code>AugmentationSchema</code> objects obtained
158 * from <code>module</code>
159 * @throws IllegalArgumentException
161 * @throws IllegalStateException
162 * if set of module augmentations is null
164 private static List<AugmentationSchema> resolveAugmentations(final Module module) {
165 Preconditions.checkArgument(module != null, "Module reference cannot be NULL.");
166 Preconditions.checkState(module.getAugmentations() != null, "Augmentations Set cannot be NULL.");
168 final Set<AugmentationSchema> augmentations = module.getAugmentations();
169 final List<AugmentationSchema> sortedAugmentations = new ArrayList<>(augmentations);
170 Collections.sort(sortedAugmentations, AUGMENT_COMP);
172 return sortedAugmentations;
176 * Converts <code>augSchema</code> to list of <code>Type</code> which
177 * contains generated type for augmentation. In addition there are also
178 * generated types for all containers, list and choices which are child of
179 * <code>augSchema</code> node or a generated types for cases are added if
180 * augmented node is choice.
182 * @param basePackageName
183 * string with the name of the package to which the augmentation
185 * @param schemaPathAugmentListEntry
186 * list of AugmentationSchema nodes grouped by target path
187 * @param module current module
188 * @param schemaContext actual schema context
189 * @param verboseClassComments verbosity switch
190 * @param genCtx generated input context
191 * @param genTypeBuilders auxiliary type builders map
192 * @param typeProvider actual type provider instance
193 * @throws IllegalArgumentException
194 * if <code>augmentPackageName</code> equals null
195 * @throws IllegalStateException
196 * if augment target path is null
197 * @return generated context
199 private static Map<Module, ModuleContext> augmentationToGenTypes(final String basePackageName,
200 final Entry<SchemaPath, List<AugmentationSchema>> schemaPathAugmentListEntry, final Module module,
201 final SchemaContext schemaContext, final boolean verboseClassComments,
202 Map<Module, ModuleContext> genCtx, Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders,
203 final TypeProvider typeProvider) {
204 Preconditions.checkArgument(basePackageName != null, "Package Name cannot be NULL.");
205 Preconditions.checkArgument(schemaPathAugmentListEntry != null, "Augmentation List Entry cannot be NULL.");
206 final SchemaPath targetPath = schemaPathAugmentListEntry.getKey();
207 Preconditions.checkState(targetPath != null,
208 "Augmentation List Entry does not contain Target Path (Target Path is NULL).");
210 final List<AugmentationSchema> augmentationSchemaList = schemaPathAugmentListEntry.getValue();
211 Preconditions.checkState(augmentationSchemaList.size() > 0,
212 "Augmentation List cannot be empty.");
214 SchemaNode targetSchemaNode = SchemaContextUtil.findDataSchemaNode(schemaContext, targetPath);
215 if (targetSchemaNode instanceof DataSchemaNode && ((DataSchemaNode) targetSchemaNode).isAddedByUses()) {
216 if (targetSchemaNode instanceof DerivableSchemaNode) {
217 targetSchemaNode = ((DerivableSchemaNode) targetSchemaNode).getOriginal().orNull();
219 if (targetSchemaNode == null) {
220 throw new IllegalStateException("Failed to find target node from grouping in augmentation " +
221 schemaPathAugmentListEntry.getValue().get(0)
222 + " in module " + module.getName());
225 if (targetSchemaNode == null) {
226 throw new IllegalArgumentException("augment target not found: " + targetPath);
229 GeneratedTypeBuilder targetTypeBuilder = GenHelperUtil.findChildNodeByPath(targetSchemaNode.getPath(),
231 if (targetTypeBuilder == null) {
232 targetTypeBuilder = GenHelperUtil.findCaseByPath(targetSchemaNode.getPath(), genCtx);
234 if (targetTypeBuilder == null) {
235 throw new NullPointerException("Target type not yet generated: " + targetSchemaNode);
238 final String augmentNamespacePackageName =
239 BindingGeneratorUtil.packageNameForAugmentedGeneratedType(basePackageName, targetPath);
241 if (!(targetSchemaNode instanceof ChoiceSchemaNode)) {
242 genCtx = GenHelperUtil.addRawAugmentGenTypeDefinition(module, augmentNamespacePackageName,
243 targetTypeBuilder.toInstance(), schemaPathAugmentListEntry.getValue(), genTypeBuilders, genCtx,
244 schemaContext, verboseClassComments, typeProvider, BindingNamespaceType.Data);
246 genCtx = generateTypesFromAugmentedChoiceCases(schemaContext, module, basePackageName,
247 targetTypeBuilder.toInstance(), (ChoiceSchemaNode) targetSchemaNode,
248 schemaPathAugmentListEntry.getValue(),
249 null, genCtx, verboseClassComments, genTypeBuilders, typeProvider,
250 BindingNamespaceType.Data);
255 static Map<Module, ModuleContext> usesAugmentationToGenTypes(final SchemaContext schemaContext,
256 final String augmentPackageName, final List<AugmentationSchema> schemaPathAugmentListEntry, final Module module,
257 final UsesNode usesNode, final DataNodeContainer usesNodeParent, Map<Module, ModuleContext> genCtx,
258 Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders, final boolean verboseClassComments,
259 final TypeProvider typeProvider, final BindingNamespaceType namespaceType) {
261 Preconditions.checkArgument(augmentPackageName != null, "Package Name cannot be NULL.");
262 Preconditions.checkArgument(schemaPathAugmentListEntry != null,
263 "Augmentation Schema List Entry cannot be NULL.");
264 Preconditions.checkState(schemaPathAugmentListEntry.size() > 0,
265 "Augmentation Schema List cannot be empty");
267 final SchemaPath targetPath = schemaPathAugmentListEntry.get(0).getTargetPath();
268 Preconditions.checkState(targetPath != null,
269 "Augmentation Schema does not contain Target Path (Target Path is NULL).");
271 final SchemaNode targetSchemaNode = findOriginalTargetFromGrouping(schemaContext, targetPath, usesNode);
272 if (targetSchemaNode == null) {
273 throw new IllegalArgumentException("augment target not found: " + targetPath);
276 GeneratedTypeBuilder targetTypeBuilder = GenHelperUtil.findChildNodeByPath(targetSchemaNode.getPath(),
278 if (targetTypeBuilder == null) {
279 targetTypeBuilder = GenHelperUtil.findCaseByPath(targetSchemaNode.getPath(), genCtx);
281 if (targetTypeBuilder == null) {
282 throw new NullPointerException("Target type not yet generated: " + targetSchemaNode);
285 if (!(targetSchemaNode instanceof ChoiceSchemaNode)) {
286 String packageName = augmentPackageName;
287 if (usesNodeParent instanceof SchemaNode) {
288 packageName = BindingGeneratorUtil.packageNameForAugmentedGeneratedType(augmentPackageName,
289 ((SchemaNode) usesNodeParent).getPath());
290 } else if (usesNodeParent instanceof AugmentationSchema) {
291 Type parentTypeBuilder = genCtx.get(module).getTargetToAugmentation()
292 .get(((AugmentationSchema) usesNodeParent).getTargetPath());
293 packageName = BindingGeneratorUtil.packageNameForAugmentedGeneratedType(parentTypeBuilder.getPackageName(),
294 (AugmentationSchema)usesNodeParent);
296 genCtx = GenHelperUtil.addRawAugmentGenTypeDefinition(module, packageName,
297 targetTypeBuilder.toInstance(), schemaPathAugmentListEntry, genTypeBuilders, genCtx,
298 schemaContext, verboseClassComments, typeProvider, namespaceType);
301 genCtx = generateTypesFromAugmentedChoiceCases(schemaContext, module, augmentPackageName,
302 targetTypeBuilder.toInstance(), (ChoiceSchemaNode) targetSchemaNode,
303 schemaPathAugmentListEntry,
304 usesNodeParent, genCtx, verboseClassComments, genTypeBuilders, typeProvider, namespaceType);
310 * Convenient method to find node added by uses statement.
311 * @param schemaContext
314 * @param parentUsesNode
315 * parent of uses node
316 * @return node from its original location in grouping
318 private static DataSchemaNode findOriginalTargetFromGrouping(final SchemaContext schemaContext, final SchemaPath targetPath,
319 final UsesNode parentUsesNode) {
320 SchemaNode targetGrouping = null;
321 QName current = parentUsesNode.getGroupingPath().getPathFromRoot().iterator().next();
322 Module module = schemaContext.findModuleByNamespaceAndRevision(current.getNamespace(), current.getRevision());
323 if (module == null) {
324 throw new IllegalArgumentException("Fialed to find module for grouping in: " + parentUsesNode);
326 for (GroupingDefinition group : module.getGroupings()) {
327 if (group.getQName().equals(current)) {
328 targetGrouping = group;
334 if (targetGrouping == null) {
335 throw new IllegalArgumentException("Failed to generate code for augment in " + parentUsesNode);
338 SchemaNode result = targetGrouping;
339 for (final QName node : targetPath.getPathFromRoot()) {
340 if (result instanceof DataNodeContainer) {
341 final QName resultNode = QName.create(result.getQName().getModule(), node.getLocalName());
342 result = ((DataNodeContainer) result).getDataChildByName(resultNode);
343 } else if (result instanceof ChoiceSchemaNode) {
344 result = ((ChoiceSchemaNode) result).getCaseNodeByName(node.getLocalName());
347 if (result == null) {
351 if (result instanceof DerivableSchemaNode) {
352 DerivableSchemaNode castedResult = (DerivableSchemaNode) result;
353 Optional<? extends SchemaNode> originalNode = castedResult
355 if (castedResult.isAddedByUses() && originalNode.isPresent()) {
356 result = originalNode.get();
360 if (result instanceof DataSchemaNode) {
361 DataSchemaNode resultDataSchemaNode = (DataSchemaNode) result;
362 if (resultDataSchemaNode.isAddedByUses()) {
363 // The original node is required, but we have only the copy of
364 // the original node.
365 // Maybe this indicates a bug in Yang parser.
366 throw new IllegalStateException(
367 "Failed to generate code for augment in "
370 return resultDataSchemaNode;
373 throw new IllegalStateException(
374 "Target node of uses-augment statement must be DataSchemaNode. Failed to generate code for augment in "
380 * Generates list of generated types for all the cases of a choice which are
381 * added to the choice through the augment.
383 * @param schemaContext
386 * @param basePackageName
387 * string contains name of package to which augment belongs. If
388 * an augmented choice is from an other package (pcg1) than an
389 * augmenting choice (pcg2) then case's of the augmenting choice
390 * will belong to pcg2.
392 * Type which represents target choice
394 * node which represents target choice
395 * @param schemaPathAugmentListEntry
396 * list of AugmentationSchema nodes grouped by target path
397 * @return list of generated types which represents augmented cases of
398 * choice <code>refChoiceType</code>
399 * @throws IllegalArgumentException
401 * <li>if <code>basePackageName</code> is null</li>
402 * <li>if <code>targetType</code> is null</li>
403 * <li>if <code>augmentedNodes</code> is null</li>
406 private static Map<Module, ModuleContext> generateTypesFromAugmentedChoiceCases(
407 final SchemaContext schemaContext, final Module module,
408 final String basePackageName, final Type targetType, final ChoiceSchemaNode targetNode,
409 final List<AugmentationSchema> schemaPathAugmentListEntry,
410 final DataNodeContainer usesNodeParent,
411 Map<Module, ModuleContext> genCtx, final boolean verboseClassComments,
412 Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders, final TypeProvider typeProvider,
413 final BindingNamespaceType namespaceType) {
414 Preconditions.checkArgument(basePackageName != null, "Base Package Name cannot be NULL.");
415 Preconditions.checkArgument(targetType != null, "Referenced Choice Type cannot be NULL.");
416 Preconditions.checkArgument(schemaPathAugmentListEntry != null, "Set of Choice Case Nodes cannot be NULL.");
419 for (final AugmentationSchema augmentationSchema : schemaPathAugmentListEntry) {
420 for (final DataSchemaNode caseNode : augmentationSchema.getChildNodes()) {
421 if (caseNode != null) {
422 final String packageName = BindingGeneratorUtil.packageNameForGeneratedType(basePackageName,
423 caseNode.getPath(), BindingNamespaceType.Data);
424 final GeneratedTypeBuilder caseTypeBuilder = GenHelperUtil.addDefaultInterfaceDefinition(packageName,
425 caseNode, module, genCtx, schemaContext, verboseClassComments, genTypeBuilders, typeProvider,
427 caseTypeBuilder.addImplementsType(targetType);
430 final SchemaPath nodeSp = targetNode.getPath();
431 parent = SchemaContextUtil.findDataSchemaNode(schemaContext, nodeSp.getParent());
433 GeneratedTypeBuilder childOfType = null;
434 if (parent instanceof Module) {
435 childOfType = genCtx.get(parent).getModuleNode();
436 } else if (parent instanceof ChoiceCaseNode) {
437 childOfType = GenHelperUtil.findCaseByPath(parent.getPath(), genCtx);
438 } else if (parent instanceof DataSchemaNode || parent instanceof NotificationDefinition) {
439 childOfType = GenHelperUtil.findChildNodeByPath(parent.getPath(), genCtx);
440 } else if (parent instanceof GroupingDefinition) {
441 childOfType = GenHelperUtil.findGroupingByPath(parent.getPath(), genCtx);
444 if (childOfType == null) {
445 throw new IllegalArgumentException("Failed to find parent type of choice " + targetNode);
448 ChoiceCaseNode node = null;
449 final String caseLocalName = caseNode.getQName().getLocalName();
450 if (caseNode instanceof ChoiceCaseNode) {
451 node = (ChoiceCaseNode) caseNode;
452 } else if (targetNode.getCaseNodeByName(caseLocalName) == null) {
453 final String targetNodeLocalName = targetNode.getQName().getLocalName();
454 for (DataSchemaNode dataSchemaNode : usesNodeParent.getChildNodes()) {
455 if (dataSchemaNode instanceof ChoiceSchemaNode && targetNodeLocalName.equals(dataSchemaNode.getQName
456 ().getLocalName())) {
457 node = ((ChoiceSchemaNode) dataSchemaNode).getCaseNodeByName(caseLocalName);
462 node = targetNode.getCaseNodeByName(caseLocalName);
464 final Iterable<DataSchemaNode> childNodes = node.getChildNodes();
465 if (childNodes != null) {
466 GenHelperUtil.resolveDataSchemaNodes(module, basePackageName, caseTypeBuilder, childOfType,
467 childNodes, genCtx, schemaContext, verboseClassComments, genTypeBuilders, typeProvider,
470 genCtx.get(module).addCaseType(caseNode.getPath(), caseTypeBuilder);
471 genCtx.get(module).addChoiceToCaseMapping(targetType, caseTypeBuilder, node);