Merge branch 'master' of ../controller
[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 org.opendaylight.yangtools.yang.common.QName;
16 import org.opendaylight.yangtools.yang.common.QNameModule;
17 import org.opendaylight.yangtools.yang.common.YangVersion;
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.RefineStatement;
22 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
23 import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement;
24 import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ChildSchemaNodeNamespace;
25 import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.YangValidationBundles;
26 import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
27 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
28 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
29 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
30 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
31 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
32 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
33 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
34 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace;
35 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType;
36 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 final class UsesStatementImpl extends AbstractDeclaredStatement<QName> implements UsesStatement {
41     private static final Logger LOG = LoggerFactory.getLogger(UsesStatementImpl.class);
42
43     UsesStatementImpl(final StmtContext<QName, UsesStatement, ?> context) {
44         super(context);
45     }
46
47     /**
48      * Copy statements from a grouping to a target node.
49      *
50      * @param sourceGrpStmtCtx
51      *            source grouping statement context
52      * @param targetCtx
53      *            target context
54      * @param usesNode
55      *            uses node
56      * @throws SourceException
57      *             instance of SourceException
58      */
59     static void copyFromSourceToTarget(final Mutable<?, ?, ?> sourceGrpStmtCtx,
60             final StatementContextBase<?, ?, ?> targetCtx,
61             final Mutable<QName, UsesStatement, EffectiveStatement<QName, UsesStatement>> usesNode) {
62         final Collection<? extends Mutable<?, ?, ?>> declared = sourceGrpStmtCtx.mutableDeclaredSubstatements();
63         final Collection<? extends Mutable<?, ?, ?>> effective = sourceGrpStmtCtx.mutableEffectiveSubstatements();
64         final Collection<Mutable<?, ?, ?>> buffer = new ArrayList<>(declared.size() + effective.size());
65         final QNameModule newQNameModule = getNewQNameModule(targetCtx, sourceGrpStmtCtx);
66
67         for (final Mutable<?, ?, ?> original : declared) {
68             if (original.isSupportedByFeatures()) {
69                 copyStatement(original, targetCtx, newQNameModule, buffer);
70             }
71         }
72
73         for (final Mutable<?, ?, ?> original : effective) {
74             copyStatement(original, targetCtx, newQNameModule, buffer);
75         }
76
77         targetCtx.addEffectiveSubstatements(buffer);
78         usesNode.addAsEffectOfStatement(buffer);
79     }
80
81     private static void copyStatement(final Mutable<?, ?, ?> original,
82             final StatementContextBase<?, ?, ?> targetCtx, final QNameModule targetModule,
83             final Collection<Mutable<?, ?, ?>> buffer) {
84         if (needToCopyByUses(original)) {
85             final Mutable<?, ?, ?> copy = targetCtx.childCopyOf(original, CopyType.ADDED_BY_USES, targetModule);
86             buffer.add(copy);
87         } else if (isReusedByUsesOnTop(original)) {
88             buffer.add(original);
89         }
90     }
91
92     private static final ImmutableSet<YangStmtMapping> TOP_REUSED_DEF_SET = ImmutableSet.of(
93         YangStmtMapping.TYPE,
94         YangStmtMapping.TYPEDEF);
95
96     private static boolean isReusedByUsesOnTop(final StmtContext<?, ?, ?> stmtContext) {
97         return TOP_REUSED_DEF_SET.contains(stmtContext.getPublicDefinition());
98     }
99
100     private static final ImmutableSet<YangStmtMapping> NOCOPY_FROM_GROUPING_SET = ImmutableSet.of(
101         YangStmtMapping.DESCRIPTION,
102         YangStmtMapping.REFERENCE,
103         YangStmtMapping.STATUS);
104     private static final ImmutableSet<YangStmtMapping> REUSED_DEF_SET = ImmutableSet.of(
105         YangStmtMapping.TYPE,
106         YangStmtMapping.TYPEDEF,
107         YangStmtMapping.USES);
108
109     public static boolean needToCopyByUses(final StmtContext<?, ?, ?> stmtContext) {
110         final StatementDefinition def = stmtContext.getPublicDefinition();
111         if (REUSED_DEF_SET.contains(def)) {
112             LOG.trace("Will reuse {} statement {}", def, stmtContext);
113             return false;
114         }
115         if (NOCOPY_FROM_GROUPING_SET.contains(def)) {
116             return !YangStmtMapping.GROUPING.equals(stmtContext.coerceParentContext().getPublicDefinition());
117         }
118
119         LOG.trace("Will copy {} statement {}", def, stmtContext);
120         return true;
121     }
122
123     public static void resolveUsesNode(
124             final Mutable<QName, UsesStatement, EffectiveStatement<QName, UsesStatement>> usesNode,
125             final StmtContext<?, ?, ?> targetNodeStmtCtx) {
126         for (final Mutable<?, ?, ?> subStmtCtx : usesNode.mutableDeclaredSubstatements()) {
127             if (StmtContextUtils.producesDeclared(subStmtCtx, RefineStatement.class)
128                     && areFeaturesSupported(subStmtCtx)) {
129                 performRefine(subStmtCtx, targetNodeStmtCtx);
130             }
131         }
132     }
133
134     private static boolean areFeaturesSupported(final StmtContext<?, ?, ?> subStmtCtx) {
135         /*
136          * In case of Yang 1.1, checks whether features are supported.
137          */
138         return !YangVersion.VERSION_1_1.equals(subStmtCtx.getRootVersion()) || subStmtCtx.isSupportedByFeatures();
139     }
140
141     private static void performRefine(final Mutable<?, ?, ?> subStmtCtx, final StmtContext<?, ?, ?> usesParentCtx) {
142         final Object refineArgument = subStmtCtx.getStatementArgument();
143         InferenceException.throwIf(!(refineArgument instanceof SchemaNodeIdentifier),
144             subStmtCtx.getStatementSourceReference(),
145             "Invalid refine argument %s. It must be instance of SchemaNodeIdentifier.", refineArgument);
146
147         final Optional<StmtContext<?, ?, ?>> optRefineTargetCtx = ChildSchemaNodeNamespace.findNode(
148             usesParentCtx, (SchemaNodeIdentifier) refineArgument);
149         InferenceException.throwIf(!optRefineTargetCtx.isPresent(), subStmtCtx.getStatementSourceReference(),
150             "Refine target node %s not found.", refineArgument);
151
152         final StmtContext<?, ?, ?> refineTargetNodeCtx = optRefineTargetCtx.get();
153         if (StmtContextUtils.isUnknownStatement(refineTargetNodeCtx)) {
154             LOG.trace("Refine node '{}' in uses '{}' has target node unknown statement '{}'. "
155                 + "Refine has been skipped. At line: {}", subStmtCtx.getStatementArgument(),
156                 subStmtCtx.coerceParentContext().getStatementArgument(),
157                 refineTargetNodeCtx.getStatementArgument(), subStmtCtx.getStatementSourceReference());
158             subStmtCtx.addAsEffectOfStatement(refineTargetNodeCtx);
159             return;
160         }
161
162         Verify.verify(refineTargetNodeCtx instanceof StatementContextBase);
163         addOrReplaceNodes(subStmtCtx, (StatementContextBase<?, ?, ?>) refineTargetNodeCtx);
164         subStmtCtx.addAsEffectOfStatement(refineTargetNodeCtx);
165     }
166
167     private static void addOrReplaceNodes(final Mutable<?, ?, ?> subStmtCtx,
168             final StatementContextBase<?, ?, ?> refineTargetNodeCtx) {
169         for (final Mutable<?, ?, ?> refineSubstatementCtx : subStmtCtx.mutableDeclaredSubstatements()) {
170             if (isSupportedRefineSubstatement(refineSubstatementCtx)) {
171                 addOrReplaceNode(refineSubstatementCtx, refineTargetNodeCtx);
172             }
173         }
174     }
175
176     private static void addOrReplaceNode(final Mutable<?, ?, ?> refineSubstatementCtx,
177             final StatementContextBase<?, ?, ?> refineTargetNodeCtx) {
178
179         final StatementDefinition refineSubstatementDef = refineSubstatementCtx.getPublicDefinition();
180
181         SourceException.throwIf(!isSupportedRefineTarget(refineSubstatementCtx, refineTargetNodeCtx),
182                 refineSubstatementCtx.getStatementSourceReference(),
183                 "Error in module '%s' in the refine of uses '%s': can not perform refine of '%s' for the target '%s'.",
184                 refineSubstatementCtx.getRoot().getStatementArgument(),
185                 refineSubstatementCtx.coerceParentContext().getStatementArgument(),
186                 refineSubstatementCtx.getPublicDefinition(), refineTargetNodeCtx.getPublicDefinition());
187
188         if (isAllowedToAddByRefine(refineSubstatementDef)) {
189             refineTargetNodeCtx.addEffectiveSubstatement(refineSubstatementCtx);
190         } else {
191             refineTargetNodeCtx.removeStatementFromEffectiveSubstatements(refineSubstatementDef);
192             refineTargetNodeCtx.addEffectiveSubstatement(refineSubstatementCtx);
193         }
194     }
195
196     private static final ImmutableSet<YangStmtMapping> ALLOWED_TO_ADD_BY_REFINE_DEF_SET =
197             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 }