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.GenHelperUtil.processUsesImplements;
13 import com.google.common.annotations.Beta;
14 import com.google.common.base.Optional;
15 import com.google.common.base.Preconditions;
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.Comparator;
19 import java.util.Iterator;
20 import java.util.List;
22 import java.util.Map.Entry;
24 import java.util.stream.Collectors;
25 import org.opendaylight.mdsal.binding.javav2.generator.context.ModuleContext;
26 import org.opendaylight.mdsal.binding.javav2.generator.spi.TypeProvider;
27 import org.opendaylight.mdsal.binding.javav2.generator.util.BindingGeneratorUtil;
28 import org.opendaylight.mdsal.binding.javav2.model.api.Type;
29 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.GeneratedTypeBuilder;
30 import org.opendaylight.mdsal.binding.javav2.spec.runtime.BindingNamespaceType;
31 import org.opendaylight.mdsal.binding.javav2.util.BindingMapping;
32 import org.opendaylight.yangtools.yang.common.QName;
33 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
34 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
35 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
37 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
39 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
40 import org.opendaylight.yangtools.yang.model.api.Module;
41 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
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 final class AugmentToGenType {
52 * Comparator based on augment target path.
54 private static final Comparator<AugmentationSchema> AUGMENT_COMP = (o1, o2) -> {
55 final Iterator<QName> thisIt = o1.getTargetPath().getPathFromRoot().iterator();
56 final Iterator<QName> otherIt = o2.getTargetPath().getPathFromRoot().iterator();
58 while (thisIt.hasNext()) {
59 if (!otherIt.hasNext()) {
63 final int comp = thisIt.next().compareTo(otherIt.next());
69 return otherIt.hasNext() ? -1 : 0;
73 * Comparator based on augment target path.
75 private static final Comparator<Map.Entry<SchemaPath, List<AugmentationSchema>>> AUGMENTS_COMP = (o1, o2) -> {
76 final Iterator<QName> thisIt = o1.getKey().getPathFromRoot().iterator();
77 final Iterator<QName> otherIt = o2.getKey().getPathFromRoot().iterator();
79 while (thisIt.hasNext()) {
80 if (!otherIt.hasNext()) {
84 final int comp = thisIt.next().compareTo(otherIt.next());
90 return otherIt.hasNext() ? -1 : 0;
93 private AugmentToGenType() {
94 throw new UnsupportedOperationException("Utility class");
98 * Converts all <b>augmentation</b> of the module to the list
99 * <code>Type</code> objects.
102 * module from which is obtained list of all augmentation objects
103 * to iterate over them
104 * @param schemaContext actual schema context
105 * @param typeProvider actual type provider instance
106 * @param genCtx generated input context
107 * @param genTypeBuilders auxiliary type builders map
108 * @param verboseClassComments verbosity switch
110 * @throws IllegalArgumentException
112 * <li>if the module is null</li>
113 * <li>if the name of module is null</li>
115 * @throws IllegalStateException
116 * if set of augmentations from module is null
118 static Map<Module, ModuleContext> generate(final Module module, final SchemaContext schemaContext,
119 final TypeProvider typeProvider, final Map<Module, ModuleContext> genCtx,
120 Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders, final boolean verboseClassComments) {
122 Preconditions.checkArgument(module != null, "Module reference cannot be NULL.");
123 Preconditions.checkArgument(module.getName() != null, "Module name cannot be NULL.");
124 Preconditions.checkState(module.getAugmentations() != null, "Augmentations Set cannot be NULL.");
126 final String basePackageName = BindingMapping.getRootPackageName(module);
127 final List<AugmentationSchema> augmentations = resolveAugmentations(module, schemaContext);
128 Map<Module, ModuleContext> resultCtx = genCtx;
130 //let's group augments by target path
131 Map<SchemaPath, List<AugmentationSchema>> augmentationsGrouped =
132 augmentations.stream().collect(Collectors.groupingBy(AugmentationSchema::getTargetPath));
134 List<Map.Entry<SchemaPath, List<AugmentationSchema>>> sortedAugmentationsGrouped =
135 new ArrayList<>(augmentationsGrouped.entrySet());
136 Collections.sort(sortedAugmentationsGrouped, AUGMENTS_COMP);
138 //process child nodes of grouped augment entries
139 for (Map.Entry<SchemaPath, List<AugmentationSchema>> schemaPathAugmentListEntry : sortedAugmentationsGrouped) {
140 resultCtx = augmentationToGenTypes(basePackageName, schemaPathAugmentListEntry, module, schemaContext,
141 verboseClassComments, resultCtx, genTypeBuilders, typeProvider);
143 for (AugmentationSchema augSchema : schemaPathAugmentListEntry.getValue()) {
144 processUsesImplements(augSchema, module, schemaContext, genCtx, BindingNamespaceType.Data);
153 * Returns list of <code>AugmentationSchema</code> objects. The objects are
154 * sorted according to the length of their target path from the shortest to
158 * module from which is obtained list of all augmentation objects
159 * @return list of sorted <code>AugmentationSchema</code> objects obtained
160 * from <code>module</code>
161 * @throws IllegalArgumentException
163 * @throws IllegalStateException
164 * if set of module augmentations is null
166 private static List<AugmentationSchema> resolveAugmentations(final Module module, final SchemaContext schemaContext) {
167 Preconditions.checkArgument(module != null, "Module reference cannot be NULL.");
168 Preconditions.checkState(module.getAugmentations() != null, "Augmentations Set cannot be NULL.");
170 final Set<AugmentationSchema> augmentations = module.getAugmentations();
171 final List<AugmentationSchema> sortedAugmentations = new ArrayList<>(augmentations).stream()
172 .filter(aug -> !module.equals(findAugmentTargetModule(schemaContext, aug)))
173 .collect(Collectors.toList());
174 Collections.sort(sortedAugmentations, AUGMENT_COMP);
176 return sortedAugmentations;
179 public static Module findAugmentTargetModule(final SchemaContext schemaContext , final AugmentationSchema aug) {
180 Preconditions.checkNotNull(aug, "Augmentation schema can not be null.");
181 final QName first = aug.getTargetPath().getPathFromRoot().iterator().next();
182 return schemaContext.findModuleByNamespaceAndRevision(first.getNamespace(), first.getRevision());
186 * Converts <code>augSchema</code> to list of <code>Type</code> which
187 * contains generated type for augmentation. In addition there are also
188 * generated types for all containers, list and choices which are child of
189 * <code>augSchema</code> node or a generated types for cases are added if
190 * augmented node is choice.
192 * @param basePackageName
193 * string with the name of the package to which the augmentation
195 * @param schemaPathAugmentListEntry
196 * list of AugmentationSchema nodes grouped by target path
197 * @param module current module
198 * @param schemaContext actual schema context
199 * @param verboseClassComments verbosity switch
200 * @param genCtx generated input context
201 * @param genTypeBuilders auxiliary type builders map
202 * @param typeProvider actual type provider instance
203 * @throws IllegalArgumentException
204 * if <code>augmentPackageName</code> equals null
205 * @throws IllegalStateException
206 * if augment target path is null
207 * @return generated context
209 private static Map<Module, ModuleContext> augmentationToGenTypes(final String basePackageName,
210 final Entry<SchemaPath, List<AugmentationSchema>> schemaPathAugmentListEntry, final Module module,
211 final SchemaContext schemaContext, final boolean verboseClassComments,
212 Map<Module, ModuleContext> genCtx, Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders,
213 final TypeProvider typeProvider) {
214 Preconditions.checkArgument(basePackageName != null, "Package Name cannot be NULL.");
215 Preconditions.checkArgument(schemaPathAugmentListEntry != null, "Augmentation List Entry cannot be NULL.");
216 final SchemaPath targetPath = schemaPathAugmentListEntry.getKey();
217 Preconditions.checkState(targetPath != null,
218 "Augmentation List Entry does not contain Target Path (Target Path is NULL).");
220 final List<AugmentationSchema> augmentationSchemaList = schemaPathAugmentListEntry.getValue();
221 Preconditions.checkState(augmentationSchemaList.size() > 0,
222 "Augmentation List cannot be empty.");
224 SchemaNode targetSchemaNode = SchemaContextUtil.findDataSchemaNode(schemaContext, targetPath);
225 if (targetSchemaNode instanceof DataSchemaNode && ((DataSchemaNode) targetSchemaNode).isAddedByUses()) {
226 if (targetSchemaNode instanceof DerivableSchemaNode) {
227 targetSchemaNode = ((DerivableSchemaNode) targetSchemaNode).getOriginal().orNull();
229 if (targetSchemaNode == null) {
230 throw new IllegalStateException("Failed to find target node from grouping in augmentation " +
231 schemaPathAugmentListEntry.getValue().get(0)
232 + " in module " + module.getName());
235 if (targetSchemaNode == null) {
236 throw new IllegalArgumentException("augment target not found: " + targetPath);
239 GeneratedTypeBuilder targetTypeBuilder = GenHelperUtil.findChildNodeByPath(targetSchemaNode.getPath(),
241 if (targetTypeBuilder == null) {
242 targetTypeBuilder = GenHelperUtil.findCaseByPath(targetSchemaNode.getPath(), genCtx);
244 if (targetTypeBuilder == null) {
245 throw new NullPointerException("Target type not yet generated: " + targetSchemaNode);
248 final String augmentPackageName =
249 BindingGeneratorUtil.packageNameWithNamespacePrefix(basePackageName, BindingNamespaceType.Data);
251 if (!(targetSchemaNode instanceof ChoiceSchemaNode)) {
252 genCtx = GenHelperUtil.addRawAugmentGenTypeDefinition(module, augmentPackageName,
253 targetTypeBuilder.toInstance(), targetSchemaNode, schemaPathAugmentListEntry.getValue(), genTypeBuilders, genCtx,
254 schemaContext, verboseClassComments, typeProvider, BindingNamespaceType.Data);
256 genCtx = generateTypesFromAugmentedChoiceCases(schemaContext, module, basePackageName,
257 targetTypeBuilder.toInstance(), (ChoiceSchemaNode) targetSchemaNode,
258 schemaPathAugmentListEntry.getValue(),
259 null, genCtx, verboseClassComments, genTypeBuilders, typeProvider,
260 BindingNamespaceType.Data);
266 static Map<Module, ModuleContext> usesAugmentationToGenTypes(final SchemaContext schemaContext,
267 final String augmentPackageName, final List<AugmentationSchema> schemaPathAugmentListEntry, final Module module,
268 final UsesNode usesNode, final DataNodeContainer usesNodeParent, Map<Module, ModuleContext> genCtx,
269 Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders, final boolean verboseClassComments,
270 final TypeProvider typeProvider, final BindingNamespaceType namespaceType) {
272 Preconditions.checkArgument(augmentPackageName != null, "Package Name cannot be NULL.");
273 Preconditions.checkArgument(schemaPathAugmentListEntry != null,
274 "Augmentation Schema List Entry cannot be NULL.");
275 Preconditions.checkState(schemaPathAugmentListEntry.size() > 0,
276 "Augmentation Schema List cannot be empty");
278 final SchemaPath targetPath = schemaPathAugmentListEntry.get(0).getTargetPath();
279 Preconditions.checkState(targetPath != null,
280 "Augmentation Schema does not contain Target Path (Target Path is NULL).");
282 final SchemaNode targetSchemaNode = findOriginalTargetFromGrouping(schemaContext, targetPath, usesNode);
283 if (targetSchemaNode == null) {
284 throw new IllegalArgumentException("augment target not found: " + targetPath);
287 GeneratedTypeBuilder targetTypeBuilder = GenHelperUtil.findChildNodeByPath(targetSchemaNode.getPath(),
289 if (targetTypeBuilder == null) {
290 targetTypeBuilder = GenHelperUtil.findCaseByPath(targetSchemaNode.getPath(), genCtx);
292 if (targetTypeBuilder == null) {
293 throw new NullPointerException("Target type not yet generated: " + targetSchemaNode);
296 if (!(targetSchemaNode instanceof ChoiceSchemaNode)) {
297 String packageName = augmentPackageName;
298 if (usesNodeParent instanceof SchemaNode) {
299 packageName = BindingGeneratorUtil.packageNameForAugmentedGeneratedType(augmentPackageName,
300 ((SchemaNode) usesNodeParent).getPath());
301 } else if (usesNodeParent instanceof AugmentationSchema) {
302 Type parentTypeBuilder = genCtx.get(module).getTargetToAugmentation()
303 .get(((AugmentationSchema) usesNodeParent).getTargetPath());
304 packageName = BindingGeneratorUtil.packageNameForAugmentedGeneratedType(parentTypeBuilder.getPackageName(),
305 (AugmentationSchema)usesNodeParent);
307 genCtx = GenHelperUtil.addRawAugmentGenTypeDefinition(module, packageName,
308 targetTypeBuilder.toInstance(), targetSchemaNode, schemaPathAugmentListEntry, genTypeBuilders, genCtx,
309 schemaContext, verboseClassComments, typeProvider, namespaceType);
312 genCtx = generateTypesFromAugmentedChoiceCases(schemaContext, module, augmentPackageName,
313 targetTypeBuilder.toInstance(), (ChoiceSchemaNode) targetSchemaNode,
314 schemaPathAugmentListEntry,
315 usesNodeParent, genCtx, verboseClassComments, genTypeBuilders, typeProvider, namespaceType);
321 * Convenient method to find node added by uses statement.
322 * @param schemaContext
325 * @param parentUsesNode
326 * parent of uses node
327 * @return node from its original location in grouping
329 private static DataSchemaNode findOriginalTargetFromGrouping(final SchemaContext schemaContext, final SchemaPath targetPath,
330 final UsesNode parentUsesNode) {
331 SchemaNode targetGrouping = null;
332 QName current = parentUsesNode.getGroupingPath().getPathFromRoot().iterator().next();
333 Module module = schemaContext.findModuleByNamespaceAndRevision(current.getNamespace(), current.getRevision());
334 if (module == null) {
335 throw new IllegalArgumentException("Fialed to find module for grouping in: " + parentUsesNode);
337 for (GroupingDefinition group : module.getGroupings()) {
338 if (group.getQName().equals(current)) {
339 targetGrouping = group;
345 if (targetGrouping == null) {
346 throw new IllegalArgumentException("Failed to generate code for augment in " + parentUsesNode);
349 SchemaNode result = targetGrouping;
350 for (final QName node : targetPath.getPathFromRoot()) {
351 if (result instanceof DataNodeContainer) {
352 final QName resultNode = QName.create(result.getQName().getModule(), node.getLocalName());
353 result = ((DataNodeContainer) result).getDataChildByName(resultNode);
354 } else if (result instanceof ChoiceSchemaNode) {
355 result = ((ChoiceSchemaNode) result).getCaseNodeByName(node.getLocalName());
358 if (result == null) {
362 if (result instanceof DerivableSchemaNode) {
363 DerivableSchemaNode castedResult = (DerivableSchemaNode) result;
364 Optional<? extends SchemaNode> originalNode = castedResult
366 if (castedResult.isAddedByUses() && originalNode.isPresent()) {
367 result = originalNode.get();
371 if (result instanceof DataSchemaNode) {
372 DataSchemaNode resultDataSchemaNode = (DataSchemaNode) result;
373 if (resultDataSchemaNode.isAddedByUses()) {
374 // The original node is required, but we have only the copy of
375 // the original node.
376 // Maybe this indicates a bug in Yang parser.
377 throw new IllegalStateException(
378 "Failed to generate code for augment in "
381 return resultDataSchemaNode;
384 throw new IllegalStateException(
385 "Target node of uses-augment statement must be DataSchemaNode. Failed to generate code for augment in "
391 * Generates list of generated types for all the cases of a choice which are
392 * added to the choice through the augment.
394 * @param schemaContext
397 * @param basePackageName
398 * string contains name of package to which augment belongs. If
399 * an augmented choice is from an other package (pcg1) than an
400 * augmenting choice (pcg2) then case's of the augmenting choice
401 * will belong to pcg2.
403 * Type which represents target choice
405 * node which represents target choice
406 * @param schemaPathAugmentListEntry
407 * list of AugmentationSchema nodes grouped by target path
408 * @return list of generated types which represents augmented cases of
409 * choice <code>refChoiceType</code>
410 * @throws IllegalArgumentException
412 * <li>if <code>basePackageName</code> is null</li>
413 * <li>if <code>targetType</code> is null</li>
414 * <li>if <code>augmentedNodes</code> is null</li>
417 private static Map<Module, ModuleContext> generateTypesFromAugmentedChoiceCases(
418 final SchemaContext schemaContext, final Module module,
419 final String basePackageName, final Type targetType, final ChoiceSchemaNode targetNode,
420 final List<AugmentationSchema> schemaPathAugmentListEntry,
421 final DataNodeContainer usesNodeParent,
422 Map<Module, ModuleContext> genCtx, final boolean verboseClassComments,
423 Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders, final TypeProvider typeProvider,
424 final BindingNamespaceType namespaceType) {
425 Preconditions.checkArgument(basePackageName != null, "Base Package Name cannot be NULL.");
426 Preconditions.checkArgument(targetType != null, "Referenced Choice Type cannot be NULL.");
427 Preconditions.checkArgument(schemaPathAugmentListEntry != null, "Set of Choice Case Nodes cannot be NULL.");
430 for (final AugmentationSchema augmentationSchema : schemaPathAugmentListEntry) {
431 for (final DataSchemaNode caseNode : augmentationSchema.getChildNodes()) {
432 if (caseNode != null) {
433 final GeneratedTypeBuilder caseTypeBuilder = GenHelperUtil.addDefaultInterfaceDefinition(basePackageName,
434 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 processUsesImplements(node, module, schemaContext, genCtx, namespaceType);
480 genCtx.get(module).addCaseType(caseNode.getPath(), caseTypeBuilder);
481 genCtx.get(module).addChoiceToCaseMapping(targetType, caseTypeBuilder, node);