57af55851f2fd78581e2e03f00f73f42234a0113
[mdsal.git] / binding / mdsal-binding-generator / src / main / java / org / opendaylight / mdsal / binding / generator / impl / reactor / CompositeRuntimeTypeBuilder.java
1 /*
2  * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.mdsal.binding.generator.impl.reactor;
9
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;
13
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;
27
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;
32
33     CompositeRuntimeTypeBuilder(final S statement) {
34         this.statement = requireNonNull(statement);
35     }
36
37     final @NonNull S statement() {
38         return statement;
39     }
40
41     final @NonNull List<CaseRuntimeType> getCaseChilden() {
42         return childTypes.stream()
43             .map(child -> {
44                 verify(child instanceof CaseRuntimeType, "Unexpected child %s in %s", child, statement);
45                 return (CaseRuntimeType) child;
46             })
47             .collect(Collectors.toUnmodifiableList());
48     }
49
50     final @NonNull R build(final @NonNull GeneratedType generatedType) {
51         return build(generatedType, statement, childTypes, augmentTypes);
52     }
53
54     abstract @NonNull R build(GeneratedType type, S statement, List<RuntimeType> children,
55         List<AugmentRuntimeType> augments);
56
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
63         // lead astray.
64         //
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);
71                 }
72             }
73         }
74
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();
80
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)) {
86                     continue;
87                 }
88
89                 final var childRuntimeType = findChildRuntimeType(lookup, generator, child);
90                 if (childRuntimeType != null) {
91                     childTypes.add(childRuntimeType);
92                 }
93             }
94         }
95
96         return this;
97     }
98
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()));
111                 if (gen != null) {
112                     return findChildRuntimeType(lookup.inGrouping(qname, grouping), grouping, stmt);
113                 }
114             }
115
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);
120         }
121
122         return childGen.createInternalRuntimeType(lookup, stmt);
123     }
124
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);
129     }
130
131     private static @Nullable AbstractExplicitGenerator<?, ?> findChild(final AbstractCompositeGenerator<?, ?> parent,
132             final QName qname) {
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())) {
138                     return gen;
139                 }
140             }
141         }
142         return null;
143     }
144 }