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 org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
12 import org.slf4j.Logger;
13 import org.slf4j.LoggerFactory;
14 import com.google.common.collect.ImmutableList.Builder;
15 import java.util.Collection;
16 import java.util.HashSet;
17 import java.util.Iterator;
18 import java.util.LinkedList;
19 import java.util.List;
21 import java.util.Objects;
23 import javax.annotation.Nullable;
24 import org.opendaylight.yangtools.yang.common.QName;
25 import org.opendaylight.yangtools.yang.common.QNameModule;
26 import org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping;
27 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
28 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
29 import org.opendaylight.yangtools.yang.model.api.stmt.AugmentStatement;
30 import org.opendaylight.yangtools.yang.model.api.stmt.DataDefinitionStatement;
31 import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryStatement;
32 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
33 import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement;
34 import org.opendaylight.yangtools.yang.model.api.stmt.WhenStatement;
35 import org.opendaylight.yangtools.yang.parser.spi.NamespaceToModule;
36 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
37 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
38 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.TypeOfCopy;
39 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
40 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
41 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace;
42 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType;
43 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
45 public final class AugmentUtils {
47 private static final Logger LOG = LoggerFactory.getLogger(AugmentUtils.class);
49 private static final String REGEX_PATH_REL1 = "\\.\\.?\\s*/(.+)";
50 private static final String REGEX_PATH_REL2 = "//.*";
52 private AugmentUtils() {
55 public static Iterable<QName> parseAugmentPath(StmtContext<?, ?, ?> ctx,
58 if (path.matches(REGEX_PATH_REL1) || path.matches(REGEX_PATH_REL2)) {
59 throw new IllegalArgumentException(
60 "An argument for augment can be only absolute path; or descendant if used in uses");
63 return Utils.parseXPath(ctx, path);
66 public static void copyFromSourceToTarget(
67 StatementContextBase<?, ?, ?> sourceCtx,
68 StatementContextBase<?, ?, ?> targetCtx) throws SourceException {
70 copyDeclaredStmts(sourceCtx, targetCtx);
71 copyEffectiveStmts(sourceCtx, targetCtx);
74 public static void copyDeclaredStmts(
75 StatementContextBase<?, ?, ?> sourceCtx,
76 StatementContextBase<?, ?, ?> targetCtx) throws SourceException {
78 Collection<? extends StatementContextBase<?, ?, ?>> declaredSubStatements = sourceCtx
79 .declaredSubstatements();
80 final List<StatementContextBase> subStatements = new Builder<StatementContextBase>()
81 .addAll(targetCtx.declaredSubstatements())
82 .addAll(targetCtx.effectiveSubstatements()).build();
83 boolean sourceAndTargetInSameModule = Utils.getRootModuleQName(
84 sourceCtx).equals(Utils.getRootModuleQName(targetCtx));
86 TypeOfCopy typeOfCopy = sourceCtx.getParentContext()
87 .getPublicDefinition().getDeclaredRepresentationClass()
88 .equals(UsesStatement.class) ? TypeOfCopy.ADDED_BY_USES_AUGMENTATION
89 : TypeOfCopy.ADDED_BY_AUGMENTATION;
91 for (StatementContextBase<?, ?, ?> originalStmtCtx : declaredSubStatements) {
92 if (needToCopyByAugment(originalStmtCtx)) {
93 validateNodeCanBeCopiedByAugment(originalStmtCtx,
94 subStatements, sourceAndTargetInSameModule);
96 StatementContextBase<?, ?, ?> copy = originalStmtCtx
97 .createCopy(targetCtx, typeOfCopy);
98 targetCtx.addEffectiveSubstatement(copy);
99 } else if (isReusedByAugment(originalStmtCtx)) {
100 targetCtx.addEffectiveSubstatement(originalStmtCtx);
105 public static void copyEffectiveStmts(
106 StatementContextBase<?, ?, ?> sourceCtx,
107 StatementContextBase<?, ?, ?> targetCtx) throws SourceException {
109 Collection<? extends StatementContextBase<?, ?, ?>> effectiveSubstatements = sourceCtx
110 .effectiveSubstatements();
111 final List<StatementContextBase> subStatements = new Builder<StatementContextBase>()
112 .addAll(targetCtx.declaredSubstatements())
113 .addAll(targetCtx.effectiveSubstatements()).build();
114 boolean sourceAndTargetInSameModule = Utils.getRootModuleQName(
115 sourceCtx).equals(Utils.getRootModuleQName(targetCtx));
117 TypeOfCopy typeOfCopy = sourceCtx.getParentContext()
118 .getPublicDefinition().getDeclaredRepresentationClass()
119 .equals(UsesStatement.class) ? TypeOfCopy.ADDED_BY_USES_AUGMENTATION
120 : TypeOfCopy.ADDED_BY_AUGMENTATION;
122 for (StatementContextBase<?, ?, ?> originalStmtCtx : effectiveSubstatements) {
123 if (needToCopyByAugment(originalStmtCtx)) {
124 validateNodeCanBeCopiedByAugment(originalStmtCtx,
125 subStatements, sourceAndTargetInSameModule);
127 StatementContextBase<?, ?, ?> copy = originalStmtCtx
128 .createCopy(targetCtx, typeOfCopy);
129 targetCtx.addEffectiveSubstatement(copy);
130 } else if (isReusedByAugment(originalStmtCtx)) {
131 targetCtx.addEffectiveSubstatement(originalStmtCtx);
136 private static void validateNodeCanBeCopiedByAugment(
137 final StatementContextBase<?, ?, ?> sourceCtx,
138 final List<StatementContextBase> targetSubStatements,
139 boolean sourceAndTargetInSameModule) {
141 if (sourceCtx.getPublicDefinition().getDeclaredRepresentationClass()
142 .equals(WhenStatement.class)) {
146 if (!sourceAndTargetInSameModule) {
147 final List<StatementContextBase> sourceSubStatements = new Builder<StatementContextBase>()
148 .addAll(sourceCtx.declaredSubstatements())
149 .addAll(sourceCtx.effectiveSubstatements()).build();
151 for (final StatementContextBase sourceSubStatement : sourceSubStatements) {
152 if (sourceSubStatement.getPublicDefinition()
153 .getDeclaredRepresentationClass()
154 .equals(MandatoryStatement.class)) {
155 throw new IllegalArgumentException(
157 "An augment cannot add node '%s' because it is mandatory and in module different from target",
158 sourceCtx.rawStatementArgument()));
163 for (final StatementContextBase subStatement : targetSubStatements) {
165 final boolean sourceIsDataNode = DataDefinitionStatement.class
166 .isAssignableFrom(sourceCtx.getPublicDefinition()
167 .getDeclaredRepresentationClass());
168 final boolean targetIsDataNode = DataDefinitionStatement.class
169 .isAssignableFrom(subStatement.getPublicDefinition()
170 .getDeclaredRepresentationClass());
171 boolean qNamesEqual = sourceIsDataNode
173 && Objects.equals(sourceCtx.getStatementArgument(),
174 subStatement.getStatementArgument());
177 throw new IllegalStateException(
179 "An augment cannot add node named '%s' because this name is already used in target",
180 sourceCtx.rawStatementArgument()));
185 public static QNameModule getNewQNameModule(
186 StatementContextBase<?, ?, ?> targetCtx,
187 StatementContextBase<?, ?, ?> sourceCtx) {
188 Object targetStmtArgument = targetCtx.getStatementArgument();
190 final StatementContextBase<?, ?, ?> root = sourceCtx.getRoot();
191 final QNameModule sourceQNameModule = root.getFromNamespace(
192 ModuleCtxToModuleQName.class, root);
194 if (targetStmtArgument instanceof QName) {
195 QName targetQName = (QName) targetStmtArgument;
196 QNameModule targetQNameModule = targetQName.getModule();
198 if (targetQNameModule.equals(sourceQNameModule)) {
201 return targetQNameModule;
208 public static boolean needToCopyByAugment(StmtContext<?, ?, ?> stmtContext) {
210 Set<StatementDefinition> noCopyDefSet = new HashSet<>();
211 noCopyDefSet.add(Rfc6020Mapping.USES);
213 StatementDefinition def = stmtContext.getPublicDefinition();
214 return !noCopyDefSet.contains(def);
217 public static boolean isReusedByAugment(StmtContext<?, ?, ?> stmtContext) {
219 Set<StatementDefinition> reusedDefSet = new HashSet<>();
220 reusedDefSet.add(Rfc6020Mapping.TYPEDEF);
222 StatementDefinition def = stmtContext.getPublicDefinition();
224 return reusedDefSet.contains(def);
227 public static StatementContextBase<?, ?, ?> getAugmentTargetCtx(
228 final Mutable<SchemaNodeIdentifier, AugmentStatement, EffectiveStatement<SchemaNodeIdentifier, AugmentStatement>> augmentNode) {
230 final SchemaNodeIdentifier augmentTargetNode = augmentNode
231 .getStatementArgument();
232 if (augmentTargetNode == null) {
233 throw new IllegalArgumentException(
234 "Augment argument null, something bad happened in some of previous parsing phases");
237 List<StatementContextBase<?, ?, ?>> rootStatementCtxList = new LinkedList<>();
238 if (augmentTargetNode.isAbsolute()) {
240 QNameModule module = augmentTargetNode.getPathFromRoot().iterator()
243 StatementContextBase<?, ?, ?> rootStatementCtx = (StatementContextBase<?, ?, ?>) augmentNode
244 .getFromNamespace(NamespaceToModule.class, module);
245 rootStatementCtxList.add(rootStatementCtx);
247 final Map<?, ?> subModules = rootStatementCtx
248 .getAllFromNamespace(IncludedModuleContext.class);
249 if (subModules != null) {
251 .addAll((Collection<? extends StatementContextBase<?, ?, ?>>) subModules
256 StatementContextBase<?, ?, ?> parent = (StatementContextBase<?, ?, ?>) augmentNode
258 if (StmtContextUtils.producesDeclared(parent, UsesStatement.class)) {
259 rootStatementCtxList.add(parent.getParentContext());
265 StatementContextBase<?, ?, ?> augmentTargetCtx = null;
266 for (final StatementContextBase<?, ?, ?> rootStatementCtx : rootStatementCtxList) {
267 augmentTargetCtx = findCtxOfNodeInRoot(rootStatementCtx,
269 if (augmentTargetCtx != null)
273 return augmentTargetCtx;
277 public static StatementContextBase<?, ?, ?> findCtxOfNodeInSubstatements(
278 StatementContextBase<?, ?, ?> rootStmtCtx,
279 final Iterable<QName> path) {
281 StatementContextBase<?, ?, ?> parent = rootStmtCtx;
283 Iterator<QName> pathIter = path.iterator();
284 while (pathIter.hasNext()) {
285 QName nextPathQName = pathIter.next();
286 StatementContextBase<?, ?, ?> foundSubstatement = getSubstatementByQName(
287 parent, nextPathQName);
289 if (foundSubstatement == null) {
292 if (!pathIter.hasNext()) {
293 return foundSubstatement;
296 parent = foundSubstatement;
302 public static StatementContextBase<?, ?, ?> getSubstatementByQName(
303 StatementContextBase<?, ?, ?> parent, QName nextPathQName) {
305 Collection<StatementContextBase<?, ?, ?>> declaredSubstatement = parent
306 .declaredSubstatements();
307 Collection<StatementContextBase<?, ?, ?>> effectiveSubstatement = parent
308 .effectiveSubstatements();
310 Collection<StatementContextBase<?, ?, ?>> allSubstatements = new LinkedList<>();
311 allSubstatements.addAll(declaredSubstatement);
312 allSubstatements.addAll(effectiveSubstatement);
314 for (StatementContextBase<?, ?, ?> substatement : allSubstatements) {
315 Object substatementArgument = substatement.getStatementArgument();
316 QName substatementQName;
317 if (substatementArgument instanceof QName) {
318 substatementQName = (QName) substatementArgument;
320 if (isSupportedAugmentTarget(substatement)
321 && nextPathQName.getLocalName().equals(
322 substatementQName.getLocalName())) {
325 } // augment to extension
326 else if (StmtContextUtils.producesDeclared(substatement,
327 UnknownStatementImpl.class)
328 && substatementArgument instanceof String) {
329 if (nextPathQName.getLocalName().equals(substatementArgument)) {
330 String message = "Module '"+substatement.getRoot().getStatementArgument()+"': augment into extension '"+substatementArgument+"'.";
340 public static boolean isSupportedAugmentTarget(
341 StatementContextBase<?, ?, ?> substatementCtx) {
344 * :TODO Substatement must be allowed augment target type e.g.
345 * Container, etc... and must be not for example grouping, identity etc.
346 * It is problem in case when more than one substatements have the same
347 * QName, for example Grouping and Container are siblings and they have
348 * the same QName. We must find the Container and the Grouping must be
349 * ignored as disallowed augment target.
352 Collection<?> allowedAugmentTargets = substatementCtx.getFromNamespace(
353 ValidationBundlesNamespace.class,
354 ValidationBundleType.SUPPORTED_AUGMENT_TARGETS);
356 // if no allowed target is returned we consider all targets allowed
357 return allowedAugmentTargets == null || allowedAugmentTargets.isEmpty()
358 || allowedAugmentTargets.contains(substatementCtx.getPublicDefinition());
362 public static StatementContextBase<?, ?, ?> findCtxOfNodeInRoot(
363 StatementContextBase<?, ?, ?> rootStmtCtx,
364 final SchemaNodeIdentifier node) {
365 return findCtxOfNodeInSubstatements(rootStmtCtx, node.getPathFromRoot());