1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  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& contextstd::std::vector<XPathObject*>&arguments) override;
 81 };
 82 
 83 XPathBooleanFunction::XPathBooleanFunction() : XPathFunction(U"boolean"11)
 84 {
 85 }
 86 
 87 std::std::unique_ptr<XPathObject>XPathBooleanFunction::Evaluate(XPathContext&contextstd::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& contextstd::std::vector<XPathObject*>&arguments) override;
125 };
126 
127 XPathNumberFunction::XPathNumberFunction() : XPathFunction(U"number"01)
128 {
129 }
130 
131 std::std::unique_ptr<XPathObject>XPathNumberFunction::Evaluate(XPathContext&contextstd::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(contextstringArgs);
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& contextstd::std::vector<XPathObject*>&arguments) override;
195 };
196 
197 XPathStringFunction::XPathStringFunction() : XPathFunction(U"string"01)
198 {
199 }
200 
201 std::std::unique_ptr<XPathObject>XPathStringFunction::Evaluate(XPathContext&contextstd::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& contextstd::std::vector<XPathObject*>&arguments) override;
263 };
264 
265 XPathLastFunction::XPathLastFunction() : XPathFunction(U"last"00)
266 {
267 }
268 
269 std::std::unique_ptr<XPathObject>XPathLastFunction::Evaluate(XPathContext&contextstd::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& contextstd::std::vector<XPathObject*>&arguments) override;
283 };
284 
285 XPathPositionFunction::XPathPositionFunction() : XPathFunction(U"position"00)
286 {
287 }
288 
289 std::std::unique_ptr<XPathObject>XPathPositionFunction::Evaluate(XPathContext&contextstd::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& contextstd::std::vector<XPathObject*>&arguments) override;
303 };
304 
305 XPathCountFunction::XPathCountFunction() : XPathFunction(U"count"11)
306 {
307 }
308 
309 std::std::unique_ptr<XPathObject>XPathCountFunction::Evaluate(XPathContext&contextstd::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::u32stringXPathFunction*> 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::u32stringXPathFunction*>::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 } } // namespace sngxml::xpath