PageRenderTime 140ms CodeModel.GetById 43ms RepoModel.GetById 4ms app.codeStats 0ms

/mcs/class/System.XML/Mono.Xml.Xsl/XslFunctions.cs

https://bitbucket.org/danipen/mono
C# | 688 lines | 550 code | 93 blank | 45 comment | 169 complexity | b1c8dfd4e8c30ee557e90a790c34dbaf MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
  1. //
  2. // XsltCompiledContext.cs
  3. //
  4. // Authors:
  5. // Ben Maurer (bmaurer@users.sourceforge.net)
  6. // Atsushi Enomoto (atsushi@ximian.com)
  7. // (C) 2003 Ben Maurer
  8. // (C) 2004 Atsushi Enomoto
  9. //
  10. //
  11. // Permission is hereby granted, free of charge, to any person obtaining
  12. // a copy of this software and associated documentation files (the
  13. // "Software"), to deal in the Software without restriction, including
  14. // without limitation the rights to use, copy, modify, merge, publish,
  15. // distribute, sublicense, and/or sell copies of the Software, and to
  16. // permit persons to whom the Software is furnished to do so, subject to
  17. // the following conditions:
  18. //
  19. // The above copyright notice and this permission notice shall be
  20. // included in all copies or substantial portions of the Software.
  21. //
  22. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  23. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  24. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  25. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  26. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  27. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  28. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  29. //
  30. using System;
  31. using System.Collections;
  32. using System.Globalization;
  33. using System.Reflection;
  34. using System.Text;
  35. using System.Xml;
  36. using System.Xml.XPath;
  37. using System.Xml.Xsl;
  38. using Mono.Xml.Xsl;
  39. using QName = System.Xml.XmlQualifiedName;
  40. namespace Mono.Xml.Xsl
  41. {
  42. internal abstract class XPFuncImpl : IXsltContextFunction
  43. {
  44. int minargs, maxargs;
  45. XPathResultType returnType;
  46. XPathResultType [] argTypes;
  47. public XPFuncImpl () {}
  48. public XPFuncImpl (int minArgs, int maxArgs, XPathResultType returnType, XPathResultType[] argTypes)
  49. {
  50. this.Init(minArgs, maxArgs, returnType, argTypes);
  51. }
  52. protected void Init (int minArgs, int maxArgs, XPathResultType returnType, XPathResultType[] argTypes)
  53. {
  54. this.minargs = minArgs;
  55. this.maxargs = maxArgs;
  56. this.returnType = returnType;
  57. this.argTypes = argTypes;
  58. }
  59. public int Minargs { get { return this.minargs; }}
  60. public int Maxargs { get { return this.maxargs; }}
  61. public XPathResultType ReturnType { get { return this.returnType; }}
  62. public XPathResultType [] ArgTypes { get { return this.argTypes; }}
  63. public object Invoke (XsltContext xsltContext, object [] args, XPathNavigator docContext)
  64. {
  65. return Invoke ((XsltCompiledContext)xsltContext, args, docContext);
  66. }
  67. public abstract object Invoke (XsltCompiledContext xsltContext, object [] args, XPathNavigator docContext);
  68. public static XPathResultType GetXPathType (Type type, XPathNavigator node) {
  69. switch (Type.GetTypeCode(type)) {
  70. case TypeCode.String:
  71. return XPathResultType.String;
  72. case TypeCode.Boolean:
  73. return XPathResultType.Boolean;
  74. case TypeCode.Object:
  75. if (typeof (XPathNavigator).IsAssignableFrom (type) || typeof (IXPathNavigable).IsAssignableFrom (type))
  76. return XPathResultType.Navigator;
  77. if (typeof (XPathNodeIterator).IsAssignableFrom (type))
  78. return XPathResultType.NodeSet;
  79. return XPathResultType.Any;
  80. case TypeCode.DateTime :
  81. throw new XsltException ("Invalid type DateTime was specified.", null, node);
  82. default: // Numeric
  83. return XPathResultType.Number;
  84. }
  85. }
  86. }
  87. class XsltExtensionFunction : XPFuncImpl
  88. {
  89. private object extension;
  90. private MethodInfo method;
  91. private TypeCode [] typeCodes;
  92. public XsltExtensionFunction (object extension, MethodInfo method, XPathNavigator currentNode)
  93. {
  94. this.extension = extension;
  95. this.method = method;
  96. ParameterInfo [] parameters = method.GetParameters ();
  97. int minArgs = parameters.Length;
  98. int maxArgs = parameters.Length;
  99. this.typeCodes = new TypeCode [parameters.Length];
  100. XPathResultType[] argTypes = new XPathResultType [parameters.Length];
  101. bool canBeOpt = true;
  102. for (int i = parameters.Length - 1; 0 <= i; i--) { // optionals at the end
  103. typeCodes [i] = Type.GetTypeCode (parameters [i].ParameterType);
  104. argTypes [i] = GetXPathType (parameters [i].ParameterType, currentNode);
  105. if (canBeOpt) {
  106. if (parameters[i].IsOptional)
  107. minArgs --;
  108. else
  109. canBeOpt = false;
  110. }
  111. }
  112. base.Init (minArgs, maxArgs, GetXPathType (method.ReturnType, currentNode), argTypes);
  113. }
  114. public override object Invoke (XsltCompiledContext xsltContext, object [] args, XPathNavigator docContext)
  115. {
  116. try {
  117. ParameterInfo [] pis = method.GetParameters ();
  118. object [] castedArgs = new object [pis.Length];
  119. for (int i = 0; i < args.Length; i++) {
  120. Type t = pis [i].ParameterType;
  121. switch (t.FullName) {
  122. case "System.Int16":
  123. case "System.UInt16":
  124. case "System.Int32":
  125. case "System.UInt32":
  126. case "System.Int64":
  127. case "System.UInt64":
  128. case "System.Single":
  129. case "System.Decimal":
  130. castedArgs [i] = Convert.ChangeType (args [i], t);
  131. break;
  132. default:
  133. castedArgs [i] = args [i];
  134. break;
  135. }
  136. }
  137. object result = null;
  138. switch (method.ReturnType.FullName) {
  139. case "System.Int16":
  140. case "System.UInt16":
  141. case "System.Int32":
  142. case "System.UInt32":
  143. case "System.Int64":
  144. case "System.UInt64":
  145. case "System.Single":
  146. case "System.Decimal":
  147. result = Convert.ChangeType (method.Invoke (extension, castedArgs), typeof (double));
  148. break;
  149. default:
  150. result = method.Invoke(extension, castedArgs);
  151. break;
  152. }
  153. IXPathNavigable navigable = result as IXPathNavigable;
  154. if (navigable != null)
  155. return navigable.CreateNavigator ();
  156. return result;
  157. } catch (Exception ex) {
  158. throw new XsltException ("Custom function reported an error.", ex);
  159. // Debug.WriteLine ("****** INCORRECT RESOLUTION **********");
  160. }
  161. }
  162. }
  163. class XsltCurrent : XPathFunction
  164. {
  165. public XsltCurrent (FunctionArguments args) : base (args)
  166. {
  167. if (args != null)
  168. throw new XPathException ("current takes 0 args");
  169. }
  170. public override XPathResultType ReturnType { get { return XPathResultType.NodeSet; }}
  171. public override object Evaluate (BaseIterator iter)
  172. {
  173. XsltCompiledContext ctx = (XsltCompiledContext) iter.NamespaceManager;
  174. return new SelfIterator ((ctx).Processor.CurrentNode, ctx);
  175. }
  176. internal override bool Peer {
  177. get { return false; }
  178. }
  179. public override string ToString ()
  180. {
  181. return "current()";
  182. }
  183. }
  184. class XsltDocument : XPathFunction
  185. {
  186. Expression arg0, arg1;
  187. XPathNavigator doc;
  188. public XsltDocument (FunctionArguments args, Compiler c) : base (args)
  189. {
  190. if (args == null || (args.Tail != null && args.Tail.Tail != null))
  191. throw new XPathException ("document takes one or two args");
  192. arg0 = args.Arg;
  193. if (args.Tail != null)
  194. arg1 = args.Tail.Arg;
  195. doc = c.Input.Clone ();
  196. }
  197. public override XPathResultType ReturnType { get { return XPathResultType.NodeSet; }}
  198. internal override bool Peer {
  199. get { return arg0.Peer && (arg1 != null ? arg1.Peer : true); }
  200. }
  201. public override object Evaluate (BaseIterator iter)
  202. {
  203. string baseUri = null;
  204. if (arg1 != null) {
  205. XPathNodeIterator it = arg1.EvaluateNodeSet (iter);
  206. if (it.MoveNext())
  207. baseUri = it.Current.BaseURI;
  208. else
  209. baseUri = VoidBaseUriFlag;
  210. }
  211. object o = arg0.Evaluate (iter);
  212. if (o is XPathNodeIterator)
  213. return GetDocument ((iter.NamespaceManager as XsltCompiledContext), (XPathNodeIterator)o, baseUri);
  214. else
  215. return GetDocument ((iter.NamespaceManager as XsltCompiledContext), o is IFormattable ? ((IFormattable) o).ToString (null, CultureInfo.InvariantCulture) : (o != null ? o.ToString () : null), baseUri);
  216. }
  217. static string VoidBaseUriFlag = "&^)(*&%*^$&$VOID!BASE!URI!";
  218. Uri Resolve (string thisUri, string baseUri, XslTransformProcessor p)
  219. {
  220. // Debug.WriteLine ("THIS: " + thisUri);
  221. // Debug.WriteLine ("BASE: " + baseUri);
  222. XmlResolver r = p.Resolver;
  223. if (r == null)
  224. return null;
  225. Uri uriBase = null;
  226. if (! object.ReferenceEquals (baseUri, VoidBaseUriFlag) && baseUri != String.Empty)
  227. uriBase = r.ResolveUri (null, baseUri);
  228. return r.ResolveUri (uriBase, thisUri);
  229. }
  230. XPathNodeIterator GetDocument (XsltCompiledContext xsltContext, XPathNodeIterator itr, string baseUri)
  231. {
  232. ArrayList list = new ArrayList ();
  233. try {
  234. Hashtable got = new Hashtable ();
  235. while (itr.MoveNext()) {
  236. Uri uri = Resolve (itr.Current.Value, baseUri != null ? baseUri : /*itr.Current.BaseURI*/doc.BaseURI, xsltContext.Processor);
  237. if (!got.ContainsKey (uri)) {
  238. got.Add (uri, null);
  239. if (uri != null && uri.ToString () == "") {
  240. XPathNavigator n = doc.Clone ();
  241. n.MoveToRoot ();
  242. list.Add (n);
  243. } else
  244. list.Add (xsltContext.Processor.GetDocument (uri));
  245. }
  246. }
  247. } catch (Exception) {
  248. // Error recovery.
  249. // See http://www.w3.org/TR/xslt#document and
  250. // bug #75663.
  251. list.Clear ();
  252. }
  253. return new ListIterator (list, xsltContext);
  254. }
  255. XPathNodeIterator GetDocument (XsltCompiledContext xsltContext, string arg0, string baseUri)
  256. {
  257. try {
  258. Uri uri = Resolve (arg0, baseUri != null ? baseUri : doc.BaseURI, xsltContext.Processor);
  259. XPathNavigator n;
  260. if (uri != null && uri.ToString () == "") {
  261. n = doc.Clone ();
  262. n.MoveToRoot ();
  263. } else
  264. n = xsltContext.Processor.GetDocument (uri);
  265. return new SelfIterator (n, xsltContext);
  266. } catch (Exception) {
  267. return new ListIterator (new ArrayList (), xsltContext);
  268. }
  269. }
  270. public override string ToString ()
  271. {
  272. return String.Concat ("document(",
  273. arg0.ToString (),
  274. arg1 != null ? "," : String.Empty,
  275. arg1 != null ? arg1.ToString () : String.Empty,
  276. ")");
  277. }
  278. }
  279. class XsltElementAvailable : XPathFunction
  280. {
  281. Expression arg0;
  282. IStaticXsltContext ctx;
  283. public XsltElementAvailable (FunctionArguments args, IStaticXsltContext ctx) : base (args)
  284. {
  285. if (args == null || args.Tail != null)
  286. throw new XPathException ("element-available takes 1 arg");
  287. arg0 = args.Arg;
  288. this.ctx = ctx;
  289. }
  290. public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
  291. internal override bool Peer {
  292. get { return arg0.Peer; }
  293. }
  294. public override object Evaluate (BaseIterator iter)
  295. {
  296. QName name = XslNameUtil.FromString (arg0.EvaluateString (iter), ctx);
  297. return (
  298. (name.Namespace == Compiler.XsltNamespace) &&
  299. (
  300. //
  301. // A list of all the instructions (does not include top-level-elements)
  302. //
  303. name.Name == "apply-imports" ||
  304. name.Name == "apply-templates" ||
  305. name.Name == "call-template" ||
  306. name.Name == "choose" ||
  307. name.Name == "comment" ||
  308. name.Name == "copy" ||
  309. name.Name == "copy-of" ||
  310. name.Name == "element" ||
  311. name.Name == "fallback" ||
  312. name.Name == "for-each" ||
  313. name.Name == "message" ||
  314. name.Name == "number" ||
  315. name.Name == "processing-instruction" ||
  316. name.Name == "text" ||
  317. name.Name == "value-of" ||
  318. name.Name == "variable"
  319. )
  320. );
  321. }
  322. }
  323. class XsltFormatNumber : XPathFunction
  324. {
  325. Expression arg0, arg1, arg2;
  326. IStaticXsltContext ctx;
  327. public XsltFormatNumber (FunctionArguments args, IStaticXsltContext ctx) : base (args)
  328. {
  329. if (args == null || args.Tail == null || (args.Tail.Tail != null && args.Tail.Tail.Tail != null))
  330. throw new XPathException ("format-number takes 2 or 3 args");
  331. arg0 = args.Arg;
  332. arg1 = args.Tail.Arg;
  333. if (args.Tail.Tail != null) {
  334. arg2= args.Tail.Tail.Arg;
  335. this.ctx = ctx;
  336. }
  337. }
  338. public override XPathResultType ReturnType { get { return XPathResultType.String; }}
  339. internal override bool Peer {
  340. get { return arg0.Peer && arg1.Peer && (arg2 != null ? arg2.Peer : true); }
  341. }
  342. public override object Evaluate (BaseIterator iter)
  343. {
  344. double d = arg0.EvaluateNumber (iter);
  345. string s = arg1.EvaluateString (iter);
  346. QName nm = QName.Empty;
  347. if (arg2 != null)
  348. nm = XslNameUtil.FromString (arg2.EvaluateString (iter), ctx);
  349. try {
  350. return ((XsltCompiledContext) iter.NamespaceManager).Processor.CompiledStyle
  351. .LookupDecimalFormat (nm).FormatNumber (d, s);
  352. } catch (ArgumentException ex) {
  353. throw new XsltException (ex.Message, ex, iter.Current);
  354. }
  355. }
  356. }
  357. class XsltFunctionAvailable : XPathFunction
  358. {
  359. Expression arg0;
  360. IStaticXsltContext ctx;
  361. public XsltFunctionAvailable (FunctionArguments args, IStaticXsltContext ctx) : base (args)
  362. {
  363. if (args == null || args.Tail != null)
  364. throw new XPathException ("element-available takes 1 arg");
  365. arg0 = args.Arg;
  366. this.ctx = ctx;
  367. }
  368. public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
  369. internal override bool Peer {
  370. get { return arg0.Peer; }
  371. }
  372. public override object Evaluate (BaseIterator iter)
  373. {
  374. string name = arg0.EvaluateString (iter);
  375. int colon = name.IndexOf (':');
  376. // extension function
  377. if (colon > 0)
  378. return (iter.NamespaceManager as XsltCompiledContext).ResolveFunction (
  379. XslNameUtil.FromString (name, ctx),
  380. null) != null;
  381. return (
  382. //
  383. // XPath
  384. //
  385. name == "boolean" ||
  386. name == "ceiling" ||
  387. name == "concat" ||
  388. name == "contains" ||
  389. name == "count" ||
  390. name == "false" ||
  391. name == "floor" ||
  392. name == "id"||
  393. name == "lang" ||
  394. name == "last" ||
  395. name == "local-name" ||
  396. name == "name" ||
  397. name == "namespace-uri" ||
  398. name == "normalize-space" ||
  399. name == "not" ||
  400. name == "number" ||
  401. name == "position" ||
  402. name == "round" ||
  403. name == "starts-with" ||
  404. name == "string" ||
  405. name == "string-length" ||
  406. name == "substring" ||
  407. name == "substring-after" ||
  408. name == "substring-before" ||
  409. name == "sum" ||
  410. name == "translate" ||
  411. name == "true" ||
  412. // XSLT
  413. name == "document" ||
  414. name == "format-number" ||
  415. name == "function-available" ||
  416. name == "generate-id" ||
  417. name == "key" ||
  418. name == "current" ||
  419. name == "unparsed-entity-uri" ||
  420. name == "element-available" ||
  421. name == "system-property"
  422. );
  423. }
  424. }
  425. class XsltGenerateId : XPathFunction
  426. {
  427. //FIXME: generate short string, not the huge thing it makes now
  428. Expression arg0;
  429. public XsltGenerateId (FunctionArguments args) : base (args)
  430. {
  431. if (args != null) {
  432. if (args.Tail != null)
  433. throw new XPathException ("generate-id takes 1 or no args");
  434. arg0 = args.Arg;
  435. }
  436. }
  437. public override XPathResultType ReturnType { get { return XPathResultType.String; }}
  438. internal override bool Peer {
  439. get { return arg0.Peer; }
  440. }
  441. public override object Evaluate (BaseIterator iter)
  442. {
  443. XPathNavigator n;
  444. if (arg0 != null) {
  445. XPathNodeIterator itr = arg0.EvaluateNodeSet (iter);
  446. if (itr.MoveNext ())
  447. n = itr.Current.Clone ();
  448. else
  449. return string.Empty; // empty nodeset == empty string
  450. } else
  451. n = iter.Current.Clone ();
  452. StringBuilder sb = new StringBuilder ("Mono"); // Ensure begins with alpha
  453. sb.Append (XmlConvert.EncodeLocalName (n.BaseURI));
  454. sb.Replace ('_', 'm'); // remove underscores from EncodeLocalName
  455. sb.Append (n.NodeType);
  456. sb.Append ('m');
  457. do {
  458. sb.Append (IndexInParent (n));
  459. sb.Append ('m');
  460. } while (n.MoveToParent ());
  461. return sb.ToString ();
  462. }
  463. int IndexInParent (XPathNavigator nav)
  464. {
  465. int n = 0;
  466. while (nav.MoveToPrevious ())
  467. n++;
  468. return n;
  469. }
  470. }
  471. class XsltKey : XPathFunction
  472. {
  473. Expression arg0, arg1;
  474. IStaticXsltContext staticContext;
  475. public XsltKey (FunctionArguments args, IStaticXsltContext ctx) : base (args)
  476. {
  477. staticContext = ctx;
  478. if (args == null || args.Tail == null)
  479. throw new XPathException ("key takes 2 args");
  480. arg0 = args.Arg;
  481. arg1 = args.Tail.Arg;
  482. }
  483. public Expression KeyName { get { return arg0; } }
  484. public Expression Field { get { return arg1; } }
  485. public override XPathResultType ReturnType { get { return XPathResultType.NodeSet; }}
  486. internal override bool Peer {
  487. get { return arg0.Peer && arg1.Peer; }
  488. }
  489. public bool PatternMatches (XPathNavigator nav, XsltContext nsmgr)
  490. {
  491. XsltCompiledContext ctx = nsmgr as XsltCompiledContext;
  492. // for key pattern, it must contain literal value
  493. return ctx.MatchesKey (nav, staticContext,
  494. arg0.StaticValueAsString,
  495. arg1.StaticValueAsString);
  496. }
  497. public override object Evaluate (BaseIterator iter)
  498. {
  499. XsltCompiledContext ctx = iter.NamespaceManager
  500. as XsltCompiledContext;
  501. return ctx.EvaluateKey (staticContext, iter, arg0, arg1);
  502. }
  503. }
  504. class XsltSystemProperty : XPathFunction
  505. {
  506. Expression arg0;
  507. IStaticXsltContext ctx;
  508. public XsltSystemProperty (FunctionArguments args, IStaticXsltContext ctx) : base (args)
  509. {
  510. if (args == null || args.Tail != null)
  511. throw new XPathException ("system-property takes 1 arg");
  512. arg0 = args.Arg;
  513. this.ctx = ctx;
  514. }
  515. public override XPathResultType ReturnType { get { return XPathResultType.String; }}
  516. internal override bool Peer {
  517. get { return arg0.Peer; }
  518. }
  519. public override object Evaluate (BaseIterator iter)
  520. {
  521. QName name = XslNameUtil.FromString (arg0.EvaluateString (iter), ctx);
  522. if (name.Namespace == Compiler.XsltNamespace) {
  523. switch (name.Name) {
  524. case "version": return "1.0";
  525. case "vendor": return "Mono";
  526. case "vendor-url": return "http://www.go-mono.com/";
  527. }
  528. }
  529. return "";
  530. }
  531. }
  532. class XsltUnparsedEntityUri : XPathFunction
  533. {
  534. Expression arg0;
  535. public XsltUnparsedEntityUri (FunctionArguments args) : base (args)
  536. {
  537. if (args == null || args.Tail != null)
  538. throw new XPathException ("unparsed-entity-uri takes 1 arg");
  539. arg0 = args.Arg;
  540. }
  541. public override XPathResultType ReturnType { get { return XPathResultType.String; }}
  542. internal override bool Peer {
  543. get { return arg0.Peer; }
  544. }
  545. public override object Evaluate (BaseIterator iter)
  546. {
  547. IHasXmlNode xn = iter.Current as IHasXmlNode;
  548. if (xn == null)
  549. return String.Empty;
  550. XmlNode n = xn.GetNode ();
  551. if (n.OwnerDocument == null)
  552. return String.Empty;
  553. XmlDocumentType doctype = n.OwnerDocument.DocumentType;
  554. if (doctype == null)
  555. return String.Empty;
  556. XmlEntity ent = doctype.Entities.GetNamedItem (arg0.EvaluateString (iter)) as XmlEntity;
  557. if (ent == null)
  558. return String.Empty;
  559. return ent.SystemId != null ? ent.SystemId : String.Empty;
  560. }
  561. }
  562. class MSXslNodeSet : XPathFunction
  563. {
  564. Expression arg0;
  565. public MSXslNodeSet (FunctionArguments args) : base (args)
  566. {
  567. if (args == null || args.Tail != null)
  568. throw new XPathException ("element-available takes 1 arg");
  569. arg0 = args.Arg;
  570. }
  571. public override XPathResultType ReturnType {
  572. get {
  573. return XPathResultType.NodeSet;
  574. }
  575. }
  576. internal override bool Peer {
  577. get { return arg0.Peer; }
  578. }
  579. public override object Evaluate (BaseIterator iter)
  580. {
  581. XsltCompiledContext ctx = iter.NamespaceManager as XsltCompiledContext;
  582. XPathNavigator loc = iter.Current != null ? iter.Current.Clone () : null;
  583. XPathNavigator nav = arg0.EvaluateAs (iter, XPathResultType.Navigator) as XPathNavigator;
  584. if (nav == null) {
  585. if (loc != null)
  586. return new XsltException ("Cannot convert the XPath argument to a result tree fragment.", null, loc);
  587. else
  588. return new XsltException ("Cannot convert the XPath argument to a result tree fragment.", null);
  589. }
  590. ArrayList al = new ArrayList ();
  591. al.Add (nav);
  592. return new ListIterator (al, ctx);
  593. }
  594. }
  595. }