2 * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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
8 package org.opendaylight.mdsal.binding.generator.impl.reactor;
10 import static com.google.common.base.Verify.verify;
11 import static com.google.common.base.Verify.verifyNotNull;
12 import static java.util.Objects.requireNonNull;
14 import java.util.ArrayList;
15 import java.util.List;
16 import java.util.stream.Collectors;
17 import org.eclipse.jdt.annotation.NonNull;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
20 import org.opendaylight.mdsal.binding.runtime.api.AugmentRuntimeType;
21 import org.opendaylight.mdsal.binding.runtime.api.CaseRuntimeType;
22 import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
23 import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
24 import org.opendaylight.yangtools.yang.common.QName;
25 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
26 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
28 abstract class CompositeRuntimeTypeBuilder<S extends EffectiveStatement<?, ?>, R extends CompositeRuntimeType> {
29 private final List<AugmentRuntimeType> augmentTypes = new ArrayList<>();
30 private final List<RuntimeType> childTypes = new ArrayList<>();
31 private final @NonNull S statement;
33 CompositeRuntimeTypeBuilder(final S statement) {
34 this.statement = requireNonNull(statement);
37 final @NonNull S statement() {
41 final @NonNull List<CaseRuntimeType> getCaseChilden() {
42 return childTypes.stream()
44 verify(child instanceof CaseRuntimeType, "Unexpected child %s in %s", child, statement);
45 return (CaseRuntimeType) child;
47 .collect(Collectors.toUnmodifiableList());
50 final @NonNull R build(final @NonNull GeneratedType generatedType) {
51 return build(generatedType, statement, childTypes, augmentTypes);
54 abstract @NonNull R build(GeneratedType type, S statement, List<RuntimeType> children,
55 List<AugmentRuntimeType> augments);
57 CompositeRuntimeTypeBuilder<S, R> fillTypes(final ChildLookup lookup,
58 final AbstractCompositeGenerator<S, R> generator) {
59 // Figure out which augments are valid in target statement and record their RuntimeTypes.
60 // We will pass the latter to create method. We will use the former to perform replacement lookups instead
61 // of 'this.augments'. That is necessary because 'this.augments' holds all augments targeting the GeneratedType,
62 // hence equivalent augmentations from differing places would match our lookup and the reverse search would be
65 // Augments targeting 'choice' statement are handled by a separate class and need to be skipped here
66 if (!(generator instanceof ChoiceGenerator)) {
67 for (var augment : generator.augments()) {
68 final var augmentRuntimeType = augment.runtimeTypeIn(lookup, statement);
69 if (augmentRuntimeType != null) {
70 augmentTypes.add(augmentRuntimeType);
75 // Now construct RuntimeTypes for each schema tree child of stmt
76 for (var stmt : statement.effectiveSubstatements()) {
77 if (stmt instanceof SchemaTreeEffectiveStatement) {
78 final var child = (SchemaTreeEffectiveStatement<?>) stmt;
79 final var qname = child.getIdentifier();
81 // Try valid augments first: they should be empty most of the time and filter all the cases where we
82 // would not find the streamChild among our local and grouping statements. Note that unlike all others,
83 // such matches are not considered to be children in Binding DataObject tree, they are only considered
84 // such in the schema tree.
85 if (isAugmentedChild(lookup, qname)) {
89 final var childRuntimeType = findChildRuntimeType(lookup, generator, child);
90 if (childRuntimeType != null) {
91 childTypes.add(childRuntimeType);
99 @SuppressWarnings("unchecked")
100 final <X extends SchemaTreeEffectiveStatement<?>> @Nullable RuntimeType findChildRuntimeType(
101 final @NonNull ChildLookup lookup, final AbstractCompositeGenerator<?, ?> parent, final @NonNull X stmt) {
102 final var qname = stmt.getIdentifier();
103 // First try our local items without adjustment ...
104 @SuppressWarnings("rawtypes")
105 AbstractExplicitGenerator childGen = findChild(parent, qname);
106 if (childGen == null) {
107 // No luck, let's see if any of the groupings can find it
108 for (GroupingGenerator grouping : parent.groupings()) {
109 final var gen = grouping.findSchemaTreeGenerator(
110 qname.bindTo(grouping.statement().argument().getModule()));
112 return findChildRuntimeType(lookup.inGrouping(qname, grouping), grouping, stmt);
116 // Finally attempt to find adjusted QName: this has to succeed
117 final var adjusted = lookup.adjustQName(qname);
118 childGen = verifyNotNull(findChild(parent, adjusted),
119 "Failed to find %s as %s in %s", stmt, adjusted, this);
122 return childGen.createInternalRuntimeType(lookup, stmt);
125 boolean isAugmentedChild(final ChildLookup lookup, final QName qname) {
126 // Note we are dealing with two different kinds of augments and they behave differently with respect
127 // to namespaces. Top-level augments do not make an adjustment, while uses-augments do.
128 return augmentTypes.stream().anyMatch(augment -> augment.schemaTreeChild(qname) != null);
131 private static @Nullable AbstractExplicitGenerator<?, ?> findChild(final AbstractCompositeGenerator<?, ?> parent,
133 for (var child : parent) {
134 if (child instanceof AbstractExplicitGenerator) {
135 final AbstractExplicitGenerator<?, ?> gen = (AbstractExplicitGenerator<?, ?>) child;
136 final EffectiveStatement<?, ?> stmt = gen.statement();
137 if (stmt instanceof SchemaTreeEffectiveStatement && qname.equals(stmt.argument())) {