Populate model/ hierarchy
[yangtools.git] / yang / yang-xpath-impl / src / main / java / org / opendaylight / yangtools / yang / xpath / impl / FunctionSupport.java
1 /*
2  * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.impl;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12
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;
21
22 /**
23  * This class provides support for validating function call arguments as well as compile-time evaluation.
24  */
25 final class FunctionSupport {
26     static final YangFunctionCallExpr POSITION = YangFunctionCallExpr.of(YangFunction.POSITION.getIdentifier());
27
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());
41
42     private final YangXPathMathSupport mathSupport;
43
44     FunctionSupport(final YangXPathMathSupport mathSupport) {
45         this.mathSupport = requireNonNull(mathSupport);
46     }
47
48     YangExpr functionToExpr(final YangFunction func, final List<YangExpr> args) {
49         switch (func) {
50             case BIT_IS_SET:
51                 checkArgument(args.size() == 2, "bit-is-set(node-set, string) takes two arguments");
52                 break;
53             case BOOLEAN:
54                 return booleanExpr(args);
55             case CEILING:
56                 checkArgument(args.size() == 1, "ceiling(number) takes one argument");
57                 // TODO: constant folding requires math support
58                 break;
59             case CONCAT:
60                 return concatExpr(args);
61             case CONTAINS:
62                 return containsExpr(args);
63             case COUNT:
64                 checkArgument(args.size() == 1, "count(node-set) takes one argument");
65                 // TODO: constant folding requires math support
66                 break;
67             case CURRENT:
68                 checkArgument(args.isEmpty(), "current() does not take any arguments");
69                 return CURRENT;
70             case DEREF:
71                 checkArgument(args.size() == 1, "deref(node-set) takes one argument");
72                 break;
73             case DERIVED_FROM:
74                 return derivedFromExpr(args);
75             case DERIVED_FROM_OR_SELF:
76                 return derivedFromOrSelfExpr(args);
77             case ENUM_VALUE:
78                 checkArgument(args.size() == 1, "enum-value(node-set) takes one argument");
79                 break;
80             case FALSE:
81                 checkArgument(args.isEmpty(), "false() does not take any arguments");
82                 return YangBooleanConstantExpr.FALSE;
83             case FLOOR:
84                 checkArgument(args.size() == 1, "floor(number) takes one argument");
85                 // TODO: constant folding requires math support
86                 break;
87             case ID:
88                 checkArgument(args.size() == 1, "id(object) takes one argument");
89                 break;
90             case LANG:
91                 checkArgument(args.size() == 1, "lang(string) takes one argument");
92                 break;
93             case LAST:
94                 checkArgument(args.isEmpty(), "last() does not take any arguments");
95                 return LAST;
96             case LOCAL_NAME:
97                 checkArgument(args.size() <= 1, "local-name(node-set?) takes at most one argument");
98                 if (args.isEmpty()) {
99                     return LOCAL_NAME;
100                 }
101                 break;
102             case NAME:
103                 checkArgument(args.size() <= 1, "name(node-set?) takes at most one argument");
104                 if (args.isEmpty()) {
105                     return NAME;
106                 }
107                 break;
108             case NAMESPACE_URI:
109                 checkArgument(args.size() <= 1, "namespace-uri(node-set?) takes at most one argument");
110                 if (args.isEmpty()) {
111                     return NAMESPACE_URI;
112                 }
113                 break;
114             case NORMALIZE_SPACE:
115                 return normalizeSpaceExpr(args);
116             case NOT:
117                 return notExpr(args);
118             case NUMBER:
119                 return numberExpr(args);
120             case POSITION:
121                 checkArgument(args.isEmpty(), "position() does not take any arguments");
122                 return POSITION;
123             case RE_MATCH:
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
127                 break;
128             case ROUND:
129                 checkArgument(args.size() == 1, "round(number) takes one argument");
130                 // TODO: constant folding requires math support
131                 break;
132             case STARTS_WITH:
133                 return startsWithExpr(args);
134             case STRING:
135                 return stringExpr(args);
136             case STRING_LENGTH:
137                 return stringLengthExpr(args);
138             case SUBSTRING:
139                 return substringExpr(args);
140             case SUBSTRING_AFTER:
141                 return substringAfterExpr(args);
142             case SUBSTRING_BEFORE:
143                 return substringBeforeExpr(args);
144             case SUM:
145                 checkArgument(args.size() == 1, "sub(node-set) takes one argument");
146                 // TODO: constant folding requires math support
147                 break;
148             case TRANSLATE:
149                 checkArgument(args.size() == 3, "translate(string, string, string) takes three arguments");
150                 // TODO: constant folding?
151                 break;
152             case TRUE:
153                 checkArgument(args.isEmpty(), "true() does not take any arguments");
154                 return YangBooleanConstantExpr.TRUE;
155             default:
156                 throw new IllegalStateException("Unhandled function " + func);
157         }
158
159         return YangFunctionCallExpr.of(func.getIdentifier(), args);
160     }
161
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) {
166             return arg;
167         }
168         if (arg instanceof YangLiteralExpr) {
169             return YangBooleanConstantExpr.of(!((YangLiteralExpr) arg).getLiteral().isEmpty());
170         }
171
172         // TODO: handling YangNumberExpr requires math support
173
174         return YangFunctionCallExpr.of(YangFunction.BOOLEAN.getIdentifier(), args);
175     }
176
177     private static YangExpr concatExpr(final List<YangExpr> args) {
178         checkArgument(args.size() >= 2, "concat(string, string, string*) takes at least two arguments");
179
180         // TODO: constant folding
181
182         return YangFunctionCallExpr.of(YangFunction.CONCAT.getIdentifier(), args);
183     }
184
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()));
193             }
194         }
195
196         // TODO: handling YangNumberExpr requires math support
197         return YangFunctionCallExpr.of(YangFunction.CONTAINS.getIdentifier(), args);
198     }
199
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);
204     }
205
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);
210     }
211
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());
217         }
218
219         return YangFunctionCallExpr.of(YangFunction.NOT.getIdentifier(), args);
220     }
221
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;
226         }
227         // final YangExpr arg = args.get(0);
228         // if (arg instanceof YangLiteralExpr) {
229         //     // TODO: normalize value
230         // }
231
232         return YangFunctionCallExpr.of(YangFunction.NORMALIZE_SPACE.getIdentifier(), args);
233     }
234
235     private YangExpr numberExpr(final List<YangExpr> args) {
236         checkArgument(args.size() <= 1, "number(object?) takes at most one argument");
237         if (args.isEmpty()) {
238             return NUMBER;
239         }
240
241         final YangExpr arg = args.get(0);
242         if (arg instanceof YangNumberExpr) {
243             return arg;
244         }
245         if (arg instanceof YangLiteralExpr) {
246             return mathSupport.createNumber(((YangLiteralExpr) arg).getLiteral());
247         }
248         if (arg instanceof YangBooleanConstantExpr) {
249             final boolean value = ((YangBooleanConstantExpr) arg).getValue();
250             return mathSupport.createNumber(value ? 1 : 0);
251         }
252
253         return YangFunctionCallExpr.of(YangFunction.NUMBER.getIdentifier(), args);
254     }
255
256     private static YangExpr startsWithExpr(final List<YangExpr> args) {
257         checkArgument(args.size() == 2, "starts-with(string, string) takes two arguments");
258
259         // TODO: constant folding
260
261         return YangFunctionCallExpr.of(YangFunction.STARTS_WITH.getIdentifier(), args);
262     }
263
264     private static YangExpr substringBeforeExpr(final List<YangExpr> args) {
265         checkArgument(args.size() == 2, "substring-before(string, string) takes two arguments");
266
267         // TODO: constant folding
268
269         return YangFunctionCallExpr.of(YangFunction.SUBSTRING_BEFORE.getIdentifier(), args);
270     }
271
272     private static YangExpr substringAfterExpr(final List<YangExpr> args) {
273         checkArgument(args.size() == 2, "substring-after(string, string) takes two arguments");
274
275         // TODO: constant folding
276
277         return YangFunctionCallExpr.of(YangFunction.SUBSTRING_AFTER.getIdentifier(), args);
278     }
279
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");
283
284         // TODO: constant folding
285
286         return YangFunctionCallExpr.of(YangFunction.SUBSTRING.getIdentifier(), args);
287     }
288
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()) {
292             return STRING;
293         }
294
295         final YangExpr arg = args.get(0);
296         if (arg instanceof YangLiteralExpr) {
297             return arg;
298         }
299         if (arg instanceof YangBooleanConstantExpr) {
300             return ((YangBooleanConstantExpr) arg).asStringLiteral();
301         }
302
303         // TODO: handling YangNumberExpr requires math support
304         return YangFunctionCallExpr.of(YangFunction.STRING.getIdentifier(), args);
305     }
306
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;
311         }
312
313         YangExpr first = args.get(0);
314         if (first instanceof YangBooleanConstantExpr) {
315             first = ((YangBooleanConstantExpr) first).asStringLiteral();
316         }
317         if (first instanceof YangLiteralExpr) {
318             return mathSupport.createNumber(((YangLiteralExpr) first).getLiteral().length());
319         }
320
321         return YangFunctionCallExpr.of(YangFunction.STRING_LENGTH.getIdentifier(), args);
322     }
323 }