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 java.util.regex.Pattern;
22 import javax.annotation.Nullable;
23 import org.opendaylight.yangtools.yang.common.QName;
24 import org.opendaylight.yangtools.yang.common.QNameModule;
25 import org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping;
26 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
27 import org.opendaylight.yangtools.yang.model.api.stmt.AugmentStatement;
28 import org.opendaylight.yangtools.yang.model.api.stmt.DataDefinitionStatement;
29 import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryStatement;
30 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
31 import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement;
32 import org.opendaylight.yangtools.yang.model.api.stmt.WhenStatement;
33 import org.opendaylight.yangtools.yang.parser.spi.NamespaceToModule;
34 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
35 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
36 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.TypeOfCopy;
37 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
38 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
39 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
40 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace;
41 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType;
42 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
46 public final class AugmentUtils {
48 private static final Logger LOG = LoggerFactory.getLogger(AugmentUtils.class);
49 private static final Pattern PATH_REL_PATTERN1 = Pattern.compile("\\.\\.?\\s*/(.+)");
50 private static final Pattern PATH_REL_PATTERN2 = Pattern.compile("//.*");
52 private AugmentUtils() {
53 throw new UnsupportedOperationException();
56 public static Iterable<QName> parseAugmentPath(final StmtContext<?, ?, ?> ctx, final String path) {
57 Preconditions.checkArgument(!PATH_REL_PATTERN1.matcher(path).matches()
58 && !PATH_REL_PATTERN2.matcher(path).matches(),
59 "An argument for augment can be only absolute path; or descendant if used in uses");
61 return Utils.parseXPath(ctx, path);
64 public static void copyFromSourceToTarget(final StatementContextBase<?, ?, ?> sourceCtx,
65 final StatementContextBase<?, ?, ?> targetCtx) throws SourceException {
67 copyDeclaredStmts(sourceCtx, targetCtx);
68 copyEffectiveStmts(sourceCtx, targetCtx);
71 public static void copyDeclaredStmts(final StatementContextBase<?, ?, ?> sourceCtx,
72 final StatementContextBase<?, ?, ?> targetCtx) throws SourceException {
74 final List<StatementContextBase<?, ?, ?>> subStatements = new Builder<StatementContextBase<?, ?, ?>>()
75 .addAll(targetCtx.declaredSubstatements()).addAll(targetCtx.effectiveSubstatements()).build();
76 boolean sourceAndTargetInSameModule = Utils.getRootModuleQName(sourceCtx).equals(
77 Utils.getRootModuleQName(targetCtx));
79 TypeOfCopy typeOfCopy = sourceCtx.getParentContext().getPublicDefinition().getDeclaredRepresentationClass()
80 .equals(UsesStatement.class) ? TypeOfCopy.ADDED_BY_USES_AUGMENTATION : TypeOfCopy.ADDED_BY_AUGMENTATION;
82 for (StatementContextBase<?, ?, ?> originalStmtCtx : sourceCtx.declaredSubstatements()) {
83 if (needToCopyByAugment(originalStmtCtx)) {
84 validateNodeCanBeCopiedByAugment(originalStmtCtx, subStatements, sourceAndTargetInSameModule);
86 StatementContextBase<?, ?, ?> copy = originalStmtCtx.createCopy(targetCtx, typeOfCopy);
87 targetCtx.addEffectiveSubstatement(copy);
88 } else if (isReusedByAugment(originalStmtCtx)) {
89 targetCtx.addEffectiveSubstatement(originalStmtCtx);
94 public static void copyEffectiveStmts(final StatementContextBase<?, ?, ?> sourceCtx,
95 final StatementContextBase<?, ?, ?> targetCtx) throws SourceException {
97 final List<StatementContextBase<?, ?, ?>> subStatements = new Builder<StatementContextBase<?, ?, ?>>()
98 .addAll(targetCtx.declaredSubstatements()).addAll(targetCtx.effectiveSubstatements()).build();
99 boolean sourceAndTargetInSameModule = Utils.getRootModuleQName(sourceCtx).equals(
100 Utils.getRootModuleQName(targetCtx));
102 TypeOfCopy typeOfCopy = sourceCtx.getParentContext().getPublicDefinition().getDeclaredRepresentationClass()
103 .equals(UsesStatement.class) ? TypeOfCopy.ADDED_BY_USES_AUGMENTATION : TypeOfCopy.ADDED_BY_AUGMENTATION;
105 for (StatementContextBase<?, ?, ?> originalStmtCtx : sourceCtx.effectiveSubstatements()) {
106 if (needToCopyByAugment(originalStmtCtx)) {
107 validateNodeCanBeCopiedByAugment(originalStmtCtx, subStatements, sourceAndTargetInSameModule);
109 StatementContextBase<?, ?, ?> copy = originalStmtCtx.createCopy(targetCtx, typeOfCopy);
110 targetCtx.addEffectiveSubstatement(copy);
111 } else if (isReusedByAugment(originalStmtCtx)) {
112 targetCtx.addEffectiveSubstatement(originalStmtCtx);
117 private static void validateNodeCanBeCopiedByAugment(final StatementContextBase<?, ?, ?> sourceCtx,
118 final Iterable<StatementContextBase<?, ?, ?>> targetSubStatements,
119 final boolean sourceAndTargetInSameModule) {
121 if (WhenStatement.class.equals(sourceCtx.getPublicDefinition().getDeclaredRepresentationClass())) {
125 if (!sourceAndTargetInSameModule) {
126 for (final StatementContextBase<?, ?, ?> sourceSubStatement :
127 Iterables.concat(sourceCtx.declaredSubstatements(), sourceCtx.effectiveSubstatements())) {
128 Preconditions.checkArgument(!MandatoryStatement.class.equals(
129 sourceSubStatement.getPublicDefinition().getDeclaredRepresentationClass()),
130 "An augment cannot add node '%s' because it is mandatory and in module different from target",
131 sourceCtx.rawStatementArgument());
135 for (final StatementContextBase<?, ?, ?> subStatement : targetSubStatements) {
137 final boolean sourceIsDataNode = DataDefinitionStatement.class.isAssignableFrom(
138 sourceCtx.getPublicDefinition().getDeclaredRepresentationClass());
139 final boolean targetIsDataNode = DataDefinitionStatement.class.isAssignableFrom(
140 subStatement.getPublicDefinition().getDeclaredRepresentationClass());
141 Preconditions.checkState(!sourceIsDataNode || !targetIsDataNode
142 || !Objects.equals(sourceCtx.getStatementArgument(), subStatement.getStatementArgument()),
143 "An augment cannot add node named '%s' because this name is already used in target",
144 sourceCtx.rawStatementArgument());
148 public static QNameModule getNewQNameModule(final StatementContextBase<?, ?, ?> targetCtx,
149 final StatementContextBase<?, ?, ?> sourceCtx) {
150 Object targetStmtArgument = targetCtx.getStatementArgument();
152 final StatementContextBase<?, ?, ?> root = sourceCtx.getRoot();
153 final QNameModule sourceQNameModule = root.getFromNamespace(ModuleCtxToModuleQName.class, root);
155 if (targetStmtArgument instanceof QName) {
156 QName targetQName = (QName) targetStmtArgument;
157 QNameModule targetQNameModule = targetQName.getModule();
159 if (targetQNameModule.equals(sourceQNameModule)) {
162 return targetQNameModule;
169 private static final Set<Rfc6020Mapping> NOCOPY_DEV_SET = ImmutableSet.of(Rfc6020Mapping.USES);
171 public static boolean needToCopyByAugment(final StmtContext<?, ?, ?> stmtContext) {
172 return !NOCOPY_DEV_SET.contains(stmtContext.getPublicDefinition());
175 private static final Set<Rfc6020Mapping> REUSED_DEF_SET = ImmutableSet.of(Rfc6020Mapping.TYPEDEF);
177 public static boolean isReusedByAugment(final StmtContext<?, ?, ?> stmtContext) {
178 return REUSED_DEF_SET.contains(stmtContext.getPublicDefinition());
181 public static StatementContextBase<?, ?, ?> getAugmentTargetCtx(
182 final Mutable<SchemaNodeIdentifier, AugmentStatement, EffectiveStatement<SchemaNodeIdentifier, AugmentStatement>> augmentNode) {
184 final SchemaNodeIdentifier augmentTargetNode = augmentNode.getStatementArgument();
185 Preconditions.checkArgument(augmentTargetNode != null,
186 "Augment argument null, something bad happened in some of previous parsing phases");
188 List<StatementContextBase<?, ?, ?>> rootStatementCtxList = new ArrayList<>();
189 if (augmentTargetNode.isAbsolute()) {
191 QNameModule module = augmentTargetNode.getPathFromRoot().iterator().next().getModule();
193 StatementContextBase<?, ?, ?> rootStatementCtx =
194 (StatementContextBase<?, ?, ?>) augmentNode.getFromNamespace(NamespaceToModule.class, module);
195 rootStatementCtxList.add(rootStatementCtx);
197 final Map<?, ?> subModules = rootStatementCtx.getAllFromNamespace(IncludedModuleContext.class);
198 if (subModules != null) {
199 rootStatementCtxList.addAll((Collection<? extends StatementContextBase<?, ?, ?>>) subModules.values());
203 StatementContextBase<?, ?, ?> parent = (StatementContextBase<?, ?, ?>) augmentNode.getParentContext();
204 if (StmtContextUtils.producesDeclared(parent, UsesStatement.class)) {
205 rootStatementCtxList.add(parent.getParentContext());
211 StatementContextBase<?, ?, ?> augmentTargetCtx = null;
212 for (final StatementContextBase<?, ?, ?> rootStatementCtx : rootStatementCtxList) {
213 augmentTargetCtx = findCtxOfNodeInRoot(rootStatementCtx, augmentTargetNode);
214 if (augmentTargetCtx != null) {
219 return augmentTargetCtx;
223 public static StatementContextBase<?, ?, ?> findCtxOfNodeInSubstatements(final StatementContextBase<?, ?, ?> rootStmtCtx,
224 final Iterable<QName> path) {
226 StatementContextBase<?, ?, ?> parent = rootStmtCtx;
228 Iterator<QName> pathIter = path.iterator();
229 while (pathIter.hasNext()) {
230 QName nextPathQName = pathIter.next();
231 StatementContextBase<?, ?, ?> foundSubstatement = getSubstatementByQName(parent, nextPathQName);
233 if (foundSubstatement == null) {
236 if (!pathIter.hasNext()) {
237 return foundSubstatement;
240 parent = foundSubstatement;
246 public static StatementContextBase<?, ?, ?> getSubstatementByQName(final StatementContextBase<?, ?, ?> parent,
247 final QName nextPathQName) {
249 Collection<StatementContextBase<?, ?, ?>> declaredSubstatement = parent.declaredSubstatements();
250 Collection<StatementContextBase<?, ?, ?>> effectiveSubstatement = parent.effectiveSubstatements();
252 for (StatementContextBase<?, ?, ?> substatement : Iterables.concat(declaredSubstatement, effectiveSubstatement)) {
253 Object substatementArgument = substatement.getStatementArgument();
254 QName substatementQName;
255 if (substatementArgument instanceof QName) {
256 substatementQName = (QName) substatementArgument;
258 if (nextPathQName.getLocalName().equals(
259 substatementQName.getLocalName())) {
260 if (isSupportedAugmentTarget(substatement)) {
262 } else if (Utils.isUnknownNode(substatement)) {
263 LOG.warn("Module '{}': augment into unknown node '{}'.",
264 substatement.getRoot().getStatementArgument(), substatementArgument);
274 public static boolean isSupportedAugmentTarget(final StatementContextBase<?, ?, ?> substatementCtx) {
277 * :TODO Substatement must be allowed augment target type e.g. Container, etc... and must be not for example
278 * grouping, identity etc. It is problem in case when more than one substatements have the same QName, for
279 * example Grouping and Container are siblings and they have the same QName. We must find the Container and the
280 * Grouping must be ignored as disallowed augment target.
283 Collection<?> allowedAugmentTargets = substatementCtx.getFromNamespace(ValidationBundlesNamespace.class,
284 ValidationBundleType.SUPPORTED_AUGMENT_TARGETS);
286 // if no allowed target is returned we consider all targets allowed
287 return allowedAugmentTargets == null || allowedAugmentTargets.isEmpty()
288 || allowedAugmentTargets.contains(substatementCtx.getPublicDefinition());
292 public static StatementContextBase<?, ?, ?> findCtxOfNodeInRoot(final StatementContextBase<?, ?, ?> rootStmtCtx,
293 final SchemaNodeIdentifier node) {
294 return findCtxOfNodeInSubstatements(rootStmtCtx, node.getPathFromRoot());