xpath.pp 74 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828
  1. {
  2. This file is part of the Free Component Library
  3. Implementation of the XML Path Language (XPath) for Free Pascal
  4. Copyright (c) 2000 - 2003 by
  5. Areca Systems GmbH / Sebastian Guenther, [email protected]
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {$MODE objfpc}
  13. {$H+}
  14. unit XPath;
  15. interface
  16. uses SysUtils, Classes, DOM;
  17. resourcestring
  18. { XPath variables type names }
  19. SNodeSet = 'node set';
  20. SBoolean = 'boolean';
  21. SNumber = 'number';
  22. SString = 'string';
  23. { Variable errors }
  24. SVarNoConversion = 'Conversion from %s to %s not possible';
  25. { Scanner errors }
  26. SScannerQuotStringIsOpen = 'Ending ''"'' for string not found';
  27. SScannerAposStringIsOpen = 'Ending "''" for string not found';
  28. SScannerInvalidChar = 'Invalid character';
  29. { Parser errors }
  30. SParserExpectedLeftBracket = 'Expected "("';
  31. SParserExpectedRightBracket = 'Expected ")"';
  32. SParserBadAxisName = 'Invalid axis name';
  33. SParserBadNodeType = 'Invalid node type';
  34. SParserExpectedRightSquareBracket = 'Expected "]" after predicate';
  35. SParserInvalidPrimExpr = 'Invalid primary expression';
  36. SParserGarbageAfterExpression = 'Unrecognized input after expression';
  37. SParserInvalidNodeTest = 'Invalid node test (syntax error)';
  38. SParserExpectedVarName = 'Expected variable name after "$"';
  39. SParserExpectedQName = 'Expected "*" or LocalName after colon';
  40. { Evaluation errors }
  41. SEvalUnknownFunction = 'Unknown function: "%s"';
  42. SEvalUnknownVariable = 'Unknown variable: "%s"';
  43. SEvalInvalidArgCount = 'Invalid number of function arguments';
  44. type
  45. TXPathContext = class;
  46. TXPathEnvironment = class;
  47. TXPathVariable = class;
  48. { XPath lexical scanner }
  49. TXPathToken = ( // [28] - [38]
  50. tkInvalid,
  51. tkEndOfStream,
  52. tkIdentifier,
  53. tkString,
  54. tkNumber,
  55. tkDollar, // "$"
  56. tkLeftBracket, // "("
  57. tkRightBracket, // ")"
  58. tkAsterisk, // "*"
  59. tkPlus, // "+"
  60. tkComma, // ","
  61. tkMinus, // "-"
  62. tkDot, // "."
  63. tkDotDot, // ".."
  64. tkSlash, // "/"
  65. tkSlashSlash, // "//"
  66. tkColon, // ":"
  67. tkColonColon, // "::"
  68. tkLess, // "<"
  69. tkLessEqual, // "<="
  70. tkEqual, // "="
  71. tkNotEqual, // "!="
  72. tkGreater, // ">"
  73. tkGreaterEqual, // ">="
  74. tkAt, // "@"
  75. tkLeftSquareBracket, // "["
  76. tkRightSquareBracket, // "]"
  77. tkPipe // "|"
  78. );
  79. { XPath expression parse tree }
  80. TXPathExprNode = class
  81. protected
  82. function EvalPredicate(AContext: TXPathContext;
  83. AEnvironment: TXPathEnvironment): Boolean;
  84. public
  85. function Evaluate(AContext: TXPathContext;
  86. AEnvironment: TXPathEnvironment): TXPathVariable; virtual; abstract;
  87. end;
  88. TXPathNodeArray = array of TXPathExprNode;
  89. TXPathConstantNode = class(TXPathExprNode)
  90. private
  91. FValue: TXPathVariable;
  92. public
  93. constructor Create(AValue: TXPathVariable);
  94. destructor Destroy; override;
  95. function Evaluate(AContext: TXPathContext;
  96. AEnvironment: TXPathEnvironment): TXPathVariable; override;
  97. end;
  98. TXPathVariableNode = class(TXPathExprNode)
  99. private
  100. FName: DOMString;
  101. public
  102. constructor Create(const AName: DOMString);
  103. function Evaluate(AContext: TXPathContext;
  104. AEnvironment: TXPathEnvironment): TXPathVariable; override;
  105. end;
  106. TXPathFunctionNode = class(TXPathExprNode)
  107. private
  108. FName: DOMString;
  109. FArgs: TFPList;
  110. public
  111. constructor Create(const AName: DOMString);
  112. destructor Destroy; override;
  113. function Evaluate(AContext: TXPathContext;
  114. AEnvironment: TXPathEnvironment): TXPathVariable; override;
  115. end;
  116. TXPathNegationNode = class(TXPathExprNode)
  117. private
  118. FOperand: TXPathExprNode;
  119. public
  120. constructor Create(AOperand: TXPathExprNode);
  121. destructor Destroy; override;
  122. function Evaluate(AContext: TXPathContext;
  123. AEnvironment: TXPathEnvironment): TXPathVariable; override;
  124. end;
  125. // common ancestor for binary operations
  126. TXPathBinaryNode = class(TXPathExprNode)
  127. protected
  128. FOperand1, FOperand2: TXPathExprNode;
  129. public
  130. destructor Destroy; override;
  131. end;
  132. // Node for (binary) mathematical operation
  133. TXPathMathOp = (opAdd, opSubtract, opMultiply, opDivide, opMod);
  134. TXPathMathOpNode = class(TXPathBinaryNode)
  135. private
  136. FOperator: TXPathMathOp;
  137. public
  138. constructor Create(AOperator: TXPathMathOp;
  139. AOperand1, AOperand2: TXPathExprNode);
  140. function Evaluate(AContext: TXPathContext;
  141. AEnvironment: TXPathEnvironment): TXPathVariable; override;
  142. end;
  143. // Node for comparison operations
  144. TXPathCompareOp = (opEqual, opNotEqual, opLess, opLessEqual, opGreater,
  145. opGreaterEqual);
  146. TXPathCompareNode = class(TXPathBinaryNode)
  147. private
  148. FOperator: TXPathCompareOp;
  149. public
  150. constructor Create(AOperator: TXPathCompareOp;
  151. AOperand1, AOperand2: TXPathExprNode);
  152. function Evaluate(AContext: TXPathContext;
  153. AEnvironment: TXPathEnvironment): TXPathVariable; override;
  154. end;
  155. // Node for boolean operations (and, or)
  156. TXPathBooleanOp = (opOr, opAnd);
  157. TXPathBooleanOpNode = class(TXPathBinaryNode)
  158. private
  159. FOperator: TXPathBooleanOp;
  160. public
  161. constructor Create(AOperator: TXPathBooleanOp;
  162. AOperand1, AOperand2: TXPathExprNode);
  163. function Evaluate(AContext: TXPathContext;
  164. AEnvironment: TXPathEnvironment): TXPathVariable; override;
  165. end;
  166. // Node for unions (see [18])
  167. TXPathUnionNode = class(TXPathBinaryNode)
  168. public
  169. constructor Create(AOperand1, AOperand2: TXPathExprNode);
  170. function Evaluate(AContext: TXPathContext;
  171. AEnvironment: TXPathEnvironment): TXPathVariable; override;
  172. end;
  173. // Filter node (for [20])
  174. TXPathFilterNode = class(TXPathExprNode)
  175. private
  176. FExpr: TXPathExprNode;
  177. FPredicates: TXPathNodeArray;
  178. public
  179. constructor Create(AExpr: TXPathExprNode);
  180. destructor Destroy; override;
  181. function Evaluate(AContext: TXPathContext;
  182. AEnvironment: TXPathEnvironment): TXPathVariable; override;
  183. end;
  184. // Node for location paths
  185. TAxis = (axisInvalid, axisAncestor, axisAncestorOrSelf, axisAttribute,
  186. axisChild, axisDescendant, axisDescendantOrSelf, axisFollowing,
  187. axisFollowingSibling, axisNamespace, axisParent, axisPreceding,
  188. axisPrecedingSibling, axisSelf);
  189. TNodeTestType = (ntAnyPrincipal, ntName, ntTextNode,
  190. ntCommentNode, ntPINode, ntAnyNode);
  191. TNodeSet = TFPList;
  192. TStep = class
  193. private
  194. procedure SelectNodes(ANode: TDOMNode; out ResultNodes: TNodeSet);
  195. procedure ApplyPredicates(Nodes: TNodeSet; AEnvironment: TXPathEnvironment);
  196. public
  197. NextStep: TStep;
  198. Axis: TAxis;
  199. NodeTestType: TNodeTestType;
  200. NodeTestString: DOMString;
  201. Predicates: TXPathNodeArray;
  202. constructor Create(aAxis: TAxis; aTest: TNodeTestType);
  203. destructor Destroy; override;
  204. end;
  205. TXPathLocationPathNode = class(TXPathExprNode)
  206. private
  207. FLeft: TXPathExprNode;
  208. FFirstStep: TStep;
  209. FIsAbsolutePath: Boolean;
  210. public
  211. constructor Create(ALeft: TXPathExprNode; AIsAbsolutePath: Boolean);
  212. destructor destroy;override;
  213. function Evaluate(AContext: TXPathContext;
  214. AEnvironment: TXPathEnvironment): TXPathVariable; override;
  215. end;
  216. { Exceptions }
  217. EXPathEvaluationError = class(Exception);
  218. procedure EvaluationError(const Msg: String);
  219. procedure EvaluationError(const Msg: String; const Args: array of const);
  220. type
  221. { XPath variables and results classes }
  222. TXPathVariable = class
  223. protected
  224. FRefCount: Integer;
  225. procedure Error(const Msg: String; const Args: array of const);
  226. public
  227. class function TypeName: String; virtual; abstract;
  228. procedure Release;
  229. function AsNodeSet: TNodeSet; virtual;
  230. function AsBoolean: Boolean; virtual;
  231. function AsNumber: Extended; virtual;
  232. function AsText: DOMString; virtual;
  233. end;
  234. TXPathNodeSetVariable = class(TXPathVariable)
  235. private
  236. FValue: TNodeSet;
  237. public
  238. constructor Create(AValue: TNodeSet);
  239. destructor Destroy; override;
  240. class function TypeName: String; override;
  241. function AsNodeSet: TNodeSet; override;
  242. function AsText: DOMString; override;
  243. function AsBoolean: Boolean; override;
  244. function AsNumber: Extended; override;
  245. property Value: TNodeSet read FValue;
  246. end;
  247. TXPathBooleanVariable = class(TXPathVariable)
  248. private
  249. FValue: Boolean;
  250. public
  251. constructor Create(AValue: Boolean);
  252. class function TypeName: String; override;
  253. function AsBoolean: Boolean; override;
  254. function AsNumber: Extended; override;
  255. function AsText: DOMString; override;
  256. property Value: Boolean read FValue;
  257. end;
  258. TXPathNumberVariable = class(TXPathVariable)
  259. private
  260. FValue: Extended;
  261. public
  262. constructor Create(AValue: Extended);
  263. class function TypeName: String; override;
  264. function AsBoolean: Boolean; override;
  265. function AsNumber: Extended; override;
  266. function AsText: DOMString; override;
  267. property Value: Extended read FValue;
  268. end;
  269. TXPathStringVariable = class(TXPathVariable)
  270. private
  271. FValue: DOMString;
  272. public
  273. constructor Create(const AValue: DOMString);
  274. class function TypeName: String; override;
  275. function AsBoolean: Boolean; override;
  276. function AsNumber: Extended; override;
  277. function AsText: DOMString; override;
  278. property Value: DOMString read FValue;
  279. end;
  280. { XPath lexical scanner }
  281. TXPathScanner = class
  282. private
  283. FExpressionString, FCurData: DOMPChar;
  284. FCurToken: TXPathToken;
  285. FCurTokenString: DOMString;
  286. FTokenStart: DOMPChar;
  287. FTokenLength: Integer;
  288. procedure Error(const Msg: String);
  289. procedure ParsePredicates(var Dest: TXPathNodeArray);
  290. procedure ParseStep(Dest: TStep); // [4]
  291. function ParsePrimaryExpr: TXPathExprNode; // [15]
  292. function ParseUnionExpr: TXPathExprNode; // [18]
  293. function ParsePathExpr: TXPathExprNode; // [19]
  294. function ParseFilterExpr: TXPathExprNode; // [20]
  295. function ParseOrExpr: TXPathExprNode; // [21]
  296. function ParseAndExpr: TXPathExprNode; // [22]
  297. function ParseEqualityExpr: TXPathExprNode; // [23]
  298. function ParseRelationalExpr: TXPathExprNode; // [24]
  299. function ParseAdditiveExpr: TXPathExprNode; // [25]
  300. function ParseMultiplicativeExpr: TXPathExprNode; // [26]
  301. function ParseUnaryExpr: TXPathExprNode; // [27]
  302. function GetToken: TXPathToken;
  303. public
  304. constructor Create(const AExpressionString: DOMString);
  305. function NextToken: TXPathToken;
  306. function PeekToken: TXPathToken;
  307. function SkipToken(tok: TXPathToken): Boolean;
  308. property CurToken: TXPathToken read FCurToken;
  309. property CurTokenString: DOMString read FCurTokenString;
  310. end;
  311. { XPath context }
  312. TXPathContext = class
  313. public
  314. constructor Create(AContextNode: TDOMNode;
  315. AContextPosition, AContextSize: Integer);
  316. ContextNode: TDOMNode;
  317. ContextPosition: Integer;
  318. ContextSize: Integer;
  319. end;
  320. { XPath environments (not defined in XPath standard: an environment contains
  321. the variables and functions, which are part of the context in the official
  322. standard). }
  323. TXPathVarList = TFPList;
  324. TXPathFunction = function(Context: TXPathContext; Args: TXPathVarList):
  325. TXPathVariable of object;
  326. TXPathEnvironment = class
  327. private
  328. FFunctions: TFPList;
  329. FVariables: TFPList;
  330. function GetFunctionCount: Integer;
  331. function GetVariableCount: Integer;
  332. function GetFunction(Index: Integer): TXPathFunction;
  333. function GetFunction(const AName: String): TXPathFunction;
  334. function GetVariable(Index: Integer): TXPathVariable;
  335. function GetVariable(const AName: String): TXPathVariable;
  336. protected
  337. // XPath Core Function Library:
  338. function xpLast(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  339. function xpPosition(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  340. function xpCount(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  341. function xpId(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  342. function xpLocalName(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  343. function xpNamespaceURI(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  344. function xpName(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  345. function xpString(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  346. function xpConcat(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  347. function xpStartsWith(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  348. function xpContains(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  349. function xpSubstringBefore(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  350. function xpSubstringAfter(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  351. function xpSubstring(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  352. function xpStringLength(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  353. function xpNormalizeSpace(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  354. function xpTranslate(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  355. function xpBoolean(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  356. function xpNot(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  357. function xpTrue(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  358. function xpFalse(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  359. function xpLang(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  360. function xpNumber(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  361. function xpSum(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  362. function xpFloor(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  363. function xpCeiling(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  364. function xpRound(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  365. public
  366. constructor Create;
  367. destructor Destroy; override;
  368. function GetFunctionIndex(const AName: String): Integer;
  369. function GetVariableIndex(const AName: String): Integer;
  370. procedure AddFunction(const AName: String; AFunction: TXPathFunction);
  371. procedure AddVariable(const AName: String; AVariable: TXPathVariable);
  372. procedure RemoveFunction(Index: Integer);
  373. procedure RemoveFunction(const AName: String);
  374. procedure RemoveVariable(Index: Integer);
  375. procedure RemoveVariable(const AName: String);
  376. property FunctionCount: Integer read GetFunctionCount;
  377. property VariableCount: Integer read GetVariableCount;
  378. property Functions[Index: Integer]: TXPathFunction read GetFunction;
  379. property FunctionsByName[const AName: String]: TXPathFunction
  380. read GetFunction;
  381. property Variables[Index: Integer]: TXPathVariable read GetVariable;
  382. property VariablesByName[const AName: String]: TXPathVariable read GetVariable;
  383. end;
  384. { XPath expressions }
  385. TXPathExpression = class
  386. private
  387. FRootNode: TXPathExprNode;
  388. public
  389. { CompleteExpresion specifies wether the parser should check for gargabe
  390. after the recognised part. True => Throw exception if there is garbage }
  391. constructor Create(AScanner: TXPathScanner; CompleteExpression: Boolean);
  392. destructor destroy;override;
  393. function Evaluate(AContextNode: TDOMNode): TXPathVariable;
  394. function Evaluate(AContextNode: TDOMNode;
  395. AEnvironment: TXPathEnvironment): TXPathVariable;
  396. end;
  397. function EvaluateXPathExpression(const AExpressionString: DOMString;
  398. AContextNode: TDOMNode): TXPathVariable;
  399. // ===================================================================
  400. // ===================================================================
  401. implementation
  402. uses Math, xmlutils;
  403. { Helper functions }
  404. function NodeToText(Node: TDOMNode): DOMString;
  405. var
  406. Child: TDOMNode;
  407. begin
  408. case Node.NodeType of
  409. DOCUMENT_NODE, DOCUMENT_FRAGMENT_NODE{, ELEMENT_NODE}:
  410. begin
  411. SetLength(Result, 0);
  412. Child := Node.FirstChild;
  413. while Assigned(Child) do
  414. begin
  415. if Result <> '' then
  416. Result := Result + LineEnding;
  417. Result := Result + NodeToText(Child);
  418. Child := Child.NextSibling;
  419. end;
  420. end;
  421. ELEMENT_NODE:
  422. Result := Node.TextContent;
  423. ATTRIBUTE_NODE, PROCESSING_INSTRUCTION_NODE, COMMENT_NODE, TEXT_NODE,
  424. CDATA_SECTION_NODE, ENTITY_REFERENCE_NODE:
  425. Result := Node.NodeValue;
  426. end;
  427. // !!!: What to do with 'namespace nodes'?
  428. end;
  429. function StrToNumber(const s: DOMString): Extended;
  430. var
  431. Code: Integer;
  432. begin
  433. Val(s, Result, Code);
  434. if Code <> 0 then
  435. Result := NaN;
  436. end;
  437. procedure TranslateWideString(var S: DOMString; const SrcPat, DstPat: DOMString);
  438. var
  439. I, J, L: Integer;
  440. P, Start: DOMPChar;
  441. begin
  442. UniqueString(S);
  443. L := Length(DstPat);
  444. P := DOMPChar(S);
  445. if Length(SrcPat) > L then // may remove some chars
  446. begin
  447. Start := P;
  448. for I := 1 to Length(S) do
  449. begin
  450. J := Pos(S[I], SrcPat);
  451. if J > 0 then
  452. begin
  453. if J <= L then
  454. begin
  455. P^ := DstPat[J];
  456. Inc(P);
  457. end;
  458. end
  459. else
  460. begin
  461. P^ := S[I];
  462. Inc(P);
  463. end;
  464. end;
  465. SetLength(S, P-Start);
  466. end
  467. else // no char removal possible
  468. for I := 1 to Length(S) do
  469. begin
  470. J := Pos(S[I], SrcPat);
  471. if J > 0 then
  472. P^ := DstPat[J]
  473. else
  474. P^ := S[I];
  475. Inc(P);
  476. end;
  477. end;
  478. function GetNodeLanguage(aNode: TDOMNode): DOMString;
  479. var
  480. Attr: TDomAttr;
  481. begin
  482. Result := '';
  483. if aNode = nil then
  484. Exit;
  485. case aNode.NodeType of
  486. ELEMENT_NODE: begin
  487. Attr := TDomElement(aNode).GetAttributeNode('xml:lang');
  488. if Assigned(Attr) then
  489. Result := Attr.Value
  490. else
  491. Result := GetNodeLanguage(aNode.ParentNode);
  492. end;
  493. TEXT_NODE, CDATA_SECTION_NODE, ENTITY_REFERENCE_NODE,
  494. PROCESSING_INSTRUCTION_NODE, COMMENT_NODE:
  495. Result := GetNodeLanguage(aNode.ParentNode);
  496. ATTRIBUTE_NODE:
  497. Result := GetNodeLanguage(TDOMAttr(aNode).OwnerElement);
  498. end;
  499. end;
  500. procedure AddNodes(var Dst: TXPathNodeArray; const Src: array of TXPathExprNode);
  501. var
  502. L: Integer;
  503. begin
  504. L := Length(Dst);
  505. SetLength(Dst, L + High(Src)+1);
  506. Move(Src[0], Dst[L], (High(Src)+1)*sizeof(TObject));
  507. end;
  508. { XPath parse tree classes }
  509. function TXPathExprNode.EvalPredicate(AContext: TXPathContext;
  510. AEnvironment: TXPathEnvironment): Boolean;
  511. var
  512. resvar: TXPathVariable;
  513. begin
  514. resvar := Evaluate(AContext, AEnvironment);
  515. try
  516. if resvar.InheritsFrom(TXPathNumberVariable) then
  517. Result := resvar.AsNumber = AContext.ContextPosition // TODO: trunc/round?
  518. else
  519. Result := resvar.AsBoolean;
  520. finally
  521. resvar.Release;
  522. end;
  523. end;
  524. constructor TXPathConstantNode.Create(AValue: TXPathVariable);
  525. begin
  526. inherited Create;
  527. FValue := AValue;
  528. end;
  529. destructor TXPathConstantNode.Destroy;
  530. begin
  531. FValue.Release;
  532. inherited Destroy;
  533. end;
  534. function TXPathConstantNode.Evaluate(AContext: TXPathContext;
  535. AEnvironment: TXPathEnvironment): TXPathVariable;
  536. begin
  537. Result := FValue;
  538. Inc(Result.FRefCount);
  539. end;
  540. constructor TXPathVariableNode.Create(const AName: DOMString);
  541. begin
  542. inherited Create;
  543. FName := AName;
  544. end;
  545. function TXPathVariableNode.Evaluate(AContext: TXPathContext;
  546. AEnvironment: TXPathEnvironment): TXPathVariable;
  547. begin
  548. Result := AEnvironment.VariablesByName[FName];
  549. if not Assigned(Result) then
  550. EvaluationError(SEvalUnknownVariable, [FName]);
  551. end;
  552. constructor TXPathFunctionNode.Create(const AName: DOMString);
  553. begin
  554. inherited Create;
  555. FName := AName;
  556. FArgs := TFPList.Create;
  557. end;
  558. destructor TXPathFunctionNode.Destroy;
  559. var
  560. i: Integer;
  561. begin
  562. for i := 0 to FArgs.Count - 1 do
  563. TXPathExprNode(FArgs[i]).Free;
  564. FArgs.Free;
  565. inherited Destroy;
  566. end;
  567. function TXPathFunctionNode.Evaluate(AContext: TXPathContext;
  568. AEnvironment: TXPathEnvironment): TXPathVariable;
  569. var
  570. Fn: TXPathFunction;
  571. Args: TXPathVarList;
  572. i: Integer;
  573. begin
  574. Fn := AEnvironment.FunctionsByName[FName];
  575. if not Assigned(Fn) then
  576. EvaluationError(SEvalUnknownFunction, [FName]);
  577. Args := TXPathVarList.Create;
  578. try
  579. for i := 0 to FArgs.Count - 1 do
  580. Args.Add(TXPathExprNode(FArgs[i]).Evaluate(AContext, AEnvironment));
  581. Result := Fn(AContext, Args);
  582. for i := 0 to FArgs.Count - 1 do
  583. TXPathVariable(Args[i]).Release;
  584. finally
  585. Args.Free;
  586. end;
  587. end;
  588. constructor TXPathNegationNode.Create(AOperand: TXPathExprNode);
  589. begin
  590. inherited Create;
  591. FOperand := AOperand;
  592. end;
  593. destructor TXPathNegationNode.Destroy;
  594. begin
  595. FOperand.Free;
  596. inherited Destroy;
  597. end;
  598. function TXPathNegationNode.Evaluate(AContext: TXPathContext;
  599. AEnvironment: TXPathEnvironment): TXPathVariable;
  600. var
  601. OpResult: TXPathVariable;
  602. begin
  603. OpResult := FOperand.Evaluate(AContext, AEnvironment);
  604. try
  605. Result := TXPathNumberVariable.Create(-OpResult.AsNumber);
  606. finally
  607. OpResult.Release;
  608. end;
  609. end;
  610. destructor TXPathBinaryNode.Destroy;
  611. begin
  612. FOperand1.Free;
  613. FOperand2.Free;
  614. inherited Destroy;
  615. end;
  616. constructor TXPathMathOpNode.Create(AOperator: TXPathMathOp;
  617. AOperand1, AOperand2: TXPathExprNode);
  618. begin
  619. inherited Create;
  620. FOperator := AOperator;
  621. FOperand1 := AOperand1;
  622. FOperand2 := AOperand2;
  623. end;
  624. function TXPathMathOpNode.Evaluate(AContext: TXPathContext;
  625. AEnvironment: TXPathEnvironment): TXPathVariable;
  626. var
  627. Op1Result, Op2Result: TXPathVariable;
  628. Op1, Op2, NumberResult: Extended;
  629. begin
  630. Op1Result := FOperand1.Evaluate(AContext, AEnvironment);
  631. try
  632. Op2Result := FOperand2.Evaluate(AContext, AEnvironment);
  633. try
  634. Op1 := Op1Result.AsNumber;
  635. Op2 := Op2Result.AsNumber;
  636. case FOperator of
  637. opAdd:
  638. NumberResult := Op1 + Op2;
  639. opSubtract:
  640. NumberResult := Op1 - Op2;
  641. opMultiply:
  642. NumberResult := Op1 * Op2;
  643. opDivide:
  644. NumberResult := Op1 / Op2;
  645. opMod: if IsNan(Op1) or IsNan(Op2) then
  646. NumberResult := NaN
  647. else
  648. NumberResult := Trunc(Op1) mod Trunc(Op2);
  649. end;
  650. finally
  651. Op2Result.Release;
  652. end;
  653. finally
  654. Op1Result.Release;
  655. end;
  656. Result := TXPathNumberVariable.Create(NumberResult);
  657. end;
  658. const
  659. reverse: array[TXPathCompareOp] of TXPathCompareOp = (
  660. opEqual, opNotEqual,
  661. opGreaterEqual, //opLess
  662. opGreater, //opLessEqual
  663. opLessEqual, //opGreater
  664. opLess //opGreaterEqual
  665. );
  666. function CmpNumbers(const n1, n2: Extended; op: TXPathCompareOp): Boolean;
  667. begin
  668. result := (op = opNotEqual);
  669. if IsNan(n1) or IsNan(n2) then
  670. Exit; // NaNs are not equal
  671. case op of
  672. // TODO: should CompareValue() be used here?
  673. opLess: result := n1 < n2;
  674. opLessEqual: result := n1 <= n2;
  675. opGreater: result := n1 > n2;
  676. opGreaterEqual: result := n1 >= n2;
  677. else
  678. if IsInfinite(n1) or IsInfinite(n2) then
  679. result := n1 = n2
  680. else
  681. result := SameValue(n1, n2);
  682. result := result xor (op = opNotEqual);
  683. end;
  684. end;
  685. function CmpStrings(const s1, s2: DOMString; op: TXPathCompareOp): Boolean;
  686. begin
  687. case op of
  688. opEqual: result := s1 = s2;
  689. opNotEqual: result := s1 <> s2;
  690. else
  691. result := CmpNumbers(StrToNumber(s1), StrToNumber(s2), op);
  692. end;
  693. end;
  694. function CmpNodesetWithString(ns: TNodeSet; const s: DOMString; op: TXPathCompareOp): Boolean;
  695. var
  696. i: Integer;
  697. begin
  698. Result := True;
  699. for i := 0 to ns.Count - 1 do
  700. begin
  701. if CmpStrings(NodeToText(TDOMNode(ns[i])), s, op) then
  702. exit;
  703. end;
  704. Result := False;
  705. end;
  706. function CmpNodesetWithNumber(ns: TNodeSet; const n: Extended; op: TXPathCompareOp): Boolean;
  707. var
  708. i: Integer;
  709. begin
  710. Result := True;
  711. for i := 0 to ns.Count - 1 do
  712. begin
  713. if CmpNumbers(StrToNumber(NodeToText(TDOMNode(ns[i]))), n, op) then
  714. exit;
  715. end;
  716. Result := False;
  717. end;
  718. function CmpNodesetWithBoolean(ns: TNodeSet; b: Boolean; op: TXPathCompareOp): Boolean;
  719. begin
  720. // TODO: handles only equality
  721. result := ((ns.Count <> 0) = b) xor (op = opNotEqual);
  722. end;
  723. function CmpNodesets(ns1, ns2: TNodeSet; op: TXPathCompareOp): Boolean;
  724. var
  725. i, j: Integer;
  726. s: DOMString;
  727. begin
  728. Result := True;
  729. for i := 0 to ns1.Count - 1 do
  730. begin
  731. s := NodeToText(TDOMNode(ns1[i]));
  732. for j := 0 to ns2.Count - 1 do
  733. if CmpStrings(s, NodeToText(TDOMNode(ns2[j])), op) then
  734. exit;
  735. end;
  736. Result := False;
  737. end;
  738. constructor TXPathCompareNode.Create(AOperator: TXPathCompareOp;
  739. AOperand1, AOperand2: TXPathExprNode);
  740. begin
  741. inherited Create;
  742. FOperator := AOperator;
  743. FOperand1 := AOperand1;
  744. FOperand2 := AOperand2;
  745. end;
  746. function TXPathCompareNode.Evaluate(AContext: TXPathContext;
  747. AEnvironment: TXPathEnvironment): TXPathVariable;
  748. var
  749. Op1, Op2: TXPathVariable;
  750. BoolResult: Boolean;
  751. nsnum: Integer;
  752. begin
  753. Op1 := FOperand1.Evaluate(AContext, AEnvironment);
  754. try
  755. Op2 := FOperand2.Evaluate(AContext, AEnvironment);
  756. try
  757. nsnum := ord(Op1 is TXPathNodeSetVariable) or
  758. (ord(Op2 is TXPathNodeSetVariable) shl 1);
  759. case nsnum of
  760. 0: begin // neither op is a nodeset
  761. if (FOperator in [opEqual, opNotEqual]) then
  762. begin
  763. if (Op1 is TXPathBooleanVariable) or (Op2 is TXPathBooleanVariable) then
  764. BoolResult := (Op1.AsBoolean = Op2.AsBoolean) xor (FOperator = opNotEqual)
  765. else if (Op1 is TXPathNumberVariable) or (Op2 is TXPathNumberVariable) then
  766. BoolResult := CmpNumbers(Op1.AsNumber, Op2.AsNumber, FOperator)
  767. else
  768. BoolResult := (Op1.AsText = Op2.AsText) xor (FOperator = opNotEqual);
  769. end
  770. else
  771. BoolResult := CmpNumbers(Op1.AsNumber, Op2.AsNumber, FOperator);
  772. end;
  773. 1: // Op1 is nodeset
  774. if Op2 is TXPathNumberVariable then
  775. BoolResult := CmpNodesetWithNumber(Op1.AsNodeSet, Op2.AsNumber, FOperator)
  776. else if Op2 is TXPathStringVariable then
  777. BoolResult := CmpNodesetWithString(Op1.AsNodeSet, Op2.AsText, FOperator)
  778. else
  779. BoolResult := CmpNodesetWithBoolean(Op1.AsNodeSet, Op2.AsBoolean, FOperator);
  780. 2: // Op2 is nodeset
  781. if Op1 is TXPathNumberVariable then
  782. BoolResult := CmpNodesetWithNumber(Op2.AsNodeSet, Op1.AsNumber, reverse[FOperator])
  783. else if Op1 is TXPathStringVariable then
  784. BoolResult := CmpNodesetWithString(Op2.AsNodeSet, Op1.AsText, reverse[FOperator])
  785. else
  786. BoolResult := CmpNodesetWithBoolean(Op2.AsNodeSet, Op1.AsBoolean, reverse[FOperator]);
  787. else // both ops are nodesets
  788. BoolResult := CmpNodesets(Op1.AsNodeSet, Op2.AsNodeSet, FOperator);
  789. end;
  790. finally
  791. Op2.Release;
  792. end;
  793. finally
  794. Op1.Release;
  795. end;
  796. Result := TXPathBooleanVariable.Create(BoolResult);
  797. end;
  798. constructor TXPathBooleanOpNode.Create(AOperator: TXPathBooleanOp;
  799. AOperand1, AOperand2: TXPathExprNode);
  800. begin
  801. inherited Create;
  802. FOperator := AOperator;
  803. FOperand1 := AOperand1;
  804. FOperand2 := AOperand2;
  805. end;
  806. function TXPathBooleanOpNode.Evaluate(AContext: TXPathContext;
  807. AEnvironment: TXPathEnvironment): TXPathVariable;
  808. var
  809. res: Boolean;
  810. Op1, Op2: TXPathVariable;
  811. begin
  812. { don't evaluate second arg if result is determined by first one }
  813. Op1 := FOperand1.Evaluate(AContext, AEnvironment);
  814. try
  815. res := Op1.AsBoolean;
  816. finally
  817. Op1.Release;
  818. end;
  819. if not (((FOperator = opAnd) and (not res)) or ((FOperator = opOr) and res)) then
  820. begin
  821. Op2 := FOperand2.Evaluate(AContext, AEnvironment);
  822. try
  823. case FOperator of
  824. opAnd: res := res and Op2.AsBoolean;
  825. opOr: res := res or Op2.AsBoolean;
  826. end;
  827. finally
  828. Op2.Release;
  829. end;
  830. end;
  831. Result := TXPathBooleanVariable.Create(res);
  832. end;
  833. constructor TXPathUnionNode.Create(AOperand1, AOperand2: TXPathExprNode);
  834. begin
  835. inherited Create;
  836. FOperand1 := AOperand1;
  837. FOperand2 := AOperand2;
  838. end;
  839. function TXPathUnionNode.Evaluate(AContext: TXPathContext;
  840. AEnvironment: TXPathEnvironment): TXPathVariable;
  841. var
  842. Op1Result, Op2Result: TXPathVariable;
  843. NodeSet, NodeSet2: TNodeSet;
  844. CurNode: Pointer;
  845. i: Integer;
  846. begin
  847. { TODO: result must be sorted by document order, i.e. 'a|b' yields the
  848. same nodeset as 'b|a' }
  849. Op1Result := FOperand1.Evaluate(AContext, AEnvironment);
  850. try
  851. Op2Result := FOperand2.Evaluate(AContext, AEnvironment);
  852. try
  853. NodeSet := Op1Result.AsNodeSet;
  854. NodeSet2 := Op2Result.AsNodeSet;
  855. for i := 0 to NodeSet2.Count - 1 do
  856. begin
  857. CurNode := NodeSet2[i];
  858. if NodeSet.IndexOf(CurNode) < 0 then
  859. NodeSet.Add(CurNode);
  860. end;
  861. finally
  862. Op2Result.Release;
  863. end;
  864. finally
  865. Result := Op1Result;
  866. end;
  867. end;
  868. constructor TXPathFilterNode.Create(AExpr: TXPathExprNode);
  869. begin
  870. inherited Create;
  871. FExpr := AExpr;
  872. end;
  873. destructor TXPathFilterNode.Destroy;
  874. var
  875. i: Integer;
  876. begin
  877. for i := 0 to High(FPredicates) do
  878. FPredicates[i].Free;
  879. inherited Destroy;
  880. end;
  881. function TXPathFilterNode.Evaluate(AContext: TXPathContext;
  882. AEnvironment: TXPathEnvironment): TXPathVariable;
  883. var
  884. ExprResult: TXPathVariable;
  885. NodeSet, NewNodeSet: TNodeSet;
  886. i, j: Integer;
  887. CurContextNode: TDOMNode;
  888. NewContext: TXPathContext;
  889. DoAdd: Boolean;
  890. begin
  891. ExprResult := FExpr.Evaluate(AContext, AEnvironment);
  892. NewContext := nil;
  893. try
  894. NodeSet := ExprResult.AsNodeSet;
  895. NewContext := TXPathContext.Create(nil, 0, NodeSet.Count);
  896. NewNodeSet := TNodeSet.Create;
  897. try
  898. for i := 0 to NodeSet.Count - 1 do
  899. begin
  900. CurContextNode := TDOMNode(NodeSet[i]);
  901. NewContext.ContextNode := CurContextNode;
  902. Inc(NewContext.ContextPosition);
  903. DoAdd := True;
  904. for j := 0 to High(FPredicates) do
  905. begin
  906. DoAdd := FPredicates[j].EvalPredicate(NewContext,
  907. AEnvironment);
  908. if not DoAdd then
  909. Break;
  910. end;
  911. if DoAdd then
  912. NewNodeSet.Add(CurContextNode);
  913. end;
  914. except
  915. NewNodeSet.Free;
  916. raise;
  917. end;
  918. Result := TXPathNodeSetVariable.Create(NewNodeSet);
  919. finally
  920. NewContext.Free;
  921. ExprResult.Release;
  922. end;
  923. end;
  924. constructor TStep.Create(aAxis: TAxis; aTest: TNodeTestType);
  925. begin
  926. inherited Create;
  927. Axis := aAxis;
  928. NodeTestType := aTest;
  929. end;
  930. destructor TStep.Destroy;
  931. var
  932. i: Integer;
  933. begin
  934. for i := 0 to High(Predicates) do
  935. Predicates[i].Free;
  936. inherited destroy;
  937. end;
  938. procedure TStep.SelectNodes(ANode: TDOMNode; out ResultNodes: TNodeSet);
  939. var
  940. Node, Node2: TDOMNode;
  941. Attr: TDOMNamedNodeMap;
  942. i: Integer;
  943. TempList: TFPList;
  944. procedure DoNodeTest(Node: TDOMNode);
  945. begin
  946. case NodeTestType of
  947. ntAnyPrincipal:
  948. // !!!: Probably this isn't ready for namespace support yet
  949. if (Axis <> axisAttribute) and
  950. (Node.NodeType <> ELEMENT_NODE) then
  951. exit;
  952. ntName:
  953. if Node.NodeName <> NodeTestString then
  954. exit;
  955. ntTextNode:
  956. if not Node.InheritsFrom(TDOMCharacterData) then
  957. exit;
  958. ntCommentNode:
  959. if Node.NodeType <> COMMENT_NODE then
  960. exit;
  961. ntPINode:
  962. if Node.NodeType <> PROCESSING_INSTRUCTION_NODE then
  963. exit;
  964. end;
  965. if ResultNodes.IndexOf(Node) < 0 then
  966. ResultNodes.Add(Node);
  967. end;
  968. procedure AddDescendants(CurNode: TDOMNode);
  969. var
  970. Child: TDOMNode;
  971. begin
  972. Child := CurNode.FirstChild;
  973. while Assigned(Child) do
  974. begin
  975. DoNodeTest(Child);
  976. AddDescendants(Child);
  977. Child := Child.NextSibling;
  978. end;
  979. end;
  980. begin
  981. ResultNodes := TNodeSet.Create;
  982. case Axis of
  983. axisAncestor:
  984. begin
  985. Node := ANode.ParentNode;
  986. while Assigned(Node) do
  987. begin
  988. DoNodeTest(Node);
  989. Node := Node.ParentNode;
  990. end;
  991. end;
  992. axisAncestorOrSelf:
  993. begin
  994. Node := ANode;
  995. repeat
  996. DoNodeTest(Node);
  997. Node := Node.ParentNode;
  998. until not Assigned(Node);
  999. end;
  1000. axisAttribute:
  1001. begin
  1002. Attr := ANode.Attributes;
  1003. if Assigned(Attr) then
  1004. for i := 0 to Attr.Length - 1 do
  1005. DoNodeTest(Attr[i]);
  1006. end;
  1007. axisChild:
  1008. begin
  1009. Node := ANode.FirstChild;
  1010. while Assigned(Node) do
  1011. begin
  1012. DoNodeTest(Node);
  1013. Node := Node.NextSibling;
  1014. end;
  1015. end;
  1016. axisDescendant:
  1017. AddDescendants(ANode);
  1018. axisDescendantOrSelf:
  1019. begin
  1020. DoNodeTest(ANode);
  1021. AddDescendants(ANode);
  1022. end;
  1023. axisFollowing:
  1024. begin
  1025. Node := ANode;
  1026. repeat
  1027. Node2 := Node.NextSibling;
  1028. while Assigned(Node2) do
  1029. begin
  1030. DoNodeTest(Node2);
  1031. AddDescendants(Node2);
  1032. Node2 := Node2.NextSibling;
  1033. end;
  1034. Node := Node.ParentNode;
  1035. until not Assigned(Node);
  1036. end;
  1037. axisFollowingSibling:
  1038. begin
  1039. Node := ANode.NextSibling;
  1040. while Assigned(Node) do
  1041. begin
  1042. DoNodeTest(Node);
  1043. Node := Node.NextSibling;
  1044. end;
  1045. end;
  1046. {axisNamespace: !!!: Not supported yet}
  1047. axisParent:
  1048. if Assigned(ANode.ParentNode) then
  1049. DoNodeTest(ANode.ParentNode);
  1050. axisPreceding:
  1051. begin
  1052. TempList := TFPList.Create;
  1053. try
  1054. Node := ANode;
  1055. // build list of ancestors
  1056. while Assigned(Node) do
  1057. begin
  1058. TempList.Add(Node);
  1059. Node := Node.ParentNode;
  1060. end;
  1061. // then process it in reverse order
  1062. for i := TempList.Count-1 downto 1 do
  1063. begin
  1064. Node := TDOMNode(TempList[i]);
  1065. Node2 := Node.FirstChild;
  1066. while Assigned(Node2) and (Node2 <> TDOMNode(TempList[i-1])) do
  1067. begin
  1068. DoNodeTest(Node2);
  1069. AddDescendants(Node2);
  1070. Node2 := Node2.NextSibling;
  1071. end;
  1072. end;
  1073. finally
  1074. TempList.Free;
  1075. end;
  1076. end;
  1077. axisPrecedingSibling:
  1078. begin
  1079. if Assigned(ANode.ParentNode) then
  1080. begin
  1081. Node := ANode.ParentNode.FirstChild;
  1082. while Assigned(Node) and (Node <> ANode) do
  1083. begin
  1084. DoNodeTest(Node);
  1085. Node := Node.NextSibling;
  1086. end;
  1087. end;
  1088. end;
  1089. axisSelf:
  1090. DoNodeTest(ANode);
  1091. end;
  1092. end;
  1093. { Filter the nodes of this step using the predicates: The current
  1094. node set is filtered, nodes not passing the filter are replaced
  1095. by nil. After one filter has been applied, Nodes is packed, and
  1096. the next filter will be processed. }
  1097. procedure TStep.ApplyPredicates(Nodes: TNodeSet; AEnvironment: TXPathEnvironment);
  1098. var
  1099. i, j: Integer;
  1100. NewContext: TXPathContext;
  1101. begin
  1102. for i := 0 to High(Predicates) do
  1103. begin
  1104. NewContext := TXPathContext.Create(nil, 0, Nodes.Count);
  1105. try
  1106. for j := 0 to Nodes.Count - 1 do
  1107. begin
  1108. // ContextPosition must honor the axis direction
  1109. if Axis in [axisAncestor, axisAncestorOrSelf,
  1110. axisPreceding, axisPrecedingSibling] then
  1111. NewContext.ContextPosition := Nodes.Count - j
  1112. else
  1113. NewContext.ContextPosition := j+1;
  1114. NewContext.ContextNode := TDOMNode(Nodes[j]);
  1115. if not Predicates[i].EvalPredicate(NewContext, AEnvironment) then
  1116. Nodes[j] := nil;
  1117. end;
  1118. Nodes.Pack;
  1119. finally
  1120. NewContext.Free;
  1121. end;
  1122. end;
  1123. end;
  1124. constructor TXPathLocationPathNode.Create(ALeft: TXPathExprNode; AIsAbsolutePath: Boolean);
  1125. begin
  1126. inherited Create;
  1127. FLeft := ALeft;
  1128. FIsAbsolutePath := AIsAbsolutePath;
  1129. end;
  1130. function TXPathLocationPathNode.Evaluate(AContext: TXPathContext;
  1131. AEnvironment: TXPathEnvironment): TXPathVariable;
  1132. var
  1133. ResultNodeSet: TNodeSet;
  1134. LeftResult: TXPathVariable;
  1135. i: Integer;
  1136. Node: TDOMNode;
  1137. procedure EvaluateStep(AStep: TStep; AContextNode: TDOMNode);
  1138. var
  1139. StepNodes: TFPList;
  1140. Node: TDOMNode;
  1141. i: Integer;
  1142. begin
  1143. AStep.SelectNodes(AContextNode, StepNodes);
  1144. try
  1145. AStep.ApplyPredicates(StepNodes, AEnvironment);
  1146. if Assigned(AStep.NextStep) then
  1147. begin
  1148. for i := 0 to StepNodes.Count - 1 do
  1149. EvaluateStep(AStep.NextStep, TDOMNode(StepNodes[i]));
  1150. end else
  1151. begin
  1152. // Only add nodes to result if it isn't duplicate
  1153. for i := 0 to StepNodes.Count - 1 do
  1154. begin
  1155. Node := TDOMNode(StepNodes[i]);
  1156. if ResultNodeSet.IndexOf(Node) < 0 then
  1157. ResultNodeSet.Add(Node);
  1158. end;
  1159. end;
  1160. finally
  1161. StepNodes.Free;
  1162. end;
  1163. end;
  1164. begin
  1165. ResultNodeSet := TNodeSet.Create;
  1166. try
  1167. if Assigned(FLeft) then
  1168. begin
  1169. LeftResult := FLeft.Evaluate(AContext, AEnvironment);
  1170. try
  1171. with LeftResult.AsNodeSet do
  1172. for i := 0 to Count-1 do
  1173. EvaluateStep(FFirstStep, TDOMNode(Items[i]));
  1174. finally
  1175. LeftResult.Release;
  1176. end;
  1177. end
  1178. else
  1179. begin
  1180. if FIsAbsolutePath and (AContext.ContextNode.NodeType <> DOCUMENT_NODE) then
  1181. Node := AContext.ContextNode.OwnerDocument
  1182. else
  1183. Node := AContext.ContextNode;
  1184. if Assigned(FFirstStep) then
  1185. EvaluateStep(FFirstStep, Node)
  1186. else
  1187. ResultNodeSet.Add(Node); // Assert(FIsAbsolutePath)
  1188. end;
  1189. except
  1190. ResultNodeSet.Free;
  1191. raise;
  1192. end;
  1193. Result := TXPathNodeSetVariable.Create(ResultNodeSet);
  1194. end;
  1195. destructor TXPathLocationPathNode.destroy;
  1196. var
  1197. tmp:TStep;
  1198. begin
  1199. FLeft.Free;
  1200. while FFirstStep<>nil do
  1201. begin
  1202. tmp:=FFirstStep.NextStep;
  1203. FFirstStep.free;
  1204. FFirstStep:=tmp;
  1205. end;
  1206. end;
  1207. { Exceptions }
  1208. procedure EvaluationError(const Msg: String);
  1209. begin
  1210. raise EXPathEvaluationError.Create(Msg) at get_caller_addr(get_frame);
  1211. end;
  1212. procedure EvaluationError(const Msg: String; const Args: array of const);
  1213. begin
  1214. raise EXPathEvaluationError.CreateFmt(Msg, Args)
  1215. at get_caller_addr(get_frame);
  1216. end;
  1217. { TXPathVariable and derived classes}
  1218. procedure TXPathVariable.Release;
  1219. begin
  1220. if FRefCount <= 0 then
  1221. Free
  1222. else
  1223. Dec(FRefCount);
  1224. end;
  1225. function TXPathVariable.AsNodeSet: TNodeSet;
  1226. begin
  1227. Error(SVarNoConversion, [TypeName, TXPathNodeSetVariable.TypeName]);
  1228. Result := nil;
  1229. end;
  1230. function TXPathVariable.AsBoolean: Boolean;
  1231. begin
  1232. Error(SVarNoConversion, [TypeName, TXPathBooleanVariable.TypeName]);
  1233. Result := False;
  1234. end;
  1235. function TXPathVariable.AsNumber: Extended;
  1236. begin
  1237. Error(SVarNoConversion, [TypeName, TXPathNumberVariable.TypeName]);
  1238. Result := 0;
  1239. end;
  1240. function TXPathVariable.AsText: DOMString;
  1241. begin
  1242. Error(SVarNoConversion, [TypeName, TXPathStringVariable.TypeName]);
  1243. SetLength(Result, 0);
  1244. end;
  1245. procedure TXPathVariable.Error(const Msg: String; const Args: array of const);
  1246. begin
  1247. raise Exception.CreateFmt(Msg, Args) at get_caller_addr(get_frame);
  1248. end;
  1249. constructor TXPathNodeSetVariable.Create(AValue: TNodeSet);
  1250. begin
  1251. inherited Create;
  1252. FValue := AValue;
  1253. end;
  1254. destructor TXPathNodeSetVariable.Destroy;
  1255. begin
  1256. FValue.Free;
  1257. inherited Destroy;
  1258. end;
  1259. class function TXPathNodeSetVariable.TypeName: String;
  1260. begin
  1261. Result := SNodeSet;
  1262. end;
  1263. function TXPathNodeSetVariable.AsNodeSet: TNodeSet;
  1264. begin
  1265. Result := FValue;
  1266. end;
  1267. function TXPathNodeSetVariable.AsText: DOMString;
  1268. begin
  1269. if FValue.Count = 0 then
  1270. Result := ''
  1271. else
  1272. Result := NodeToText(TDOMNode(FValue.First));
  1273. end;
  1274. function TXPathNodeSetVariable.AsBoolean: Boolean;
  1275. begin
  1276. Result := FValue.Count <> 0;
  1277. end;
  1278. function TXPathNodeSetVariable.AsNumber: Extended;
  1279. begin
  1280. Result := StrToNumber(AsText);
  1281. end;
  1282. constructor TXPathBooleanVariable.Create(AValue: Boolean);
  1283. begin
  1284. inherited Create;
  1285. FValue := AValue;
  1286. end;
  1287. class function TXPathBooleanVariable.TypeName: String;
  1288. begin
  1289. Result := SBoolean;
  1290. end;
  1291. function TXPathBooleanVariable.AsBoolean: Boolean;
  1292. begin
  1293. Result := FValue;
  1294. end;
  1295. function TXPathBooleanVariable.AsNumber: Extended;
  1296. begin
  1297. if FValue then
  1298. Result := 1
  1299. else
  1300. Result := 0;
  1301. end;
  1302. function TXPathBooleanVariable.AsText: DOMString;
  1303. begin
  1304. if FValue then
  1305. Result := 'true' // Do not localize!
  1306. else
  1307. Result := 'false'; // Do not localize!
  1308. end;
  1309. constructor TXPathNumberVariable.Create(AValue: Extended);
  1310. begin
  1311. inherited Create;
  1312. FValue := AValue;
  1313. end;
  1314. class function TXPathNumberVariable.TypeName: String;
  1315. begin
  1316. Result := SNumber;
  1317. end;
  1318. function TXPathNumberVariable.AsBoolean: Boolean;
  1319. begin
  1320. Result := not (IsNan(FValue) or IsZero(FValue));
  1321. end;
  1322. function TXPathNumberVariable.AsNumber: Extended;
  1323. begin
  1324. Result := FValue;
  1325. end;
  1326. function TXPathNumberVariable.AsText: DOMString;
  1327. begin
  1328. // TODO: Decimal separator!!!
  1329. Result := FloatToStr(FValue);
  1330. end;
  1331. constructor TXPathStringVariable.Create(const AValue: DOMString);
  1332. begin
  1333. inherited Create;
  1334. FValue := AValue;
  1335. end;
  1336. class function TXPathStringVariable.TypeName: String;
  1337. begin
  1338. Result := SString;
  1339. end;
  1340. function TXPathStringVariable.AsBoolean: Boolean;
  1341. begin
  1342. Result := Length(FValue) > 0;
  1343. end;
  1344. function TXPathStringVariable.AsNumber: Extended;
  1345. begin
  1346. Result := StrToNumber(FValue);
  1347. end;
  1348. function TXPathStringVariable.AsText: DOMString;
  1349. begin
  1350. Result := FValue;
  1351. end;
  1352. { XPath lexical scanner }
  1353. constructor TXPathScanner.Create(const AExpressionString: DOMString);
  1354. begin
  1355. inherited Create;
  1356. FExpressionString := DOMPChar(AExpressionString);
  1357. FCurData := FExpressionString;
  1358. NextToken;
  1359. end;
  1360. function TXPathScanner.PeekToken: TXPathToken;
  1361. var
  1362. save: DOMPChar;
  1363. begin
  1364. save := FCurData;
  1365. Result := GetToken;
  1366. FCurData := save;
  1367. end;
  1368. function TXPathScanner.NextToken: TXPathToken;
  1369. begin
  1370. Result := GetToken;
  1371. FCurToken := Result;
  1372. if Result in [tkIdentifier, tkNumber, tkString] then
  1373. SetString(FCurTokenString, FTokenStart, FTokenLength);
  1374. end;
  1375. function TXPathScanner.SkipToken(tok: TXPathToken): Boolean; { inline? }
  1376. begin
  1377. Result := (FCurToken = tok);
  1378. if Result then
  1379. NextToken;
  1380. end;
  1381. function TXPathScanner.GetToken: TXPathToken;
  1382. procedure GetNumber(HasDot: Boolean);
  1383. begin
  1384. FTokenLength := 1;
  1385. while ((FCurData[1] >= '0') and (FCurData[1] <= '9')) or ((FCurData[1] = '.') and not HasDot) do
  1386. begin
  1387. Inc(FCurData);
  1388. Inc(FTokenLength);
  1389. if FCurData[0] = '.' then
  1390. HasDot := True;
  1391. end;
  1392. Result := tkNumber;
  1393. end;
  1394. // TODO: no surrogate pairs/XML 1.1 support yet
  1395. function ScanNCName: Boolean;
  1396. begin
  1397. Result := Byte(FCurData^) in NamingBitmap[NamePages[hi(Word(FCurData^))]];
  1398. if Result then
  1399. begin
  1400. FTokenLength := 1;
  1401. while Byte(FCurData[1]) in NamingBitmap[NamePages[$100+hi(Word(FCurData[1]))]] do
  1402. begin
  1403. Inc(FCurData);
  1404. Inc(FTokenLength);
  1405. end;
  1406. end;
  1407. end;
  1408. begin
  1409. // Skip whitespace
  1410. while (FCurData[0] < #255) and (char(ord(FCurData[0])) in [#9, #10, #13, ' ']) do
  1411. Inc(FCurData);
  1412. FTokenStart := FCurData;
  1413. FTokenLength := 0;
  1414. case FCurData[0] of
  1415. #0:
  1416. Result := tkEndOfStream;
  1417. '!':
  1418. if FCurData[1] = '=' then
  1419. begin
  1420. Inc(FCurData);
  1421. Result := tkNotEqual;
  1422. end
  1423. else
  1424. Error(SScannerInvalidChar);
  1425. '"':
  1426. begin
  1427. FTokenLength := 0;
  1428. Inc(FCurData);
  1429. FTokenStart := FCurData;
  1430. while FCurData[0] <> '"' do
  1431. begin
  1432. if FCurData[0] = #0 then
  1433. Error(SScannerQuotStringIsOpen);
  1434. Inc(FCurData);
  1435. Inc(FTokenLength);
  1436. end;
  1437. Result := tkString;
  1438. end;
  1439. '$':
  1440. Result := tkDollar;
  1441. '''':
  1442. begin
  1443. FTokenLength := 0;
  1444. Inc(FCurData);
  1445. FTokenStart := FCurData;
  1446. while FCurData[0] <> '''' do
  1447. begin
  1448. if FCurData[0] = #0 then
  1449. Error(SScannerAposStringIsOpen);
  1450. Inc(FCurData);
  1451. Inc(FTokenLength);
  1452. end;
  1453. Result := tkString;
  1454. end;
  1455. '(':
  1456. Result := tkLeftBracket;
  1457. ')':
  1458. Result := tkRightBracket;
  1459. '*':
  1460. Result := tkAsterisk;
  1461. '+':
  1462. Result := tkPlus;
  1463. ',':
  1464. Result := tkComma;
  1465. '-':
  1466. Result := tkMinus;
  1467. '.':
  1468. if FCurData[1] = '.' then
  1469. begin
  1470. Inc(FCurData);
  1471. Result := tkDotDot;
  1472. end else if (FCurData[1] >= '0') and (FCurData[1] <= '9') then
  1473. GetNumber(True)
  1474. else
  1475. Result := tkDot;
  1476. '/':
  1477. if FCurData[1] = '/' then
  1478. begin
  1479. Inc(FCurData);
  1480. Result := tkSlashSlash;
  1481. end else
  1482. Result := tkSlash;
  1483. '0'..'9':
  1484. GetNumber(False);
  1485. ':':
  1486. if FCurData[1] = ':' then
  1487. begin
  1488. Inc(FCurData);
  1489. Result := tkColonColon;
  1490. end else
  1491. Result := tkColon;
  1492. '<':
  1493. if FCurData[1] = '=' then
  1494. begin
  1495. Inc(FCurData);
  1496. Result := tkLessEqual;
  1497. end else
  1498. Result := tkLess;
  1499. '=':
  1500. Result := tkEqual;
  1501. '>':
  1502. if FCurData[1] = '=' then
  1503. begin
  1504. Inc(FCurData);
  1505. Result := tkGreaterEqual;
  1506. end else
  1507. Result := tkGreater;
  1508. '@':
  1509. Result := tkAt;
  1510. '[':
  1511. Result := tkLeftSquareBracket;
  1512. ']':
  1513. Result := tkRightSquareBracket;
  1514. '|':
  1515. Result := tkPipe;
  1516. else
  1517. if ScanNCName then
  1518. // TODO: must handle 'NCName:*' and 'NCName:NCName' here,
  1519. // these are single tokens which may not have whitespace inbetween.
  1520. Result := tkIdentifier
  1521. else
  1522. Error(SScannerInvalidChar);
  1523. end;
  1524. // We have processed at least one character now; eat it:
  1525. if Result <> tkEndOfStream then
  1526. Inc(FCurData);
  1527. end;
  1528. procedure TXPathScanner.Error(const Msg: String);
  1529. begin
  1530. raise Exception.Create(Msg) at get_caller_addr(get_frame);
  1531. end;
  1532. procedure TXPathScanner.ParsePredicates(var Dest: TXPathNodeArray);
  1533. var
  1534. Buffer: array[0..15] of TXPathExprNode;
  1535. I: Integer;
  1536. begin
  1537. I := 0;
  1538. // accumulate nodes in local buffer, then add all at once
  1539. // this reduces amount of ReallocMem's
  1540. while SkipToken(tkLeftSquareBracket) do
  1541. begin
  1542. Buffer[I] := ParseOrExpr;
  1543. Inc(I);
  1544. if I > High(Buffer) then
  1545. begin
  1546. AddNodes(Dest, Buffer);
  1547. I := 0;
  1548. end;
  1549. if not SkipToken(tkRightSquareBracket) then
  1550. Error(SParserExpectedRightSquareBracket);
  1551. end;
  1552. if I > 0 then
  1553. AddNodes(Dest, Slice(Buffer, I));
  1554. end;
  1555. procedure TXPathScanner.ParseStep(Dest: TStep); // [4]
  1556. procedure NeedBrackets;
  1557. begin
  1558. NextToken;
  1559. if NextToken <> tkRightBracket then
  1560. Error(SParserExpectedRightBracket);
  1561. NextToken;
  1562. end;
  1563. begin
  1564. if CurToken = tkDot then // [12] Abbreviated step, first case
  1565. begin
  1566. NextToken;
  1567. Dest.Axis := axisSelf;
  1568. Dest.NodeTestType := ntAnyNode;
  1569. end
  1570. else if CurToken = tkDotDot then // [12] Abbreviated step, second case
  1571. begin
  1572. NextToken;
  1573. Dest.Axis := axisParent;
  1574. Dest.NodeTestType := ntAnyNode;
  1575. end
  1576. else // Parse [5] AxisSpecifier
  1577. begin
  1578. if CurToken = tkAt then // [13] AbbreviatedAxisSpecifier
  1579. begin
  1580. Dest.Axis := axisAttribute;
  1581. NextToken;
  1582. end
  1583. else if (CurToken = tkIdentifier) and (PeekToken = tkColonColon) then // [5] AxisName '::'
  1584. begin
  1585. // Check for [6] AxisName
  1586. if CurTokenString = 'ancestor' then
  1587. Dest.Axis := axisAncestor
  1588. else if CurTokenString = 'ancestor-or-self' then
  1589. Dest.Axis := axisAncestorOrSelf
  1590. else if CurTokenString = 'attribute' then
  1591. Dest.Axis := axisAttribute
  1592. else if CurTokenString = 'child' then
  1593. Dest.Axis := axisChild
  1594. else if CurTokenString = 'descendant' then
  1595. Dest.Axis := axisDescendant
  1596. else if CurTokenString = 'descendant-or-self' then
  1597. Dest.Axis := axisDescendantOrSelf
  1598. else if CurTokenString = 'following' then
  1599. Dest.Axis := axisFollowing
  1600. else if CurTokenString = 'following-sibling' then
  1601. Dest.Axis := axisFollowingSibling
  1602. else if CurTokenString = 'namespace' then
  1603. Dest.Axis := axisNamespace
  1604. else if CurTokenString = 'parent' then
  1605. Dest.Axis := axisParent
  1606. else if CurTokenString = 'preceding' then
  1607. Dest.Axis := axisPreceding
  1608. else if CurTokenString = 'preceding-sibling' then
  1609. Dest.Axis := axisPrecedingSibling
  1610. else if CurTokenString = 'self' then
  1611. Dest.Axis := axisSelf
  1612. else
  1613. Error(SParserBadAxisName);
  1614. NextToken; // skip identifier and the '::'
  1615. NextToken;
  1616. end;
  1617. // Parse [7] NodeTest
  1618. if CurToken = tkAsterisk then // [37] NameTest, first case
  1619. begin
  1620. Dest.NodeTestType := ntAnyPrincipal;
  1621. NextToken;
  1622. end
  1623. else if CurToken = tkIdentifier then
  1624. begin
  1625. // Check for case [38] NodeType
  1626. if PeekToken = tkLeftBracket then
  1627. begin
  1628. if CurTokenString = 'comment' then
  1629. begin
  1630. NeedBrackets;
  1631. Dest.NodeTestType := ntCommentNode;
  1632. end
  1633. else if CurTokenString = 'text' then
  1634. begin
  1635. NeedBrackets;
  1636. Dest.NodeTestType := ntTextNode;
  1637. end
  1638. else if CurTokenString = 'processing-instruction' then
  1639. begin
  1640. NextToken; { skip '('; we know it's there }
  1641. if NextToken = tkString then
  1642. begin
  1643. // TODO: Handle processing-instruction('name') constructs
  1644. Dest.NodeTestString := CurTokenString;
  1645. NextToken;
  1646. end;
  1647. if CurToken <> tkRightBracket then
  1648. Error(SParserExpectedRightBracket);
  1649. NextToken;
  1650. Dest.NodeTestType := ntPINode;
  1651. end
  1652. else if CurTokenString = 'node' then
  1653. begin
  1654. NeedBrackets;
  1655. Dest.NodeTestType := ntAnyNode;
  1656. end
  1657. else
  1658. Error(SParserBadNodeType);
  1659. end
  1660. else // [37] NameTest, second or third case
  1661. begin
  1662. // !!!: Doesn't support namespaces yet
  1663. // (this will have to wait until the DOM unit supports them)
  1664. Dest.NodeTestType := ntName;
  1665. Dest.NodeTestString := CurTokenString;
  1666. if NextToken = tkColon then
  1667. begin
  1668. case NextToken of
  1669. tkIdentifier: NextToken; { foo:bar }
  1670. tkAsterisk: NextToken; { foo:* }
  1671. else
  1672. Error(SParserExpectedQName);
  1673. end;
  1674. end;
  1675. end;
  1676. end
  1677. else
  1678. Exit;
  1679. ParsePredicates(Dest.Predicates);
  1680. end;
  1681. end;
  1682. function TXPathScanner.ParsePrimaryExpr: TXPathExprNode; // [15]
  1683. var
  1684. IsFirstArg: Boolean;
  1685. begin
  1686. case CurToken of
  1687. tkDollar: // [36] Variable reference
  1688. begin
  1689. if NextToken <> tkIdentifier then
  1690. Error(SParserExpectedVarName);
  1691. Result := TXPathVariableNode.Create(CurTokenString);
  1692. end;
  1693. tkLeftBracket:
  1694. begin
  1695. NextToken;
  1696. Result := ParseOrExpr;
  1697. if CurToken <> tkRightBracket then
  1698. Error(SParserExpectedRightBracket);
  1699. end;
  1700. tkString: // [29] Literal
  1701. Result := TXPathConstantNode.Create(
  1702. TXPathStringVariable.Create(CurTokenString));
  1703. tkNumber: // [30] Number
  1704. Result := TXPathConstantNode.Create(
  1705. TXPathNumberVariable.Create(StrToNumber(CurTokenString)));
  1706. tkIdentifier: // [16] Function call
  1707. begin
  1708. Result := TXPathFunctionNode.Create(CurTokenString);
  1709. if NextToken <> tkLeftBracket then
  1710. Error(SParserExpectedLeftBracket);
  1711. NextToken;
  1712. // Parse argument list
  1713. IsFirstArg := True;
  1714. while CurToken <> tkRightBracket do
  1715. begin
  1716. if IsFirstArg then
  1717. IsFirstArg := False
  1718. else if CurToken <> tkComma then
  1719. Error(SParserExpectedRightBracket)
  1720. else
  1721. NextToken; { skip comma }
  1722. TXPathFunctionNode(Result).FArgs.Add(ParseOrExpr);
  1723. end;
  1724. end;
  1725. else
  1726. Error(SParserInvalidPrimExpr);
  1727. Result := nil; // satisfy compiler
  1728. end;
  1729. NextToken;
  1730. end;
  1731. function TXPathScanner.ParseUnionExpr: TXPathExprNode; // [18]
  1732. begin
  1733. Result := ParsePathExpr;
  1734. while SkipToken(tkPipe) do
  1735. Result := TXPathUnionNode.Create(Result, ParsePathExpr);
  1736. end;
  1737. function TXPathScanner.ParsePathExpr: TXPathExprNode; // [19]
  1738. var
  1739. CurStep, NextStep: TStep;
  1740. begin
  1741. Result := nil;
  1742. CurStep := nil;
  1743. // Try to detect whether a LocationPath [1] or a FilterExpr [20] follows
  1744. if ((CurToken = tkIdentifier) and (PeekToken = tkLeftBracket) and
  1745. (CurTokenString <> 'comment') and
  1746. (CurTokenString <> 'text') and
  1747. (CurTokenString <> 'processing-instruction') and
  1748. (CurTokenString <> 'node')) or
  1749. (CurToken in [tkDollar, tkLeftBracket, tkString, tkNumber]) then
  1750. begin
  1751. // second, third or fourth case of [19]
  1752. Result := ParseFilterExpr;
  1753. if not (CurToken in [tkSlash, tkSlashSlash]) then
  1754. Exit;
  1755. end;
  1756. Result := TXPathLocationPathNode.Create(Result,
  1757. (Result = nil) and (CurToken in [tkSlash, tkSlashSlash]));
  1758. if CurToken = tkSlashSlash then
  1759. begin
  1760. CurStep := TStep.Create(axisDescendantOrSelf, ntAnyNode);
  1761. TXPathLocationPathNode(Result).FFirstStep := CurStep;
  1762. NextToken;
  1763. end
  1764. else if CurToken = tkSlash then
  1765. NextToken;
  1766. while CurToken in [tkDot, tkDotDot, tkAt, tkAsterisk, tkIdentifier] do
  1767. begin
  1768. // axisChild is the default. ntAnyPrincipal is dummy.
  1769. NextStep := TStep.Create(axisChild, ntAnyPrincipal);
  1770. if Assigned(CurStep) then
  1771. CurStep.NextStep := NextStep
  1772. else
  1773. TXPathLocationPathNode(Result).FFirstStep := NextStep;
  1774. CurStep := NextStep;
  1775. // Parse [4] Step
  1776. ParseStep(CurStep);
  1777. // Continue with parsing of [3] RelativeLocationPath
  1778. if CurToken = tkSlashSlash then
  1779. begin
  1780. NextToken;
  1781. // Found abbreviated step ("//" for "descendant-or-self::node()")
  1782. NextStep := TStep.Create(axisDescendantOrSelf, ntAnyNode);
  1783. CurStep.NextStep := NextStep;
  1784. CurStep := NextStep;
  1785. end
  1786. else if not SkipToken(tkSlash) then
  1787. break;
  1788. end;
  1789. end;
  1790. function TXPathScanner.ParseFilterExpr: TXPathExprNode; // [20]
  1791. begin
  1792. Result := ParsePrimaryExpr;
  1793. // Parse predicates
  1794. if CurToken = tkLeftSquareBracket then
  1795. begin
  1796. Result := TXPathFilterNode.Create(Result);
  1797. ParsePredicates(TXPathFilterNode(Result).FPredicates);
  1798. end;
  1799. end;
  1800. function TXPathScanner.ParseOrExpr: TXPathExprNode; // [21]
  1801. begin
  1802. Result := ParseAndExpr;
  1803. while (CurToken = tkIdentifier) and (CurTokenString = 'or') do
  1804. begin
  1805. NextToken;
  1806. Result := TXPathBooleanOpNode.Create(opOr, Result, ParseAndExpr);
  1807. end;
  1808. end;
  1809. function TXPathScanner.ParseAndExpr: TXPathExprNode; // [22]
  1810. begin
  1811. Result := ParseEqualityExpr;
  1812. while (CurToken = tkIdentifier) and (CurTokenString = 'and') do
  1813. begin
  1814. NextToken;
  1815. Result := TXPathBooleanOpNode.Create(opAnd, Result, ParseEqualityExpr);
  1816. end;
  1817. end;
  1818. function TXPathScanner.ParseEqualityExpr: TXPathExprNode; // [23]
  1819. var
  1820. op: TXPathCompareOp;
  1821. begin
  1822. Result := ParseRelationalExpr;
  1823. repeat
  1824. case CurToken of
  1825. tkEqual: op := opEqual;
  1826. tkNotEqual: op := opNotEqual;
  1827. else
  1828. Break;
  1829. end;
  1830. NextToken;
  1831. Result := TXPathCompareNode.Create(op, Result, ParseRelationalExpr);
  1832. until False;
  1833. end;
  1834. function TXPathScanner.ParseRelationalExpr: TXPathExprNode; // [24]
  1835. var
  1836. op: TXPathCompareOp;
  1837. begin
  1838. Result := ParseAdditiveExpr;
  1839. repeat
  1840. case CurToken of
  1841. tkLess: op := opLess;
  1842. tkLessEqual: op := opLessEqual;
  1843. tkGreater: op := opGreater;
  1844. tkGreaterEqual: op := opGreaterEqual;
  1845. else
  1846. Break;
  1847. end;
  1848. NextToken;
  1849. Result := TXPathCompareNode.Create(op, Result, ParseAdditiveExpr);
  1850. until False;
  1851. end;
  1852. function TXPathScanner.ParseAdditiveExpr: TXPathExprNode; // [25]
  1853. var
  1854. op: TXPathMathOp;
  1855. begin
  1856. Result := ParseMultiplicativeExpr;
  1857. repeat
  1858. case CurToken of
  1859. tkPlus: op := opAdd;
  1860. tkMinus: op := opSubtract;
  1861. else
  1862. Break;
  1863. end;
  1864. NextToken;
  1865. Result := TXPathMathOpNode.Create(op, Result, ParseMultiplicativeExpr);
  1866. until False;
  1867. end;
  1868. function TXPathScanner.ParseMultiplicativeExpr: TXPathExprNode; // [26]
  1869. var
  1870. op: TXPathMathOp;
  1871. begin
  1872. Result := ParseUnaryExpr;
  1873. repeat
  1874. case CurToken of
  1875. tkAsterisk:
  1876. op := opMultiply;
  1877. tkIdentifier:
  1878. if CurTokenString = 'div' then
  1879. op := opDivide
  1880. else if CurTokenString = 'mod' then
  1881. op := opMod
  1882. else
  1883. break;
  1884. else
  1885. break;
  1886. end;
  1887. NextToken;
  1888. Result := TXPathMathOpNode.Create(op, Result, ParseUnaryExpr);
  1889. until False;
  1890. end;
  1891. function TXPathScanner.ParseUnaryExpr: TXPathExprNode; // [27]
  1892. var
  1893. NegCount: Integer;
  1894. begin
  1895. NegCount := 0;
  1896. while SkipToken(tkMinus) do
  1897. Inc(NegCount);
  1898. Result := ParseUnionExpr;
  1899. if Odd(NegCount) then
  1900. Result := TXPathNegationNode.Create(Result);
  1901. end;
  1902. { TXPathContext }
  1903. constructor TXPathContext.Create(AContextNode: TDOMNode;
  1904. AContextPosition, AContextSize: Integer);
  1905. begin
  1906. inherited Create;
  1907. ContextNode := AContextNode;
  1908. ContextPosition := AContextPosition;
  1909. ContextSize := AContextSize;
  1910. end;
  1911. { TXPathEnvironment }
  1912. type
  1913. PFunctionInfo = ^TFunctionInfo;
  1914. TFunctionInfo = record
  1915. Name: String;
  1916. Fn: TXPathFunction;
  1917. end;
  1918. PVariableInfo = ^TVariableInfo;
  1919. TVariableInfo = record
  1920. Name: String;
  1921. Variable: TXPathVariable;
  1922. end;
  1923. constructor TXPathEnvironment.Create;
  1924. begin
  1925. inherited Create;
  1926. FFunctions := TFPList.Create;
  1927. FVariables := TFPList.Create;
  1928. // Add the functions of the XPath Core Function Library
  1929. // Node set functions
  1930. AddFunction('last', @xpLast);
  1931. AddFunction('position', @xpPosition);
  1932. AddFunction('count', @xpCount);
  1933. AddFunction('id', @xpId);
  1934. AddFunction('local-name', @xpLocalName);
  1935. AddFunction('namespace-uri', @xpNamespaceURI);
  1936. AddFunction('name', @xpName);
  1937. // String functions
  1938. AddFunction('string', @xpString);
  1939. AddFunction('concat', @xpConcat);
  1940. AddFunction('starts-with', @xpStartsWith);
  1941. AddFunction('contains', @xpContains);
  1942. AddFunction('substring-before', @xpSubstringBefore);
  1943. AddFunction('substring-after', @xpSubstringAfter);
  1944. AddFunction('substring', @xpSubstring);
  1945. AddFunction('string-length', @xpStringLength);
  1946. AddFunction('normalize-space', @xpNormalizeSpace);
  1947. AddFunction('translate', @xpTranslate);
  1948. // Boolean functions
  1949. AddFunction('boolean', @xpBoolean);
  1950. AddFunction('not', @xpNot);
  1951. AddFunction('true', @xpTrue);
  1952. AddFunction('false', @xpFalse);
  1953. AddFunction('lang', @xpLang);
  1954. // Number functions
  1955. AddFunction('number', @xpNumber);
  1956. AddFunction('sum', @xpSum);
  1957. AddFunction('floor', @xpFloor);
  1958. AddFunction('ceiling', @xpCeiling);
  1959. AddFunction('round', @xpRound);
  1960. end;
  1961. destructor TXPathEnvironment.Destroy;
  1962. var
  1963. i: Integer;
  1964. FunctionInfo: PFunctionInfo;
  1965. VariableInfo: PVariableInfo;
  1966. begin
  1967. for i := 0 to FFunctions.Count - 1 do
  1968. begin
  1969. FunctionInfo := PFunctionInfo(FFunctions[i]);
  1970. FreeMem(FunctionInfo);
  1971. end;
  1972. FFunctions.Free;
  1973. for i := 0 to FVariables.Count - 1 do
  1974. begin
  1975. VariableInfo := PVariableInfo(FVariables[i]);
  1976. FreeMem(VariableInfo);
  1977. end;
  1978. FVariables.Free;
  1979. inherited Destroy;
  1980. end;
  1981. function TXPathEnvironment.GetFunctionIndex(const AName: String): Integer;
  1982. var
  1983. i: Integer;
  1984. begin
  1985. for i := 0 to FFunctions.Count - 1 do
  1986. if PFunctionInfo(FFunctions[i])^.Name = AName then
  1987. begin
  1988. Result := i;
  1989. exit;
  1990. end;
  1991. Result := -1;
  1992. end;
  1993. function TXPathEnvironment.GetVariableIndex(const AName: String): Integer;
  1994. var
  1995. i: Integer;
  1996. begin
  1997. for i := 0 to FVariables.Count - 1 do
  1998. if PVariableInfo(FFunctions[i])^.Name = AName then
  1999. begin
  2000. Result := i;
  2001. exit;
  2002. end;
  2003. Result := -1;
  2004. end;
  2005. procedure TXPathEnvironment.AddFunction(const AName: String; AFunction: TXPathFunction);
  2006. var
  2007. NewFunctionInfo: PFunctionInfo;
  2008. begin
  2009. // !!!: Prevent the addition of duplicate functions
  2010. New(NewFunctionInfo);
  2011. NewFunctionInfo^.Name := AName;
  2012. NewFunctionInfo^.Fn := AFunction;
  2013. FFunctions.Add(NewFunctionInfo);
  2014. end;
  2015. procedure TXPathEnvironment.AddVariable(const AName: String; AVariable: TXPathVariable);
  2016. var
  2017. NewVariableInfo: PVariableInfo;
  2018. begin
  2019. // !!!: Prevent the addition of duplicate variables
  2020. New(NewVariableInfo);
  2021. NewVariableInfo^.Name := AName;
  2022. NewVariableInfo^.Variable := AVariable;
  2023. FVariables.Add(NewVariableInfo);
  2024. end;
  2025. procedure TXPathEnvironment.RemoveFunction(Index: Integer);
  2026. var
  2027. FunctionInfo: PFunctionInfo;
  2028. begin
  2029. FunctionInfo := PFunctionInfo(FFunctions[Index]);
  2030. Dispose(FunctionInfo);
  2031. FFunctions.Delete(Index);
  2032. end;
  2033. procedure TXPathEnvironment.RemoveFunction(const AName: String);
  2034. var
  2035. i: Integer;
  2036. begin
  2037. for i := 0 to FFunctions.Count - 1 do
  2038. if PFunctionInfo(FFunctions[i])^.Name = AName then
  2039. begin
  2040. RemoveFunction(i);
  2041. exit;
  2042. end;
  2043. end;
  2044. procedure TXPathEnvironment.RemoveVariable(Index: Integer);
  2045. var
  2046. VariableInfo: PVariableInfo;
  2047. begin
  2048. VariableInfo := PVariableInfo(FVariables[Index]);
  2049. Dispose(VariableInfo);
  2050. FVariables.Delete(Index);
  2051. end;
  2052. procedure TXPathEnvironment.RemoveVariable(const AName: String);
  2053. var
  2054. Index: Integer;
  2055. begin
  2056. Index := GetVariableIndex(AName);
  2057. if Index >= 0 then
  2058. RemoveVariable(Index);
  2059. end;
  2060. function TXPathEnvironment.GetFunctionCount: Integer;
  2061. begin
  2062. Result := FFunctions.Count;
  2063. end;
  2064. function TXPathEnvironment.GetVariableCount: Integer;
  2065. begin
  2066. Result := FVariables.Count;
  2067. end;
  2068. function TXPathEnvironment.GetFunction(Index: Integer): TXPathFunction;
  2069. begin
  2070. Result := PFunctionInfo(FFunctions[Index])^.Fn;
  2071. end;
  2072. function TXPathEnvironment.GetFunction(const AName: String): TXPathFunction;
  2073. var
  2074. i: Integer;
  2075. begin
  2076. for i := 0 to FFunctions.Count - 1 do
  2077. if PFunctionInfo(FFunctions[i])^.Name = AName then
  2078. begin
  2079. Result := PFunctionInfo(FFunctions[i])^.Fn;
  2080. exit;
  2081. end;
  2082. Result := nil;
  2083. end;
  2084. function TXPathEnvironment.GetVariable(Index: Integer): TXPathVariable;
  2085. begin
  2086. Result := PVariableInfo(FVariables[Index])^.Variable;
  2087. end;
  2088. function TXPathEnvironment.GetVariable(const AName: String): TXPathVariable;
  2089. var
  2090. i: Integer;
  2091. begin
  2092. for i := 0 to FVariables.Count - 1 do
  2093. if PFunctionInfo(FVariables[i])^.Name = AName then
  2094. begin
  2095. Result := PVariableInfo(FVariables[i])^.Variable;
  2096. exit;
  2097. end;
  2098. Result := nil;
  2099. end;
  2100. function TXPathEnvironment.xpLast(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2101. begin
  2102. if Args.Count <> 0 then
  2103. EvaluationError(SEvalInvalidArgCount);
  2104. Result := TXPathNumberVariable.Create(Context.ContextSize);
  2105. end;
  2106. function TXPathEnvironment.xpPosition(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2107. begin
  2108. if Args.Count <> 0 then
  2109. EvaluationError(SEvalInvalidArgCount);
  2110. Result := TXPathNumberVariable.Create(Context.ContextPosition);
  2111. end;
  2112. function TXPathEnvironment.xpCount(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2113. begin
  2114. if Args.Count <> 1 then
  2115. EvaluationError(SEvalInvalidArgCount);
  2116. Result := TXPathNumberVariable.Create(TXPathVariable(Args[0]).AsNodeSet.Count);
  2117. end;
  2118. function TXPathEnvironment.xpId(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2119. var
  2120. i: Integer;
  2121. ResultSet: TNodeSet;
  2122. TheArg: TXPathVariable;
  2123. doc: TDOMDocument;
  2124. procedure AddId(ns: TNodeSet; const s: DOMString);
  2125. var
  2126. Head, Tail, L: Integer;
  2127. Token: DOMString;
  2128. Element: TDOMNode;
  2129. begin
  2130. Head := 1;
  2131. L := Length(s);
  2132. while (Head <= L) and IsXmlWhiteSpace(s[Head]) do
  2133. Inc(Head);
  2134. while Head <= L do
  2135. begin
  2136. Tail := Head;
  2137. while (Tail <= L) and not IsXmlWhiteSpace(s[Tail]) do
  2138. Inc(Tail);
  2139. SetString(Token, @s[Head], Tail - Head);
  2140. Element := doc.GetElementById(Token);
  2141. if Assigned(Element) then
  2142. ns.Add(Element);
  2143. Head := Tail;
  2144. while IsXmlWhiteSpace(s[Head]) do
  2145. Inc(Head);
  2146. end;
  2147. end;
  2148. begin
  2149. if Args.Count <> 1 then
  2150. EvaluationError(SEvalInvalidArgCount);
  2151. // TODO: probably have doc as member of Context
  2152. if Context.ContextNode.NodeType = DOCUMENT_NODE then
  2153. doc := TDOMDocument(Context.ContextNode)
  2154. else
  2155. doc := Context.ContextNode.OwnerDocument;
  2156. ResultSet := TNodeSet.Create;
  2157. TheArg := TXPathVariable(Args[0]);
  2158. if TheArg is TXPathNodeSetVariable then
  2159. begin
  2160. with TheArg.AsNodeSet do
  2161. for i := 0 to Count-1 do
  2162. AddId(ResultSet, NodeToText(TDOMNode(Items[i])));
  2163. end
  2164. else
  2165. AddId(ResultSet, TheArg.AsText);
  2166. Result := TXPathNodeSetVariable.Create(ResultSet);
  2167. end;
  2168. function TXPathEnvironment.xpLocalName(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2169. var
  2170. n: TDOMNode;
  2171. NodeSet: TNodeSet;
  2172. s: DOMString;
  2173. begin
  2174. if Args.Count > 1 then
  2175. EvaluationError(SEvalInvalidArgCount);
  2176. n := nil;
  2177. if Args.Count = 0 then
  2178. n := Context.ContextNode
  2179. else
  2180. begin
  2181. NodeSet := TXPathVariable(Args[0]).AsNodeSet;
  2182. if NodeSet.Count > 0 then
  2183. n := TDOMNode(NodeSet[0]);
  2184. end;
  2185. if Assigned(n) then
  2186. s := n.localName
  2187. else
  2188. s := '';
  2189. Result := TXPathStringVariable.Create(s);
  2190. end;
  2191. function TXPathEnvironment.xpNamespaceURI(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2192. var
  2193. n: TDOMNode;
  2194. NodeSet: TNodeSet;
  2195. s: DOMString;
  2196. begin
  2197. if Args.Count > 1 then
  2198. EvaluationError(SEvalInvalidArgCount);
  2199. n := nil;
  2200. if Args.Count = 0 then
  2201. n := Context.ContextNode
  2202. else
  2203. begin
  2204. NodeSet := TXPathVariable(Args[0]).AsNodeSet;
  2205. if NodeSet.Count > 0 then
  2206. n := TDOMNode(NodeSet[0]);
  2207. end;
  2208. if Assigned(n) then
  2209. s := n.namespaceUri
  2210. else
  2211. s := '';
  2212. Result := TXPathStringVariable.Create(s);
  2213. end;
  2214. function TXPathEnvironment.xpName(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2215. var
  2216. NodeSet: TNodeSet;
  2217. begin
  2218. // TODO: arg is optional, omission case must be handled
  2219. if Args.Count <> 1 then
  2220. EvaluationError(SEvalInvalidArgCount);
  2221. NodeSet := TXPathVariable(Args[0]).AsNodeSet;
  2222. if NodeSet.Count = 0 then
  2223. Result := TXPathStringVariable.Create('')
  2224. else
  2225. // !!!: Probably not really correct regarding namespaces...
  2226. Result := TXPathStringVariable.Create(TDOMNode(NodeSet[0]).NodeName);
  2227. end;
  2228. function TXPathEnvironment.xpString(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2229. var
  2230. s: DOMString;
  2231. begin
  2232. if Args.Count > 1 then
  2233. EvaluationError(SEvalInvalidArgCount);
  2234. if Args.Count = 0 then
  2235. s := NodeToText(Context.ContextNode)
  2236. else
  2237. s := TXPathVariable(Args[0]).AsText;
  2238. Result := TXPathStringVariable.Create(s);
  2239. end;
  2240. function TXPathEnvironment.xpConcat(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2241. var
  2242. i: Integer;
  2243. s: DOMString;
  2244. begin
  2245. if Args.Count < 2 then
  2246. EvaluationError(SEvalInvalidArgCount);
  2247. SetLength(s, 0);
  2248. for i := 0 to Args.Count - 1 do
  2249. s := s + TXPathVariable(Args[i]).AsText;
  2250. Result := TXPathStringVariable.Create(s);
  2251. end;
  2252. function TXPathEnvironment.xpStartsWith(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2253. var
  2254. s1, s2: DOMString;
  2255. res: Boolean;
  2256. begin
  2257. if Args.Count <> 2 then
  2258. EvaluationError(SEvalInvalidArgCount);
  2259. s1 := TXPathVariable(Args[0]).AsText;
  2260. s2 := TXPathVariable(Args[1]).AsText;
  2261. if s2 = '' then
  2262. res := True
  2263. else
  2264. res := Pos(s2, s1) = 1;
  2265. Result := TXPathBooleanVariable.Create(res);
  2266. end;
  2267. function TXPathEnvironment.xpContains(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2268. var
  2269. s1, s2: DOMString;
  2270. res: Boolean;
  2271. begin
  2272. if Args.Count <> 2 then
  2273. EvaluationError(SEvalInvalidArgCount);
  2274. s1 := TXPathVariable(Args[0]).AsText;
  2275. s2 := TXPathVariable(Args[1]).AsText;
  2276. if s2 = '' then
  2277. res := True
  2278. else
  2279. res := Pos(s2, s1) <> 0;
  2280. Result := TXPathBooleanVariable.Create(res);
  2281. end;
  2282. function TXPathEnvironment.xpSubstringBefore(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2283. var
  2284. s, substr: DOMString;
  2285. begin
  2286. if Args.Count <> 2 then
  2287. EvaluationError(SEvalInvalidArgCount);
  2288. s := TXPathVariable(Args[0]).AsText;
  2289. substr := TXPathVariable(Args[1]).AsText;
  2290. Result := TXPathStringVariable.Create(Copy(s, 1, Pos(substr, s)-1));
  2291. end;
  2292. function TXPathEnvironment.xpSubstringAfter(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2293. var
  2294. s, substr: DOMString;
  2295. i: Integer;
  2296. begin
  2297. if Args.Count <> 2 then
  2298. EvaluationError(SEvalInvalidArgCount);
  2299. s := TXPathVariable(Args[0]).AsText;
  2300. substr := TXPathVariable(Args[1]).AsText;
  2301. i := Pos(substr, s);
  2302. if i <> 0 then
  2303. Result := TXPathStringVariable.Create(Copy(s, i + Length(substr), MaxInt))
  2304. else
  2305. Result := TXPathStringVariable.Create('');
  2306. end;
  2307. function TXPathEnvironment.xpSubstring(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2308. var
  2309. s: DOMString;
  2310. i, n1, n2: Integer;
  2311. e1, e2: Extended;
  2312. empty: Boolean;
  2313. begin
  2314. if (Args.Count < 2) or (Args.Count > 3) then
  2315. EvaluationError(SEvalInvalidArgCount);
  2316. s := TXPathVariable(Args[0]).AsText;
  2317. e1 := TXPathVariable(Args[1]).AsNumber;
  2318. n1 := 1; // satisfy compiler
  2319. n2 := MaxInt;
  2320. empty := IsNaN(e1) or IsInfinite(e1);
  2321. if not empty then
  2322. n1 := floor(0.5 + e1);
  2323. if Args.Count = 3 then
  2324. begin
  2325. e2 := TXPathVariable(Args[2]).AsNumber;
  2326. if IsNaN(e2) or (IsInfinite(e2) and (e2 < 0)) then
  2327. empty := True
  2328. else if not IsInfinite(e2) then
  2329. n2 := floor(0.5 + e2);
  2330. end;
  2331. i := Max(n1, 1);
  2332. n2 := n2 + n1 - i;
  2333. if empty then
  2334. n2 := -1;
  2335. Result := TXPathStringVariable.Create(Copy(s, i, n2));
  2336. end;
  2337. function TXPathEnvironment.xpStringLength(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2338. var
  2339. s: DOMString;
  2340. begin
  2341. if Args.Count > 1 then
  2342. EvaluationError(SEvalInvalidArgCount);
  2343. if Args.Count = 0 then
  2344. s := NodeToText(Context.ContextNode)
  2345. else
  2346. s := TXPathVariable(Args[0]).AsText;
  2347. Result := TXPathNumberVariable.Create(Length(s));
  2348. end;
  2349. function TXPathEnvironment.xpNormalizeSpace(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2350. var
  2351. s: DOMString;
  2352. p: DOMPChar;
  2353. i: Integer;
  2354. begin
  2355. if Args.Count > 1 then
  2356. EvaluationError(SEvalInvalidArgCount);
  2357. if Args.Count = 0 then
  2358. s := NodeToText(Context.ContextNode)
  2359. else
  2360. s := TXPathVariable(Args[0]).AsText;
  2361. UniqueString(s);
  2362. p := DOMPChar(s);
  2363. for i := 1 to Length(s) do
  2364. begin
  2365. if (p^ = #10) or (p^ = #13) or (p^ = #9) then
  2366. p^ := #32;
  2367. Inc(p);
  2368. end;
  2369. NormalizeSpaces(s);
  2370. Result := TXPathStringVariable.Create(s);
  2371. end;
  2372. function TXPathEnvironment.xpTranslate(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2373. var
  2374. S: DOMString;
  2375. begin
  2376. if Args.Count <> 3 then
  2377. EvaluationError(SEvalInvalidArgCount);
  2378. S := TXPathVariable(Args[0]).AsText;
  2379. TranslateWideString(S, TXPathVariable(Args[1]).AsText, TXPathVariable(Args[2]).AsText);
  2380. Result := TXPathStringVariable.Create(S);
  2381. end;
  2382. function TXPathEnvironment.xpBoolean(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2383. begin
  2384. if Args.Count <> 1 then
  2385. EvaluationError(SEvalInvalidArgCount);
  2386. Result := TXPathBooleanVariable.Create(TXPathVariable(Args[0]).AsBoolean);
  2387. end;
  2388. function TXPathEnvironment.xpNot(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2389. begin
  2390. if Args.Count <> 1 then
  2391. EvaluationError(SEvalInvalidArgCount);
  2392. Result := TXPathBooleanVariable.Create(not TXPathVariable(Args[0]).AsBoolean);
  2393. end;
  2394. function TXPathEnvironment.xpTrue(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2395. begin
  2396. if Args.Count <> 0 then
  2397. EvaluationError(SEvalInvalidArgCount);
  2398. Result := TXPathBooleanVariable.Create(True);
  2399. end;
  2400. function TXPathEnvironment.xpFalse(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2401. begin
  2402. if Args.Count <> 0 then
  2403. EvaluationError(SEvalInvalidArgCount);
  2404. Result := TXPathBooleanVariable.Create(False);
  2405. end;
  2406. function TXPathEnvironment.xpLang(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2407. var
  2408. L: Integer;
  2409. TheArg, NodeLang: DOMString;
  2410. res: Boolean;
  2411. begin
  2412. if Args.Count <> 1 then
  2413. EvaluationError(SEvalInvalidArgCount);
  2414. TheArg := TXPathVariable(Args[0]).AsText;
  2415. NodeLang := GetNodeLanguage(Context.ContextNode);
  2416. L := Length(TheArg);
  2417. res := (L <= Length(NodeLang)) and
  2418. (WStrLIComp(DOMPChar(NodeLang), DOMPChar(TheArg), L) = 0) and
  2419. ((L = Length(NodeLang)) or (NodeLang[L+1] = '-'));
  2420. Result := TXPathBooleanVariable.Create(res);
  2421. end;
  2422. function TXPathEnvironment.xpNumber(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2423. begin
  2424. if Args.Count > 1 then
  2425. EvaluationError(SEvalInvalidArgCount);
  2426. if Args.Count = 0 then
  2427. Result := TXPathNumberVariable.Create(StrToNumber(NodeToText(Context.ContextNode)))
  2428. else
  2429. Result := TXPathNumberVariable.Create(TXPathVariable(Args[0]).AsNumber);
  2430. end;
  2431. function TXPathEnvironment.xpSum(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2432. var
  2433. i: Integer;
  2434. ns: TNodeSet;
  2435. sum: Extended;
  2436. begin
  2437. if Args.Count <> 1 then
  2438. EvaluationError(SEvalInvalidArgCount);
  2439. ns := TXPathVariable(Args[0]).AsNodeSet;
  2440. sum := 0.0;
  2441. for i := 0 to ns.Count-1 do
  2442. sum := sum + StrToNumber(NodeToText(TDOMNode(ns[i])));
  2443. Result := TXPathNumberVariable.Create(sum);
  2444. end;
  2445. function TXPathEnvironment.xpFloor(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2446. var
  2447. n: Extended;
  2448. begin
  2449. if Args.Count <> 1 then
  2450. EvaluationError(SEvalInvalidArgCount);
  2451. n := TXPathVariable(Args[0]).AsNumber;
  2452. if not IsNan(n) then
  2453. n := floor(n);
  2454. Result := TXPathNumberVariable.Create(n);
  2455. end;
  2456. function TXPathEnvironment.xpCeiling(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2457. var
  2458. n: Extended;
  2459. begin
  2460. if Args.Count <> 1 then
  2461. EvaluationError(SEvalInvalidArgCount);
  2462. n := TXPathVariable(Args[0]).AsNumber;
  2463. if not IsNan(n) then
  2464. n := ceil(n);
  2465. Result := TXPathNumberVariable.Create(n);
  2466. end;
  2467. function TXPathEnvironment.xpRound(Context: TXPathContext; Args: TXPathVarList): TXPathVariable;
  2468. var
  2469. num: Extended;
  2470. begin
  2471. if Args.Count <> 1 then
  2472. EvaluationError(SEvalInvalidArgCount);
  2473. num := TXPathVariable(Args[0]).AsNumber;
  2474. if not (IsNan(num) or IsInfinite(num)) then
  2475. num := floor(0.5 + num);
  2476. Result := TXPathNumberVariable.Create(num);
  2477. end;
  2478. { TXPathExpression }
  2479. constructor TXPathExpression.Create(AScanner: TXPathScanner;
  2480. CompleteExpression: Boolean);
  2481. begin
  2482. inherited Create;
  2483. FRootNode := AScanner.ParseOrExpr;
  2484. if CompleteExpression and (AScanner.CurToken <> tkEndOfStream) then
  2485. EvaluationError(SParserGarbageAfterExpression);
  2486. end;
  2487. function TXPathExpression.Evaluate(AContextNode: TDOMNode): TXPathVariable;
  2488. var
  2489. Environment: TXPathEnvironment;
  2490. begin
  2491. Environment := TXPathEnvironment.Create;
  2492. try
  2493. Result := Evaluate(AContextNode, Environment);
  2494. finally
  2495. Environment.Free;
  2496. end;
  2497. end;
  2498. destructor TXPathExpression.Destroy;
  2499. begin
  2500. FRootNode.Free;
  2501. inherited Destroy;
  2502. end;
  2503. function TXPathExpression.Evaluate(AContextNode: TDOMNode;
  2504. AEnvironment: TXPathEnvironment): TXPathVariable;
  2505. var
  2506. Context: TXPathContext;
  2507. mask: TFPUExceptionMask;
  2508. begin
  2509. if Assigned(FRootNode) then
  2510. begin
  2511. mask := GetExceptionMask;
  2512. SetExceptionMask(mask + [exInvalidOp, exZeroDivide]);
  2513. Context := TXPathContext.Create(AContextNode, 1, 1);
  2514. try
  2515. Result := FRootNode.Evaluate(Context, AEnvironment);
  2516. finally
  2517. Context.Free;
  2518. SetExceptionMask(mask);
  2519. end;
  2520. end else
  2521. Result := nil;
  2522. end;
  2523. function EvaluateXPathExpression(const AExpressionString: DOMString;
  2524. AContextNode: TDOMNode): TXPathVariable;
  2525. var
  2526. Scanner: TXPathScanner;
  2527. Expression: TXPathExpression;
  2528. begin
  2529. Scanner := TXPathScanner.Create(AExpressionString);
  2530. try
  2531. Expression := TXPathExpression.Create(Scanner, True);
  2532. try
  2533. Result := Expression.Evaluate(AContextNode);
  2534. finally
  2535. Expression.Free;
  2536. end;
  2537. finally
  2538. Scanner.Free;
  2539. end;
  2540. end;
  2541. end.