Fix missing format placeholder
[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.model.api.ModuleIdentifier;
31 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
32 import org.opendaylight.yangtools.yang.model.api.stmt.BelongsToStatement;
33 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
34 import org.opendaylight.yangtools.yang.model.api.stmt.PrefixStatement;
35 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
36 import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleStatement;
37 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
38 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
39 import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToPrefixToModuleName;
40 import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToModuleIdentifier;
41 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleIdentifierToModuleQName;
42 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToModuleQName;
43 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
44 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
45
46 import com.google.common.base.CharMatcher;
47 import com.google.common.base.Splitter;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 public final class Utils {
52
53     private Utils() {
54     }
55
56     private static final Logger LOG = LoggerFactory.getLogger(Utils.class);
57     private static final CharMatcher DOUBLE_QUOTE_MATCHER = CharMatcher.is('"');
58     private static final CharMatcher SINGLE_QUOTE_MATCHER = CharMatcher.is('\'');
59
60     private static final char SEPARATOR_NODENAME = '/';
61
62     private static final String REGEX_PATH_ABS = "/[^/].+";
63
64     public static List<String> splitPathToNodeNames(String path) {
65
66         Splitter keySplitter = Splitter.on(SEPARATOR_NODENAME).omitEmptyStrings().trimResults();
67         return keySplitter.splitToList(path);
68     }
69
70     public static void validateXPath(String path) {
71
72         final XPath xPath = XPathFactory.newInstance().newXPath();
73
74         try {
75             xPath.compile(path);
76         } catch (XPathExpressionException e) {
77             throw new IllegalArgumentException("Argument is not valid XPath string", e);
78         }
79     }
80
81     public static boolean isXPathAbsolute(String path) {
82
83         validateXPath(path);
84
85         return path.matches(REGEX_PATH_ABS);
86     }
87
88     public static Iterable<QName> parseXPath(StmtContext<?, ?, ?> ctx, String path) {
89
90         validateXPath(path);
91
92         List<String> nodeNames = splitPathToNodeNames(path);
93         List<QName> qNames = new ArrayList<>();
94
95         for (String nodeName : nodeNames) {
96             try {
97                 final QName qName = Utils.qNameFromArgument(ctx, nodeName);
98                 qNames.add(qName);
99             } catch (Exception e) {
100                 throw new IllegalArgumentException(e);
101             }
102         }
103
104         return qNames;
105     }
106
107     public static String stringFromStringContext(final YangStatementParser.ArgumentContext context) {
108         StringBuilder sb = new StringBuilder();
109         List<TerminalNode> strings = context.STRING();
110         if (strings.size() == 0) {
111             strings = Arrays.asList(context.IDENTIFIER());
112         }
113         for (TerminalNode stringNode : strings) {
114             final String str = stringNode.getText();
115             char firstChar = str.charAt(0);
116             final CharMatcher quoteMatcher;
117             if (SINGLE_QUOTE_MATCHER.matches(firstChar)) {
118                 quoteMatcher = SINGLE_QUOTE_MATCHER;
119             } else if (DOUBLE_QUOTE_MATCHER.matches(firstChar)) {
120                 quoteMatcher = DOUBLE_QUOTE_MATCHER;
121             } else {
122                 sb.append(str);
123                 continue;
124             }
125             sb.append(quoteMatcher.removeFrom(str.substring(1, str.length() - 1)));
126         }
127         return sb.toString();
128     }
129
130     public static QName qNameFromArgument(StmtContext<?, ?, ?> ctx, String value) {
131
132         String prefix = null;
133         QNameModule qNameModule = null;
134         try {
135             qNameModule = QNameModule.create(new URI(""), new Date(0));
136         } catch (URISyntaxException e) {
137             LOG.warn(e.getMessage(), e);
138         }
139         String localName = null;
140
141         String[] namesParts = value.split(":");
142         switch (namesParts.length) {
143         case 1:
144             localName = namesParts[0];
145
146             if (StmtContextUtils.producesDeclared(ctx.getRoot(), ModuleStatement.class)) {
147                 prefix = firstAttributeOf(ctx.getRoot().declaredSubstatements(), PrefixStatement.class);
148                 qNameModule = ctx.getFromNamespace(PrefixToModule.class, prefix);
149
150             } else if (StmtContextUtils.producesDeclared(ctx.getRoot(), SubmoduleStatement.class)) {
151                 String belongsToModuleName = firstAttributeOf(ctx.getRoot().declaredSubstatements(),
152                         BelongsToStatement.class);
153                 qNameModule = ctx.getFromNamespace(ModuleNameToModuleQName.class, belongsToModuleName);
154             }
155             break;
156         case 2:
157             prefix = namesParts[0];
158             localName = namesParts[1];
159
160             ModuleIdentifier impModIdentifier = ctx.getRoot().getFromNamespace(ImpPrefixToModuleIdentifier.class,
161                     prefix);
162             qNameModule = ctx.getFromNamespace(ModuleIdentifierToModuleQName.class, impModIdentifier);
163
164             if (qNameModule == null && StmtContextUtils.producesDeclared(ctx.getRoot(), SubmoduleStatement.class)) {
165                 String moduleName = ctx.getRoot().getFromNamespace(BelongsToPrefixToModuleName.class, prefix);
166                 qNameModule = ctx.getFromNamespace(ModuleNameToModuleQName.class, moduleName);
167             }
168
169             break;
170         default:
171             break;
172         }
173
174         return QName.create(qNameModule, localName);
175     }
176
177     @Nullable
178     public static StatementContextBase<?, ?, ?> findCtxOfNodeInRoot(StatementContextBase<?, ?, ?> rootStmtCtx,
179             final SchemaNodeIdentifier node) {
180
181         StatementContextBase<?, ?, ?> parent = rootStmtCtx;
182         final Iterator<QName> pathIter = node.getPathFromRoot().iterator();
183
184         QName targetNode = pathIter.next();
185
186         while (pathIter.hasNext()) {
187
188             for (StatementContextBase<?, ?, ?> child : parent.declaredSubstatements()) {
189
190                 if (targetNode.equals(child.getStatementArgument())) {
191                     parent = child;
192                     targetNode = pathIter.next();
193                 }
194             }
195
196             if (parent.equals(rootStmtCtx)) {
197
198                 return null;
199             }
200         }
201
202         StatementContextBase<?, ?, ?> targetCtx = null;
203
204         for (StatementContextBase<?, ?, ?> child : parent.declaredSubstatements()) {
205
206             if (targetNode.equals(child.getStatementArgument())) {
207                 targetCtx = child;
208             }
209         }
210
211         return targetCtx;
212     }
213
214     public static SchemaPath getSchemaPath(StmtContext<?, ?, ?> ctx) {
215
216         Iterator<Object> argumentsIterator = ctx.getArgumentsFromRoot().iterator();
217         argumentsIterator.next(); // skip root argument
218
219         List<QName> qNamesFromRoot = new LinkedList<>();
220
221         while (argumentsIterator.hasNext()) {
222             Object argument = argumentsIterator.next();
223             if (argument instanceof QName) {
224                 QName qname = (QName) argument;
225                 qNamesFromRoot.add(qname);
226             } else {
227                 return SchemaPath.SAME;
228             }
229         }
230
231         return SchemaPath.create(qNamesFromRoot, true);
232     }
233 }