2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.parser.stmt.rfc6020;
10 import com.google.common.base.Preconditions;
11 import com.google.common.collect.ImmutableList.Builder;
12 import com.google.common.collect.ImmutableSet;
13 import com.google.common.collect.Iterables;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.Iterator;
17 import java.util.List;
19 import java.util.Objects;
21 import javax.annotation.Nullable;
22 import org.opendaylight.yangtools.yang.common.QName;
23 import org.opendaylight.yangtools.yang.common.QNameModule;
24 import org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping;
25 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
26 import org.opendaylight.yangtools.yang.model.api.stmt.AugmentStatement;
27 import org.opendaylight.yangtools.yang.model.api.stmt.DataDefinitionStatement;
28 import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryStatement;
29 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
30 import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement;
31 import org.opendaylight.yangtools.yang.model.api.stmt.WhenStatement;
32 import org.opendaylight.yangtools.yang.parser.spi.NamespaceToModule;
33 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
34 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
35 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.TypeOfCopy;
36 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
37 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
38 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
39 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace;
40 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType;
41 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
45 public final class AugmentUtils {
47 private static final Logger LOG = LoggerFactory.getLogger(AugmentUtils.class);
49 private AugmentUtils() {
50 throw new UnsupportedOperationException();
53 public static void copyFromSourceToTarget(final StatementContextBase<?, ?, ?> sourceCtx,
54 final StatementContextBase<?, ?, ?> targetCtx) throws SourceException {
56 copyDeclaredStmts(sourceCtx, targetCtx);
57 copyEffectiveStmts(sourceCtx, targetCtx);
60 public static void copyDeclaredStmts(final StatementContextBase<?, ?, ?> sourceCtx,
61 final StatementContextBase<?, ?, ?> targetCtx) throws SourceException {
63 final List<StatementContextBase<?, ?, ?>> subStatements = new Builder<StatementContextBase<?, ?, ?>>()
64 .addAll(targetCtx.declaredSubstatements()).addAll(targetCtx.effectiveSubstatements()).build();
65 boolean sourceAndTargetInSameModule = Utils.getRootModuleQName(sourceCtx).equals(
66 Utils.getRootModuleQName(targetCtx));
68 TypeOfCopy typeOfCopy = sourceCtx.getParentContext().getPublicDefinition().getDeclaredRepresentationClass()
69 .equals(UsesStatement.class) ? TypeOfCopy.ADDED_BY_USES_AUGMENTATION : TypeOfCopy.ADDED_BY_AUGMENTATION;
71 for (StatementContextBase<?, ?, ?> originalStmtCtx : sourceCtx.declaredSubstatements()) {
72 if (needToCopyByAugment(originalStmtCtx)) {
73 validateNodeCanBeCopiedByAugment(originalStmtCtx, subStatements, sourceAndTargetInSameModule);
75 StatementContextBase<?, ?, ?> copy = originalStmtCtx.createCopy(targetCtx, typeOfCopy);
76 targetCtx.addEffectiveSubstatement(copy);
77 } else if (isReusedByAugment(originalStmtCtx)) {
78 targetCtx.addEffectiveSubstatement(originalStmtCtx);
83 public static void copyEffectiveStmts(final StatementContextBase<?, ?, ?> sourceCtx,
84 final StatementContextBase<?, ?, ?> targetCtx) throws SourceException {
86 final List<StatementContextBase<?, ?, ?>> subStatements = new Builder<StatementContextBase<?, ?, ?>>()
87 .addAll(targetCtx.declaredSubstatements()).addAll(targetCtx.effectiveSubstatements()).build();
88 boolean sourceAndTargetInSameModule = Utils.getRootModuleQName(sourceCtx).equals(
89 Utils.getRootModuleQName(targetCtx));
91 TypeOfCopy typeOfCopy = sourceCtx.getParentContext().getPublicDefinition().getDeclaredRepresentationClass()
92 .equals(UsesStatement.class) ? TypeOfCopy.ADDED_BY_USES_AUGMENTATION : TypeOfCopy.ADDED_BY_AUGMENTATION;
94 for (StatementContextBase<?, ?, ?> originalStmtCtx : sourceCtx.effectiveSubstatements()) {
95 if (needToCopyByAugment(originalStmtCtx)) {
96 validateNodeCanBeCopiedByAugment(originalStmtCtx, subStatements, sourceAndTargetInSameModule);
98 StatementContextBase<?, ?, ?> copy = originalStmtCtx.createCopy(targetCtx, typeOfCopy);
99 targetCtx.addEffectiveSubstatement(copy);
100 } else if (isReusedByAugment(originalStmtCtx)) {
101 targetCtx.addEffectiveSubstatement(originalStmtCtx);
106 private static void validateNodeCanBeCopiedByAugment(final StatementContextBase<?, ?, ?> sourceCtx,
107 final Iterable<StatementContextBase<?, ?, ?>> targetSubStatements,
108 final boolean sourceAndTargetInSameModule) {
110 if (WhenStatement.class.equals(sourceCtx.getPublicDefinition().getDeclaredRepresentationClass())) {
114 if (!sourceAndTargetInSameModule) {
115 for (final StatementContextBase<?, ?, ?> sourceSubStatement :
116 Iterables.concat(sourceCtx.declaredSubstatements(), sourceCtx.effectiveSubstatements())) {
117 Preconditions.checkArgument(!MandatoryStatement.class.equals(
118 sourceSubStatement.getPublicDefinition().getDeclaredRepresentationClass()),
119 "An augment cannot add node '%s' because it is mandatory and in module different from target",
120 sourceCtx.rawStatementArgument());
124 for (final StatementContextBase<?, ?, ?> subStatement : targetSubStatements) {
126 final boolean sourceIsDataNode = DataDefinitionStatement.class.isAssignableFrom(
127 sourceCtx.getPublicDefinition().getDeclaredRepresentationClass());
128 final boolean targetIsDataNode = DataDefinitionStatement.class.isAssignableFrom(
129 subStatement.getPublicDefinition().getDeclaredRepresentationClass());
130 Preconditions.checkState(!sourceIsDataNode || !targetIsDataNode
131 || !Objects.equals(sourceCtx.getStatementArgument(), subStatement.getStatementArgument()),
132 "An augment cannot add node named '%s' because this name is already used in target",
133 sourceCtx.rawStatementArgument());
137 public static QNameModule getNewQNameModule(final StatementContextBase<?, ?, ?> targetCtx,
138 final StatementContextBase<?, ?, ?> sourceCtx) {
139 Object targetStmtArgument = targetCtx.getStatementArgument();
141 final StatementContextBase<?, ?, ?> root = sourceCtx.getRoot();
142 final QNameModule sourceQNameModule = root.getFromNamespace(ModuleCtxToModuleQName.class, root);
144 if (targetStmtArgument instanceof QName) {
145 QName targetQName = (QName) targetStmtArgument;
146 QNameModule targetQNameModule = targetQName.getModule();
148 if (targetQNameModule.equals(sourceQNameModule)) {
151 return targetQNameModule;
158 private static final Set<Rfc6020Mapping> NOCOPY_DEV_SET = ImmutableSet.of(Rfc6020Mapping.USES);
160 public static boolean needToCopyByAugment(final StmtContext<?, ?, ?> stmtContext) {
161 return !NOCOPY_DEV_SET.contains(stmtContext.getPublicDefinition());
164 private static final Set<Rfc6020Mapping> REUSED_DEF_SET = ImmutableSet.of(Rfc6020Mapping.TYPEDEF);
166 public static boolean isReusedByAugment(final StmtContext<?, ?, ?> stmtContext) {
167 return REUSED_DEF_SET.contains(stmtContext.getPublicDefinition());
170 public static StatementContextBase<?, ?, ?> getAugmentTargetCtx(
171 final Mutable<SchemaNodeIdentifier, AugmentStatement, EffectiveStatement<SchemaNodeIdentifier, AugmentStatement>> augmentNode) {
173 final SchemaNodeIdentifier augmentTargetNode = augmentNode.getStatementArgument();
174 Preconditions.checkArgument(augmentTargetNode != null,
175 "Augment argument null, something bad happened in some of previous parsing phases");
177 List<StatementContextBase<?, ?, ?>> rootStatementCtxList = new ArrayList<>();
178 if (augmentTargetNode.isAbsolute()) {
180 QNameModule module = augmentTargetNode.getPathFromRoot().iterator().next().getModule();
182 StatementContextBase<?, ?, ?> rootStatementCtx =
183 (StatementContextBase<?, ?, ?>) augmentNode.getFromNamespace(NamespaceToModule.class, module);
184 rootStatementCtxList.add(rootStatementCtx);
186 final Map<?, ?> subModules = rootStatementCtx.getAllFromNamespace(IncludedModuleContext.class);
187 if (subModules != null) {
188 rootStatementCtxList.addAll((Collection<? extends StatementContextBase<?, ?, ?>>) subModules.values());
192 StatementContextBase<?, ?, ?> parent = (StatementContextBase<?, ?, ?>) augmentNode.getParentContext();
193 if (StmtContextUtils.producesDeclared(parent, UsesStatement.class)) {
194 rootStatementCtxList.add(parent.getParentContext());
200 StatementContextBase<?, ?, ?> augmentTargetCtx = null;
201 for (final StatementContextBase<?, ?, ?> rootStatementCtx : rootStatementCtxList) {
202 augmentTargetCtx = findCtxOfNodeInRoot(rootStatementCtx, augmentTargetNode);
203 if (augmentTargetCtx != null) {
208 return augmentTargetCtx;
212 public static StatementContextBase<?, ?, ?> findCtxOfNodeInSubstatements(final StatementContextBase<?, ?, ?> rootStmtCtx,
213 final Iterable<QName> path) {
215 StatementContextBase<?, ?, ?> parent = rootStmtCtx;
217 Iterator<QName> pathIter = path.iterator();
218 while (pathIter.hasNext()) {
219 QName nextPathQName = pathIter.next();
220 StatementContextBase<?, ?, ?> foundSubstatement = getSubstatementByQName(parent, nextPathQName);
222 if (foundSubstatement == null) {
225 if (!pathIter.hasNext()) {
226 return foundSubstatement;
229 parent = foundSubstatement;
235 public static StatementContextBase<?, ?, ?> getSubstatementByQName(final StatementContextBase<?, ?, ?> parent,
236 final QName nextPathQName) {
238 Collection<StatementContextBase<?, ?, ?>> declaredSubstatement = parent.declaredSubstatements();
239 Collection<StatementContextBase<?, ?, ?>> effectiveSubstatement = parent.effectiveSubstatements();
241 for (StatementContextBase<?, ?, ?> substatement : Iterables.concat(declaredSubstatement, effectiveSubstatement)) {
242 Object substatementArgument = substatement.getStatementArgument();
243 QName substatementQName;
244 if (substatementArgument instanceof QName) {
245 substatementQName = (QName) substatementArgument;
247 if (nextPathQName.getLocalName().equals(
248 substatementQName.getLocalName())) {
249 if (isSupportedAugmentTarget(substatement)) {
251 } else if (Utils.isUnknownNode(substatement)) {
252 LOG.warn("Module '{}': augment into unknown node '{}'.",
253 substatement.getRoot().getStatementArgument(), substatementArgument);
263 public static boolean isSupportedAugmentTarget(final StatementContextBase<?, ?, ?> substatementCtx) {
266 * :TODO Substatement must be allowed augment target type e.g. Container, etc... and must be not for example
267 * grouping, identity etc. It is problem in case when more than one substatements have the same QName, for
268 * example Grouping and Container are siblings and they have the same QName. We must find the Container and the
269 * Grouping must be ignored as disallowed augment target.
272 Collection<?> allowedAugmentTargets = substatementCtx.getFromNamespace(ValidationBundlesNamespace.class,
273 ValidationBundleType.SUPPORTED_AUGMENT_TARGETS);
275 // if no allowed target is returned we consider all targets allowed
276 return allowedAugmentTargets == null || allowedAugmentTargets.isEmpty()
277 || allowedAugmentTargets.contains(substatementCtx.getPublicDefinition());
281 public static StatementContextBase<?, ?, ?> findCtxOfNodeInRoot(final StatementContextBase<?, ?, ?> rootStmtCtx,
282 final SchemaNodeIdentifier node) {
283 return findCtxOfNodeInSubstatements(rootStmtCtx, node.getPathFromRoot());