xpath.pp 76 KB

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