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