Bump byte-buddy to 1.14.16
[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.opendaylight.mdsal.binding.model.api.GeneratedType;
19 import org.opendaylight.mdsal.binding.runtime.api.AugmentRuntimeType;
20 import org.opendaylight.mdsal.binding.runtime.api.CaseRuntimeType;
21 import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
22 import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
23 import org.opendaylight.yangtools.yang.common.QName;
24 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
25 import org.opendaylight.yangtools.yang.model.api.stmt.AugmentEffectiveStatement;
26 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
27 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
28
29 abstract class CompositeRuntimeTypeBuilder<S extends EffectiveStatement<?, ?>, R extends CompositeRuntimeType> {
30     private final List<AugmentRuntimeType> augmentTypes = new ArrayList<>();
31     private final List<RuntimeType> childTypes = new ArrayList<>();
32     private final @NonNull S statement;
33
34     CompositeRuntimeTypeBuilder(final S statement) {
35         this.statement = requireNonNull(statement);
36     }
37
38     final CompositeRuntimeTypeBuilder<S, R> populate(final AugmentResolver resolver,
39             final AbstractCompositeGenerator<S, R> generator) {
40         resolver.enter(generator);
41         try {
42             processGenerator(resolver, generator);
43         } finally {
44             resolver.exit();
45         }
46         return this;
47     }
48
49     final @NonNull R build(final @NonNull GeneratedType generatedType) {
50         return build(generatedType, statement, childTypes, augmentTypes);
51     }
52
53     abstract @NonNull R build(GeneratedType type, S statement, List<RuntimeType> children,
54         List<AugmentRuntimeType> augments);
55
56     final @NonNull List<CaseRuntimeType> getCaseChilden() {
57         return childTypes.stream()
58             .map(child -> {
59                 verify(child instanceof CaseRuntimeType, "Unexpected child %s in %s", child, statement);
60                 return (CaseRuntimeType) child;
61             })
62             .collect(Collectors.toUnmodifiableList());
63     }
64
65     final @NonNull S statement() {
66         return statement;
67     }
68
69     boolean isAugmentedChild(final QName qname) {
70         // Note we are dealing with two different kinds of augments and they behave differently with respect
71         // to namespaces. Top-level augments do not make an adjustment, while uses-augments do.
72         for (var augment : augmentTypes) {
73             if (augment.schemaTreeChild(qname) != null) {
74                 return true;
75             }
76         }
77         return false;
78     }
79
80     void processAugment(final AugmentResolver resolver, final AbstractAugmentGenerator augment) {
81         augmentTypes.add(augment.runtimeTypeIn(resolver, statement));
82     }
83
84     private void processGenerator(final AugmentResolver resolver, final AbstractCompositeGenerator<S, R> generator) {
85         // Figure out which augments are valid in target statement and record their RuntimeTypes.
86         // We will pass the latter to create method. We will use the former to perform replacement lookups instead
87         // of 'this.augments'. That is necessary because 'this.augments' holds all augments targeting the GeneratedType,
88         // hence equivalent augmentations from differing places would match our lookup and the reverse search would be
89         // lead astray.
90         //
91         // Note we should not do this for 'module' and 'uses' statements, as those are not valid augment targets. Of
92         // those two we only generate things for 'module'.
93         if (!(statement instanceof ModuleEffectiveStatement)) {
94             for (var stmt : statement.effectiveSubstatements()) {
95                 if (stmt instanceof AugmentEffectiveStatement augment) {
96                     processAugment(resolver, resolver.getAugment(augment));
97                 }
98             }
99         }
100
101         // Now construct RuntimeTypes for each schema tree child of stmt
102         for (var stmt : statement.effectiveSubstatements()) {
103             if (stmt instanceof SchemaTreeEffectiveStatement<?> child) {
104                 // Try valid augments first: they should be empty most of the time and filter all the cases where we
105                 // would not find the streamChild among our local and grouping statements. Note that unlike all others,
106                 // such matches are not considered to be children in Binding DataObject tree, they are only considered
107                 // such in the schema tree.
108                 //
109                 // That is in general -- 'choice' statements are doing their own thing separately.
110                 if (!isAugmentedChild(child.argument())) {
111                     final var childGen = verifyNotNull(findChildGenerator(generator, child.argument().getLocalName()),
112                         "Cannot find child for %s in %s", child, generator);
113                     final var childRuntimeType = childGen.createInternalRuntimeType(resolver, child);
114                     if (childRuntimeType != null) {
115                         childTypes.add(childRuntimeType);
116                     }
117                 }
118             }
119         }
120     }
121
122     // When we reach here we have dealt with all known augments in this scope, hence the only option is that the
123     // statement is either local or added via 'uses' -- in either case it has namespace equal to whatever the local
124     // namespace is and there can be no conflicts on QName.getLocalName(). That simplifies things a ton.
125     private static <S extends SchemaTreeEffectiveStatement<?>> AbstractExplicitGenerator<S, ?> findChildGenerator(
126             final AbstractCompositeGenerator<?, ?> parent, final String localName) {
127         // Search direct children first ...
128         for (var child : parent) {
129             if (child instanceof AbstractExplicitGenerator) {
130                 @SuppressWarnings("unchecked")
131                 final AbstractExplicitGenerator<S, ?> gen = (AbstractExplicitGenerator<S, ?>) child;
132                 final EffectiveStatement<?, ?> genStmt = gen.statement();
133                 if (genStmt instanceof SchemaTreeEffectiveStatement<?> schemaStmt
134                     && localName.equals(schemaStmt.argument().getLocalName())) {
135                     return gen;
136                 }
137             }
138         }
139
140         // ... groupings recursively next ...
141         for (var grouping : parent.groupings()) {
142             final AbstractExplicitGenerator<S, ?> found = findChildGenerator(grouping, localName);
143             if (found != null) {
144                 return found;
145             }
146         }
147
148         // ... and finally anything along instantiation axis ...
149         final var origParent = (AbstractCompositeGenerator<?, ?>) parent.previous();
150         return origParent == null ? null : findChildGenerator(origParent, localName);
151     }
152 }