Bug 6261: Introduce resolution of deviation statement during SchemaContext assembly
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / rfc6020 / DeviateStatementImpl.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. 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.stmt.rfc6020;
9
10 import com.google.common.collect.ImmutableSet;
11 import com.google.common.collect.Iterables;
12 import java.util.Collection;
13 import java.util.Objects;
14 import java.util.Set;
15 import javax.annotation.Nonnull;
16 import org.opendaylight.yangtools.yang.common.YangVersion;
17 import org.opendaylight.yangtools.yang.model.api.DeviateKind;
18 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
19 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
20 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
21 import org.opendaylight.yangtools.yang.model.api.stmt.DeviateStatement;
22 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
23 import org.opendaylight.yangtools.yang.parser.spi.SubstatementValidator;
24 import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
25 import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
26 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
27 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
28 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
29 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceAction;
30 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prerequisite;
31 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
32 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
33 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
34 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
35 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.DeviateEffectiveStatementImpl;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 public class DeviateStatementImpl extends AbstractDeclaredStatement<DeviateKind> implements DeviateStatement {
40     private static final Logger LOG = LoggerFactory.getLogger(DeviateStatementImpl.class);
41
42     private static final SubstatementValidator DEVIATE_NOT_SUPPORTED_SUBSTATEMENT_VALIDATOR =
43             SubstatementValidator.builder(YangStmtMapping.DEVIATE).build();
44
45     private static final SubstatementValidator DEVIATE_ADD_SUBSTATEMENT_VALIDATOR =
46             SubstatementValidator.builder(YangStmtMapping.DEVIATE)
47                 .addOptional(YangStmtMapping.CONFIG)
48                 .addOptional(YangStmtMapping.DEFAULT)
49                 .addOptional(YangStmtMapping.MANDATORY)
50                 .addOptional(YangStmtMapping.MAX_ELEMENTS)
51                 .addOptional(YangStmtMapping.MIN_ELEMENTS)
52                 .addAny(YangStmtMapping.MUST)
53                 .addAny(YangStmtMapping.UNIQUE)
54                 .addOptional(YangStmtMapping.UNITS)
55                 .build();
56
57     private static final SubstatementValidator DEVIATE_REPLACE_SUBSTATEMENT_VALIDATOR =
58             SubstatementValidator.builder(YangStmtMapping.DEVIATE)
59                 .addOptional(YangStmtMapping.CONFIG)
60                 .addOptional(YangStmtMapping.DEFAULT)
61                 .addOptional(YangStmtMapping.MANDATORY)
62                 .addOptional(YangStmtMapping.MAX_ELEMENTS)
63                 .addOptional(YangStmtMapping.MIN_ELEMENTS)
64                 .addOptional(YangStmtMapping.TYPE)
65                 .addOptional(YangStmtMapping.UNITS)
66                 .build();
67
68     private static final SubstatementValidator DEVIATE_DELETE_SUBSTATEMENT_VALIDATOR =
69             SubstatementValidator.builder(YangStmtMapping.DEVIATE)
70                 .addOptional(YangStmtMapping.DEFAULT)
71                 .addAny(YangStmtMapping.MUST)
72                 .addAny(YangStmtMapping.UNIQUE)
73                 .addOptional(YangStmtMapping.UNITS)
74                 .build();
75
76     protected DeviateStatementImpl(final StmtContext<DeviateKind, DeviateStatement, ?> context) {
77         super(context);
78     }
79
80     public static class Definition extends AbstractStatementSupport<DeviateKind, DeviateStatement,
81             EffectiveStatement<DeviateKind, DeviateStatement>> {
82
83         private static final Set<YangStmtMapping> SINGLETON_STATEMENTS = ImmutableSet.of(
84                 YangStmtMapping.UNITS, YangStmtMapping.CONFIG, YangStmtMapping.MANDATORY,
85                 YangStmtMapping.MIN_ELEMENTS, YangStmtMapping.MAX_ELEMENTS);
86
87         public Definition() {
88             super(YangStmtMapping.DEVIATE);
89         }
90
91         @Override public DeviateKind parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
92             return Utils.parseDeviateFromString(ctx, value);
93         }
94
95         @Override public DeviateStatement createDeclared(final StmtContext<DeviateKind, DeviateStatement, ?> ctx) {
96             return new DeviateStatementImpl(ctx);
97         }
98
99         @Override public EffectiveStatement<DeviateKind, DeviateStatement> createEffective(
100                 final StmtContext<DeviateKind, DeviateStatement, EffectiveStatement<DeviateKind,
101                         DeviateStatement>> ctx) {
102             return new DeviateEffectiveStatementImpl(ctx);
103         }
104
105         @Override
106         public void onFullDefinitionDeclared(final StmtContext.Mutable<DeviateKind, DeviateStatement,
107                 EffectiveStatement<DeviateKind, DeviateStatement>> deviateStmtCtx) {
108             final DeviateKind deviateKind = deviateStmtCtx.getStatementArgument();
109             getSubstatementValidatorForDeviate(deviateKind).validate(deviateStmtCtx);
110
111             final ModelActionBuilder deviateAction = deviateStmtCtx.newInferenceAction(
112                     ModelProcessingPhase.EFFECTIVE_MODEL);
113
114             final SchemaNodeIdentifier deviationTarget =
115                     (SchemaNodeIdentifier) deviateStmtCtx.getParentContext().getStatementArgument();
116
117             final Prerequisite<StmtContext<DeviateKind, DeviateStatement, EffectiveStatement<DeviateKind,
118                     DeviateStatement>>> sourceCtxPrerequisite =
119                     deviateAction.requiresCtx(deviateStmtCtx, ModelProcessingPhase.EFFECTIVE_MODEL);
120
121             final Prerequisite<StmtContext.Mutable<?, ?, EffectiveStatement<?, ?>>> targetCtxPrerequisite =
122                     (Prerequisite<StmtContext.Mutable<?, ?, EffectiveStatement<?, ?>>>) deviateAction
123                     .mutatesEffectiveCtx(deviateStmtCtx.getRoot(), SchemaNodeIdentifierBuildNamespace.class,
124                             deviationTarget);
125
126                     deviateAction.apply(new InferenceAction() {
127                         @Override
128                         public void apply() throws InferenceException {
129                             // FIXME once BUG-7760 gets fixed, there will be no need for these dirty casts
130                             final StatementContextBase<?, ?, ?> sourceNodeStmtCtx =
131                                     (StatementContextBase<?, ?, ?>) sourceCtxPrerequisite.get();
132                             final StatementContextBase<?, ?, ?> targetNodeStmtCtx =
133                                     (StatementContextBase<?, ?, ?>) targetCtxPrerequisite.get();
134
135                             switch (deviateKind) {
136                                 case NOT_SUPPORTED:
137                                     targetNodeStmtCtx.setIsSupportedToBuildEffective(false);
138                                     break;
139                                 case ADD:
140                                     performDeviateAdd(sourceNodeStmtCtx, targetNodeStmtCtx);
141                                     break;
142                                 case REPLACE:
143                                     performDeviateReplace(sourceNodeStmtCtx, targetNodeStmtCtx);
144                                     break;
145                                 case DELETE:
146                                     performDeviateDelete(sourceNodeStmtCtx, targetNodeStmtCtx);
147                             }
148                         }
149
150                         @Override
151                         public void prerequisiteFailed(Collection<? extends Prerequisite<?>> failed) {
152                             throw new InferenceException(deviateStmtCtx.getParentContext().getStatementSourceReference(),
153                                     "Deviation target '%s' not found.", deviationTarget);
154                         }
155                     });
156         }
157
158         private static void performDeviateAdd(final StatementContextBase<?, ?, ?> deviateStmtCtx,
159                 final StatementContextBase<?, ?, ?> targetCtx) {
160             for (StatementContextBase<?, ?, ?> originalStmtCtx : deviateStmtCtx.declaredSubstatements()) {
161                 validateDeviationTarget(originalStmtCtx, targetCtx);
162                 addStatement(originalStmtCtx, targetCtx);
163             }
164         }
165
166         private static void addStatement(final StatementContextBase<?, ?, ?> stmtCtxToBeAdded,
167                 final StatementContextBase<?, ?, ?> targetCtx) {
168             if (StmtContextUtils.isUnknownStatement(stmtCtxToBeAdded)) {
169                 targetCtx.addEffectiveSubstatement(stmtCtxToBeAdded.createCopy(targetCtx, CopyType.ORIGINAL));
170                 return;
171             }
172
173             final StatementDefinition stmtToBeAdded = stmtCtxToBeAdded.getPublicDefinition();
174
175             if (SINGLETON_STATEMENTS.contains(stmtToBeAdded) || (YangStmtMapping.DEFAULT.equals(stmtToBeAdded)
176                     && YangStmtMapping.LEAF.equals(targetCtx.getPublicDefinition()))) {
177                 final Iterable<StatementContextBase<?, ?, ?>> targetCtxSubstatements = Iterables.concat(
178                         targetCtx.declaredSubstatements(), targetCtx.effectiveSubstatements());
179
180                 for (final StatementContextBase<?, ?, ?> targetCtxSubstatement : targetCtxSubstatements) {
181                     InferenceException.throwIf(stmtToBeAdded.equals(targetCtxSubstatement.getPublicDefinition()),
182                             stmtCtxToBeAdded.getStatementSourceReference(), "Deviation cannot add substatement %s " +
183                             "to target node %s because it is already defined in target and can appear only once.",
184                             stmtToBeAdded.getStatementName(), targetCtx.getStatementArgument());
185                 }
186             }
187
188             targetCtx.addEffectiveSubstatement(stmtCtxToBeAdded.createCopy(targetCtx, CopyType.ORIGINAL));
189         }
190
191         private static void performDeviateReplace(final StatementContextBase<?, ?, ?> deviateStmtCtx,
192                 final StatementContextBase<?, ?, ?> targetCtx) {
193             for (StatementContextBase<?, ?, ?> originalStmtCtx : deviateStmtCtx.declaredSubstatements()) {
194                 validateDeviationTarget(originalStmtCtx, targetCtx);
195                 replaceStatement(originalStmtCtx, targetCtx);
196             }
197         }
198
199         private static void replaceStatement(final StatementContextBase<?, ?, ?> stmtCtxToBeReplaced,
200                 final StatementContextBase<?, ?, ?> targetCtx) {
201             final StatementDefinition stmtToBeReplaced = stmtCtxToBeReplaced.getPublicDefinition();
202
203             if (YangStmtMapping.DEFAULT.equals(stmtToBeReplaced)
204                     && YangStmtMapping.LEAF_LIST.equals(targetCtx.getPublicDefinition())) {
205                 LOG.error("Deviation cannot replace substatement {} in target leaf-list {} because a leaf-list can " +
206                         "have multiple default statements. At line: {}", stmtToBeReplaced.getStatementName(),
207                         targetCtx.getStatementArgument(), stmtCtxToBeReplaced.getStatementSourceReference());
208                 return;
209             }
210
211             for (final StatementContextBase<?, ?, ?> targetCtxSubstatement : targetCtx.effectiveSubstatements()) {
212                 if (stmtToBeReplaced.equals(targetCtxSubstatement.getPublicDefinition())) {
213                     targetCtx.removeStatementFromEffectiveSubstatements(stmtToBeReplaced);
214                     targetCtx.addEffectiveSubstatement(stmtCtxToBeReplaced.createCopy(targetCtx, CopyType.ORIGINAL));
215                     return;
216                 }
217             }
218
219             for (final StatementContextBase<?, ?, ?> targetCtxSubstatement : targetCtx.declaredSubstatements()) {
220                 if (stmtToBeReplaced.equals(targetCtxSubstatement.getPublicDefinition())) {
221                     targetCtxSubstatement.setIsSupportedToBuildEffective(false);
222                     targetCtx.addEffectiveSubstatement(stmtCtxToBeReplaced.createCopy(targetCtx, CopyType.ORIGINAL));
223                     return;
224                 }
225             }
226
227             throw new InferenceException(stmtCtxToBeReplaced.getStatementSourceReference(), "Deviation cannot " +
228                     "replace substatement %s in target node %s because it does not exist in target node.",
229                     stmtToBeReplaced.getStatementName(), targetCtx.getStatementArgument());
230         }
231
232         private static void performDeviateDelete(final StatementContextBase<?, ?, ?> deviateStmtCtx,
233                 final StatementContextBase<?, ?, ?> targetCtx) {
234             for (StatementContextBase<?, ?, ?> originalStmtCtx : deviateStmtCtx.declaredSubstatements()) {
235                 validateDeviationTarget(originalStmtCtx, targetCtx);
236                 deleteStatement(originalStmtCtx, targetCtx);
237             }
238         }
239
240         private static void deleteStatement(final StatementContextBase<?, ?, ?> stmtCtxToBeDeleted,
241                 final StatementContextBase<?, ?, ?> targetCtx) {
242             final StatementDefinition stmtToBeDeleted = stmtCtxToBeDeleted.getPublicDefinition();
243             final String stmtArgument = stmtCtxToBeDeleted.rawStatementArgument();
244
245             for (final StatementContextBase<?, ?, ?> targetCtxSubstatement : targetCtx.effectiveSubstatements()) {
246                 if (statementsAreEqual(stmtToBeDeleted, stmtArgument, targetCtxSubstatement.getPublicDefinition(),
247                         targetCtxSubstatement.rawStatementArgument())) {
248                     targetCtx.removeStatementFromEffectiveSubstatements(stmtToBeDeleted, stmtArgument);
249                     return;
250                 }
251             }
252
253             for (final StatementContextBase<?, ?, ?> targetCtxSubstatement : targetCtx.declaredSubstatements()) {
254                 if (statementsAreEqual(stmtToBeDeleted, stmtArgument, targetCtxSubstatement.getPublicDefinition(),
255                         targetCtxSubstatement.rawStatementArgument())) {
256                     targetCtxSubstatement.setIsSupportedToBuildEffective(false);
257                     return;
258                 }
259             }
260
261             LOG.error("Deviation cannot delete substatement {} with argument '{}' in target node {} because it does " +
262                     "not exist in the target node. At line: {}", stmtToBeDeleted.getStatementName(), stmtArgument,
263                     targetCtx.getStatementArgument(), stmtCtxToBeDeleted.getStatementSourceReference());
264         }
265
266         private static boolean statementsAreEqual(final StatementDefinition firstStmtDef, final String firstStmtArg,
267                 final StatementDefinition secondStmtDef, final String secondStmtArg) {
268             return firstStmtDef.equals(secondStmtDef) && Objects.equals(firstStmtArg, secondStmtArg);
269         }
270
271         private static void validateDeviationTarget(final StatementContextBase<?, ?, ?> deviateSubStmtCtx,
272                 final StatementContextBase<?, ?, ?> targetCtx) {
273             InferenceException.throwIf(!isSupportedDeviationTarget(deviateSubStmtCtx, targetCtx,
274                     targetCtx.getRootVersion()), deviateSubStmtCtx.getStatementSourceReference(),
275                     "%s is not a valid deviation target for substatement %s.",
276                     targetCtx.getStatementArgument(), deviateSubStmtCtx.getPublicDefinition().getStatementName());
277         }
278
279         private static boolean isSupportedDeviationTarget(final StatementContextBase<?, ?, ?> deviateSubstatementCtx,
280                 final StatementContextBase<?, ?, ?> deviateTargetCtx, final YangVersion yangVersion) {
281             Set<StatementDefinition> supportedDeviationTargets =
282                     YangValidationBundles.SUPPORTED_DEVIATION_TARGETS.get(deviateTargetCtx.getRootVersion(),
283                             deviateSubstatementCtx.getPublicDefinition());
284
285             if (supportedDeviationTargets == null) {
286                 supportedDeviationTargets = YangValidationBundles.SUPPORTED_DEVIATION_TARGETS.get(YangVersion.VERSION_1,
287                         deviateSubstatementCtx.getPublicDefinition());
288             }
289
290             // if supportedDeviationTargets is null, it means that the deviate substatement is an unknown statement
291             return supportedDeviationTargets == null || supportedDeviationTargets.contains(
292                     deviateTargetCtx.getPublicDefinition());
293         }
294
295         protected SubstatementValidator getSubstatementValidatorForDeviate(final DeviateKind deviateKind) {
296             switch (deviateKind) {
297                 case NOT_SUPPORTED:
298                     return DEVIATE_NOT_SUPPORTED_SUBSTATEMENT_VALIDATOR;
299                 case ADD:
300                     return DEVIATE_ADD_SUBSTATEMENT_VALIDATOR;
301                 case REPLACE:
302                     return DEVIATE_REPLACE_SUBSTATEMENT_VALIDATOR;
303                 case DELETE:
304                     return DEVIATE_DELETE_SUBSTATEMENT_VALIDATOR;
305                 default:
306                     throw new IllegalStateException(String.format(
307                             "Substatement validator for deviate %s has not been defined.", deviateKind));
308             }
309         }
310
311         @Override
312         protected SubstatementValidator getSubstatementValidator() {
313             return null;
314         }
315     }
316
317     @Nonnull @Override
318     public DeviateKind getValue() {
319         return argument();
320     }
321 }