Merge "Bug 2363, Bug 2205. Beta version of LeafRefContext tree computation"
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / rfc6020 / Utils.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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 static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.firstAttributeOf;
11
12 import java.net.URI;
13 import java.net.URISyntaxException;
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.Date;
17 import java.util.Iterator;
18 import java.util.LinkedList;
19 import java.util.List;
20 import javax.annotation.Nullable;
21 import javax.xml.xpath.XPath;
22 import javax.xml.xpath.XPathExpressionException;
23 import javax.xml.xpath.XPathFactory;
24 import com.google.common.base.CharMatcher;
25 import com.google.common.base.Splitter;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28 import org.antlr.v4.runtime.tree.TerminalNode;
29 import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser;
30 import org.opendaylight.yangtools.yang.common.QName;
31 import org.opendaylight.yangtools.yang.common.QNameModule;
32 import org.opendaylight.yangtools.yang.common.YangConstants;
33 import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier;
34 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
35 import org.opendaylight.yangtools.yang.model.api.stmt.BelongsToStatement;
36 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
37 import org.opendaylight.yangtools.yang.model.api.stmt.PrefixStatement;
38 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
39 import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleStatement;
40 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
41 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
42 import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToPrefixToModuleName;
43 import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToModuleIdentifier;
44 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleIdentifierToModuleQName;
45 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToModuleQName;
46 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
47 import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition;
48 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
49
50 public final class Utils {
51
52     private static final Logger LOG = LoggerFactory.getLogger(Utils.class);
53     private static final CharMatcher DOUBLE_QUOTE_MATCHER = CharMatcher.is('"');
54     private static final CharMatcher SINGLE_QUOTE_MATCHER = CharMatcher.is('\'');
55
56     private static final char SEPARATOR_NODENAME = '/';
57
58     private static final String REGEX_PATH_ABS = "/[^/].*";
59
60     private Utils() {
61     }
62
63     public static List<String> splitPathToNodeNames(String path) {
64
65         Splitter keySplitter = Splitter.on(SEPARATOR_NODENAME).omitEmptyStrings().trimResults();
66         return keySplitter.splitToList(path);
67     }
68
69     public static void validateXPath(String path) {
70
71         final XPath xPath = XPathFactory.newInstance().newXPath();
72
73         try {
74             xPath.compile(path);
75         } catch (XPathExpressionException e) {
76             throw new IllegalArgumentException("Argument is not valid XPath string", e);
77         }
78     }
79
80     public static boolean isXPathAbsolute(String path) {
81
82         validateXPath(path);
83
84         return path.matches(REGEX_PATH_ABS);
85     }
86
87     public static QName trimPrefix(QName identifier) {
88         String prefixedLocalName = identifier.getLocalName();
89         String[] namesParts = prefixedLocalName.split(":");
90
91         if (namesParts.length == 2) {
92             String localName = namesParts[1];
93             return QName.create(identifier.getModule(), localName);
94         }
95
96         return identifier;
97     }
98
99     public static boolean isValidStatementDefinition(PrefixToModule prefixes, QNameToStatementDefinition stmtDef,
100             QName identifier) {
101         if (stmtDef.get(identifier) != null) {
102             return true;
103         } else {
104             String prefixedLocalName = identifier.getLocalName();
105             String[] namesParts = prefixedLocalName.split(":");
106
107             if (namesParts.length == 2) {
108                 String prefix = namesParts[0];
109                 String localName = namesParts[1];
110                 if (prefixes != null && prefixes.get(prefix) != null
111                         && stmtDef.get(new QName(YangConstants.RFC6020_YIN_NAMESPACE, localName)) != null) {
112                     return true;
113                 }
114             }
115         }
116         return false;
117     }
118
119     public static Iterable<QName> parseXPath(StmtContext<?, ?, ?> ctx, String path) {
120
121         validateXPath(path);
122
123         List<String> nodeNames = splitPathToNodeNames(path);
124         List<QName> qNames = new ArrayList<>();
125
126         for (String nodeName : nodeNames) {
127             try {
128                 final QName qName = Utils.qNameFromArgument(ctx, nodeName);
129                 qNames.add(qName);
130             } catch (Exception e) {
131                 throw new IllegalArgumentException(e);
132             }
133         }
134
135         return qNames;
136     }
137
138     public static String stringFromStringContext(final YangStatementParser.ArgumentContext context) {
139         StringBuilder sb = new StringBuilder();
140         List<TerminalNode> strings = context.STRING();
141         if (strings.isEmpty()) {
142             strings = Arrays.asList(context.IDENTIFIER());
143         }
144         for (TerminalNode stringNode : strings) {
145             final String str = stringNode.getText();
146             char firstChar = str.charAt(0);
147             final CharMatcher quoteMatcher;
148             if (SINGLE_QUOTE_MATCHER.matches(firstChar)) {
149                 quoteMatcher = SINGLE_QUOTE_MATCHER;
150             } else if (DOUBLE_QUOTE_MATCHER.matches(firstChar)) {
151                 quoteMatcher = DOUBLE_QUOTE_MATCHER;
152             } else {
153                 sb.append(str);
154                 continue;
155             }
156             sb.append(quoteMatcher.removeFrom(str.substring(1, str.length() - 1)));
157         }
158         return sb.toString();
159     }
160
161     public static QName qNameFromArgument(StmtContext<?, ?, ?> ctx, String value) {
162
163         String prefix;
164         QNameModule qNameModule = null;
165         try {
166             qNameModule = QNameModule.create(new URI(""), new Date(0));
167         } catch (URISyntaxException e) {
168             LOG.warn(e.getMessage(), e);
169         }
170         String localName = null;
171
172         String[] namesParts = value.split(":");
173         switch (namesParts.length) {
174         case 1:
175             localName = namesParts[0];
176
177             if (StmtContextUtils.producesDeclared(ctx.getRoot(), ModuleStatement.class)) {
178                 prefix = firstAttributeOf(ctx.getRoot().declaredSubstatements(), PrefixStatement.class);
179                 qNameModule = ctx.getFromNamespace(PrefixToModule.class, prefix);
180
181             } else if (StmtContextUtils.producesDeclared(ctx.getRoot(), SubmoduleStatement.class)) {
182                 String belongsToModuleName = firstAttributeOf(ctx.getRoot().declaredSubstatements(),
183                         BelongsToStatement.class);
184                 qNameModule = ctx.getFromNamespace(ModuleNameToModuleQName.class, belongsToModuleName);
185             }
186             break;
187         case 2:
188             prefix = namesParts[0];
189             localName = namesParts[1];
190
191             ModuleIdentifier impModIdentifier = ctx.getRoot().getFromNamespace(ImpPrefixToModuleIdentifier.class,
192                     prefix);
193             qNameModule = ctx.getFromNamespace(ModuleIdentifierToModuleQName.class, impModIdentifier);
194
195             if (qNameModule == null && StmtContextUtils.producesDeclared(ctx.getRoot(), SubmoduleStatement.class)) {
196                 String moduleName = ctx.getRoot().getFromNamespace(BelongsToPrefixToModuleName.class, prefix);
197                 qNameModule = ctx.getFromNamespace(ModuleNameToModuleQName.class, moduleName);
198             }
199
200             break;
201         default:
202             break;
203         }
204
205         return QName.create(qNameModule, localName);
206     }
207
208     @Nullable
209     private static StatementContextBase<?, ?, ?> findCtxOfNodeInSubstatements(
210             StatementContextBase<?, ?, ?> rootStmtCtx, final Iterable<QName> path, boolean searchInEffective) {
211
212         StatementContextBase<?, ?, ?> parent = rootStmtCtx;
213
214         Iterator<QName> pathIter = path.iterator();
215         QName targetNode = pathIter.next();
216
217         while (pathIter.hasNext()) {
218
219             for (StatementContextBase<?, ?, ?> child : searchInEffective ? parent.effectiveSubstatements() : parent
220                     .declaredSubstatements()) {
221
222                 if (targetNode.equals(child.getStatementArgument())) {
223                     parent = child;
224                     targetNode = pathIter.next();
225                 }
226             }
227
228             if (parent.equals(rootStmtCtx)) {
229
230                 return null;
231             }
232         }
233
234         StatementContextBase<?, ?, ?> targetCtx = null;
235
236         for (StatementContextBase<?, ?, ?> child : searchInEffective ? parent.effectiveSubstatements() : parent
237                 .declaredSubstatements()) {
238
239             if (targetNode.equals(child.getStatementArgument())) {
240                 targetCtx = child;
241             }
242         }
243
244         return targetCtx;
245     }
246
247     @Nullable
248     public static StatementContextBase<?, ?, ?> findCtxOfNodeInRoot(StatementContextBase<?, ?, ?> rootStmtCtx,
249             final SchemaNodeIdentifier node) {
250
251         System.out.println(node.getPathFromRoot());
252
253         StatementContextBase<?, ?, ?> targetCtx = findCtxOfNodeInSubstatements(rootStmtCtx, node.getPathFromRoot(),
254                 false);
255
256         if (targetCtx == null) {
257
258             targetCtx = findCtxOfNodeInSubstatements(rootStmtCtx, node.getPathFromRoot(), true);
259         }
260
261         return targetCtx;
262     }
263
264     @Nullable
265     public static StatementContextBase<?, ?, ?> findCtxOfNodeInRoot(StatementContextBase<?, ?, ?> rootStmtCtx,
266             final Iterable<QName> path) {
267
268         StatementContextBase<?, ?, ?> targetCtx = findCtxOfNodeInSubstatements(rootStmtCtx, path, false);
269
270         if (targetCtx == null) {
271
272             targetCtx = findCtxOfNodeInSubstatements(rootStmtCtx, path, true);
273         }
274
275         return targetCtx;
276     }
277
278     public static SchemaPath getSchemaPath(StmtContext<?, ?, ?> ctx) {
279
280         Iterator<Object> argumentsIterator = ctx.getArgumentsFromRoot().iterator();
281         argumentsIterator.next(); // skip root argument
282
283         List<QName> qNamesFromRoot = new LinkedList<>();
284
285         while (argumentsIterator.hasNext()) {
286             Object argument = argumentsIterator.next();
287             if (argument instanceof QName) {
288                 QName qname = (QName) argument;
289                 qNamesFromRoot.add(qname);
290             } else {
291                 return SchemaPath.SAME;
292             }
293         }
294
295         return SchemaPath.create(qNamesFromRoot, true);
296     }
297 }