xpath.pp 69 KB

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