2 * Copyright (c) 2016 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.binding2.generator.impl;
11 import static com.google.common.base.Preconditions.checkArgument;
12 import static com.google.common.base.Preconditions.checkState;
13 import static org.opendaylight.mdsal.binding2.generator.impl.GenHelperUtil.findCaseByPath;
14 import static org.opendaylight.mdsal.binding2.generator.impl.GenHelperUtil.findChildNodeByPath;
15 import static org.opendaylight.mdsal.binding2.generator.impl.GenHelperUtil.processUsesAugments;
16 import static org.opendaylight.mdsal.binding2.generator.impl.GenHelperUtil.resolveDataSchemaNodes;
17 import static org.opendaylight.mdsal.binding2.generator.util.Binding2GeneratorUtil.packageNameForAugmentedGeneratedType;
18 import static org.opendaylight.mdsal.binding2.generator.util.Binding2GeneratorUtil.packageNameForGeneratedType;
19 import static org.opendaylight.mdsal.binding2.generator.util.Binding2Mapping.getRootPackageName;
20 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findDataSchemaNode;
22 import com.google.common.annotations.Beta;
23 import com.google.common.base.Optional;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.Comparator;
27 import java.util.Iterator;
28 import java.util.List;
31 import org.opendaylight.mdsal.binding2.generator.util.ReferencedTypeImpl;
32 import org.opendaylight.mdsal.binding2.model.api.Type;
33 import org.opendaylight.mdsal.binding2.model.api.type.builder.GeneratedTypeBuilder;
34 import org.opendaylight.yangtools.yang.common.QName;
35 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
36 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
37 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
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.DerivableSchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
42 import org.opendaylight.yangtools.yang.model.api.Module;
43 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
44 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
45 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
46 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
47 import org.opendaylight.yangtools.yang.model.api.UsesNode;
48 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
51 final class AugmentToGenType {
53 private AugmentToGenType() {
54 throw new UnsupportedOperationException("Utility class");
58 * Comparator based on augment target path.
60 private static final Comparator<AugmentationSchema> AUGMENT_COMP = (o1, o2) -> {
61 final Iterator<QName> thisIt = o1.getTargetPath().getPathFromRoot().iterator();
62 final Iterator<QName> otherIt = o2.getTargetPath().getPathFromRoot().iterator();
64 while (thisIt.hasNext()) {
65 if (!otherIt.hasNext()) {
69 final int comp = thisIt.next().compareTo(otherIt.next());
75 return otherIt.hasNext() ? -1 : 0;
79 * Converts all <b>augmentation</b> of the module to the list
80 * <code>Type</code> objects.
83 * module from which is obtained list of all augmentation objects
84 * to iterate over them
85 * @param schemaContext
87 * @param genTypeBuilders
89 * @throws IllegalArgumentException
91 * <li>if the module is null</li>
92 * <li>if the name of module is null</li>
94 * @throws IllegalStateException
95 * if set of augmentations from module is null
97 static Map<Module, ModuleContext> generate(final Module module, final SchemaContext schemaContext, Map<Module,
98 ModuleContext> genCtx, Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders,
99 final boolean verboseClassComments) {
101 checkArgument(module != null, "Module reference cannot be NULL.");
102 checkArgument(module.getName() != null, "Module name cannot be NULL.");
103 checkState(module.getAugmentations() != null, "Augmentations Set cannot be NULL.");
105 final String basePackageName = getRootPackageName(module);
106 final List<AugmentationSchema> augmentations = resolveAugmentations(module);
107 for (final AugmentationSchema augment : augmentations) {
108 genCtx = augmentationToGenTypes(basePackageName, augment, module, schemaContext, verboseClassComments,
109 genCtx, genTypeBuilders);
115 * Returns list of <code>AugmentationSchema</code> objects. The objects are
116 * sorted according to the length of their target path from the shortest to
120 * module from which is obtained list of all augmentation objects
121 * @return list of sorted <code>AugmentationSchema</code> objects obtained
122 * from <code>module</code>
123 * @throws IllegalArgumentException
125 * @throws IllegalStateException
126 * if set of module augmentations is null
128 private static List<AugmentationSchema> resolveAugmentations(final Module module) {
129 checkArgument(module != null, "Module reference cannot be NULL.");
130 checkState(module.getAugmentations() != null, "Augmentations Set cannot be NULL.");
132 final Set<AugmentationSchema> augmentations = module.getAugmentations();
133 final List<AugmentationSchema> sortedAugmentations = new ArrayList<>(augmentations);
134 Collections.sort(sortedAugmentations, AUGMENT_COMP);
136 return sortedAugmentations;
140 * Converts <code>augSchema</code> to list of <code>Type</code> which
141 * contains generated type for augmentation. In addition there are also
142 * generated types for all containers, list and choices which are child of
143 * <code>augSchema</code> node or a generated types for cases are added if
144 * augmented node is choice.
146 * @param augmentPackageName
147 * string with the name of the package to which the augmentation
150 * AugmentationSchema which is contains data about augmentation
151 * (target path, childs...)
154 * @param schemaContext
156 * @param genTypeBuilders
157 * @throws IllegalArgumentException
159 * <li>if <code>augmentPackageName</code> equals null</li>
160 * <li>if <code>augSchema</code> equals null</li>
162 * @throws IllegalStateException
163 * if augment target path is null
166 private static Map<Module, ModuleContext> augmentationToGenTypes(final String augmentPackageName, final AugmentationSchema augSchema,
167 final Module module, final SchemaContext schemaContext, final boolean verboseClassComments,
168 Map<Module, ModuleContext> genCtx, Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders) {
170 Map<Module, ModuleContext> generatedCtx;
171 checkArgument(augmentPackageName != null, "Package Name cannot be NULL.");
172 checkArgument(augSchema != null, "Augmentation Schema cannot be NULL.");
173 checkState(augSchema.getTargetPath() != null,
174 "Augmentation Schema does not contain Target Path (Target Path is NULL).");
176 generatedCtx = processUsesAugments(schemaContext, augSchema, module, genCtx, genTypeBuilders, verboseClassComments);
177 final SchemaPath targetPath = augSchema.getTargetPath();
178 SchemaNode targetSchemaNode;
180 targetSchemaNode = findDataSchemaNode(schemaContext, targetPath);
181 if (targetSchemaNode instanceof DataSchemaNode && ((DataSchemaNode) targetSchemaNode).isAddedByUses()) {
182 if (targetSchemaNode instanceof DerivableSchemaNode) {
183 targetSchemaNode = ((DerivableSchemaNode) targetSchemaNode).getOriginal().orNull();
185 if (targetSchemaNode == null) {
186 throw new IllegalStateException("Failed to find target node from grouping in augmentation " + augSchema
187 + " in module " + module.getName());
190 if (targetSchemaNode == null) {
191 throw new IllegalArgumentException("augment target not found: " + targetPath);
194 GeneratedTypeBuilder targetTypeBuilder = findChildNodeByPath(targetSchemaNode.getPath(), generatedCtx);
195 if (targetTypeBuilder == null) {
196 targetTypeBuilder = findCaseByPath(targetSchemaNode.getPath(), generatedCtx);
198 if (targetTypeBuilder == null) {
199 throw new NullPointerException("Target type not yet generated: " + targetSchemaNode);
202 if (!(targetSchemaNode instanceof ChoiceSchemaNode)) {
203 final String packageName = augmentPackageName;
204 final Type targetType = new ReferencedTypeImpl(targetTypeBuilder.getPackageName(),
205 targetTypeBuilder.getName());
206 generatedCtx = GenHelperUtil.addRawAugmentGenTypeDefinition(module, packageName, augmentPackageName, targetType,
207 augSchema, genTypeBuilders, generatedCtx);
211 generatedCtx = generateTypesFromAugmentedChoiceCases(schemaContext, module, augmentPackageName,
212 targetTypeBuilder.toInstance(), (ChoiceSchemaNode) targetSchemaNode, augSchema.getChildNodes(),
213 null, generatedCtx, verboseClassComments, genTypeBuilders);
218 public static Map<Module, ModuleContext> usesAugmentationToGenTypes(final SchemaContext schemaContext, final String
219 augmentPackageName, final AugmentationSchema augSchema, final Module module, final UsesNode usesNode, final DataNodeContainer
220 usesNodeParent, Map<Module, ModuleContext> genCtx,
221 Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders,
222 final boolean verboseClassComments) {
224 Map<Module, ModuleContext> generatedCtx;
225 checkArgument(augmentPackageName != null, "Package Name cannot be NULL.");
226 checkArgument(augSchema != null, "Augmentation Schema cannot be NULL.");
227 checkState(augSchema.getTargetPath() != null,
228 "Augmentation Schema does not contain Target Path (Target Path is NULL).");
230 generatedCtx = processUsesAugments(schemaContext, augSchema, module, genCtx, genTypeBuilders, verboseClassComments);
231 final SchemaPath targetPath = augSchema.getTargetPath();
232 final SchemaNode targetSchemaNode = findOriginalTargetFromGrouping(schemaContext, targetPath, usesNode);
233 if (targetSchemaNode == null) {
234 throw new IllegalArgumentException("augment target not found: " + targetPath);
237 GeneratedTypeBuilder targetTypeBuilder = findChildNodeByPath(targetSchemaNode.getPath(), generatedCtx);
238 if (targetTypeBuilder == null) {
239 targetTypeBuilder = findCaseByPath(targetSchemaNode.getPath(), generatedCtx);
241 if (targetTypeBuilder == null) {
242 throw new NullPointerException("Target type not yet generated: " + targetSchemaNode);
245 if (!(targetSchemaNode instanceof ChoiceSchemaNode)) {
246 String packageName = augmentPackageName;
247 if (usesNodeParent instanceof SchemaNode) {
248 packageName = packageNameForAugmentedGeneratedType(augmentPackageName, ((SchemaNode) usesNodeParent).getPath());
250 generatedCtx = GenHelperUtil.addRawAugmentGenTypeDefinition(module, packageName, augmentPackageName,
251 targetTypeBuilder.toInstance(), augSchema, genTypeBuilders, generatedCtx);
254 generatedCtx = generateTypesFromAugmentedChoiceCases(schemaContext, module, augmentPackageName,
255 targetTypeBuilder.toInstance(), (ChoiceSchemaNode) targetSchemaNode, augSchema.getChildNodes(),
256 usesNodeParent, generatedCtx, verboseClassComments, genTypeBuilders);
262 * Convenient method to find node added by uses statement.
263 * @param schemaContext
266 * @param parentUsesNode
267 * parent of uses node
268 * @return node from its original location in grouping
270 private static DataSchemaNode findOriginalTargetFromGrouping(final SchemaContext schemaContext, final SchemaPath targetPath,
271 final UsesNode parentUsesNode) {
272 final SchemaNode targetGrouping = SchemaContextUtil.findNodeInSchemaContext(schemaContext, parentUsesNode
275 if (!(targetGrouping instanceof GroupingDefinition)) {
276 throw new IllegalArgumentException("Failed to generate code for augment in " + parentUsesNode);
279 final GroupingDefinition grouping = (GroupingDefinition) targetGrouping;
280 SchemaNode result = grouping;
281 for (final QName node : targetPath.getPathFromRoot()) {
282 if (result instanceof DataNodeContainer) {
283 final QName resultNode = QName.create(result.getQName().getModule(), node.getLocalName());
284 result = ((DataNodeContainer) result).getDataChildByName(resultNode);
285 } else if (result instanceof ChoiceSchemaNode) {
286 result = ((ChoiceSchemaNode) result).getCaseNodeByName(node.getLocalName());
289 if (result == null) {
293 if (result instanceof DerivableSchemaNode) {
294 DerivableSchemaNode castedResult = (DerivableSchemaNode) result;
295 Optional<? extends SchemaNode> originalNode = castedResult
297 if (castedResult.isAddedByUses() && originalNode.isPresent()) {
298 result = originalNode.get();
302 if (result instanceof DataSchemaNode) {
303 DataSchemaNode resultDataSchemaNode = (DataSchemaNode) result;
304 if (resultDataSchemaNode.isAddedByUses()) {
305 // The original node is required, but we have only the copy of
306 // the original node.
307 // Maybe this indicates a bug in Yang parser.
308 throw new IllegalStateException(
309 "Failed to generate code for augment in "
312 return resultDataSchemaNode;
315 throw new IllegalStateException(
316 "Target node of uses-augment statement must be DataSchemaNode. Failed to generate code for augment in "
322 * Generates list of generated types for all the cases of a choice which are
323 * added to the choice through the augment.
325 * @param schemaContext
328 * @param basePackageName
329 * string contains name of package to which augment belongs. If
330 * an augmented choice is from an other package (pcg1) than an
331 * augmenting choice (pcg2) then case's of the augmenting choice
332 * will belong to pcg2.
334 * Type which represents target choice
336 * node which represents target choice
337 * @param augmentedNodes
338 * set of choice case nodes for which is checked if are/aren't
339 * added to choice through augmentation
340 * @return list of generated types which represents augmented cases of
341 * choice <code>refChoiceType</code>
342 * @throws IllegalArgumentException
344 * <li>if <code>basePackageName</code> is null</li>
345 * <li>if <code>targetType</code> is null</li>
346 * <li>if <code>augmentedNodes</code> is null</li>
349 private static Map<Module, ModuleContext> generateTypesFromAugmentedChoiceCases(final SchemaContext schemaContext, final Module module,
350 final String basePackageName, final Type targetType, final ChoiceSchemaNode targetNode,
351 final Iterable<DataSchemaNode> augmentedNodes, final DataNodeContainer usesNodeParent,
352 Map<Module, ModuleContext> genCtx, final boolean verboseClassComments,
353 Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders) {
354 checkArgument(basePackageName != null, "Base Package Name cannot be NULL.");
355 checkArgument(targetType != null, "Referenced Choice Type cannot be NULL.");
356 checkArgument(augmentedNodes != null, "Set of Choice Case Nodes cannot be NULL.");
358 for (final DataSchemaNode caseNode : augmentedNodes) {
359 if (caseNode != null) {
360 final String packageName = packageNameForGeneratedType(basePackageName, caseNode.getPath());
361 final GeneratedTypeBuilder caseTypeBuilder = GenHelperUtil.addDefaultInterfaceDefinition(packageName,
362 caseNode, module, genCtx, schemaContext, verboseClassComments, genTypeBuilders);
363 caseTypeBuilder.addImplementsType(targetType);
366 final SchemaPath nodeSp = targetNode.getPath();
367 parent = findDataSchemaNode(schemaContext, nodeSp.getParent());
369 GeneratedTypeBuilder childOfType = null;
370 if (parent instanceof Module) {
371 childOfType = genCtx.get(parent).getModuleNode();
372 } else if (parent instanceof ChoiceCaseNode) {
373 childOfType = findCaseByPath(parent.getPath(), genCtx);
374 } else if (parent instanceof DataSchemaNode || parent instanceof NotificationDefinition) {
375 childOfType = findChildNodeByPath(parent.getPath(), genCtx);
376 } else if (parent instanceof GroupingDefinition) {
377 childOfType = GenHelperUtil.findGroupingByPath(parent.getPath(), genCtx);
380 if (childOfType == null) {
381 throw new IllegalArgumentException("Failed to find parent type of choice " + targetNode);
384 ChoiceCaseNode node = null;
385 final String caseLocalName = caseNode.getQName().getLocalName();
386 if (caseNode instanceof ChoiceCaseNode) {
387 node = (ChoiceCaseNode) caseNode;
388 } else if (targetNode.getCaseNodeByName(caseLocalName) == null) {
389 final String targetNodeLocalName = targetNode.getQName().getLocalName();
390 for (DataSchemaNode dataSchemaNode : usesNodeParent.getChildNodes()) {
391 if (dataSchemaNode instanceof ChoiceSchemaNode && targetNodeLocalName.equals(dataSchemaNode.getQName
392 ().getLocalName())) {
393 node = ((ChoiceSchemaNode) dataSchemaNode).getCaseNodeByName(caseLocalName);
398 node = targetNode.getCaseNodeByName(caseLocalName);
400 final Iterable<DataSchemaNode> childNodes = node.getChildNodes();
401 if (childNodes != null) {
402 resolveDataSchemaNodes(module, basePackageName, caseTypeBuilder, childOfType, childNodes);
404 genCtx.get(module).addCaseType(caseNode.getPath(), caseTypeBuilder);
405 genCtx.get(module).addChoiceToCaseMapping(targetType, caseTypeBuilder, node);