1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.jxpath.ri.compiler;
19
20 import static org.junit.jupiter.api.Assertions.assertEquals;
21 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
22
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.List;
26 import java.util.Locale;
27
28 import org.apache.commons.jxpath.AbstractJXPathTest;
29 import org.apache.commons.jxpath.ClassFunctions;
30 import org.apache.commons.jxpath.ExpressionContext;
31 import org.apache.commons.jxpath.Function;
32 import org.apache.commons.jxpath.FunctionLibrary;
33 import org.apache.commons.jxpath.Functions;
34 import org.apache.commons.jxpath.JXPathContext;
35 import org.apache.commons.jxpath.NodeSet;
36 import org.apache.commons.jxpath.PackageFunctions;
37 import org.apache.commons.jxpath.Pointer;
38 import org.apache.commons.jxpath.TestBean;
39 import org.apache.commons.jxpath.Variables;
40 import org.apache.commons.jxpath.ri.model.NodePointer;
41 import org.apache.commons.jxpath.util.JXPath11CompatibleTypeConverter;
42 import org.apache.commons.jxpath.util.TypeConverter;
43 import org.apache.commons.jxpath.util.TypeUtils;
44 import org.junit.jupiter.api.AfterEach;
45 import org.junit.jupiter.api.BeforeEach;
46 import org.junit.jupiter.api.Test;
47
48
49
50
51 public class ExtensionFunctionTest extends AbstractJXPathTest {
52
53 private static final class Context implements ExpressionContext {
54
55 private final Object object;
56
57 public Context(final Object object) {
58 this.object = object;
59 }
60
61 @Override
62 public List<Pointer> getContextNodeList() {
63 return null;
64 }
65
66 @Override
67 public Pointer getContextNodePointer() {
68 return NodePointer.newNodePointer(null, object, Locale.getDefault());
69 }
70
71 @Override
72 public JXPathContext getJXPathContext() {
73 return null;
74 }
75
76 @Override
77 public int getPosition() {
78 return 0;
79 }
80 }
81
82 private Functions functions;
83 private JXPathContext context;
84 private TestBean testBean;
85 private TypeConverter typeConverter;
86
87 @Override
88 @BeforeEach
89 public void setUp() {
90 if (context == null) {
91 testBean = new TestBean();
92 context = JXPathContext.newContext(testBean);
93 final Variables vars = context.getVariables();
94 vars.declareVariable("test", new TestFunctions(4, "test"));
95 final FunctionLibrary lib = new FunctionLibrary();
96 lib.addFunctions(new ClassFunctions(TestFunctions.class, "test"));
97 lib.addFunctions(new ClassFunctions(TestFunctions2.class, "test"));
98 lib.addFunctions(new PackageFunctions("", "call"));
99 lib.addFunctions(new PackageFunctions("org.apache.commons.jxpath.ri.compiler.", "jxpathtest"));
100 lib.addFunctions(new PackageFunctions("", null));
101 context.setFunctions(lib);
102 context.getVariables().declareVariable("List.class", List.class);
103 context.getVariables().declareVariable("NodeSet.class", NodeSet.class);
104 }
105 functions = new ClassFunctions(TestFunctions.class, "test");
106 typeConverter = TypeUtils.getTypeConverter();
107 }
108
109 @AfterEach
110 public void tearDown() {
111 TypeUtils.setTypeConverter(typeConverter);
112 }
113
114 @Test
115 public void testAllocation() {
116
117 assertXPathValue(context, "string(test:new())", "foo=0; bar=null");
118
119 assertXPathValue(context, "string(jxpathtest:TestFunctions.new())", "foo=0; bar=null");
120
121 assertXPathValue(context, "string(" + TestFunctions.class.getName() + ".new())", "foo=0; bar=null");
122
123 assertXPathValue(context, "string(test:new(3, 'baz'))", "foo=3; bar=baz");
124
125 assertXPathValue(context, "string(test:new('3', 4))", "foo=3; bar=4.0");
126 context.getVariables().declareVariable("A", "baz");
127 assertXPathValue(context, "string(test:new(2, $A, false))", "foo=2; bar=baz");
128 }
129
130 @Test
131 public void testBCNodeSetHack() {
132 TypeUtils.setTypeConverter(new JXPath11CompatibleTypeConverter());
133 assertXPathValue(context, "test:isInstance(//strings, $List.class)", Boolean.FALSE);
134 assertXPathValue(context, "test:isInstance(//strings, $NodeSet.class)", Boolean.TRUE);
135 }
136
137 @Test
138 public void testCollectionMethodCall() {
139 final List list = new ArrayList();
140 list.add("foo");
141 context.getVariables().declareVariable("myList", list);
142 assertXPathValue(context, "size($myList)", Integer.valueOf(1));
143 assertXPathValue(context, "size(beans)", Integer.valueOf(2));
144 context.getValue("add($myList, 'hello')");
145 assertEquals(2, list.size(), "After adding an element");
146 final JXPathContext context = JXPathContext.newContext(new ArrayList());
147 assertEquals("0", String.valueOf(context.getValue("size(/)")), "Extension function on root collection");
148 }
149
150 @Test
151 public void testCollectionReturn() {
152 assertXPathValueIterator(context, "test:collection()/name", list("foo", "bar"));
153 assertXPathPointerIterator(context, "test:collection()/name", list("/.[1]/name", "/.[2]/name"));
154 assertXPathValue(context, "test:collection()/name", "foo");
155 assertXPathValue(context, "test:collection()/@name", "foo");
156 final List list = new ArrayList();
157 list.add("foo");
158 list.add("bar");
159 context.getVariables().declareVariable("list", list);
160 final Object values = context.getValue("test:items($list)");
161 assertInstanceOf(Collection.class, values, "Return type: ");
162 assertEquals(list, new ArrayList((Collection) values), "Return values: ");
163 }
164
165 @Test
166 public void testConstructorLookup() {
167 final Object[] args = { Integer.valueOf(1), "x" };
168 final Function func = functions.getFunction("test", "new", args);
169 assertEquals("foo=1; bar=x", func.invoke(new Context(null), args).toString(), "test:new(1, x)");
170 }
171
172 @Test
173 public void testConstructorLookupWithExpressionContext() {
174 final Object[] args = { "baz" };
175 final Function func = functions.getFunction("test", "new", args);
176 assertEquals("foo=1; bar=baz", func.invoke(new Context(Integer.valueOf(1)), args).toString(), "test:new('baz')");
177 }
178
179 @Test
180 public void testEstablishNodeSetBaseline() {
181 assertXPathValue(context, "test:isInstance(//strings, $List.class)", Boolean.TRUE);
182 assertXPathValue(context, "test:isInstance(//strings, $NodeSet.class)", Boolean.FALSE);
183 }
184
185 @Test
186 public void testExpressionContext() {
187
188
189
190 assertXPathValue(context, "//.[test:isMap()]/Key1", "Value 1");
191
192
193 assertXPathValue(context, "count(//.[test:count(strings) = 3])", Double.valueOf(7));
194
195
196 assertXPathValue(context, "test:count(//strings)", Integer.valueOf(21));
197
198
199 assertXPathValue(context, "test:countPointers(//strings)", Integer.valueOf(21));
200
201
202 assertXPathValue(context, "/beans[contains(test:path(), '[2]')]/name", "Name 2");
203 }
204
205 @Test
206 public void testMethodCall() {
207 assertXPathValue(context, "length('foo')", Integer.valueOf(3));
208
209 assertXPathValue(context, "call:substring('foo', 1, 2)", "o");
210
211 assertXPathValue(context, "string(test:getFoo($test))", "4");
212
213 assertXPathValue(context, "string(call:getFoo($test))", "4");
214
215 assertXPathValue(context, "string(getFoo($test))", "4");
216
217 assertXPathValue(context, "string(test:setFooAndBar($test, 7, 'biz'))", "foo=7; bar=biz");
218 }
219
220 @Test
221 public void testMethodLookup() {
222 final Object[] args = { new TestFunctions() };
223 final Function func = functions.getFunction("test", "getFoo", args);
224 assertEquals("0", func.invoke(new Context(null), args).toString(), "test:getFoo($test, 1, x)");
225 }
226
227 @Test
228 public void testMethodLookupWithExpressionContext() {
229 final Object[] args = { new TestFunctions() };
230 final Function func = functions.getFunction("test", "instancePath", args);
231 assertEquals("1", func.invoke(new Context(Integer.valueOf(1)), args), "test:instancePath()");
232 }
233
234 @Test
235 public void testMethodLookupWithExpressionContextAndArgument() {
236 final Object[] args = { new TestFunctions(), "*" };
237 final Function func = functions.getFunction("test", "pathWithSuffix", args);
238 assertEquals("1*", func.invoke(new Context(Integer.valueOf(1)), args), "test:pathWithSuffix('*')");
239 }
240
241 @Test
242 public void testNodeSetReturn() {
243 assertXPathValueIterator(context, "test:nodeSet()/name", list("Name 1", "Name 2"));
244 assertXPathValueIterator(context, "test:nodeSet()", list(testBean.getBeans()[0], testBean.getBeans()[1]));
245 assertXPathPointerIterator(context, "test:nodeSet()/name", list("/beans[1]/name", "/beans[2]/name"));
246 assertXPathValueAndPointer(context, "test:nodeSet()/name", "Name 1", "/beans[1]/name");
247 assertXPathValueAndPointer(context, "test:nodeSet()/@name", "Name 1", "/beans[1]/@name");
248 assertEquals(2, ((Number) context.getValue("count(test:nodeSet())")).intValue());
249 assertXPathValue(context, "test:nodeSet()", testBean.getBeans()[0]);
250 }
251
252 @Test
253 public void testStaticMethodCall() {
254 assertXPathValue(context, "string(test:build(8, 'goober'))", "foo=8; bar=goober");
255
256 assertXPathValue(context, "string(jxpathtest:TestFunctions.build(8, 'goober'))", "foo=8; bar=goober");
257
258 assertXPathValue(context, "string(" + TestFunctions.class.getName() + ".build(8, 'goober'))", "foo=8; bar=goober");
259
260
261 assertXPathValue(context, "string(test:increment(8))", "9");
262
263 assertXPathValue(context, "test:string(/beans/name)", "Name 1");
264 }
265
266 @Test
267 public void testStaticMethodLookup() {
268 final Object[] args = { Integer.valueOf(1), "x" };
269 final Function func = functions.getFunction("test", "build", args);
270 assertEquals("foo=1; bar=x", func.invoke(new Context(null), args).toString(), "test:build(1, x)");
271 }
272
273 @Test
274 public void testStaticMethodLookupWithConversion() {
275 final Object[] args = { "7", Integer.valueOf(1) };
276 final Function func = functions.getFunction("test", "build", args);
277 assertEquals("foo=7; bar=1", func.invoke(new Context(null), args).toString(), "test:build('7', 1)");
278 }
279
280 @Test
281 public void testStaticMethodLookupWithExpressionContext() {
282 final Object[] args = {};
283 final Function func = functions.getFunction("test", "path", args);
284 assertEquals("1", func.invoke(new Context(Integer.valueOf(1)), args), "test:path()");
285 }
286 }