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.collect.ImmutableList.Builder;
11 import java.util.Collection;
12 import java.util.HashSet;
13 import java.util.Iterator;
14 import java.util.LinkedList;
15 import java.util.List;
17 import java.util.Objects;
19 import javax.annotation.Nullable;
20 import org.opendaylight.yangtools.yang.common.QName;
21 import org.opendaylight.yangtools.yang.common.QNameModule;
22 import org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping;
23 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
24 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
25 import org.opendaylight.yangtools.yang.model.api.stmt.AugmentStatement;
26 import org.opendaylight.yangtools.yang.model.api.stmt.DataDefinitionStatement;
27 import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryStatement;
28 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
29 import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement;
30 import org.opendaylight.yangtools.yang.model.api.stmt.WhenStatement;
31 import org.opendaylight.yangtools.yang.parser.spi.NamespaceToModule;
32 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
33 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
34 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.TypeOfCopy;
35 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
36 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
37 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
38 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace;
39 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType;
40 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
44 public final class AugmentUtils {
46 private static final Logger LOG = LoggerFactory.getLogger(AugmentUtils.class);
48 private static final String REGEX_PATH_REL1 = "\\.\\.?\\s*/(.+)";
49 private static final String REGEX_PATH_REL2 = "//.*";
51 private AugmentUtils() {
54 public static Iterable<QName> parseAugmentPath(StmtContext<?, ?, ?> ctx, String path) {
56 if (path.matches(REGEX_PATH_REL1) || path.matches(REGEX_PATH_REL2)) {
57 throw new IllegalArgumentException(
58 "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(StatementContextBase<?, ?, ?> sourceCtx,
65 StatementContextBase<?, ?, ?> targetCtx) throws SourceException {
67 copyDeclaredStmts(sourceCtx, targetCtx);
68 copyEffectiveStmts(sourceCtx, targetCtx);
71 public static void copyDeclaredStmts(StatementContextBase<?, ?, ?> sourceCtx,
72 StatementContextBase<?, ?, ?> targetCtx) throws SourceException {
74 Collection<? extends StatementContextBase<?, ?, ?>> declaredSubStatements = sourceCtx.declaredSubstatements();
75 final List<StatementContextBase> subStatements = new Builder<StatementContextBase>()
76 .addAll(targetCtx.declaredSubstatements()).addAll(targetCtx.effectiveSubstatements()).build();
77 boolean sourceAndTargetInSameModule = Utils.getRootModuleQName(sourceCtx).equals(
78 Utils.getRootModuleQName(targetCtx));
80 TypeOfCopy typeOfCopy = sourceCtx.getParentContext().getPublicDefinition().getDeclaredRepresentationClass()
81 .equals(UsesStatement.class) ? TypeOfCopy.ADDED_BY_USES_AUGMENTATION : TypeOfCopy.ADDED_BY_AUGMENTATION;
83 for (StatementContextBase<?, ?, ?> originalStmtCtx : declaredSubStatements) {
84 if (needToCopyByAugment(originalStmtCtx)) {
85 validateNodeCanBeCopiedByAugment(originalStmtCtx, subStatements, sourceAndTargetInSameModule);
87 StatementContextBase<?, ?, ?> copy = originalStmtCtx.createCopy(targetCtx, typeOfCopy);
88 targetCtx.addEffectiveSubstatement(copy);
89 } else if (isReusedByAugment(originalStmtCtx)) {
90 targetCtx.addEffectiveSubstatement(originalStmtCtx);
95 public static void copyEffectiveStmts(StatementContextBase<?, ?, ?> sourceCtx,
96 StatementContextBase<?, ?, ?> targetCtx) throws SourceException {
98 Collection<? extends StatementContextBase<?, ?, ?>> effectiveSubstatements = sourceCtx.effectiveSubstatements();
99 final List<StatementContextBase> subStatements = new Builder<StatementContextBase>()
100 .addAll(targetCtx.declaredSubstatements()).addAll(targetCtx.effectiveSubstatements()).build();
101 boolean sourceAndTargetInSameModule = Utils.getRootModuleQName(sourceCtx).equals(
102 Utils.getRootModuleQName(targetCtx));
104 TypeOfCopy typeOfCopy = sourceCtx.getParentContext().getPublicDefinition().getDeclaredRepresentationClass()
105 .equals(UsesStatement.class) ? TypeOfCopy.ADDED_BY_USES_AUGMENTATION : TypeOfCopy.ADDED_BY_AUGMENTATION;
107 for (StatementContextBase<?, ?, ?> originalStmtCtx : effectiveSubstatements) {
108 if (needToCopyByAugment(originalStmtCtx)) {
109 validateNodeCanBeCopiedByAugment(originalStmtCtx, subStatements, sourceAndTargetInSameModule);
111 StatementContextBase<?, ?, ?> copy = originalStmtCtx.createCopy(targetCtx, typeOfCopy);
112 targetCtx.addEffectiveSubstatement(copy);
113 } else if (isReusedByAugment(originalStmtCtx)) {
114 targetCtx.addEffectiveSubstatement(originalStmtCtx);
119 private static void validateNodeCanBeCopiedByAugment(final StatementContextBase<?, ?, ?> sourceCtx,
120 final List<StatementContextBase> targetSubStatements, boolean sourceAndTargetInSameModule) {
122 if (sourceCtx.getPublicDefinition().getDeclaredRepresentationClass().equals(WhenStatement.class)) {
126 if (!sourceAndTargetInSameModule) {
127 final List<StatementContextBase> sourceSubStatements = new Builder<StatementContextBase>()
128 .addAll(sourceCtx.declaredSubstatements()).addAll(sourceCtx.effectiveSubstatements()).build();
130 for (final StatementContextBase sourceSubStatement : sourceSubStatements) {
131 if (sourceSubStatement.getPublicDefinition().getDeclaredRepresentationClass()
132 .equals(MandatoryStatement.class)) {
133 throw new IllegalArgumentException(
135 "An augment cannot add node '%s' because it is mandatory and in module different from target",
136 sourceCtx.rawStatementArgument()));
141 for (final StatementContextBase subStatement : targetSubStatements) {
143 final boolean sourceIsDataNode = DataDefinitionStatement.class.isAssignableFrom(sourceCtx
144 .getPublicDefinition().getDeclaredRepresentationClass());
145 final boolean targetIsDataNode = DataDefinitionStatement.class.isAssignableFrom(subStatement
146 .getPublicDefinition().getDeclaredRepresentationClass());
147 boolean qNamesEqual = sourceIsDataNode && targetIsDataNode
148 && Objects.equals(sourceCtx.getStatementArgument(), subStatement.getStatementArgument());
151 throw new IllegalStateException(String.format(
152 "An augment cannot add node named '%s' because this name is already used in target",
153 sourceCtx.rawStatementArgument()));
158 public static QNameModule getNewQNameModule(StatementContextBase<?, ?, ?> targetCtx,
159 StatementContextBase<?, ?, ?> sourceCtx) {
160 Object targetStmtArgument = targetCtx.getStatementArgument();
162 final StatementContextBase<?, ?, ?> root = sourceCtx.getRoot();
163 final QNameModule sourceQNameModule = root.getFromNamespace(ModuleCtxToModuleQName.class, root);
165 if (targetStmtArgument instanceof QName) {
166 QName targetQName = (QName) targetStmtArgument;
167 QNameModule targetQNameModule = targetQName.getModule();
169 if (targetQNameModule.equals(sourceQNameModule)) {
172 return targetQNameModule;
179 public static boolean needToCopyByAugment(StmtContext<?, ?, ?> stmtContext) {
181 Set<StatementDefinition> noCopyDefSet = new HashSet<>();
182 noCopyDefSet.add(Rfc6020Mapping.USES);
184 StatementDefinition def = stmtContext.getPublicDefinition();
185 return !noCopyDefSet.contains(def);
188 public static boolean isReusedByAugment(StmtContext<?, ?, ?> stmtContext) {
190 Set<StatementDefinition> reusedDefSet = new HashSet<>();
191 reusedDefSet.add(Rfc6020Mapping.TYPEDEF);
193 StatementDefinition def = stmtContext.getPublicDefinition();
195 return reusedDefSet.contains(def);
198 public static StatementContextBase<?, ?, ?> getAugmentTargetCtx(
199 final Mutable<SchemaNodeIdentifier, AugmentStatement, EffectiveStatement<SchemaNodeIdentifier, AugmentStatement>> augmentNode) {
201 final SchemaNodeIdentifier augmentTargetNode = augmentNode.getStatementArgument();
202 if (augmentTargetNode == null) {
203 throw new IllegalArgumentException(
204 "Augment argument null, something bad happened in some of previous parsing phases");
207 List<StatementContextBase<?, ?, ?>> rootStatementCtxList = new LinkedList<>();
208 if (augmentTargetNode.isAbsolute()) {
210 QNameModule module = augmentTargetNode.getPathFromRoot().iterator().next().getModule();
212 StatementContextBase<?, ?, ?> rootStatementCtx = (StatementContextBase<?, ?, ?>) augmentNode
213 .getFromNamespace(NamespaceToModule.class, module);
214 rootStatementCtxList.add(rootStatementCtx);
216 final Map<?, ?> subModules = rootStatementCtx.getAllFromNamespace(IncludedModuleContext.class);
217 if (subModules != null) {
218 rootStatementCtxList.addAll((Collection<? extends StatementContextBase<?, ?, ?>>) subModules.values());
222 StatementContextBase<?, ?, ?> parent = (StatementContextBase<?, ?, ?>) augmentNode.getParentContext();
223 if (StmtContextUtils.producesDeclared(parent, UsesStatement.class)) {
224 rootStatementCtxList.add(parent.getParentContext());
230 StatementContextBase<?, ?, ?> augmentTargetCtx = null;
231 for (final StatementContextBase<?, ?, ?> rootStatementCtx : rootStatementCtxList) {
232 augmentTargetCtx = findCtxOfNodeInRoot(rootStatementCtx, augmentTargetNode);
233 if (augmentTargetCtx != null)
237 return augmentTargetCtx;
241 public static StatementContextBase<?, ?, ?> findCtxOfNodeInSubstatements(StatementContextBase<?, ?, ?> rootStmtCtx,
242 final Iterable<QName> path) {
244 StatementContextBase<?, ?, ?> parent = rootStmtCtx;
246 Iterator<QName> pathIter = path.iterator();
247 while (pathIter.hasNext()) {
248 QName nextPathQName = pathIter.next();
249 StatementContextBase<?, ?, ?> foundSubstatement = getSubstatementByQName(parent, nextPathQName);
251 if (foundSubstatement == null) {
254 if (!pathIter.hasNext()) {
255 return foundSubstatement;
258 parent = foundSubstatement;
264 public static StatementContextBase<?, ?, ?> getSubstatementByQName(StatementContextBase<?, ?, ?> parent,
265 QName nextPathQName) {
267 Collection<StatementContextBase<?, ?, ?>> declaredSubstatement = parent.declaredSubstatements();
268 Collection<StatementContextBase<?, ?, ?>> effectiveSubstatement = parent.effectiveSubstatements();
270 Collection<StatementContextBase<?, ?, ?>> allSubstatements = new LinkedList<>();
271 allSubstatements.addAll(declaredSubstatement);
272 allSubstatements.addAll(effectiveSubstatement);
274 for (StatementContextBase<?, ?, ?> substatement : allSubstatements) {
275 Object substatementArgument = substatement.getStatementArgument();
276 QName substatementQName;
277 if (substatementArgument instanceof QName) {
278 substatementQName = (QName) substatementArgument;
280 if (isSupportedAugmentTarget(substatement)
281 && nextPathQName.getLocalName().equals(substatementQName.getLocalName())) {
284 } // augment to extension
285 else if (StmtContextUtils.producesDeclared(substatement, UnknownStatementImpl.class)
286 && substatementArgument instanceof String) {
288 final String nextPathName = nextPathQName.getLocalName();
290 if (nextPathName.equals(substatementArgument)
291 || nextPathName.equals(substatement.getPublicDefinition().getStatementName().getLocalName())) {
292 String message = "Module '" + substatement.getRoot().getStatementArgument()
293 + "': augment into extension '" + substatementArgument + "'.";
303 public static boolean isSupportedAugmentTarget(StatementContextBase<?, ?, ?> substatementCtx) {
306 * :TODO Substatement must be allowed augment target type e.g. Container, etc... and must be not for example
307 * grouping, identity etc. It is problem in case when more than one substatements have the same QName, for
308 * example Grouping and Container are siblings and they have the same QName. We must find the Container and the
309 * Grouping must be ignored as disallowed augment target.
312 Collection<?> allowedAugmentTargets = substatementCtx.getFromNamespace(ValidationBundlesNamespace.class,
313 ValidationBundleType.SUPPORTED_AUGMENT_TARGETS);
315 // if no allowed target is returned we consider all targets allowed
316 return allowedAugmentTargets == null || allowedAugmentTargets.isEmpty()
317 || allowedAugmentTargets.contains(substatementCtx.getPublicDefinition());
321 public static StatementContextBase<?, ?, ?> findCtxOfNodeInRoot(StatementContextBase<?, ?, ?> rootStmtCtx,
322 final SchemaNodeIdentifier node) {
323 return findCtxOfNodeInSubstatements(rootStmtCtx, node.getPathFromRoot());