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