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.annotations.VisibleForTesting;
15 import com.google.common.base.Preconditions;
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.Comparator;
20 import java.util.Iterator;
21 import java.util.List;
23 import java.util.Map.Entry;
24 import java.util.Optional;
25 import java.util.stream.Collectors;
26 import org.opendaylight.mdsal.binding.javav2.generator.context.ModuleContext;
27 import org.opendaylight.mdsal.binding.javav2.generator.spi.TypeProvider;
28 import org.opendaylight.mdsal.binding.javav2.generator.util.BindingGeneratorUtil;
29 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedType;
30 import org.opendaylight.mdsal.binding.javav2.model.api.Type;
31 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.GeneratedTypeBuilder;
32 import org.opendaylight.mdsal.binding.javav2.spec.runtime.BindingNamespaceType;
33 import org.opendaylight.mdsal.binding.javav2.util.BindingMapping;
34 import org.opendaylight.yangtools.yang.common.QName;
35 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
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 {
54 * Comparator based on augment target path.
56 private static final Comparator<AugmentationSchemaNode> AUGMENT_COMP = (o1, o2) -> {
57 final Iterator<QName> thisIt = o1.getTargetPath().getPathFromRoot().iterator();
58 final Iterator<QName> otherIt = o2.getTargetPath().getPathFromRoot().iterator();
60 while (thisIt.hasNext()) {
61 if (!otherIt.hasNext()) {
65 final int comp = thisIt.next().compareTo(otherIt.next());
71 return otherIt.hasNext() ? -1 : 0;
75 * Comparator based on augment target path.
77 private static final Comparator<Map.Entry<SchemaPath, List<AugmentationSchemaNode>>> AUGMENTS_COMP = (o1, o2) -> {
78 final Iterator<QName> thisIt = o1.getKey().getPathFromRoot().iterator();
79 final Iterator<QName> otherIt = o2.getKey().getPathFromRoot().iterator();
81 while (thisIt.hasNext()) {
82 if (!otherIt.hasNext()) {
86 final int comp = thisIt.next().compareTo(otherIt.next());
92 return otherIt.hasNext() ? -1 : 0;
95 private AugmentToGenType() {
96 throw new UnsupportedOperationException("Utility class");
100 * Converts all <b>augmentation</b> of the module to the list
101 * <code>Type</code> objects.
104 * module from which is obtained list of all augmentation objects
105 * to iterate over them
106 * @param schemaContext actual schema context
107 * @param typeProvider actual type provider instance
108 * @param genCtx generated input context
109 * @param genTypeBuilders auxiliary type builders map
110 * @param verboseClassComments verbosity switch
112 * @throws IllegalArgumentException
114 * <li>if the module is null</li>
115 * <li>if the name of module is null</li>
117 * @throws IllegalStateException
118 * if set of augmentations from module is null
120 static Map<Module, ModuleContext> generate(final Module module, final SchemaContext schemaContext,
121 final TypeProvider typeProvider, final Map<Module, ModuleContext> genCtx,
122 final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders, final boolean verboseClassComments) {
124 Preconditions.checkArgument(module != null, "Module reference cannot be NULL.");
125 Preconditions.checkArgument(module.getName() != null, "Module name cannot be NULL.");
126 Preconditions.checkState(module.getAugmentations() != null, "Augmentations Set cannot be NULL.");
128 final String basePackageName = BindingMapping.getRootPackageName(module);
129 final List<AugmentationSchemaNode> augmentations = resolveAugmentations(module, schemaContext);
130 Map<Module, ModuleContext> resultCtx = genCtx;
132 //let's group augments by target path
133 Map<SchemaPath, List<AugmentationSchemaNode>> augmentationsGrouped =
134 augmentations.stream().collect(Collectors.groupingBy(AugmentationSchemaNode::getTargetPath));
136 List<Map.Entry<SchemaPath, List<AugmentationSchemaNode>>> sortedAugmentationsGrouped =
137 new ArrayList<>(augmentationsGrouped.entrySet());
138 Collections.sort(sortedAugmentationsGrouped, AUGMENTS_COMP);
140 //process child nodes of grouped augment entries
141 for (Map.Entry<SchemaPath, List<AugmentationSchemaNode>> schemaPathAugmentListEntry : sortedAugmentationsGrouped) {
142 resultCtx = augmentationToGenTypes(basePackageName, schemaPathAugmentListEntry, module, schemaContext,
143 verboseClassComments, resultCtx, genTypeBuilders, typeProvider);
145 for (AugmentationSchemaNode augSchema : schemaPathAugmentListEntry.getValue()) {
146 processUsesImplements(augSchema, module, schemaContext, genCtx, BindingNamespaceType.Data);
155 * Returns list of <code>AugmentationSchema</code> objects. The objects are
156 * sorted according to the length of their target path from the shortest to
160 * module from which is obtained list of all augmentation objects
161 * @return list of sorted <code>AugmentationSchema</code> objects obtained
162 * from <code>module</code>
163 * @throws IllegalArgumentException
165 * @throws IllegalStateException
166 * if set of module augmentations is null
169 static List<AugmentationSchemaNode> resolveAugmentations(final Module module,
170 final SchemaContext schemaContext) {
171 Preconditions.checkArgument(module != null, "Module reference cannot be NULL.");
172 Preconditions.checkState(module.getAugmentations() != null, "Augmentations Set cannot be NULL.");
174 final List<AugmentationSchemaNode> sortedAugmentations = module.getAugmentations().stream()
175 .filter(aug -> !module.equals(findAugmentTargetModule(schemaContext, aug)))
176 .collect(Collectors.toList());
177 sortedAugmentations.sort(AUGMENT_COMP);
178 return sortedAugmentations;
181 public static Module findAugmentTargetModule(final SchemaContext schemaContext,
182 final AugmentationSchemaNode aug) {
183 Preconditions.checkNotNull(aug, "Augmentation schema can not be null.");
184 final QName first = aug.getTargetPath().getPathFromRoot().iterator().next();
185 return schemaContext.findModule(first.getModule()).orElse(null);
189 * Converts <code>augSchema</code> to list of <code>Type</code> which
190 * contains generated type for augmentation. In addition there are also
191 * generated types for all containers, list and choices which are child of
192 * <code>augSchema</code> node or a generated types for cases are added if
193 * augmented node is choice.
195 * @param basePackageName
196 * string with the name of the package to which the augmentation
198 * @param schemaPathAugmentListEntry
199 * list of AugmentationSchema nodes grouped by target path
200 * @param module current module
201 * @param schemaContext actual schema context
202 * @param verboseClassComments verbosity switch
203 * @param genCtx generated input context
204 * @param genTypeBuilders auxiliary type builders map
205 * @param typeProvider actual type provider instance
206 * @throws IllegalArgumentException
207 * if <code>augmentPackageName</code> equals null
208 * @throws IllegalStateException
209 * if augment target path is null
210 * @return generated context
213 static Map<Module, ModuleContext> augmentationToGenTypes(final String basePackageName,
214 final Entry<SchemaPath, List<AugmentationSchemaNode>> schemaPathAugmentListEntry, final Module module,
215 final SchemaContext schemaContext, final boolean verboseClassComments,
216 Map<Module, ModuleContext> genCtx, final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders,
217 final TypeProvider typeProvider) {
218 Preconditions.checkArgument(basePackageName != null, "Package Name cannot be NULL.");
219 Preconditions.checkArgument(schemaPathAugmentListEntry != null, "Augmentation List Entry cannot be NULL.");
220 final SchemaPath targetPath = schemaPathAugmentListEntry.getKey();
221 Preconditions.checkState(targetPath != null,
222 "Augmentation List Entry does not contain Target Path (Target Path is NULL).");
224 final List<AugmentationSchemaNode> augmentationSchemaList = schemaPathAugmentListEntry.getValue();
225 Preconditions.checkState(!augmentationSchemaList.isEmpty(),
226 "Augmentation List cannot be empty.");
228 SchemaNode targetSchemaNode = SchemaContextUtil.findDataSchemaNode(schemaContext, targetPath);
229 if (targetSchemaNode == null) {
230 throw new IllegalArgumentException("augment target not found: " + targetPath);
233 GeneratedTypeBuilder targetTypeBuilder = GenHelperUtil.findChildNodeByPath(targetSchemaNode.getPath(),
235 if (targetTypeBuilder == null) {
236 targetTypeBuilder = GenHelperUtil.findCaseByPath(targetSchemaNode.getPath(), genCtx);
238 if (targetTypeBuilder == null) {
239 throw new NullPointerException("Target type not yet generated: " + targetSchemaNode);
242 final String augmentPackageName =
243 BindingGeneratorUtil.packageNameWithNamespacePrefix(basePackageName, BindingNamespaceType.Data);
245 if (!(targetSchemaNode instanceof ChoiceSchemaNode)) {
246 genCtx = GenHelperUtil.addRawAugmentGenTypeDefinition(module, augmentPackageName,
247 targetTypeBuilder.toInstance(), targetSchemaNode, schemaPathAugmentListEntry.getValue(),
248 genTypeBuilders, genCtx, schemaContext, verboseClassComments, typeProvider, BindingNamespaceType.Data);
250 genCtx = generateTypesFromAugmentedChoiceCases(schemaContext, module, basePackageName,
251 targetTypeBuilder.toInstance(), (ChoiceSchemaNode) targetSchemaNode,
252 schemaPathAugmentListEntry.getValue(),genCtx, verboseClassComments, genTypeBuilders, typeProvider,
253 BindingNamespaceType.Data);
259 * Convenient method to find node added by uses statement.
260 * @param schemaContext
263 * @param parentUsesNode
264 * parent of uses node
265 * @return node from its original location in grouping
268 static DataSchemaNode findOriginalTargetFromGrouping(final SchemaContext schemaContext,
269 final SchemaPath targetPath, final UsesNode parentUsesNode) {
270 SchemaNode targetGrouping = null;
271 QName current = parentUsesNode.getGroupingPath().getPathFromRoot().iterator().next();
272 Module module = schemaContext.findModule(current.getModule()).orElse(null);
273 if (module == null) {
274 throw new IllegalArgumentException("Fialed to find module for grouping in: " + parentUsesNode);
276 for (GroupingDefinition group : module.getGroupings()) {
277 if (group.getQName().equals(current)) {
278 targetGrouping = group;
283 if (targetGrouping == null) {
284 throw new IllegalArgumentException("Failed to generate code for augment in " + parentUsesNode);
287 SchemaNode result = targetGrouping;
288 for (final QName node : targetPath.getPathFromRoot()) {
289 if (result instanceof DataNodeContainer) {
290 final QName resultNode = QName.create(result.getQName().getModule(), node.getLocalName());
291 result = ((DataNodeContainer) result).getDataChildByName(resultNode);
292 } else if (result instanceof ChoiceSchemaNode) {
293 result = findNamedCase((ChoiceSchemaNode) result, node.getLocalName());
296 if (result == null) {
300 if (result instanceof DerivableSchemaNode) {
301 DerivableSchemaNode castedResult = (DerivableSchemaNode) result;
302 Optional<? extends SchemaNode> originalNode = castedResult
304 if (castedResult.isAddedByUses() && originalNode.isPresent()) {
305 result = originalNode.get();
309 if (result instanceof DataSchemaNode) {
310 DataSchemaNode resultDataSchemaNode = (DataSchemaNode) result;
311 if (resultDataSchemaNode.isAddedByUses()) {
312 // The original node is required, but we have only the copy of
313 // the original node.
314 // Maybe this indicates a bug in Yang parser.
315 throw new IllegalStateException(
316 "Failed to generate code for augment in "
319 return resultDataSchemaNode;
322 throw new IllegalStateException(
323 "Target node of uses-augment statement must be DataSchemaNode. Failed to generate code for augment in "
329 * Generates list of generated types for all the cases of a choice which are
330 * added to the choice through the augment.
332 * @param schemaContext
335 * @param basePackageName
336 * string contains name of package to which augment belongs. If
337 * an augmented choice is from an other package (pcg1) than an
338 * augmenting choice (pcg2) then case's of the augmenting choice
339 * will belong to pcg2.
341 * Type which represents target choice
343 * node which represents target choice
344 * @param schemaPathAugmentListEntry
345 * list of AugmentationSchema nodes grouped by target path
346 * @return list of generated types which represents augmented cases of
347 * choice <code>refChoiceType</code>
348 * @throws IllegalArgumentException
350 * <li>if <code>basePackageName</code> is null</li>
351 * <li>if <code>targetType</code> is null</li>
352 * <li>if <code>augmentedNodes</code> is null</li>
356 static Map<Module, ModuleContext> generateTypesFromAugmentedChoiceCases(
357 final SchemaContext schemaContext, final Module module,
358 final String basePackageName, final GeneratedType targetType, final ChoiceSchemaNode targetNode,
359 final List<AugmentationSchemaNode> schemaPathAugmentListEntry,
360 final Map<Module, ModuleContext> genCtx, final boolean verboseClassComments,
361 final Map<String, Map<String, GeneratedTypeBuilder>> genTypeBuilders, final TypeProvider typeProvider,
362 final BindingNamespaceType namespaceType) {
363 Preconditions.checkArgument(basePackageName != null, "Base Package Name cannot be NULL.");
364 Preconditions.checkArgument(targetType != null, "Referenced Choice Type cannot be NULL.");
365 Preconditions.checkArgument(schemaPathAugmentListEntry != null, "Set of Choice Case Nodes cannot be NULL.");
368 for (final AugmentationSchemaNode augmentationSchema : schemaPathAugmentListEntry) {
369 for (final DataSchemaNode childNode : augmentationSchema.getChildNodes()) {
370 if (childNode != null) {
371 final GeneratedTypeBuilder caseTypeBuilder =
372 GenHelperUtil.addDefaultInterfaceDefinition(basePackageName, childNode, null, module,
373 genCtx, schemaContext, verboseClassComments, genTypeBuilders, typeProvider, namespaceType);
374 caseTypeBuilder.addImplementsType(targetType);
375 caseTypeBuilder.setParentTypeForBuilder(targetType.getParentTypeForBuilder());
377 //Since uses augment nodes has been processed as inline nodes,
378 //we just take two situations below.
379 final CaseSchemaNode caseNode;
380 if (childNode instanceof CaseSchemaNode) {
381 caseNode = (CaseSchemaNode) childNode;
383 caseNode = findNamedCase(targetNode, childNode.getQName().getLocalName());
384 if (caseNode == null) {
385 throw new IllegalArgumentException("Failed to find case node " + childNode);
389 final Collection<DataSchemaNode> childNodes = caseNode.getChildNodes();
390 if (!childNodes.isEmpty()) {
391 GenHelperUtil.resolveDataSchemaNodes(module, basePackageName, caseTypeBuilder,
392 (GeneratedTypeBuilder) targetType.getParentTypeForBuilder(), childNodes, genCtx,
393 schemaContext, verboseClassComments, genTypeBuilders, typeProvider, namespaceType);
394 processUsesImplements(caseNode, module, schemaContext, genCtx, namespaceType);
396 genCtx.get(module).addCaseType(childNode.getPath(), caseTypeBuilder);
397 genCtx.get(module).addChoiceToCaseMapping(targetType, caseTypeBuilder, caseNode);
404 private static CaseSchemaNode findNamedCase(final ChoiceSchemaNode choice, final String caseName) {
405 final List<CaseSchemaNode> cases = choice.findCaseNodes(caseName);
406 return cases.isEmpty() ? null : cases.get(0);