matcher: add user callback and support implicit index/key compares
[project/jsonpath.git] / matcher.c
1 /*
2 * Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include "matcher.h"
18
19 static struct json_object *
20 jp_match_next(struct jp_opcode *ptr,
21 struct json_object *root, struct json_object *cur,
22 jp_match_cb_t cb, void *priv);
23
24 static bool
25 jp_json_to_op(struct json_object *obj, struct jp_opcode *op)
26 {
27 switch (json_object_get_type(obj))
28 {
29 case json_type_boolean:
30 op->type = T_BOOL;
31 op->num = json_object_get_boolean(obj);
32 return true;
33
34 case json_type_int:
35 op->type = T_NUMBER;
36 op->num = json_object_get_int(obj);
37 return true;
38
39 case json_type_string:
40 op->type = T_STRING;
41 op->str = (char *)json_object_get_string(obj);
42 return true;
43
44 default:
45 return false;
46 }
47 }
48
49 static bool
50 jp_resolve(struct json_object *root, struct json_object *cur,
51 struct jp_opcode *op, struct jp_opcode *res)
52 {
53 struct json_object *val;
54
55 switch (op->type)
56 {
57 case T_THIS:
58 val = jp_match(op, cur, NULL, NULL);
59
60 if (val)
61 return jp_json_to_op(val, res);
62
63 return false;
64
65 case T_ROOT:
66 val = jp_match(op, root, NULL, NULL);
67
68 if (val)
69 return jp_json_to_op(val, res);
70
71 return false;
72
73 default:
74 *res = *op;
75 return true;
76 }
77 }
78
79 static bool
80 jp_cmp(struct jp_opcode *op, struct json_object *root, struct json_object *cur)
81 {
82 int delta;
83 struct jp_opcode left, right;
84
85 if (!jp_resolve(root, cur, op->down, &left) ||
86 !jp_resolve(root, cur, op->down->sibling, &right))
87 return false;
88
89 if (left.type != right.type)
90 return false;
91
92 switch (left.type)
93 {
94 case T_BOOL:
95 case T_NUMBER:
96 delta = left.num - right.num;
97 break;
98
99 case T_STRING:
100 delta = strcmp(left.str, right.str);
101 break;
102
103 default:
104 return false;
105 }
106
107 switch (op->type)
108 {
109 case T_EQ:
110 return (delta == 0);
111
112 case T_LT:
113 return (delta < 0);
114
115 case T_LE:
116 return (delta <= 0);
117
118 case T_GT:
119 return (delta > 0);
120
121 case T_GE:
122 return (delta >= 0);
123
124 case T_NE:
125 return (delta != 0);
126
127 default:
128 return false;
129 }
130 }
131
132 static bool
133 jp_expr(struct jp_opcode *op, struct json_object *root, struct json_object *cur,
134 int idx, const char *key, jp_match_cb_t cb, void *priv)
135 {
136 struct jp_opcode *sop;
137
138 switch (op->type)
139 {
140 case T_WILDCARD:
141 return true;
142
143 case T_EQ:
144 case T_NE:
145 case T_LT:
146 case T_LE:
147 case T_GT:
148 case T_GE:
149 return jp_cmp(op, root, cur);
150
151 case T_ROOT:
152 return !!jp_match(op, root, NULL, NULL);
153
154 case T_THIS:
155 return !!jp_match(op, cur, NULL, NULL);
156
157 case T_NOT:
158 return !jp_expr(op->down, root, cur, idx, key, cb, priv);
159
160 case T_AND:
161 for (sop = op->down; sop; sop = sop->sibling)
162 if (!jp_expr(sop, root, cur, idx, key, cb, priv))
163 return false;
164 return true;
165
166 case T_OR:
167 for (sop = op->down; sop; sop = sop->sibling)
168 if (jp_expr(sop, root, cur, idx, key, cb, priv))
169 return true;
170 return false;
171
172 case T_STRING:
173 return (key && !strcmp(op->str, key));
174
175 case T_NUMBER:
176 return (idx == op->num);
177
178 default:
179 return false;
180 }
181 }
182
183 static struct json_object *
184 jp_match_expr(struct jp_opcode *ptr,
185 struct json_object *root, struct json_object *cur,
186 jp_match_cb_t cb, void *priv)
187 {
188 int idx, len;
189 struct json_object *tmp, *res = NULL;
190
191 switch (json_object_get_type(cur))
192 {
193 case json_type_object:
194 ; /* a label can only be part of a statement and a declaration is not a statement */
195 json_object_object_foreach(cur, key, val)
196 {
197 if (jp_expr(ptr, root, val, -1, key, cb, priv))
198 {
199 tmp = jp_match_next(ptr->sibling, root, val, cb, priv);
200
201 if (tmp && !res)
202 res = tmp;
203 }
204 }
205
206 break;
207
208 case json_type_array:
209 len = json_object_array_length(cur);
210
211 for (idx = 0; idx < len; idx++)
212 {
213 tmp = json_object_array_get_idx(cur, idx);
214
215 if (jp_expr(ptr, root, tmp, idx, NULL, cb, priv))
216 {
217 tmp = jp_match_next(ptr->sibling, root, tmp, cb, priv);
218
219 if (tmp && !res)
220 res = tmp;
221 }
222 }
223
224 break;
225
226 default:
227 break;
228 }
229
230 return res;
231 }
232
233 static struct json_object *
234 jp_match_next(struct jp_opcode *ptr,
235 struct json_object *root, struct json_object *cur,
236 jp_match_cb_t cb, void *priv)
237 {
238 struct json_object *next;
239
240 if (!ptr)
241 {
242 if (cb)
243 cb(cur, priv);
244
245 return cur;
246 }
247
248 switch (ptr->type)
249 {
250 case T_STRING:
251 case T_LABEL:
252 if (json_object_object_get_ex(cur, ptr->str, &next))
253 return jp_match_next(ptr->sibling, root, next, cb, priv);
254
255 break;
256
257 case T_NUMBER:
258 next = json_object_array_get_idx(cur, ptr->num);
259
260 if (next)
261 return jp_match_next(ptr->sibling, root, next, cb, priv);
262
263 break;
264
265 default:
266 return jp_match_expr(ptr, root, cur, cb, priv);
267 }
268
269 return NULL;
270 }
271
272 struct json_object *
273 jp_match(struct jp_opcode *path, json_object *jsobj,
274 jp_match_cb_t cb, void *priv)
275 {
276 if (path->type == T_LABEL)
277 path = path->down;
278
279 return jp_match_next(path->down, jsobj, jsobj, cb, priv);
280 }