Recognize 'choice' in 'choice' with YANG 1.1
[yangtools.git] / parser / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / stmt / meta / ChoiceStatementSupport.java
1 /*
2  * Copyright (c) 2017 Pantheon Technologies, 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.yang.parser.rfc7950.stmt.meta;
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 com.google.common.annotations.Beta;
15 import com.google.common.collect.ImmutableList;
16 import com.google.common.collect.ImmutableSet;
17 import java.util.Collection;
18 import java.util.Optional;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.opendaylight.yangtools.yang.common.QName;
21 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
22 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
23 import org.opendaylight.yangtools.yang.model.api.Status;
24 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
25 import org.opendaylight.yangtools.yang.model.api.meta.DeclarationReference;
26 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
27 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
28 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
29 import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceEffectiveStatement;
30 import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceStatement;
31 import org.opendaylight.yangtools.yang.model.api.stmt.DefaultEffectiveStatement;
32 import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryEffectiveStatement;
33 import org.opendaylight.yangtools.yang.model.api.stmt.StatusEffectiveStatement;
34 import org.opendaylight.yangtools.yang.model.ri.stmt.DeclaredStatementDecorators;
35 import org.opendaylight.yangtools.yang.model.ri.stmt.DeclaredStatements;
36 import org.opendaylight.yangtools.yang.model.ri.stmt.EffectiveStatements;
37 import org.opendaylight.yangtools.yang.model.spi.meta.EffectiveStatementMixins.EffectiveStatementWithFlags.FlagsBuilder;
38 import org.opendaylight.yangtools.yang.model.spi.meta.SubstatementIndexingException;
39 import org.opendaylight.yangtools.yang.parser.api.YangParserConfiguration;
40 import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractSchemaTreeStatementSupport;
41 import org.opendaylight.yangtools.yang.parser.spi.meta.BoundStmtCtx;
42 import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStatementState;
43 import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx.Current;
44 import org.opendaylight.yangtools.yang.parser.spi.meta.ImplicitParentAwareStatementSupport;
45 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
46 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceStmtCtx;
47 import org.opendaylight.yangtools.yang.parser.spi.meta.QNameWithFlagsEffectiveStatementState;
48 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
49 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupportNamespace;
50 import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
51 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
52
53 @Beta
54 public final class ChoiceStatementSupport
55         extends AbstractSchemaTreeStatementSupport<ChoiceStatement, ChoiceEffectiveStatement>
56         implements ImplicitParentAwareStatementSupport {
57     private static final SubstatementValidator RFC6020_VALIDATOR = SubstatementValidator.builder(YangStmtMapping.CHOICE)
58             .addAny(YangStmtMapping.ANYXML)
59             .addAny(YangStmtMapping.CASE)
60             .addOptional(YangStmtMapping.CONFIG)
61             .addAny(YangStmtMapping.CONTAINER)
62             .addOptional(YangStmtMapping.DEFAULT)
63             .addOptional(YangStmtMapping.DESCRIPTION)
64             .addAny(YangStmtMapping.IF_FEATURE)
65             .addAny(YangStmtMapping.LEAF)
66             .addAny(YangStmtMapping.LEAF_LIST)
67             .addAny(YangStmtMapping.LIST)
68             .addOptional(YangStmtMapping.MANDATORY)
69             .addOptional(YangStmtMapping.REFERENCE)
70             .addOptional(YangStmtMapping.STATUS)
71             .addOptional(YangStmtMapping.WHEN)
72             .build();
73     private static final ImmutableSet<StatementDefinition> RFC6020_CASE_SHORTHANDS = ImmutableSet.of(
74             YangStmtMapping.ANYXML, YangStmtMapping.CONTAINER, YangStmtMapping.LEAF, YangStmtMapping.LIST,
75             YangStmtMapping.LEAF_LIST);
76
77     private static final SubstatementValidator RFC7950_VALIDATOR = SubstatementValidator.builder(YangStmtMapping.CHOICE)
78             .addAny(YangStmtMapping.ANYDATA)
79             .addAny(YangStmtMapping.ANYXML)
80             .addAny(YangStmtMapping.CASE)
81             .addAny(YangStmtMapping.CHOICE)
82             .addOptional(YangStmtMapping.CONFIG)
83             .addAny(YangStmtMapping.CONTAINER)
84             .addOptional(YangStmtMapping.DEFAULT)
85             .addOptional(YangStmtMapping.DESCRIPTION)
86             .addAny(YangStmtMapping.IF_FEATURE)
87             .addAny(YangStmtMapping.LEAF)
88             .addAny(YangStmtMapping.LEAF_LIST)
89             .addAny(YangStmtMapping.LIST)
90             .addOptional(YangStmtMapping.MANDATORY)
91             .addOptional(YangStmtMapping.REFERENCE)
92             .addOptional(YangStmtMapping.STATUS)
93             .addOptional(YangStmtMapping.WHEN)
94             .build();
95     private static final ImmutableSet<StatementDefinition> RFC7950_CASE_SHORTHANDS = ImmutableSet.of(
96         YangStmtMapping.ANYDATA, YangStmtMapping.ANYXML, YangStmtMapping.CHOICE, YangStmtMapping.CONTAINER,
97         YangStmtMapping.LEAF, YangStmtMapping.LIST, YangStmtMapping.LEAF_LIST);
98
99     private final ImmutableSet<StatementDefinition> caseShorthands;
100
101     private ChoiceStatementSupport(final YangParserConfiguration config, final SubstatementValidator validator,
102             final ImmutableSet<StatementDefinition> caseShorthands) {
103         super(YangStmtMapping.CHOICE, instantiatedPolicy(), config, requireNonNull(validator));
104         this.caseShorthands = requireNonNull(caseShorthands);
105     }
106
107     public static @NonNull ChoiceStatementSupport rfc6020Instance(final YangParserConfiguration config) {
108         return new ChoiceStatementSupport(config, RFC6020_VALIDATOR, RFC6020_CASE_SHORTHANDS);
109     }
110
111     public static @NonNull ChoiceStatementSupport rfc7950Instance(final YangParserConfiguration config) {
112         return new ChoiceStatementSupport(config, RFC7950_VALIDATOR, RFC7950_CASE_SHORTHANDS);
113     }
114
115     @Override
116     public Optional<StatementSupport<?, ?, ?>> getImplicitParentFor(final NamespaceStmtCtx parent,
117             final StatementDefinition stmtDef) {
118         if (!caseShorthands.contains(stmtDef)) {
119             return Optional.empty();
120         }
121         return Optional.of(verifyNotNull(parent.getFromNamespace(StatementSupportNamespace.class,
122             YangStmtMapping.CASE.getStatementName())));
123     }
124
125     @Override
126     protected ChoiceStatement createDeclared(final BoundStmtCtx<QName> ctx,
127             final ImmutableList<DeclaredStatement<?>> substatements) {
128         return DeclaredStatements.createChoice(ctx.getArgument(), substatements);
129     }
130
131     @Override
132     protected ChoiceStatement attachDeclarationReference(final ChoiceStatement stmt,
133             final DeclarationReference reference) {
134         return DeclaredStatementDecorators.decorateChoice(stmt, reference);
135     }
136
137     @Override
138     public ChoiceEffectiveStatement copyEffective(final Current<QName, ChoiceStatement> stmt,
139             final ChoiceEffectiveStatement original) {
140         return EffectiveStatements.copyChoice(original, stmt.getArgument(),
141             computeFlags(stmt, original.effectiveSubstatements()));
142     }
143
144     @Override
145     protected ChoiceEffectiveStatement createEffective(final Current<QName, ChoiceStatement> stmt,
146             final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
147         final String defaultArg = findFirstArgument(substatements, DefaultEffectiveStatement.class, null);
148         final CaseSchemaNode defaultCase;
149         if (defaultArg != null) {
150             final QName qname;
151             try {
152                 qname = QName.create(stmt.getArgument(), defaultArg);
153             } catch (IllegalArgumentException e) {
154                 throw new SourceException(stmt, e, "Default statement has invalid name '%s'", defaultArg);
155             }
156
157             // FIXME: this does not work with submodules, as they are
158             defaultCase = InferenceException.throwIfNull(findCase(qname, substatements), stmt,
159                 "Default statement refers to missing case %s", qname);
160         } else {
161             defaultCase = null;
162         }
163
164         try {
165             return EffectiveStatements.createChoice(stmt.declared(), stmt.getArgument(),
166                 computeFlags(stmt, substatements), substatements, defaultCase);
167         } catch (SubstatementIndexingException e) {
168             throw new SourceException(e.getMessage(), stmt, e);
169         }
170     }
171
172     @Override
173     public EffectiveStatementState extractEffectiveState(final ChoiceEffectiveStatement stmt) {
174         verify(stmt instanceof ChoiceSchemaNode, "Unexpected statement %s", stmt);
175         final var schema = (ChoiceSchemaNode) stmt;
176         return new QNameWithFlagsEffectiveStatementState(stmt.argument(), new FlagsBuilder()
177             .setHistory(schema)
178             .setStatus(schema.getStatus())
179             .setConfiguration(schema.effectiveConfig().orElse(null))
180             .setMandatory(schema.isMandatory())
181             .toFlags());
182     }
183
184     private static int computeFlags(final Current<?, ?> stmt,
185             final Collection<? extends EffectiveStatement<?, ?>> substatements) {
186         return new FlagsBuilder()
187             .setHistory(stmt.history())
188             .setStatus(findFirstArgument(substatements, StatusEffectiveStatement.class, Status.CURRENT))
189             .setConfiguration(stmt.effectiveConfig().asNullable())
190             .setMandatory(findFirstArgument(substatements, MandatoryEffectiveStatement.class, Boolean.FALSE))
191             .toFlags();
192     }
193
194     private static CaseSchemaNode findCase(final QName qname,
195             final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
196         for (final EffectiveStatement<?, ?> effectiveStatement : substatements) {
197             if (effectiveStatement instanceof CaseSchemaNode) {
198                 final CaseSchemaNode choiceCaseNode = (CaseSchemaNode) effectiveStatement;
199                 if (qname.equals(choiceCaseNode.getQName())) {
200                     return choiceCaseNode;
201                 }
202             }
203         }
204
205         return null;
206     }
207 }