Define a feature-parent
[yangtools.git] / parser / rfc8040-parser-support / src / main / java / org / opendaylight / yangtools / rfc8040 / parser / OperationsCreateLeafStatements.java
1 /*
2  * Copyright (c) 2021 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.yangtools.rfc8040.parser;
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.Collection;
16 import java.util.Comparator;
17 import java.util.List;
18 import java.util.stream.Collectors;
19 import org.opendaylight.yangtools.yang.common.QName;
20 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
21 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
22 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
23 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
24 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
25 import org.opendaylight.yangtools.yang.model.api.stmt.LeafEffectiveStatement;
26 import org.opendaylight.yangtools.yang.model.api.stmt.RpcStatement;
27 import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
28 import org.opendaylight.yangtools.yang.model.api.type.TypeDefinitions;
29 import org.opendaylight.yangtools.yang.parser.spi.ParserNamespaces;
30 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceAction;
31 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceContext;
32 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prerequisite;
33 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
34 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
35 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
36 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
37
38 /**
39  * Once we have identified the {@code operations} container we want to enrich, we need to identify all RPC statements
40  * in the reactor. For that we need that all sources complete {@link ModelProcessingPhase#FULL_DECLARATION} -- after
41  * which we are still not done, as not all those RPCs may make it to the resulting {@link EffectiveModelContext}.
42  */
43 final class OperationsCreateLeafStatements implements InferenceAction {
44     private final List<Prerequisite<? extends StmtContext<?, ?, ?>>> prereqs;
45     private final Mutable<?, ?, ?> operations;
46
47     private OperationsCreateLeafStatements(final Mutable<?, ?, ?> operations,
48             final List<Prerequisite<? extends StmtContext<?, ?, ?>>> prereqs) {
49         this.operations = requireNonNull(operations);
50         this.prereqs = requireNonNull(prereqs);
51     }
52
53     static void applyTo(final StmtContext<?, ?, ?> ietfRestconfModule, final Mutable<?, ?, ?> operations) {
54         final var action = operations.newInferenceAction(ModelProcessingPhase.EFFECTIVE_MODEL);
55         action.mutatesEffectiveCtx(operations);
56
57         final var prereqs = new ArrayList<Prerequisite<? extends StmtContext<?, ?, ?>>>();
58         // FIXME: this not accurate: we need all sources, not just modules
59         for (var module : ietfRestconfModule.namespace(ParserNamespaces.MODULE).values()) {
60             if (!ietfRestconfModule.equals(module)) {
61                 prereqs.add(action.requiresCtx((StmtContext<?, ?, ?>)module, ModelProcessingPhase.EFFECTIVE_MODEL));
62             }
63         }
64
65         action.apply(new OperationsCreateLeafStatements(operations, prereqs));
66     }
67
68     @Override
69     public void apply(final InferenceContext ctx) {
70         // Enumerate all RPCs that can be built
71         final var qnames = prereqs.stream()
72             .flatMap(prereq -> prereq.resolve(ctx).declaredSubstatements().stream())
73             .filter(stmt -> stmt.producesDeclared(RpcStatement.class)
74                 && stmt.isSupportedToBuildEffective() && stmt.isSupportedByFeatures())
75             .map(stmt -> (QName) stmt.argument())
76             // predictable order...
77             .sorted(Comparator.naturalOrder())
78             // each QName should be distinct, but let's make sure anyway
79             .distinct()
80             .collect(Collectors.toUnmodifiableList());
81
82         if (!qnames.isEmpty()) {
83             final var leafSupport = getSupport(YangStmtMapping.LEAF, LeafEffectiveStatement.class);
84             final var typeSupport = getSupport(YangStmtMapping.TYPE, TypeEffectiveStatement.class);
85
86             for (var qname : qnames) {
87                 final var leaf = operations.createUndeclaredSubstatement(leafSupport, qname);
88                 leaf.addEffectiveSubstatement(leaf.createUndeclaredSubstatement(typeSupport, TypeDefinitions.EMPTY));
89                 operations.addEffectiveSubstatement(leaf);
90             }
91         }
92     }
93
94     @Override
95     public void prerequisiteFailed(final Collection<? extends Prerequisite<?>> failed) {
96         // We do not really need to fail, as this means reactor will fail anyway
97     }
98
99     private <X, Y extends DeclaredStatement<X>, Z extends EffectiveStatement<X, Y>>
100             StatementSupport<X, Y, Z> getSupport(final StatementDefinition def, final Class<Z> effectiveClass) {
101         final var tmp = verifyNotNull(operations.namespaceItem(StatementSupport.NAMESPACE, def.getStatementName()));
102         final var repr = tmp.definition().getEffectiveRepresentationClass();
103         verify(effectiveClass.equals(repr), "Unexpected support %s representation %s", tmp, repr);
104
105         @SuppressWarnings("unchecked")
106         final var ret = (StatementSupport<X, Y, Z>) tmp;
107         return ret;
108     }
109 }