xpath.pp 76 KB

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