2 * Copyright (c) 2018 Pantheon Technologies, s.r.o. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.xpath.impl;
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
13 import java.util.List;
14 import org.opendaylight.yangtools.yang.xpath.api.YangBooleanConstantExpr;
15 import org.opendaylight.yangtools.yang.xpath.api.YangExpr;
16 import org.opendaylight.yangtools.yang.xpath.api.YangFunction;
17 import org.opendaylight.yangtools.yang.xpath.api.YangFunctionCallExpr;
18 import org.opendaylight.yangtools.yang.xpath.api.YangLiteralExpr;
19 import org.opendaylight.yangtools.yang.xpath.api.YangNumberExpr;
20 import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathSupport;
23 * This class provides support for validating function call arguments as well as compile-time evaluation.
25 final class FunctionSupport {
26 static final YangFunctionCallExpr POSITION = YangFunctionCallExpr.of(YangFunction.POSITION.getIdentifier());
28 private static final YangFunctionCallExpr CURRENT = YangFunctionCallExpr.of(YangFunction.CURRENT.getIdentifier());
29 private static final YangFunctionCallExpr LAST = YangFunctionCallExpr.of(YangFunction.LAST.getIdentifier());
30 private static final YangFunctionCallExpr LOCAL_NAME = YangFunctionCallExpr.of(
31 YangFunction.LOCAL_NAME.getIdentifier());
32 private static final YangFunctionCallExpr NAME = YangFunctionCallExpr.of(YangFunction.NAME.getIdentifier());
33 private static final YangFunctionCallExpr NAMESPACE_URI = YangFunctionCallExpr.of(
34 YangFunction.NAMESPACE_URI.getIdentifier());
35 private static final YangFunctionCallExpr NORMALIZE_SPACE = YangFunctionCallExpr.of(
36 YangFunction.NORMALIZE_SPACE.getIdentifier());
37 private static final YangFunctionCallExpr NUMBER = YangFunctionCallExpr.of(YangFunction.NUMBER.getIdentifier());
38 private static final YangFunctionCallExpr STRING = YangFunctionCallExpr.of(YangFunction.STRING.getIdentifier());
39 private static final YangFunctionCallExpr STRING_LENGTH = YangFunctionCallExpr.of(
40 YangFunction.STRING_LENGTH.getIdentifier());
42 private final YangXPathMathSupport mathSupport;
44 FunctionSupport(final YangXPathMathSupport mathSupport) {
45 this.mathSupport = requireNonNull(mathSupport);
48 YangExpr functionToExpr(final YangFunction func, final List<YangExpr> args) {
51 checkArgument(args.size() == 2, "bit-is-set(node-set, string) takes two arguments");
54 return booleanExpr(args);
56 checkArgument(args.size() == 1, "ceiling(number) takes one argument");
57 // TODO: constant folding requires math support
60 return concatExpr(args);
62 return containsExpr(args);
64 checkArgument(args.size() == 1, "count(node-set) takes one argument");
65 // TODO: constant folding requires math support
68 checkArgument(args.isEmpty(), "current() does not take any arguments");
71 checkArgument(args.size() == 1, "deref(node-set) takes one argument");
74 return derivedFromExpr(args);
75 case DERIVED_FROM_OR_SELF:
76 return derivedFromOrSelfExpr(args);
78 checkArgument(args.size() == 1, "enum-value(node-set) takes one argument");
81 checkArgument(args.isEmpty(), "false() does not take any arguments");
82 return YangBooleanConstantExpr.FALSE;
84 checkArgument(args.size() == 1, "floor(number) takes one argument");
85 // TODO: constant folding requires math support
88 checkArgument(args.size() == 1, "id(object) takes one argument");
91 checkArgument(args.size() == 1, "lang(string) takes one argument");
94 checkArgument(args.isEmpty(), "last() does not take any arguments");
97 checkArgument(args.size() <= 1, "local-name(node-set?) takes at most one argument");
103 checkArgument(args.size() <= 1, "name(node-set?) takes at most one argument");
104 if (args.isEmpty()) {
109 checkArgument(args.size() <= 1, "namespace-uri(node-set?) takes at most one argument");
110 if (args.isEmpty()) {
111 return NAMESPACE_URI;
114 case NORMALIZE_SPACE:
115 return normalizeSpaceExpr(args);
117 return notExpr(args);
119 return numberExpr(args);
121 checkArgument(args.isEmpty(), "position() does not take any arguments");
124 checkArgument(args.size() == 2, "re-match(string, string) takes two arguments");
125 // TODO: static analysis requires XSD regex support -- we should validate args[1] and match it to
126 // args[0] if that is a literal
129 checkArgument(args.size() == 1, "round(number) takes one argument");
130 // TODO: constant folding requires math support
133 return startsWithExpr(args);
135 return stringExpr(args);
137 return stringLengthExpr(args);
139 return substringExpr(args);
140 case SUBSTRING_AFTER:
141 return substringAfterExpr(args);
142 case SUBSTRING_BEFORE:
143 return substringBeforeExpr(args);
145 checkArgument(args.size() == 1, "sub(node-set) takes one argument");
146 // TODO: constant folding requires math support
149 checkArgument(args.size() == 3, "translate(string, string, string) takes three arguments");
150 // TODO: constant folding?
153 checkArgument(args.isEmpty(), "true() does not take any arguments");
154 return YangBooleanConstantExpr.TRUE;
156 throw new IllegalStateException("Unhandled function " + func);
159 return YangFunctionCallExpr.of(func.getIdentifier(), args);
162 private static YangExpr booleanExpr(final List<YangExpr> args) {
163 checkArgument(args.size() == 1, "boolean(object) takes one argument");
164 final YangExpr arg = args.get(0);
165 if (arg instanceof YangBooleanConstantExpr) {
168 if (arg instanceof YangLiteralExpr) {
169 return YangBooleanConstantExpr.of(!((YangLiteralExpr) arg).getLiteral().isEmpty());
172 // TODO: handling YangNumberExpr requires math support
174 return YangFunctionCallExpr.of(YangFunction.BOOLEAN.getIdentifier(), args);
177 private static YangExpr concatExpr(final List<YangExpr> args) {
178 checkArgument(args.size() >= 2, "concat(string, string, string*) takes at least two arguments");
180 // TODO: constant folding
182 return YangFunctionCallExpr.of(YangFunction.CONCAT.getIdentifier(), args);
185 private static YangExpr containsExpr(final List<YangExpr> args) {
186 checkArgument(args.size() == 2, "contains(string, string) takes two arguments");
187 final YangExpr first = args.get(0);
188 if (first instanceof YangLiteralExpr) {
189 final YangExpr second = args.get(1);
190 if (second instanceof YangLiteralExpr) {
191 return YangBooleanConstantExpr.of(
192 ((YangLiteralExpr) first).getLiteral().contains(((YangLiteralExpr) second).getLiteral()));
196 // TODO: handling YangNumberExpr requires math support
197 return YangFunctionCallExpr.of(YangFunction.CONTAINS.getIdentifier(), args);
200 private static YangExpr derivedFromExpr(final List<YangExpr> args) {
201 checkArgument(args.size() == 2, "derived-from(node-set, string) takes two arguments");
202 // FIXME: coerce second arg to a QName
203 return YangFunctionCallExpr.of(YangFunction.DERIVED_FROM.getIdentifier(), args);
206 private static YangExpr derivedFromOrSelfExpr(final List<YangExpr> args) {
207 checkArgument(args.size() == 2, "derived-from-or-self(node-set, string) takes two arguments");
208 // FIXME: coerce second arg to a QName
209 return YangFunctionCallExpr.of(YangFunction.DERIVED_FROM_OR_SELF.getIdentifier(), args);
212 private static YangExpr notExpr(final List<YangExpr> args) {
213 checkArgument(args.size() == 1, "not(boolean) takes one argument");
214 final YangExpr arg = args.get(0);
215 if (arg instanceof YangBooleanConstantExpr) {
216 return YangBooleanConstantExpr.of(((YangBooleanConstantExpr) arg).getValue());
219 return YangFunctionCallExpr.of(YangFunction.NOT.getIdentifier(), args);
222 private static YangExpr normalizeSpaceExpr(final List<YangExpr> args) {
223 checkArgument(args.size() <= 1, "normalize-space(object?) takes at most one argument");
224 if (args.isEmpty()) {
225 return NORMALIZE_SPACE;
227 // final YangExpr arg = args.get(0);
228 // if (arg instanceof YangLiteralExpr) {
229 // // TODO: normalize value
232 return YangFunctionCallExpr.of(YangFunction.NORMALIZE_SPACE.getIdentifier(), args);
235 private YangExpr numberExpr(final List<YangExpr> args) {
236 checkArgument(args.size() <= 1, "number(object?) takes at most one argument");
237 if (args.isEmpty()) {
241 final YangExpr arg = args.get(0);
242 if (arg instanceof YangNumberExpr) {
245 if (arg instanceof YangLiteralExpr) {
246 return mathSupport.createNumber(((YangLiteralExpr) arg).getLiteral());
248 if (arg instanceof YangBooleanConstantExpr) {
249 final boolean value = ((YangBooleanConstantExpr) arg).getValue();
250 return mathSupport.createNumber(value ? 1 : 0);
253 return YangFunctionCallExpr.of(YangFunction.NUMBER.getIdentifier(), args);
256 private static YangExpr startsWithExpr(final List<YangExpr> args) {
257 checkArgument(args.size() == 2, "starts-with(string, string) takes two arguments");
259 // TODO: constant folding
261 return YangFunctionCallExpr.of(YangFunction.STARTS_WITH.getIdentifier(), args);
264 private static YangExpr substringBeforeExpr(final List<YangExpr> args) {
265 checkArgument(args.size() == 2, "substring-before(string, string) takes two arguments");
267 // TODO: constant folding
269 return YangFunctionCallExpr.of(YangFunction.SUBSTRING_BEFORE.getIdentifier(), args);
272 private static YangExpr substringAfterExpr(final List<YangExpr> args) {
273 checkArgument(args.size() == 2, "substring-after(string, string) takes two arguments");
275 // TODO: constant folding
277 return YangFunctionCallExpr.of(YangFunction.SUBSTRING_AFTER.getIdentifier(), args);
280 private static YangExpr substringExpr(final List<YangExpr> args) {
281 final int size = args.size();
282 checkArgument(size == 2 || size == 3, "substring-(string, number, number?) takes two or three arguments");
284 // TODO: constant folding
286 return YangFunctionCallExpr.of(YangFunction.SUBSTRING.getIdentifier(), args);
289 private static YangExpr stringExpr(final List<YangExpr> args) {
290 checkArgument(args.size() <= 1, "string(object?) takes at most one argument");
291 if (args.isEmpty()) {
295 final YangExpr arg = args.get(0);
296 if (arg instanceof YangLiteralExpr) {
299 if (arg instanceof YangBooleanConstantExpr) {
300 return ((YangBooleanConstantExpr) arg).asStringLiteral();
303 // TODO: handling YangNumberExpr requires math support
304 return YangFunctionCallExpr.of(YangFunction.STRING.getIdentifier(), args);
307 private YangExpr stringLengthExpr(final List<YangExpr> args) {
308 checkArgument(args.size() <= 1, "string-length(object?) takes at most one argument");
309 if (args.isEmpty()) {
310 return STRING_LENGTH;
313 YangExpr first = args.get(0);
314 if (first instanceof YangBooleanConstantExpr) {
315 first = ((YangBooleanConstantExpr) first).asStringLiteral();
317 if (first instanceof YangLiteralExpr) {
318 return mathSupport.createNumber(((YangLiteralExpr) first).getLiteral().length());
321 return YangFunctionCallExpr.of(YangFunction.STRING_LENGTH.getIdentifier(), args);