DeclaredStatements can contain default implementations
[yangtools.git] / yang / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / stmt / uses / UsesStatementImpl.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.rfc7950.stmt.uses;
9
10 import com.google.common.base.Verify;
11 import com.google.common.collect.ImmutableSet;
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.Optional;
15 import java.util.Set;
16 import org.opendaylight.yangtools.yang.common.QName;
17 import org.opendaylight.yangtools.yang.common.QNameModule;
18 import org.opendaylight.yangtools.yang.common.YangVersion;
19 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
20 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
21 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
22 import org.opendaylight.yangtools.yang.model.api.stmt.RefineStatement;
23 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
24 import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement;
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.AbstractDeclaredStatement;
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.StmtContext;
31 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
32 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
33 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
34 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
35 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace;
36 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType;
37 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 final class UsesStatementImpl extends AbstractDeclaredStatement<QName> implements UsesStatement {
42     private static final Logger LOG = LoggerFactory.getLogger(UsesStatementImpl.class);
43
44     UsesStatementImpl(final StmtContext<QName, UsesStatement, ?> context) {
45         super(context);
46     }
47
48     /**
49      * Copy statements from a grouping to a target node.
50      *
51      * @param sourceGrpStmtCtx
52      *            source grouping statement context
53      * @param targetCtx
54      *            target context
55      * @param usesNode
56      *            uses node
57      * @throws SourceException
58      *             instance of SourceException
59      */
60     static void copyFromSourceToTarget(final Mutable<?, ?, ?> sourceGrpStmtCtx,
61             final StatementContextBase<?, ?, ?> targetCtx,
62             final Mutable<QName, UsesStatement, EffectiveStatement<QName, UsesStatement>> usesNode) {
63         final Collection<? extends Mutable<?, ?, ?>> declared = sourceGrpStmtCtx.mutableDeclaredSubstatements();
64         final Collection<? extends Mutable<?, ?, ?>> effective = sourceGrpStmtCtx.mutableEffectiveSubstatements();
65         final Collection<Mutable<?, ?, ?>> buffer = new ArrayList<>(declared.size() + effective.size());
66         final QNameModule newQNameModule = getNewQNameModule(targetCtx, sourceGrpStmtCtx);
67
68         for (final Mutable<?, ?, ?> original : declared) {
69             if (original.isSupportedByFeatures()) {
70                 copyStatement(original, targetCtx, newQNameModule, buffer);
71             }
72         }
73
74         for (final Mutable<?, ?, ?> original : effective) {
75             copyStatement(original, targetCtx, newQNameModule, buffer);
76         }
77
78         targetCtx.addEffectiveSubstatements(buffer);
79         usesNode.addAsEffectOfStatement(buffer);
80     }
81
82     private static void copyStatement(final Mutable<?, ?, ?> original,
83             final StatementContextBase<?, ?, ?> targetCtx, final QNameModule targetModule,
84             final Collection<Mutable<?, ?, ?>> buffer) {
85         if (needToCopyByUses(original)) {
86             final Mutable<?, ?, ?> copy = targetCtx.childCopyOf(original, CopyType.ADDED_BY_USES, targetModule);
87             buffer.add(copy);
88         } else if (isReusedByUsesOnTop(original)) {
89             buffer.add(original);
90         }
91     }
92
93     private static final Set<YangStmtMapping> TOP_REUSED_DEF_SET = ImmutableSet.of(
94         YangStmtMapping.TYPE,
95         YangStmtMapping.TYPEDEF);
96
97     private static boolean isReusedByUsesOnTop(final StmtContext<?, ?, ?> stmtContext) {
98         return TOP_REUSED_DEF_SET.contains(stmtContext.getPublicDefinition());
99     }
100
101     private static final Set<YangStmtMapping> NOCOPY_FROM_GROUPING_SET = ImmutableSet.of(
102         YangStmtMapping.DESCRIPTION,
103         YangStmtMapping.REFERENCE,
104         YangStmtMapping.STATUS);
105     private static final Set<YangStmtMapping> REUSED_DEF_SET = ImmutableSet.of(
106         YangStmtMapping.TYPE,
107         YangStmtMapping.TYPEDEF,
108         YangStmtMapping.USES);
109
110     public static boolean needToCopyByUses(final StmtContext<?, ?, ?> stmtContext) {
111         final StatementDefinition def = stmtContext.getPublicDefinition();
112         if (REUSED_DEF_SET.contains(def)) {
113             LOG.trace("Will reuse {} statement {}", def, stmtContext);
114             return false;
115         }
116         if (NOCOPY_FROM_GROUPING_SET.contains(def)) {
117             return !YangStmtMapping.GROUPING.equals(stmtContext.getParentContext().getPublicDefinition());
118         }
119
120         LOG.trace("Will copy {} statement {}", def, stmtContext);
121         return true;
122     }
123
124     public static void resolveUsesNode(
125             final Mutable<QName, UsesStatement, EffectiveStatement<QName, UsesStatement>> usesNode,
126             final StmtContext<?, ?, ?> targetNodeStmtCtx) {
127         for (final Mutable<?, ?, ?> subStmtCtx : usesNode.mutableDeclaredSubstatements()) {
128             if (StmtContextUtils.producesDeclared(subStmtCtx, RefineStatement.class)
129                     && areFeaturesSupported(subStmtCtx)) {
130                 performRefine(subStmtCtx, targetNodeStmtCtx);
131             }
132         }
133     }
134
135     private static boolean areFeaturesSupported(final StmtContext<?, ?, ?> subStmtCtx) {
136         /*
137          * In case of Yang 1.1, checks whether features are supported.
138          */
139         return !YangVersion.VERSION_1_1.equals(subStmtCtx.getRootVersion()) || subStmtCtx.isSupportedByFeatures();
140     }
141
142     private static void performRefine(final Mutable<?, ?, ?> subStmtCtx, final StmtContext<?, ?, ?> usesParentCtx) {
143         final Object refineArgument = subStmtCtx.getStatementArgument();
144         InferenceException.throwIf(!(refineArgument instanceof SchemaNodeIdentifier),
145             subStmtCtx.getStatementSourceReference(),
146             "Invalid refine argument %s. It must be instance of SchemaNodeIdentifier.", refineArgument);
147
148         final Optional<StmtContext<?, ?, ?>> optRefineTargetCtx = SchemaNodeIdentifierBuildNamespace.findNode(
149             usesParentCtx, (SchemaNodeIdentifier) refineArgument);
150         InferenceException.throwIf(!optRefineTargetCtx.isPresent(), subStmtCtx.getStatementSourceReference(),
151             "Refine target node %s not found.", refineArgument);
152
153         final StmtContext<?, ?, ?> refineTargetNodeCtx = optRefineTargetCtx.get();
154         if (StmtContextUtils.isUnknownStatement(refineTargetNodeCtx)) {
155             LOG.trace("Refine node '{}' in uses '{}' has target node unknown statement '{}'. "
156                 + "Refine has been skipped. At line: {}", subStmtCtx.getStatementArgument(),
157                 subStmtCtx.getParentContext().getStatementArgument(),
158                 refineTargetNodeCtx.getStatementArgument(), subStmtCtx.getStatementSourceReference());
159             subStmtCtx.addAsEffectOfStatement(refineTargetNodeCtx);
160             return;
161         }
162
163         Verify.verify(refineTargetNodeCtx instanceof StatementContextBase);
164         addOrReplaceNodes(subStmtCtx, (StatementContextBase<?, ?, ?>) refineTargetNodeCtx);
165         subStmtCtx.addAsEffectOfStatement(refineTargetNodeCtx);
166     }
167
168     private static void addOrReplaceNodes(final Mutable<?, ?, ?> subStmtCtx,
169             final StatementContextBase<?, ?, ?> refineTargetNodeCtx) {
170         for (final Mutable<?, ?, ?> refineSubstatementCtx : subStmtCtx.mutableDeclaredSubstatements()) {
171             if (isSupportedRefineSubstatement(refineSubstatementCtx)) {
172                 addOrReplaceNode(refineSubstatementCtx, refineTargetNodeCtx);
173             }
174         }
175     }
176
177     private static void addOrReplaceNode(final Mutable<?, ?, ?> refineSubstatementCtx,
178             final StatementContextBase<?, ?, ?> refineTargetNodeCtx) {
179
180         final StatementDefinition refineSubstatementDef = refineSubstatementCtx.getPublicDefinition();
181
182         SourceException.throwIf(!isSupportedRefineTarget(refineSubstatementCtx, refineTargetNodeCtx),
183                 refineSubstatementCtx.getStatementSourceReference(),
184                 "Error in module '%s' in the refine of uses '%s': can not perform refine of '%s' for the target '%s'.",
185                 refineSubstatementCtx.getRoot().getStatementArgument(), refineSubstatementCtx.getParentContext()
186                         .getStatementArgument(), refineSubstatementCtx.getPublicDefinition(), refineTargetNodeCtx
187                         .getPublicDefinition());
188
189         if (isAllowedToAddByRefine(refineSubstatementDef)) {
190             refineTargetNodeCtx.addEffectiveSubstatement(refineSubstatementCtx);
191         } else {
192             refineTargetNodeCtx.removeStatementFromEffectiveSubstatements(refineSubstatementDef);
193             refineTargetNodeCtx.addEffectiveSubstatement(refineSubstatementCtx);
194         }
195     }
196
197     private static final Set<YangStmtMapping> ALLOWED_TO_ADD_BY_REFINE_DEF_SET = ImmutableSet.of(YangStmtMapping.MUST);
198
199     private static boolean isAllowedToAddByRefine(final StatementDefinition publicDefinition) {
200         return ALLOWED_TO_ADD_BY_REFINE_DEF_SET.contains(publicDefinition);
201     }
202
203     private static boolean isSupportedRefineSubstatement(final StmtContext<?, ?, ?> refineSubstatementCtx) {
204         final Collection<?> supportedRefineSubstatements = refineSubstatementCtx.getFromNamespace(
205                 ValidationBundlesNamespace.class, ValidationBundleType.SUPPORTED_REFINE_SUBSTATEMENTS);
206
207         return supportedRefineSubstatements == null || supportedRefineSubstatements.isEmpty()
208                 || supportedRefineSubstatements.contains(refineSubstatementCtx.getPublicDefinition())
209                 || StmtContextUtils.isUnknownStatement(refineSubstatementCtx);
210     }
211
212     private static boolean isSupportedRefineTarget(final StmtContext<?, ?, ?> refineSubstatementCtx,
213             final StmtContext<?, ?, ?> refineTargetNodeCtx) {
214         final Collection<?> supportedRefineTargets = YangValidationBundles.SUPPORTED_REFINE_TARGETS
215                 .get(refineSubstatementCtx.getPublicDefinition());
216
217         return supportedRefineTargets == null || supportedRefineTargets.isEmpty()
218                 || supportedRefineTargets.contains(refineTargetNodeCtx.getPublicDefinition());
219     }
220
221
222     private static QNameModule getNewQNameModule(final StmtContext<?, ?, ?> targetCtx,
223             final StmtContext<?, ?, ?> stmtContext) {
224         if (targetCtx.getParentContext() == null) {
225             return targetCtx.getFromNamespace(ModuleCtxToModuleQName.class, targetCtx);
226         }
227         if (targetCtx.getPublicDefinition() == YangStmtMapping.AUGMENT) {
228             return StmtContextUtils.getRootModuleQName(targetCtx);
229         }
230
231         final Object targetStmtArgument = targetCtx.getStatementArgument();
232         final Object sourceStmtArgument = stmtContext.getStatementArgument();
233         if (targetStmtArgument instanceof QName && sourceStmtArgument instanceof QName) {
234             return ((QName) targetStmtArgument).getModule();
235         }
236
237         return null;
238     }
239 }