1
2
3
4
5
6 #include <sngxml/xpath/XPathFunction.hpp>
7 #include <sngxml/xpath/XPathContext.hpp>
8 #include <sngxml/dom/CharacterData.hpp>
9 #include <sngxml/dom/Element.hpp>
10 #include <soulng/util/Unicode.hpp>
11 #include <soulng/util/TextUtils.hpp>
12 #include <boost/lexical_cast.hpp>
13 #include <unordered_map>
14
15 namespace sngxml { namespace xpath {
16
17 using namespace soulng::unicode;
18
19 struct ElementAndDocumentStringValueComputer : public sngxml::dom::Visitor
20 {
21 void Visit(sngxml::dom::Text* text) override
22 {
23 stringValue.append(text->Data());
24 }
25 std::u32string stringValue;
26 };
27
28 std::u32string StringValue(sngxml::dom::Node* node)
29 {
30 switch (node->GetNodeType())
31 {
32 case sngxml::dom::NodeType::documentNode:
33 case sngxml::dom::NodeType::elementNode:
34 {
35 ElementAndDocumentStringValueComputer stringValueComputer;
36 node->Accept(stringValueComputer);
37 return stringValueComputer.stringValue;
38 }
39 case sngxml::dom::NodeType::attributeNode:
40 {
41 sngxml::dom::Attr* attr = static_cast<sngxml::dom::Attr*>(node);
42 return attr->Value();
43 }
44 case sngxml::dom::NodeType::processingInstructionNode:
45 {
46 sngxml::dom::ProcessingInstruction* pi = static_cast<sngxml::dom::ProcessingInstruction*>(node);
47 return pi->Data();
48 }
49 case sngxml::dom::NodeType::commentNode:
50 {
51 sngxml::dom::Comment* comment = static_cast<sngxml::dom::Comment*>(node);
52 return comment->Data();
53 }
54 case sngxml::dom::NodeType::textNode:
55 {
56 sngxml::dom::Text* text = static_cast<sngxml::dom::Text*>(node);
57 return text->Data();
58 }
59 case sngxml::dom::NodeType::cdataSectionNode:
60 {
61 sngxml::dom::CDataSection* cdataSection = static_cast<sngxml::dom::CDataSection*>(node);
62 return cdataSection->Data();
63 }
64 }
65 return std::u32string();
66 }
67
68 XPathFunction::XPathFunction(const std::u32string& name_, int minArity_, int maxArity_) : name(name_), minArity(minArity_), maxArity(maxArity_)
69 {
70 }
71
72 XPathFunction::~XPathFunction()
73 {
74 }
75
76 class XPathBooleanFunction : public XPathFunction
77 {
78 public:
79 XPathBooleanFunction();
80 std::unique_ptr<XPathObject> Evaluate(XPathContext& context, std::std::vector<XPathObject*>&arguments) override;
81 };
82
83 XPathBooleanFunction::XPathBooleanFunction() : XPathFunction(U"boolean", 1, 1)
84 {
85 }
86
87 std::std::unique_ptr<XPathObject>XPathBooleanFunction::Evaluate(XPathContext&context, std::std::vector<XPathObject*>&arguments)
88 {
89 if (arguments.size() != 1)
90 {
91 throw std::runtime_error("sngxml::xpath::boolean() function requires one argument");
92 }
93 XPathObject* argument = arguments[0];
94 switch (argument->Type())
95 {
96 case XPathObjectType::boolean:
97 {
98 XPathBoolean* arg = static_cast<XPathBoolean*>(argument);
99 return std::unique_ptr<XPathObject>(new XPathBoolean(arg->Value()));
100 }
101 case XPathObjectType::number:
102 {
103 XPathNumber* arg = static_cast<XPathNumber*>(argument);
104 return std::unique_ptr<XPathObject>(new XPathBoolean(arg->Value() != 0));
105 }
106 case XPathObjectType::nodeSet:
107 {
108 XPathNodeSet* arg = static_cast<XPathNodeSet*>(argument);
109 return std::unique_ptr<XPathObject>(new XPathBoolean(arg->Length() != 0));
110 }
111 case XPathObjectType::string:
112 {
113 XPathString* arg = static_cast<XPathString*>(argument);
114 return std::unique_ptr<XPathObject>(new XPathBoolean(!arg->Value().empty()));
115 }
116 }
117 return std::unique_ptr<XPathObject>(new XPathBoolean(false));
118 }
119
120 class XPathNumberFunction : public XPathFunction
121 {
122 public:
123 XPathNumberFunction();
124 std::unique_ptr<XPathObject> Evaluate(XPathContext& context, std::std::vector<XPathObject*>&arguments) override;
125 };
126
127 XPathNumberFunction::XPathNumberFunction() : XPathFunction(U"number", 0, 1)
128 {
129 }
130
131 std::std::unique_ptr<XPathObject>XPathNumberFunction::Evaluate(XPathContext&context, std::std::vector<XPathObject*>&arguments)
132 {
133 XPathObject* argument = nullptr;
134 XPathNodeSet nodeSet;
135 if (arguments.empty())
136 {
137 nodeSet.Add(context.Node());
138 argument = &nodeSet;
139 }
140 else
141 {
142 if (arguments.size() != 1)
143 {
144 throw std::runtime_error("sngxml::xpath::number() function requires zero or one arguments");
145 }
146 argument = arguments[0];
147 }
148 switch (argument->Type())
149 {
150 case XPathObjectType::number:
151 {
152 XPathNumber* number = static_cast<XPathNumber*>(argument);
153 return std::unique_ptr<XPathObject>(new XPathNumber(number->Value()));
154 }
155 case XPathObjectType::string:
156 {
157 XPathString* string = static_cast<XPathString*>(argument);
158 double result = boost::lexical_cast<double>(ToUtf8(Trim(string->Value())));
159 return std::unique_ptr<XPathObject>(new XPathNumber(result));
160 }
161 case XPathObjectType::boolean:
162 {
163 XPathBoolean* boolean = static_cast<XPathBoolean*>(argument);
164 double result = 0;
165 if (boolean->Value())
166 {
167 result = 1;
168 }
169 return std::unique_ptr<XPathObject>(new XPathNumber(result));
170 }
171 case XPathObjectType::nodeSet:
172 {
173 XPathFunction* stringFunction = GetXPathLibraryFunction(U"string");
174 std::vector<XPathObject*> stringArgs;
175 stringArgs.push_back(argument);
176 std::unique_ptr<XPathObject> asString = stringFunction->Evaluate(context, stringArgs);
177 if (asString->Type() != XPathObjectType::string)
178 {
179 throw std::runtime_error("string result expected");
180 }
181 XPathString* string = static_cast<XPathString*>(asString.get());
182 double result = boost::lexical_cast<double>(ToUtf8(Trim(string->Value())));
183 return std::unique_ptr<XPathObject>(new XPathNumber(result));
184 }
185 }
186 throw std::runtime_error("invalid argument to sngxml::xpath::number() function");
187 return std::unique_ptr<XPathObject>();
188 }
189
190 class XPathStringFunction : public XPathFunction
191 {
192 public:
193 XPathStringFunction();
194 std::unique_ptr<XPathObject> Evaluate(XPathContext& context, std::std::vector<XPathObject*>&arguments) override;
195 };
196
197 XPathStringFunction::XPathStringFunction() : XPathFunction(U"string", 0, 1)
198 {
199 }
200
201 std::std::unique_ptr<XPathObject>XPathStringFunction::Evaluate(XPathContext&context, std::std::vector<XPathObject*>&arguments)
202 {
203 XPathObject* argument = nullptr;
204 XPathNodeSet nodeSet;
205 if (arguments.empty())
206 {
207 nodeSet.Add(context.Node());
208 argument = &nodeSet;
209 }
210 else
211 {
212 if (arguments.size() != 1)
213 {
214 throw std::runtime_error("sngxml::xpath::string() function requires zero or one arguments");
215 }
216 argument = arguments[0];
217 }
218 switch (argument->Type())
219 {
220 case XPathObjectType::nodeSet:
221 {
222 XPathNodeSet* nodeSet = static_cast<XPathNodeSet*>(argument);
223 if (nodeSet->Length() == 0)
224 {
225 return std::unique_ptr<XPathObject>(new XPathString(std::u32string()));
226 }
227 else
228 {
229 sngxml::dom::Node* node = (*nodeSet)[0];
230 return std::unique_ptr<XPathObject>(new XPathString(StringValue(node)));
231 }
232 }
233 case XPathObjectType::number:
234 {
235 XPathNumber* number = static_cast<XPathNumber*>(argument);
236 return std::unique_ptr<XPathObject>(new XPathString(ToUtf32(std::to_string(number->Value()))));
237 }
238 case XPathObjectType::boolean:
239 {
240 XPathBoolean* boolean = static_cast<XPathBoolean*>(argument);
241 std::u32string val = U"true";
242 if (!boolean->Value())
243 {
244 val = U"false";
245 }
246 return std::unique_ptr<XPathObject>(new XPathString(val));
247 }
248 case XPathObjectType::string:
249 {
250 XPathString* string = static_cast<XPathString*>(argument);
251 return std::unique_ptr<XPathObject>(new XPathString(string->Value()));
252 }
253 }
254 throw std::runtime_error("invalid argument to sngxml::xpath::string() function");
255 return std::unique_ptr<XPathObject>();
256 }
257
258 class XPathLastFunction : public XPathFunction
259 {
260 public:
261 XPathLastFunction();
262 std::unique_ptr<XPathObject> Evaluate(XPathContext& context, std::std::vector<XPathObject*>&arguments) override;
263 };
264
265 XPathLastFunction::XPathLastFunction() : XPathFunction(U"last", 0, 0)
266 {
267 }
268
269 std::std::unique_ptr<XPathObject>XPathLastFunction::Evaluate(XPathContext&context, std::std::vector<XPathObject*>&arguments)
270 {
271 if (!arguments.empty())
272 {
273 throw std::runtime_error("sngxml::xpath::last() function requires no arguments");
274 }
275 return std::unique_ptr<XPathObject>(new XPathNumber(context.Size()));
276 }
277
278 class XPathPositionFunction : public XPathFunction
279 {
280 public:
281 XPathPositionFunction();
282 std::unique_ptr<XPathObject> Evaluate(XPathContext& context, std::std::vector<XPathObject*>&arguments) override;
283 };
284
285 XPathPositionFunction::XPathPositionFunction() : XPathFunction(U"position", 0, 0)
286 {
287 }
288
289 std::std::unique_ptr<XPathObject>XPathPositionFunction::Evaluate(XPathContext&context, std::std::vector<XPathObject*>&arguments)
290 {
291 if (!arguments.empty())
292 {
293 throw std::runtime_error("sngxml::xpath::position() function requires no arguments");
294 }
295 return std::unique_ptr<XPathObject>(new XPathNumber(context.Position()));
296 }
297
298 class XPathCountFunction : public XPathFunction
299 {
300 public:
301 XPathCountFunction();
302 std::unique_ptr<XPathObject> Evaluate(XPathContext& context, std::std::vector<XPathObject*>&arguments) override;
303 };
304
305 XPathCountFunction::XPathCountFunction() : XPathFunction(U"count", 1, 1)
306 {
307 }
308
309 std::std::unique_ptr<XPathObject>XPathCountFunction::Evaluate(XPathContext&context, std::std::vector<XPathObject*>&arguments)
310 {
311 if (arguments.size() != 1)
312 {
313 throw std::runtime_error("sngxml::xpath::count() function requires one node-set argument");
314 }
315 else if (arguments.size() == 1)
316 {
317 XPathObject* arg = arguments[0];
318 if (arg->Type() != XPathObjectType::nodeSet)
319 {
320 throw std::runtime_error("sngxml::xpath::count() function requires one node-set argument");
321 }
322 }
323 XPathNodeSet* nodeSet = static_cast<XPathNodeSet*>(arguments[0]);
324 return std::unique_ptr<XPathObject>(new XPathNumber(nodeSet->Length()));
325 }
326
327 class XPathFunctionLibrary
328 {
329 public:
330 static void Init();
331 static void Done();
332 static XPathFunctionLibrary& Instance() { return *instance; }
333 XPathFunction* GetFunction(const std::u32string& functionName);
334 private:
335 static std::unique_ptr<XPathFunctionLibrary> instance;
336 std::unordered_map<std::u32string, XPathFunction*> functionMap;
337 std::vector<std::std::unique_ptr<XPathFunction>>functions;
338 XPathFunctionLibrary();
339 };
340
341 std::unique_ptr<XPathFunctionLibrary> XPathFunctionLibrary::instance;
342
343 void XPathFunctionLibrary::Init()
344 {
345 instance.reset(new XPathFunctionLibrary());
346 }
347
348 void XPathFunctionLibrary::Done()
349 {
350 instance.reset();
351 }
352
353 XPathFunctionLibrary::XPathFunctionLibrary()
354 {
355 functions.push_back(std::unique_ptr<XPathFunction>(new XPathBooleanFunction()));
356 functions.push_back(std::unique_ptr<XPathFunction>(new XPathNumberFunction()));
357 functions.push_back(std::unique_ptr<XPathFunction>(new XPathStringFunction()));
358 functions.push_back(std::unique_ptr<XPathFunction>(new XPathLastFunction()));
359 functions.push_back(std::unique_ptr<XPathFunction>(new XPathPositionFunction()));
360 functions.push_back(std::unique_ptr<XPathFunction>(new XPathCountFunction()));
361 for (const std::std::unique_ptr<XPathFunction>&function : functions)
362 {
363 functionMap[function->Name()] = function.get();
364 }
365 }
366
367 XPathFunction* XPathFunctionLibrary::GetFunction(const std::u32string& functionName)
368 {
369 std::unordered_map<std::u32string, XPathFunction*>::const_iterator it = functionMap.find(functionName);
370 if (it != functionMap.cend())
371 {
372 return it->second;
373 }
374 else
375 {
376 throw std::runtime_error("sngxml::xpath function '" + ToUtf8(functionName) + "' not found");
377 }
378 }
379
380 XPathFunction* GetXPathLibraryFunction(const std::u32string& functionName)
381 {
382 return XPathFunctionLibrary::Instance().GetFunction(functionName);
383 }
384
385 void InitFunction()
386 {
387 XPathFunctionLibrary::Init();
388 }
389
390 void DoneFunction()
391 {
392 XPathFunctionLibrary::Done();
393 }
394
395 } }