xpath.pp 76 KB

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