Merge "Bug 2983 - Throws ResultAlreadySet instead of IllegalStateException"
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / rfc6020 / AugmentUtils.java
1 /**
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  * <p/>
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.stmt.rfc6020;
9
10 import java.util.Iterator;
11 import javax.annotation.Nullable;
12
13 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.TypeOfCopy;
14 import java.util.Collection;
15 import java.util.HashSet;
16 import java.util.LinkedList;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Set;
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.SchemaNodeIdentifier;
27 import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement;
28 import org.opendaylight.yangtools.yang.parser.spi.NamespaceToModule;
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.ModuleNameToModuleQName;
33 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
34 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
35
36 public final class AugmentUtils {
37
38     private static final String REGEX_PATH_REL1 = "\\.\\.?\\s*/(.+)";
39     private static final String REGEX_PATH_REL2 = "//.*";
40
41     private AugmentUtils() {
42     }
43
44     public static Iterable<QName> parseAugmentPath(StmtContext<?, ?, ?> ctx,
45             String path) {
46
47         if (path.matches(REGEX_PATH_REL1) || path.matches(REGEX_PATH_REL2)) {
48             throw new IllegalArgumentException(
49                     "An argument for augment can be only absolute path; or descendant if used in uses");
50         }
51
52         return Utils.parseXPath(ctx, path);
53     }
54
55     public static void copyFromSourceToTarget(
56             StatementContextBase<?, ?, ?> sourceCtx,
57             StatementContextBase<?, ?, ?> targetCtx) throws SourceException {
58
59         QNameModule newQNameModule = getNewQNameModule(targetCtx, sourceCtx);
60         copyDeclaredStmts(sourceCtx, targetCtx, newQNameModule);
61         copyEffectiveStmts(sourceCtx, targetCtx, newQNameModule);
62
63     }
64
65     public static void copyDeclaredStmts(
66             StatementContextBase<?, ?, ?> sourceCtx,
67             StatementContextBase<?, ?, ?> targetCtx, QNameModule newQNameModule)
68             throws SourceException {
69         Collection<? extends StatementContextBase<?, ?, ?>> declaredSubstatements = sourceCtx
70                 .declaredSubstatements();
71         for (StatementContextBase<?, ?, ?> originalStmtCtx : declaredSubstatements) {
72             if (needToCopyByAugment(originalStmtCtx)) {
73                 StatementContextBase<?, ?, ?> copy = originalStmtCtx
74                         .createCopy(newQNameModule, targetCtx,
75                                 TypeOfCopy.ADDED_BY_AUGMENTATION);
76                 targetCtx.addEffectiveSubstatement(copy);
77             } else if (isReusedByAugment(originalStmtCtx)) {
78                 targetCtx.addEffectiveSubstatement(originalStmtCtx);
79             }
80         }
81     }
82
83     public static void copyEffectiveStmts(
84             StatementContextBase<?, ?, ?> sourceCtx,
85             StatementContextBase<?, ?, ?> targetCtx, QNameModule newQNameModule)
86             throws SourceException {
87         Collection<? extends StatementContextBase<?, ?, ?>> effectiveSubstatements = sourceCtx
88                 .effectiveSubstatements();
89         for (StatementContextBase<?, ?, ?> originalStmtCtx : effectiveSubstatements) {
90             if (needToCopyByAugment(originalStmtCtx)) {
91                 StatementContextBase<?, ?, ?> copy = originalStmtCtx
92                         .createCopy(newQNameModule, targetCtx,
93                                 TypeOfCopy.ADDED_BY_AUGMENTATION);
94                 targetCtx.addEffectiveSubstatement(copy);
95             } else if (isReusedByAugment(originalStmtCtx)) {
96                 targetCtx.addEffectiveSubstatement(originalStmtCtx);
97             }
98         }
99     }
100
101     public static QNameModule getNewQNameModule(
102             StatementContextBase<?, ?, ?> targetCtx,
103             StatementContextBase<?, ?, ?> sourceCtx) {
104         Object targetStmtArgument = targetCtx.getStatementArgument();
105
106         final StatementContextBase<?, ?, ?> root = sourceCtx.getRoot();
107         final String moduleName = (String) root.getStatementArgument();
108         final QNameModule sourceQNameModule = root.getFromNamespace(
109                 ModuleNameToModuleQName.class, moduleName);
110
111         if (targetStmtArgument instanceof QName) {
112             QName targetQName = (QName) targetStmtArgument;
113             QNameModule targetQNameModule = targetQName.getModule();
114
115             if (targetQNameModule.equals(sourceQNameModule)) {
116                 return null;
117             } else {
118                 return targetQNameModule;
119             }
120         } else {
121             return null;
122         }
123     }
124
125     public static boolean needToCopyByAugment(StmtContext<?, ?, ?> stmtContext) {
126
127         Set<StatementDefinition> noCopyDefSet = new HashSet<>();
128         noCopyDefSet.add(Rfc6020Mapping.USES);
129
130         StatementDefinition def = stmtContext.getPublicDefinition();
131         return !noCopyDefSet.contains(def);
132     }
133
134     public static boolean isReusedByAugment(StmtContext<?, ?, ?> stmtContext) {
135
136         Set<StatementDefinition> reusedDefSet = new HashSet<>();
137         reusedDefSet.add(Rfc6020Mapping.TYPEDEF);
138
139         StatementDefinition def = stmtContext.getPublicDefinition();
140
141         return reusedDefSet.contains(def);
142     }
143
144     public static StatementContextBase<?, ?, ?> getAugmentTargetCtx(
145             final Mutable<SchemaNodeIdentifier, AugmentStatement, EffectiveStatement<SchemaNodeIdentifier, AugmentStatement>> augmentNode) {
146
147         final SchemaNodeIdentifier augmentTargetNode = augmentNode
148                 .getStatementArgument();
149         if (augmentTargetNode == null) {
150             throw new IllegalArgumentException(
151                     "Augment argument null, something bad happened in some of previous parsing phases");
152         }
153
154         List<StatementContextBase<?, ?, ?>> rootStatementCtxList = new LinkedList<>();
155
156         if (augmentTargetNode.isAbsolute()) {
157
158             QNameModule module = augmentTargetNode.getPathFromRoot().iterator()
159                     .next().getModule();
160
161             StatementContextBase<?, ?, ?> rootStatementCtx = (StatementContextBase<?, ?, ?>) augmentNode
162                     .getFromNamespace(NamespaceToModule.class, module);
163             rootStatementCtxList.add(rootStatementCtx);
164
165             final Map<?, ?> subModules = rootStatementCtx
166                     .getAllFromNamespace(IncludedModuleContext.class);
167             if (subModules != null) {
168                 rootStatementCtxList
169                         .addAll((Collection<? extends StatementContextBase<?, ?, ?>>) subModules
170                                 .values());
171             }
172
173         } else {
174             StatementContextBase<?, ?, ?> parent = (StatementContextBase<?, ?, ?>) augmentNode
175                     .getParentContext();
176             if (StmtContextUtils.producesDeclared(parent, UsesStatement.class)) {
177                 rootStatementCtxList.add(parent.getParentContext());
178             } else {
179                 // error
180             }
181         }
182
183         StatementContextBase<?, ?, ?> augmentTargetCtx = null;
184         for (final StatementContextBase<?, ?, ?> rootStatementCtx : rootStatementCtxList) {
185             augmentTargetCtx = findCtxOfNodeInRoot(rootStatementCtx,
186                     augmentTargetNode);
187             if (augmentTargetCtx != null)
188                 break;
189         }
190
191         return augmentTargetCtx;
192     }
193
194     @Nullable
195     public static StatementContextBase<?, ?, ?> findCtxOfNodeInSubstatements(
196             StatementContextBase<?, ?, ?> rootStmtCtx,
197             final Iterable<QName> path) {
198
199         StatementContextBase<?, ?, ?> parent = rootStmtCtx;
200
201         Iterator<QName> pathIter = path.iterator();
202         while (pathIter.hasNext()) {
203             QName nextPathQName = pathIter.next();
204             StatementContextBase<?, ?, ?> foundSubstatement = getSubstatementByQName(
205                     parent, nextPathQName);
206
207             if (foundSubstatement == null) {
208                 return null;
209             }
210             if (!pathIter.hasNext()) {
211                 return foundSubstatement;
212             }
213
214             parent = foundSubstatement;
215         }
216
217         return null;
218     }
219
220     public static StatementContextBase<?, ?, ?> getSubstatementByQName(
221             StatementContextBase<?, ?, ?> parent, QName nextPathQName) {
222
223         Collection<StatementContextBase<?, ?, ?>> declaredSubstatement = parent
224                 .declaredSubstatements();
225         Collection<StatementContextBase<?, ?, ?>> effectiveSubstatement = parent
226                 .effectiveSubstatements();
227
228         Collection<StatementContextBase<?, ?, ?>> allSubstatements = new LinkedList<>();
229         allSubstatements.addAll(declaredSubstatement);
230         allSubstatements.addAll(effectiveSubstatement);
231
232         for (StatementContextBase<?, ?, ?> substatement : allSubstatements) {
233             if (isAllowedAugmentTarget(substatement)
234                     && nextPathQName
235                             .equals(substatement.getStatementArgument())) {
236                 return substatement;
237             }
238         }
239
240         return null;
241     }
242
243     public static boolean isAllowedAugmentTarget(
244             StatementContextBase<?, ?, ?> substatement) {
245
246         /*
247          * :TODO Substatement must be allowed augment target type e.g.
248          * Container, etc... and must be not for example grouping, identity etc.
249          * It is problem in case when more than one substatements have the same
250          * QName, for example Grouping and Container are siblings and they have the
251          * same QName. We must find the Container and the Grouping must be ignored
252          * as disallowed augment target.
253          */
254
255         return true;
256     }
257
258     @Nullable
259     public static StatementContextBase<?, ?, ?> findCtxOfNodeInRoot(
260             StatementContextBase<?, ?, ?> rootStmtCtx,
261             final SchemaNodeIdentifier node) {
262         return findCtxOfNodeInSubstatements(rootStmtCtx, node.getPathFromRoot());
263     }
264 }