xpath.pp 78 KB

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