YANGTOOLS-706: Split out yang-parser-rfc7950
[yangtools.git] / yang / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / stmt / deviate / AbstractDeviateStatementSupport.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.deviate;
9
10 import com.google.common.collect.ImmutableMap;
11 import com.google.common.collect.ImmutableMap.Builder;
12 import com.google.common.collect.ImmutableSet;
13 import java.util.Collection;
14 import java.util.Map;
15 import java.util.Objects;
16 import java.util.Set;
17 import org.opendaylight.yangtools.yang.common.QNameModule;
18 import org.opendaylight.yangtools.yang.common.YangVersion;
19 import org.opendaylight.yangtools.yang.model.api.DeviateKind;
20 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
21 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
22 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
23 import org.opendaylight.yangtools.yang.model.api.stmt.DeviateStatement;
24 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
25 import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.SchemaNodeIdentifierBuildNamespace;
26 import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.YangValidationBundles;
27 import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
28 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
29 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
30 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
31 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceAction;
32 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceContext;
33 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prerequisite;
34 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
35 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
36 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
37 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
38 import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
39 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
40 import org.opendaylight.yangtools.yang.parser.spi.source.ModulesDeviatedByModules;
41 import org.opendaylight.yangtools.yang.parser.spi.source.ModulesDeviatedByModules.SupportedModules;
42 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
43 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 abstract class AbstractDeviateStatementSupport extends AbstractStatementSupport<DeviateKind, DeviateStatement,
48         EffectiveStatement<DeviateKind, DeviateStatement>> {
49     private static final Logger LOG = LoggerFactory.getLogger(DeviateStatementImpl.class);
50
51     private static final SubstatementValidator DEVIATE_NOT_SUPPORTED_SUBSTATEMENT_VALIDATOR =
52             SubstatementValidator.builder(YangStmtMapping.DEVIATE).build();
53
54     private static final SubstatementValidator DEVIATE_ADD_SUBSTATEMENT_VALIDATOR =
55             SubstatementValidator.builder(YangStmtMapping.DEVIATE)
56                 .addOptional(YangStmtMapping.CONFIG)
57                 .addOptional(YangStmtMapping.DEFAULT)
58                 .addOptional(YangStmtMapping.MANDATORY)
59                 .addOptional(YangStmtMapping.MAX_ELEMENTS)
60                 .addOptional(YangStmtMapping.MIN_ELEMENTS)
61                 .addAny(YangStmtMapping.MUST)
62                 .addAny(YangStmtMapping.UNIQUE)
63                 .addOptional(YangStmtMapping.UNITS)
64                 .build();
65
66     private static final SubstatementValidator DEVIATE_REPLACE_SUBSTATEMENT_VALIDATOR =
67             SubstatementValidator.builder(YangStmtMapping.DEVIATE)
68                 .addOptional(YangStmtMapping.CONFIG)
69                 .addOptional(YangStmtMapping.DEFAULT)
70                 .addOptional(YangStmtMapping.MANDATORY)
71                 .addOptional(YangStmtMapping.MAX_ELEMENTS)
72                 .addOptional(YangStmtMapping.MIN_ELEMENTS)
73                 .addOptional(YangStmtMapping.TYPE)
74                 .addOptional(YangStmtMapping.UNITS)
75                 .build();
76
77     private static final SubstatementValidator DEVIATE_DELETE_SUBSTATEMENT_VALIDATOR =
78             SubstatementValidator.builder(YangStmtMapping.DEVIATE)
79                 .addOptional(YangStmtMapping.DEFAULT)
80                 .addAny(YangStmtMapping.MUST)
81                 .addAny(YangStmtMapping.UNIQUE)
82                 .addOptional(YangStmtMapping.UNITS)
83                 .build();
84
85     private static final Map<String, DeviateKind> KEYWORD_TO_DEVIATE_MAP;
86
87     static {
88         final Builder<String, DeviateKind> keywordToDeviateMapBuilder = ImmutableMap.builder();
89         for (final DeviateKind deviate : DeviateKind.values()) {
90             keywordToDeviateMapBuilder.put(deviate.getKeyword(), deviate);
91         }
92         KEYWORD_TO_DEVIATE_MAP = keywordToDeviateMapBuilder.build();
93     }
94
95     private static final Set<YangStmtMapping> SINGLETON_STATEMENTS = ImmutableSet.of(
96             YangStmtMapping.UNITS, YangStmtMapping.CONFIG, YangStmtMapping.MANDATORY,
97             YangStmtMapping.MIN_ELEMENTS, YangStmtMapping.MAX_ELEMENTS);
98
99     private static final Set<YangStmtMapping> IMPLICIT_STATEMENTS = ImmutableSet.of(YangStmtMapping.CONFIG,
100             YangStmtMapping.MANDATORY, YangStmtMapping.MAX_ELEMENTS, YangStmtMapping.MIN_ELEMENTS);
101
102     AbstractDeviateStatementSupport() {
103         super(YangStmtMapping.DEVIATE);
104     }
105
106     @Override
107     public final DeviateKind parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
108         return SourceException.throwIfNull(KEYWORD_TO_DEVIATE_MAP.get(value),
109             ctx.getStatementSourceReference(), "String '%s' is not valid deviate argument", value);
110     }
111
112     @Override
113     public final DeviateStatement createDeclared(final StmtContext<DeviateKind, DeviateStatement, ?> ctx) {
114         return new DeviateStatementImpl(ctx);
115     }
116
117     @Override
118     public final EffectiveStatement<DeviateKind, DeviateStatement> createEffective(
119             final StmtContext<DeviateKind, DeviateStatement, EffectiveStatement<DeviateKind,
120                     DeviateStatement>> ctx) {
121         return new DeviateEffectiveStatementImpl(ctx);
122     }
123
124     @Override
125     public final void onFullDefinitionDeclared(final Mutable<DeviateKind, DeviateStatement,
126             EffectiveStatement<DeviateKind, DeviateStatement>> deviateStmtCtx) {
127         final DeviateKind deviateKind = deviateStmtCtx.getStatementArgument();
128         getSubstatementValidatorForDeviate(deviateKind).validate(deviateStmtCtx);
129
130         final SchemaNodeIdentifier deviationTarget =
131                 (SchemaNodeIdentifier) deviateStmtCtx.getParentContext().getStatementArgument();
132
133         if (!isDeviationSupported(deviateStmtCtx, deviationTarget)) {
134             return;
135         }
136
137         final ModelActionBuilder deviateAction = deviateStmtCtx.newInferenceAction(
138                 ModelProcessingPhase.EFFECTIVE_MODEL);
139
140         final Prerequisite<StmtContext<DeviateKind, DeviateStatement, EffectiveStatement<DeviateKind,
141                 DeviateStatement>>> sourceCtxPrerequisite =
142                 deviateAction.requiresCtx(deviateStmtCtx, ModelProcessingPhase.EFFECTIVE_MODEL);
143
144         final Prerequisite<Mutable<?, ?, EffectiveStatement<?, ?>>> targetCtxPrerequisite =
145                 deviateAction.mutatesEffectiveCtx(deviateStmtCtx.getRoot(),
146                     SchemaNodeIdentifierBuildNamespace.class,  deviationTarget);
147
148         deviateAction.apply(new InferenceAction() {
149             @Override
150             public void apply(final InferenceContext ctx) throws InferenceException {
151                 // FIXME once BUG-7760 gets fixed, there will be no need for these dirty casts
152                 final StatementContextBase<?, ?, ?> sourceNodeStmtCtx =
153                         (StatementContextBase<?, ?, ?>) sourceCtxPrerequisite.resolve(ctx);
154                 final StatementContextBase<?, ?, ?> targetNodeStmtCtx =
155                         (StatementContextBase<?, ?, ?>) targetCtxPrerequisite.resolve(ctx);
156
157                 switch (deviateKind) {
158                     case NOT_SUPPORTED:
159                         targetNodeStmtCtx.setIsSupportedToBuildEffective(false);
160                         break;
161                     case ADD:
162                         performDeviateAdd(sourceNodeStmtCtx, targetNodeStmtCtx);
163                         break;
164                     case REPLACE:
165                         performDeviateReplace(sourceNodeStmtCtx, targetNodeStmtCtx);
166                         break;
167                     case DELETE:
168                         performDeviateDelete(sourceNodeStmtCtx, targetNodeStmtCtx);
169                         break;
170                     default:
171                         throw new IllegalStateException("Unsupported deviate " + deviateKind);
172                 }
173             }
174
175             @Override
176             public void prerequisiteFailed(final Collection<? extends Prerequisite<?>> failed) {
177                 throw new InferenceException(deviateStmtCtx.getParentContext().getStatementSourceReference(),
178                     "Deviation target '%s' not found.", deviationTarget);
179             }
180         });
181     }
182
183     @Override
184     protected final SubstatementValidator getSubstatementValidator() {
185         return null;
186     }
187
188     protected SubstatementValidator getSubstatementValidatorForDeviate(final DeviateKind deviateKind) {
189         switch (deviateKind) {
190             case NOT_SUPPORTED:
191                 return DEVIATE_NOT_SUPPORTED_SUBSTATEMENT_VALIDATOR;
192             case ADD:
193                 return DEVIATE_ADD_SUBSTATEMENT_VALIDATOR;
194             case REPLACE:
195                 return DEVIATE_REPLACE_SUBSTATEMENT_VALIDATOR;
196             case DELETE:
197                 return DEVIATE_DELETE_SUBSTATEMENT_VALIDATOR;
198             default:
199                 throw new IllegalStateException(String.format(
200                         "Substatement validator for deviate %s has not been defined.", deviateKind));
201         }
202     }
203
204     private static boolean isDeviationSupported(final Mutable<DeviateKind, DeviateStatement,
205             EffectiveStatement<DeviateKind, DeviateStatement>> deviateStmtCtx,
206             final SchemaNodeIdentifier deviationTarget) {
207         final Map<QNameModule, Set<QNameModule>> modulesDeviatedByModules = deviateStmtCtx.getFromNamespace(
208                 ModulesDeviatedByModules.class, SupportedModules.SUPPORTED_MODULES);
209         if (modulesDeviatedByModules == null) {
210             return true;
211         }
212
213         final QNameModule currentModule = deviateStmtCtx.getFromNamespace(ModuleCtxToModuleQName.class,
214                 deviateStmtCtx.getRoot());
215         final QNameModule targetModule = deviationTarget.getLastComponent().getModule();
216
217         final Set<QNameModule> deviationModulesSupportedByTargetModule = modulesDeviatedByModules.get(targetModule);
218         if (deviationModulesSupportedByTargetModule != null) {
219             return deviationModulesSupportedByTargetModule.contains(currentModule);
220         }
221
222         return false;
223     }
224
225     private static void performDeviateAdd(final StatementContextBase<?, ?, ?> deviateStmtCtx,
226             final StatementContextBase<?, ?, ?> targetCtx) {
227         for (Mutable<?, ?, ?> originalStmtCtx : deviateStmtCtx.mutableDeclaredSubstatements()) {
228             validateDeviationTarget(originalStmtCtx, targetCtx);
229             addStatement(originalStmtCtx, targetCtx);
230         }
231     }
232
233     private static void addStatement(final Mutable<?, ?, ?> stmtCtxToBeAdded,
234             final StatementContextBase<?, ?, ?> targetCtx) {
235         if (!StmtContextUtils.isUnknownStatement(stmtCtxToBeAdded)) {
236             final StatementDefinition stmtToBeAdded = stmtCtxToBeAdded.getPublicDefinition();
237             if (SINGLETON_STATEMENTS.contains(stmtToBeAdded) || YangStmtMapping.DEFAULT.equals(stmtToBeAdded)
238                     && YangStmtMapping.LEAF.equals(targetCtx.getPublicDefinition())) {
239                 for (final StmtContext<?, ?, ?> targetCtxSubstatement : targetCtx.allSubstatements()) {
240                     InferenceException.throwIf(stmtToBeAdded.equals(targetCtxSubstatement.getPublicDefinition()),
241                         stmtCtxToBeAdded.getStatementSourceReference(),
242                         "Deviation cannot add substatement %s to target node %s because it is already defined "
243                         + "in target and can appear only once.",
244                         stmtToBeAdded.getStatementName(), targetCtx.getStatementArgument());
245                 }
246             }
247         }
248
249         copyStatement(stmtCtxToBeAdded, targetCtx);
250     }
251
252     private static void performDeviateReplace(final StatementContextBase<?, ?, ?> deviateStmtCtx,
253             final StatementContextBase<?, ?, ?> targetCtx) {
254         for (Mutable<?, ?, ?> originalStmtCtx : deviateStmtCtx.mutableDeclaredSubstatements()) {
255             validateDeviationTarget(originalStmtCtx, targetCtx);
256             replaceStatement(originalStmtCtx, targetCtx);
257         }
258     }
259
260     private static void replaceStatement(final Mutable<?, ?, ?> stmtCtxToBeReplaced,
261             final StatementContextBase<?, ?, ?> targetCtx) {
262         final StatementDefinition stmtToBeReplaced = stmtCtxToBeReplaced.getPublicDefinition();
263
264         if (YangStmtMapping.DEFAULT.equals(stmtToBeReplaced)
265                 && YangStmtMapping.LEAF_LIST.equals(targetCtx.getPublicDefinition())) {
266             LOG.error("Deviation cannot replace substatement {} in target leaf-list {} because a leaf-list can "
267                     + "have multiple default statements. At line: {}", stmtToBeReplaced.getStatementName(),
268                     targetCtx.getStatementArgument(), stmtCtxToBeReplaced.getStatementSourceReference());
269             return;
270         }
271
272         for (final StmtContext<?, ?, ?> targetCtxSubstatement : targetCtx.effectiveSubstatements()) {
273             if (stmtToBeReplaced.equals(targetCtxSubstatement.getPublicDefinition())) {
274                 targetCtx.removeStatementFromEffectiveSubstatements(stmtToBeReplaced);
275                 copyStatement(stmtCtxToBeReplaced, targetCtx);
276                 return;
277             }
278         }
279
280         for (final Mutable<?, ?, ?> targetCtxSubstatement : targetCtx.mutableDeclaredSubstatements()) {
281             if (stmtToBeReplaced.equals(targetCtxSubstatement.getPublicDefinition())) {
282                 targetCtxSubstatement.setIsSupportedToBuildEffective(false);
283                 copyStatement(stmtCtxToBeReplaced, targetCtx);
284                 return;
285             }
286         }
287
288         // This is a special case when deviate replace of a config/mandatory/max/min-elements substatement targets
289         // a node which does not contain an explicitly declared config/mandatory/max/min-elements.
290         // However, according to RFC6020/RFC7950, these properties are always implicitly present.
291         if (IMPLICIT_STATEMENTS.contains(stmtToBeReplaced)) {
292             addStatement(stmtCtxToBeReplaced, targetCtx);
293             return;
294         }
295
296         throw new InferenceException(stmtCtxToBeReplaced.getStatementSourceReference(), "Deviation cannot replace "
297                 + "substatement %s in target node %s because it does not exist in target node.",
298                 stmtToBeReplaced.getStatementName(), targetCtx.getStatementArgument());
299     }
300
301     private static void performDeviateDelete(final StatementContextBase<?, ?, ?> deviateStmtCtx,
302             final StatementContextBase<?, ?, ?> targetCtx) {
303         for (Mutable<?, ?, ?> originalStmtCtx : deviateStmtCtx.mutableDeclaredSubstatements()) {
304             validateDeviationTarget(originalStmtCtx, targetCtx);
305             deleteStatement(originalStmtCtx, targetCtx);
306         }
307     }
308
309     private static void deleteStatement(final StmtContext<?, ?, ?> stmtCtxToBeDeleted,
310             final StatementContextBase<?, ?, ?> targetCtx) {
311         final StatementDefinition stmtToBeDeleted = stmtCtxToBeDeleted.getPublicDefinition();
312         final String stmtArgument = stmtCtxToBeDeleted.rawStatementArgument();
313
314         for (final Mutable<?, ?, ?> targetCtxSubstatement : targetCtx.mutableEffectiveSubstatements()) {
315             if (statementsAreEqual(stmtToBeDeleted, stmtArgument, targetCtxSubstatement.getPublicDefinition(),
316                     targetCtxSubstatement.rawStatementArgument())) {
317                 targetCtx.removeStatementFromEffectiveSubstatements(stmtToBeDeleted, stmtArgument);
318                 return;
319             }
320         }
321
322         for (final Mutable<?, ?, ?> targetCtxSubstatement : targetCtx.mutableDeclaredSubstatements()) {
323             if (statementsAreEqual(stmtToBeDeleted, stmtArgument, targetCtxSubstatement.getPublicDefinition(),
324                     targetCtxSubstatement.rawStatementArgument())) {
325                 targetCtxSubstatement.setIsSupportedToBuildEffective(false);
326                 return;
327             }
328         }
329
330         LOG.error("Deviation cannot delete substatement {} with argument '{}' in target node {} because it does "
331                 + "not exist in the target node. At line: {}", stmtToBeDeleted.getStatementName(), stmtArgument,
332                 targetCtx.getStatementArgument(), stmtCtxToBeDeleted.getStatementSourceReference());
333     }
334
335     private static void copyStatement(final Mutable<?, ?, ?> stmtCtxToBeCopied,
336             final StatementContextBase<?, ?, ?> targetCtx) {
337         // we need to make a copy of the statement context only if it is an unknown statement, otherwise
338         // we can reuse the original statement context
339         if (!StmtContextUtils.isUnknownStatement(stmtCtxToBeCopied)) {
340             targetCtx.addEffectiveSubstatement(stmtCtxToBeCopied);
341         } else {
342             targetCtx.addEffectiveSubstatement(targetCtx.childCopyOf(stmtCtxToBeCopied, CopyType.ORIGINAL));
343         }
344     }
345
346     private static boolean statementsAreEqual(final StatementDefinition firstStmtDef, final String firstStmtArg,
347             final StatementDefinition secondStmtDef, final String secondStmtArg) {
348         return firstStmtDef.equals(secondStmtDef) && Objects.equals(firstStmtArg, secondStmtArg);
349     }
350
351     private static void validateDeviationTarget(final StmtContext<?, ?, ?> deviateSubStmtCtx,
352             final StmtContext<?, ?, ?> targetCtx) {
353         InferenceException.throwIf(!isSupportedDeviationTarget(deviateSubStmtCtx, targetCtx,
354                 targetCtx.getRootVersion()), deviateSubStmtCtx.getStatementSourceReference(),
355                 "%s is not a valid deviation target for substatement %s.",
356                 targetCtx.getStatementArgument(), deviateSubStmtCtx.getPublicDefinition().getStatementName());
357     }
358
359     private static boolean isSupportedDeviationTarget(final StmtContext<?, ?, ?> deviateSubstatementCtx,
360             final StmtContext<?, ?, ?> deviateTargetCtx, final YangVersion yangVersion) {
361         Set<StatementDefinition> supportedDeviationTargets =
362                 YangValidationBundles.SUPPORTED_DEVIATION_TARGETS.get(deviateTargetCtx.getRootVersion(),
363                         deviateSubstatementCtx.getPublicDefinition());
364
365         if (supportedDeviationTargets == null) {
366             supportedDeviationTargets = YangValidationBundles.SUPPORTED_DEVIATION_TARGETS.get(YangVersion.VERSION_1,
367                     deviateSubstatementCtx.getPublicDefinition());
368         }
369
370         // if supportedDeviationTargets is null, it means that the deviate substatement is an unknown statement
371         return supportedDeviationTargets == null || supportedDeviationTargets.contains(
372                 deviateTargetCtx.getPublicDefinition());
373     }
374 }