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