Add yang-xpath-impl
[yangtools.git] / yang / yang-xpath-impl / src / main / java / org / opendaylight / yangtools / yang / xpath / impl / Functions.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
12 import java.util.List;
13 import org.opendaylight.yangtools.yang.xpath.api.YangBooleanConstantExpr;
14 import org.opendaylight.yangtools.yang.xpath.api.YangExpr;
15 import org.opendaylight.yangtools.yang.xpath.api.YangFunction;
16 import org.opendaylight.yangtools.yang.xpath.api.YangFunctionCallExpr;
17 import org.opendaylight.yangtools.yang.xpath.api.YangLiteralExpr;
18 import org.opendaylight.yangtools.yang.xpath.api.YangNumberExpr;
19
20 final class Functions {
21     private static final YangFunctionCallExpr CURRENT = YangFunctionCallExpr.of(YangFunction.CURRENT.getIdentifier());
22     private static final YangFunctionCallExpr LAST = YangFunctionCallExpr.of(YangFunction.LAST.getIdentifier());
23     private static final YangFunctionCallExpr LOCAL_NAME = YangFunctionCallExpr.of(
24         YangFunction.LOCAL_NAME.getIdentifier());
25     private static final YangFunctionCallExpr NAME = YangFunctionCallExpr.of(YangFunction.NAME.getIdentifier());
26     private static final YangFunctionCallExpr NAMESPACE_URI = YangFunctionCallExpr.of(
27         YangFunction.NAMESPACE_URI.getIdentifier());
28     private static final YangFunctionCallExpr NORMALIZE_SPACE = YangFunctionCallExpr.of(
29         YangFunction.NORMALIZE_SPACE.getIdentifier());
30     private static final YangFunctionCallExpr NUMBER = YangFunctionCallExpr.of(YangFunction.NUMBER.getIdentifier());
31     static final YangFunctionCallExpr POSITION = YangFunctionCallExpr.of(YangFunction.POSITION.getIdentifier());
32     private static final YangFunctionCallExpr STRING = YangFunctionCallExpr.of(YangFunction.STRING.getIdentifier());
33     private static final YangFunctionCallExpr STRING_LENGTH = YangFunctionCallExpr.of(
34         YangFunction.STRING_LENGTH.getIdentifier());
35
36     private Functions() {
37
38     }
39
40     static YangExpr functionToExpr(final YangFunction func, final List<YangExpr> args) {
41         switch (func) {
42             case BIT_IS_SET:
43                 checkArgument(args.size() == 2, "bit-is-set(node-set, string) takes two arguments");
44                 break;
45             case BOOLEAN:
46                 return booleanExpr(args);
47             case CEILING:
48                 checkArgument(args.size() == 1, "ceiling(number) takes one argument");
49                 // TODO: constant folding requires math support
50                 break;
51             case CONCAT:
52                 return concatExpr(args);
53             case CONTAINS:
54                 return containsExpr(args);
55             case COUNT:
56                 checkArgument(args.size() == 1, "count(node-set) takes one argument");
57                 // TODO: constant folding requires math support
58                 break;
59             case CURRENT:
60                 checkArgument(args.isEmpty(), "current() does not take any arguments");
61                 return CURRENT;
62             case DEREF:
63                 checkArgument(args.size() == 1, "deref(node-set) takes one argument");
64                 break;
65             case DERIVED_FROM:
66                 return derivedFromExpr(args);
67             case DERIVED_FROM_OR_SELF:
68                 return derivedFromOrSelfExpr(args);
69             case ENUM_VALUE:
70                 checkArgument(args.size() == 1, "enum-value(node-set) takes one argument");
71                 break;
72             case FALSE:
73                 checkArgument(args.isEmpty(), "false() does not take any arguments");
74                 return YangBooleanConstantExpr.FALSE;
75             case FLOOR:
76                 checkArgument(args.size() == 1, "floor(number) takes one argument");
77                 // TODO: constant folding requires math support
78                 break;
79             case ID:
80                 checkArgument(args.size() == 1, "id(object) takes one argument");
81                 break;
82             case LANG:
83                 checkArgument(args.size() == 1, "lang(string) takes one argument");
84                 break;
85             case LAST:
86                 checkArgument(args.isEmpty(), "last() does not take any arguments");
87                 return LAST;
88             case LOCAL_NAME:
89                 checkArgument(args.size() <= 1, "local-name(node-set?) takes at most one argument");
90                 if (args.isEmpty()) {
91                     return LOCAL_NAME;
92                 }
93                 break;
94             case NAME:
95                 checkArgument(args.size() <= 1, "name(node-set?) takes at most one argument");
96                 if (args.isEmpty()) {
97                     return NAME;
98                 }
99                 break;
100             case NAMESPACE_URI:
101                 checkArgument(args.size() <= 1, "namespace-uri(node-set?) takes at most one argument");
102                 if (args.isEmpty()) {
103                     return NAMESPACE_URI;
104                 }
105                 break;
106             case NORMALIZE_SPACE:
107                 return normalizeSpaceExpr(args);
108             case NOT:
109                 return notExpr(args);
110             case NUMBER:
111                 return numberExpr(args);
112             case POSITION:
113                 checkArgument(args.isEmpty(), "position() does not take any arguments");
114                 return POSITION;
115             case RE_MATCH:
116                 checkArgument(args.size() == 2, "re-match(string, string) takes two arguments");
117                 // TODO: static analysis requires XSD regex support -- we should validate args[1] and match it to
118                 //       args[0] if that is a literal
119                 break;
120             case ROUND:
121                 checkArgument(args.size() == 1, "round(number) takes one argument");
122                 // TODO: constant folding requires math support
123                 break;
124             case STARTS_WITH:
125                 return startsWithExpr(args);
126             case STRING:
127                 return stringExpr(args);
128             case STRING_LENGTH:
129                 return stringLengthExpr(args);
130             case SUBSTRING:
131                 return substringExpr(args);
132             case SUBSTRING_AFTER:
133                 return substringAfterExpr(args);
134             case SUBSTRING_BEFORE:
135                 return substringBeforeExpr(args);
136             case SUM:
137                 checkArgument(args.size() == 1, "sub(node-set) takes one argument");
138                 // TODO: constant folding requires math support
139                 break;
140             case TRANSLATE:
141                 checkArgument(args.size() == 3, "translate(string, string, string) takes three arguments");
142                 // TODO: constant folding?
143                 break;
144             case TRUE:
145                 checkArgument(args.isEmpty(), "true() does not take any arguments");
146                 return YangBooleanConstantExpr.TRUE;
147             default:
148                 throw new IllegalStateException("Unhandled function " + func);
149         }
150
151         return YangFunctionCallExpr.of(func.getIdentifier(), args);
152     }
153
154     private static YangExpr booleanExpr(final List<YangExpr> args) {
155         checkArgument(args.size() == 1, "boolean(object) takes one argument");
156         final YangExpr arg = args.get(0);
157         if (arg instanceof YangBooleanConstantExpr) {
158             return arg;
159         }
160         if (arg instanceof YangLiteralExpr) {
161             return YangBooleanConstantExpr.of(((YangLiteralExpr) arg).getLiteral().isEmpty());
162         }
163         // TODO: handling YangNumberExpr requires math support
164         return YangFunctionCallExpr.of(YangFunction.BOOLEAN.getIdentifier(), args);
165     }
166
167     private static YangExpr concatExpr(final List<YangExpr> args) {
168         checkArgument(args.size() >= 2, "concat(string, string, string*) takes at least two arguments");
169
170         // TODO: constant folding
171
172         return YangFunctionCallExpr.of(YangFunction.CONCAT.getIdentifier(), args);
173     }
174
175     private static YangExpr containsExpr(final List<YangExpr> args) {
176         checkArgument(args.size() == 2, "contains(string, string) takes two arguments");
177         final YangExpr first = args.get(0);
178         if (first instanceof YangLiteralExpr) {
179             final YangExpr second = args.get(1);
180             if (second instanceof YangLiteralExpr) {
181                 return YangBooleanConstantExpr.of(
182                     ((YangLiteralExpr) first).getLiteral().contains(((YangLiteralExpr) second).getLiteral()));
183             }
184         }
185
186         // TODO: handling YangNumberExpr requires math support
187         return YangFunctionCallExpr.of(YangFunction.CONTAINS.getIdentifier(), args);
188     }
189
190     private static YangExpr derivedFromExpr(final List<YangExpr> args) {
191         checkArgument(args.size() == 2, "derived-from(node-set, string) takes two arguments");
192         // FIXME: coerce second arg to a QName
193         return YangFunctionCallExpr.of(YangFunction.DERIVED_FROM.getIdentifier(), args);
194     }
195
196     private static YangExpr derivedFromOrSelfExpr(final List<YangExpr> args) {
197         checkArgument(args.size() == 2, "derived-from-or-self(node-set, string) takes two arguments");
198         // FIXME: coerce second arg to a QName
199         return YangFunctionCallExpr.of(YangFunction.DERIVED_FROM_OR_SELF.getIdentifier(), args);
200     }
201
202     private static YangExpr notExpr(final List<YangExpr> args) {
203         checkArgument(args.size() == 1, "not(boolean) takes one argument");
204         final YangExpr arg = args.get(0);
205         if (arg instanceof YangBooleanConstantExpr) {
206             return YangBooleanConstantExpr.of(((YangBooleanConstantExpr) arg).getValue());
207         }
208
209         return YangFunctionCallExpr.of(YangFunction.NOT.getIdentifier(), args);
210     }
211
212     private static YangExpr normalizeSpaceExpr(final List<YangExpr> args) {
213         checkArgument(args.size() <= 1, "number(object?) takes at most one argument");
214         if (args.isEmpty()) {
215             return NORMALIZE_SPACE;
216         }
217         final YangExpr arg = args.get(0);
218         if (arg instanceof YangLiteralExpr) {
219             // TODO: normalize value
220         }
221
222         return YangFunctionCallExpr.of(YangFunction.NORMALIZE_SPACE.getIdentifier(), args);
223     }
224
225     private static YangExpr numberExpr(final List<YangExpr> args) {
226         checkArgument(args.size() <= 1, "number(object?) takes at most one argument");
227         if (args.isEmpty()) {
228             return NUMBER;
229         }
230
231         final YangExpr arg = args.get(0);
232         if (arg instanceof YangNumberExpr) {
233             return arg;
234         }
235
236         // TODO: constant literal folding requires math support
237         return YangFunctionCallExpr.of(YangFunction.NUMBER.getIdentifier(), args);
238     }
239
240     private static YangExpr startsWithExpr(final List<YangExpr> args) {
241         checkArgument(args.size() == 2, "starts-with(string, string) takes two arguments");
242
243         // TODO: constant folding
244
245         return YangFunctionCallExpr.of(YangFunction.STARTS_WITH.getIdentifier(), args);
246     }
247
248     private static YangExpr substringBeforeExpr(final List<YangExpr> args) {
249         checkArgument(args.size() == 2, "substring-before(string, string) takes two arguments");
250
251         // TODO: constant folding
252
253         return YangFunctionCallExpr.of(YangFunction.SUBSTRING_BEFORE.getIdentifier(), args);
254     }
255
256     private static YangExpr substringAfterExpr(final List<YangExpr> args) {
257         checkArgument(args.size() == 2, "substring-after(string, string) takes two arguments");
258
259         // TODO: constant folding
260
261         return YangFunctionCallExpr.of(YangFunction.SUBSTRING_AFTER.getIdentifier(), args);
262     }
263
264     private static YangExpr substringExpr(final List<YangExpr> args) {
265         final int size = args.size();
266         checkArgument(size == 2 || size == 3, "substring-(string, number, number?) takes two or three arguments");
267
268         // TODO: constant folding
269
270         return YangFunctionCallExpr.of(YangFunction.SUBSTRING.getIdentifier(), args);
271     }
272
273     private static YangExpr stringExpr(final List<YangExpr> args) {
274         checkArgument(args.size() <= 1, "string(object?) takes at most one argument");
275         if (args.isEmpty()) {
276             return STRING;
277         }
278
279         final YangExpr arg = args.get(0);
280         if (arg instanceof YangLiteralExpr) {
281             return arg;
282         }
283
284         // TODO: handling YangNumberExpr requires math support
285         return YangFunctionCallExpr.of(YangFunction.STRING.getIdentifier(), args);
286     }
287
288     private static YangExpr stringLengthExpr(final List<YangExpr> args) {
289         checkArgument(args.size() <= 1, "string(object?) takes at most one argument");
290         if (args.isEmpty()) {
291             return STRING_LENGTH;
292         }
293
294         // TODO: constant literal requires math support
295
296         return YangFunctionCallExpr.of(YangFunction.STRING_LENGTH.getIdentifier(), args);
297     }
298 }