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;
21 import org.opendaylight.mdsal.binding.javav2.generator.spi.TypeProvider;
22 import org.opendaylight.mdsal.binding.javav2.generator.util.BindingGeneratorUtil;
23 import org.opendaylight.mdsal.binding.javav2.generator.util.ReferencedTypeImpl;
24 import org.opendaylight.mdsal.binding.javav2.model.api.Type;
25 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.GeneratedTypeBuilder;
26 import org.opendaylight.mdsal.binding.javav2.util.BindingMapping;
27 import org.opendaylight.yangtools.yang.common.QName;
28 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
29 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
30 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
32 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
35 import org.opendaylight.yangtools.yang.model.api.Module;
36 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
37 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
38 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
39 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
40 import org.opendaylight.yangtools.yang.model.api.UsesNode;
41 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
44 final class AugmentToGenType {
47 * Comparator based on augment target path.
49 private static final Comparator<AugmentationSchema> AUGMENT_COMP = (o1, o2) -> {
50 final Iterator<QName> thisIt = o1.getTargetPath().getPathFromRoot().iterator();
51 final Iterator<QName> otherIt = o2.getTargetPath().getPathFromRoot().iterator();
53 while (thisIt.hasNext()) {
54 if (!otherIt.hasNext()) {
58 final int comp = thisIt.next().compareTo(otherIt.next());
64 return otherIt.hasNext() ? -1 : 0;
67 private AugmentToGenType() {
68 throw new UnsupportedOperationException("Utility class");
72 * Converts all <b>augmentation</b> of the module to the list
73 * <code>Type</code> objects.
76 * module from which is obtained list of all augmentation objects
77 * to iterate over them
78 * @param schemaContext
80 * @param genTypeBuilders
82 * @throws IllegalArgumentException
84 * <li>if the module is null</li>
85 * <li>if the name of module is null</li>
87 * @throws IllegalStateException
88 * if set of augmentations from module is null
90 static Map<Module, ModuleContext> generate(final Module module, final SchemaContext schemaContext,
91 final TypeProvider typeProvider, final Map<Module, ModuleContext> genCtx,
92 Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders, final boolean verboseClassComments) {
94 Preconditions.checkArgument(module != null, "Module reference cannot be NULL.");
95 Preconditions.checkArgument(module.getName() != null, "Module name cannot be NULL.");
96 Preconditions.checkState(module.getAugmentations() != null, "Augmentations Set cannot be NULL.");
98 final String basePackageName = BindingMapping.getRootPackageName(module);
99 final List<AugmentationSchema> augmentations = resolveAugmentations(module);
100 Map<Module, ModuleContext> resultCtx = genCtx;
101 for (final AugmentationSchema augment : augmentations) {
102 resultCtx = augmentationToGenTypes(basePackageName, augment, module, schemaContext, verboseClassComments,
103 resultCtx, genTypeBuilders, typeProvider);
109 * Returns list of <code>AugmentationSchema</code> objects. The objects are
110 * sorted according to the length of their target path from the shortest to
114 * module from which is obtained list of all augmentation objects
115 * @return list of sorted <code>AugmentationSchema</code> objects obtained
116 * from <code>module</code>
117 * @throws IllegalArgumentException
119 * @throws IllegalStateException
120 * if set of module augmentations is null
122 private static List<AugmentationSchema> resolveAugmentations(final Module module) {
123 Preconditions.checkArgument(module != null, "Module reference cannot be NULL.");
124 Preconditions.checkState(module.getAugmentations() != null, "Augmentations Set cannot be NULL.");
126 final Set<AugmentationSchema> augmentations = module.getAugmentations();
127 final List<AugmentationSchema> sortedAugmentations = new ArrayList<>(augmentations);
128 Collections.sort(sortedAugmentations, AUGMENT_COMP);
130 return sortedAugmentations;
134 * Converts <code>augSchema</code> to list of <code>Type</code> which
135 * contains generated type for augmentation. In addition there are also
136 * generated types for all containers, list and choices which are child of
137 * <code>augSchema</code> node or a generated types for cases are added if
138 * augmented node is choice.
140 * @param augmentPackageName
141 * string with the name of the package to which the augmentation
144 * AugmentationSchema which is contains data about augmentation
145 * (target path, childs...)
148 * @param schemaContext
150 * @param genTypeBuilders
151 * @throws IllegalArgumentException
153 * <li>if <code>augmentPackageName</code> equals null</li>
154 * <li>if <code>augSchema</code> equals null</li>
156 * @throws IllegalStateException
157 * if augment target path is null
160 private static Map<Module, ModuleContext> augmentationToGenTypes(final String augmentPackageName, final AugmentationSchema augSchema,
161 final Module module, final SchemaContext schemaContext, final boolean verboseClassComments,
162 Map<Module, ModuleContext> genCtx, Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders,
163 final TypeProvider typeProvider) {
165 Map<Module, ModuleContext> generatedCtx;
166 Preconditions.checkArgument(augmentPackageName != null, "Package Name cannot be NULL.");
167 Preconditions.checkArgument(augSchema != null, "Augmentation Schema cannot be NULL.");
168 Preconditions.checkState(augSchema.getTargetPath() != null,
169 "Augmentation Schema does not contain Target Path (Target Path is NULL).");
171 generatedCtx = GenHelperUtil.processUsesAugments(schemaContext, augSchema, module, genCtx, genTypeBuilders,
172 verboseClassComments, typeProvider);
173 final SchemaPath targetPath = augSchema.getTargetPath();
174 SchemaNode targetSchemaNode;
176 targetSchemaNode = SchemaContextUtil.findDataSchemaNode(schemaContext, targetPath);
177 if (targetSchemaNode instanceof DataSchemaNode && ((DataSchemaNode) targetSchemaNode).isAddedByUses()) {
178 if (targetSchemaNode instanceof DerivableSchemaNode) {
179 targetSchemaNode = ((DerivableSchemaNode) targetSchemaNode).getOriginal().orNull();
181 if (targetSchemaNode == null) {
182 throw new IllegalStateException("Failed to find target node from grouping in augmentation " + augSchema
183 + " in module " + module.getName());
186 if (targetSchemaNode == null) {
187 throw new IllegalArgumentException("augment target not found: " + targetPath);
190 GeneratedTypeBuilder targetTypeBuilder = GenHelperUtil.findChildNodeByPath(targetSchemaNode.getPath(),
192 if (targetTypeBuilder == null) {
193 targetTypeBuilder = GenHelperUtil.findCaseByPath(targetSchemaNode.getPath(), generatedCtx);
195 if (targetTypeBuilder == null) {
196 throw new NullPointerException("Target type not yet generated: " + targetSchemaNode);
199 if (!(targetSchemaNode instanceof ChoiceSchemaNode)) {
200 final String packageName = augmentPackageName;
201 final Type targetType = new ReferencedTypeImpl(targetTypeBuilder.getPackageName(),
202 targetTypeBuilder.getName());
203 generatedCtx = GenHelperUtil.addRawAugmentGenTypeDefinition(module, packageName, augmentPackageName, targetType,
204 augSchema, genTypeBuilders, generatedCtx, schemaContext, verboseClassComments, typeProvider);
208 generatedCtx = generateTypesFromAugmentedChoiceCases(schemaContext, module, augmentPackageName,
209 targetTypeBuilder.toInstance(), (ChoiceSchemaNode) targetSchemaNode, augSchema.getChildNodes(),
210 null, generatedCtx, verboseClassComments, genTypeBuilders, typeProvider);
215 public static Map<Module, ModuleContext> usesAugmentationToGenTypes(final SchemaContext schemaContext, final String
216 augmentPackageName, final AugmentationSchema augSchema, final Module module, final UsesNode usesNode, final DataNodeContainer
217 usesNodeParent, Map<Module, ModuleContext> genCtx,
218 Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders,
219 final boolean verboseClassComments, final TypeProvider typeProvider) {
221 Map<Module, ModuleContext> generatedCtx;
222 Preconditions.checkArgument(augmentPackageName != null, "Package Name cannot be NULL.");
223 Preconditions.checkArgument(augSchema != null, "Augmentation Schema cannot be NULL.");
224 Preconditions.checkState(augSchema.getTargetPath() != null,
225 "Augmentation Schema does not contain Target Path (Target Path is NULL).");
227 generatedCtx = GenHelperUtil.processUsesAugments(schemaContext, augSchema, module, genCtx, genTypeBuilders,
228 verboseClassComments, typeProvider);
229 final SchemaPath targetPath = augSchema.getTargetPath();
230 final SchemaNode targetSchemaNode = findOriginalTargetFromGrouping(schemaContext, targetPath, usesNode);
231 if (targetSchemaNode == null) {
232 throw new IllegalArgumentException("augment target not found: " + targetPath);
235 GeneratedTypeBuilder targetTypeBuilder = GenHelperUtil.findChildNodeByPath(targetSchemaNode.getPath(),
237 if (targetTypeBuilder == null) {
238 targetTypeBuilder = GenHelperUtil.findCaseByPath(targetSchemaNode.getPath(), generatedCtx);
240 if (targetTypeBuilder == null) {
241 throw new NullPointerException("Target type not yet generated: " + targetSchemaNode);
244 if (!(targetSchemaNode instanceof ChoiceSchemaNode)) {
245 String packageName = augmentPackageName;
246 if (usesNodeParent instanceof SchemaNode) {
247 packageName = BindingGeneratorUtil.packageNameForAugmentedGeneratedType(augmentPackageName,
248 ((SchemaNode) usesNodeParent).getPath());
250 generatedCtx = GenHelperUtil.addRawAugmentGenTypeDefinition(module, packageName, augmentPackageName,
251 targetTypeBuilder.toInstance(), augSchema, genTypeBuilders, generatedCtx, schemaContext,
252 verboseClassComments, typeProvider);
255 generatedCtx = generateTypesFromAugmentedChoiceCases(schemaContext, module, augmentPackageName,
256 targetTypeBuilder.toInstance(), (ChoiceSchemaNode) targetSchemaNode, augSchema.getChildNodes(),
257 usesNodeParent, generatedCtx, verboseClassComments, genTypeBuilders, typeProvider);
263 * Convenient method to find node added by uses statement.
264 * @param schemaContext
267 * @param parentUsesNode
268 * parent of uses node
269 * @return node from its original location in grouping
271 private static DataSchemaNode findOriginalTargetFromGrouping(final SchemaContext schemaContext, final SchemaPath targetPath,
272 final UsesNode parentUsesNode) {
273 SchemaNode targetGrouping = null;
274 QName current = parentUsesNode.getGroupingPath().getPathFromRoot().iterator().next();
275 Module module = schemaContext.findModuleByNamespaceAndRevision(current.getNamespace(), current.getRevision());
276 if (module == null) {
277 throw new IllegalArgumentException("Fialed to find module for grouping in: " + parentUsesNode);
279 for (GroupingDefinition group : module.getGroupings()) {
280 if (group.getQName().equals(current)) {
281 targetGrouping = group;
287 if (targetGrouping == null) {
288 throw new IllegalArgumentException("Failed to generate code for augment in " + parentUsesNode);
291 final GroupingDefinition grouping = (GroupingDefinition) targetGrouping;
292 SchemaNode result = grouping;
293 for (final QName node : targetPath.getPathFromRoot()) {
294 if (result instanceof DataNodeContainer) {
295 final QName resultNode = QName.create(result.getQName().getModule(), node.getLocalName());
296 result = ((DataNodeContainer) result).getDataChildByName(resultNode);
297 } else if (result instanceof ChoiceSchemaNode) {
298 result = ((ChoiceSchemaNode) result).getCaseNodeByName(node.getLocalName());
301 if (result == null) {
305 if (result instanceof DerivableSchemaNode) {
306 DerivableSchemaNode castedResult = (DerivableSchemaNode) result;
307 Optional<? extends SchemaNode> originalNode = castedResult
309 if (castedResult.isAddedByUses() && originalNode.isPresent()) {
310 result = originalNode.get();
314 if (result instanceof DataSchemaNode) {
315 DataSchemaNode resultDataSchemaNode = (DataSchemaNode) result;
316 if (resultDataSchemaNode.isAddedByUses()) {
317 // The original node is required, but we have only the copy of
318 // the original node.
319 // Maybe this indicates a bug in Yang parser.
320 throw new IllegalStateException(
321 "Failed to generate code for augment in "
324 return resultDataSchemaNode;
327 throw new IllegalStateException(
328 "Target node of uses-augment statement must be DataSchemaNode. Failed to generate code for augment in "
334 * Generates list of generated types for all the cases of a choice which are
335 * added to the choice through the augment.
337 * @param schemaContext
340 * @param basePackageName
341 * string contains name of package to which augment belongs. If
342 * an augmented choice is from an other package (pcg1) than an
343 * augmenting choice (pcg2) then case's of the augmenting choice
344 * will belong to pcg2.
346 * Type which represents target choice
348 * node which represents target choice
349 * @param augmentedNodes
350 * set of choice case nodes for which is checked if are/aren't
351 * added to choice through augmentation
352 * @return list of generated types which represents augmented cases of
353 * choice <code>refChoiceType</code>
354 * @throws IllegalArgumentException
356 * <li>if <code>basePackageName</code> is null</li>
357 * <li>if <code>targetType</code> is null</li>
358 * <li>if <code>augmentedNodes</code> is null</li>
361 private static Map<Module, ModuleContext> generateTypesFromAugmentedChoiceCases(final SchemaContext schemaContext, final Module module,
362 final String basePackageName, final Type targetType, final ChoiceSchemaNode targetNode,
363 final Iterable<DataSchemaNode> augmentedNodes, final DataNodeContainer usesNodeParent,
364 Map<Module, ModuleContext> genCtx, final boolean verboseClassComments,
365 Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders, final TypeProvider typeProvider) {
366 Preconditions.checkArgument(basePackageName != null, "Base Package Name cannot be NULL.");
367 Preconditions.checkArgument(targetType != null, "Referenced Choice Type cannot be NULL.");
368 Preconditions.checkArgument(augmentedNodes != null, "Set of Choice Case Nodes cannot be NULL.");
370 for (final DataSchemaNode caseNode : augmentedNodes) {
371 if (caseNode != null) {
372 final String packageName = BindingGeneratorUtil.packageNameForGeneratedType(basePackageName,
373 caseNode.getPath(), null);
374 final GeneratedTypeBuilder caseTypeBuilder = GenHelperUtil.addDefaultInterfaceDefinition(packageName,
375 caseNode, module, genCtx, schemaContext, verboseClassComments, genTypeBuilders, typeProvider);
376 caseTypeBuilder.addImplementsType(targetType);
379 final SchemaPath nodeSp = targetNode.getPath();
380 parent = SchemaContextUtil.findDataSchemaNode(schemaContext, nodeSp.getParent());
382 GeneratedTypeBuilder childOfType = null;
383 if (parent instanceof Module) {
384 childOfType = genCtx.get(parent).getModuleNode();
385 } else if (parent instanceof ChoiceCaseNode) {
386 childOfType = GenHelperUtil.findCaseByPath(parent.getPath(), genCtx);
387 } else if (parent instanceof DataSchemaNode || parent instanceof NotificationDefinition) {
388 childOfType = GenHelperUtil.findChildNodeByPath(parent.getPath(), genCtx);
389 } else if (parent instanceof GroupingDefinition) {
390 childOfType = GenHelperUtil.findGroupingByPath(parent.getPath(), genCtx);
393 if (childOfType == null) {
394 throw new IllegalArgumentException("Failed to find parent type of choice " + targetNode);
397 ChoiceCaseNode node = null;
398 final String caseLocalName = caseNode.getQName().getLocalName();
399 if (caseNode instanceof ChoiceCaseNode) {
400 node = (ChoiceCaseNode) caseNode;
401 } else if (targetNode.getCaseNodeByName(caseLocalName) == null) {
402 final String targetNodeLocalName = targetNode.getQName().getLocalName();
403 for (DataSchemaNode dataSchemaNode : usesNodeParent.getChildNodes()) {
404 if (dataSchemaNode instanceof ChoiceSchemaNode && targetNodeLocalName.equals(dataSchemaNode.getQName
405 ().getLocalName())) {
406 node = ((ChoiceSchemaNode) dataSchemaNode).getCaseNodeByName(caseLocalName);
411 node = targetNode.getCaseNodeByName(caseLocalName);
413 final Iterable<DataSchemaNode> childNodes = node.getChildNodes();
414 if (childNodes != null) {
415 GenHelperUtil.resolveDataSchemaNodes(module, basePackageName, caseTypeBuilder, childOfType,
416 childNodes, genCtx, schemaContext, verboseClassComments, genTypeBuilders, typeProvider);
418 genCtx.get(module).addCaseType(caseNode.getPath(), caseTypeBuilder);
419 genCtx.get(module).addChoiceToCaseMapping(targetType, caseTypeBuilder, node);