Shahid4

xml.d edit 3

May 1st, 2012
42
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Diff 18.18 KB | None | 0 0
  1. --- a/xml.d 2012-04-30 22:58:38.496710573 +0100
  2. +++ b/xml.d 2012-05-01 17:22:43.410371452 +0100
  3. @@ -842,14 +842,14 @@
  4.         debug(xpath)logline("Got xpath "~to!string(xpath)~" in node "~to!string(getName)~"\n");
  5.         R truncxpath;
  6.         auto nextnode = getNextNode(xpath,truncxpath);
  7. -       R attrmatch;
  8. +       R predmatch;
  9.         // XXX need to be able to split the attribute match off even when it doesn't have [] around it
  10.         ptrdiff_t offset = nextnode.length - find(nextnode, cast(R)"[").length;
  11.         if (offset != nextnode.length) {
  12.             // rip out attribute string
  13. -           attrmatch = nextnode[offset..nextnode.length];
  14. +           predmatch = nextnode[offset..nextnode.length];
  15.             nextnode = nextnode[0..offset];
  16. -           debug(xpath)logline("Found attribute chunk: "~to!string(attrmatch)~"\n");
  17. +           debug(xpath)logline("Found predicate chunk: "~to!string(predmatch)~"\n");
  18.         }
  19.         debug(xpath)logline("Looking for "~to!string(nextnode)~"\n");
  20.         XmlNode[]retarr;
  21. @@ -858,7 +858,13 @@
  22.             // we were searching for nodes, and this is one
  23.             debug(xpath)logline("Found a node we want! name is: "~to!string(getName)~"\n");
  24.             retarr ~= this;
  25. -       } else foreach(child;getChildren) if (!child.isCData && !child.isXmlComment && !child.isXmlPI && child.matchXPathPredicate(attrmatch,caseSensitive)) {
  26. +       } else if (nextnode.front == '@') {
  27. +           if( matchXPathPredicate(nextnode, caseSensitive)) {
  28. +               auto attr = getWSToken(nextnode);
  29. +               attr.popFront();
  30. +               retarr ~= new CData!R(getAttribute(attr));
  31. +           }
  32. +       } else foreach(child;getChildren) if (!child.isCData && !child.isXmlComment && !child.isXmlPI && child.matchXPathPredicate(predmatch,caseSensitive)) {
  33.             if (!nextnode.length || (caseSensitive && child.getName == nextnode) || (!caseSensitive && !icmp(child.getName(), nextnode))) {
  34.                 // child that matches the search string, pass on the truncated string
  35.                 debug(xpath)logline("Sending "~to!string(truncxpath)~" to "~to!string(child.getName)~"\n");
  36. @@ -875,152 +881,139 @@
  37.         return retarr;
  38.     }
  39.  
  40. -   private bool matchXPathPredicate(R attrstr,bool caseSen) {
  41. -       debug(xpath)logline("matching attribute string "~to!string(attrstr)~"\n");
  42. +   private bool matchXPathPredicate(R predstr,bool caseSen) {
  43. +       debug(xpath)logline("matching predicate string "~to!string(predstr)~"\n");
  44.         // strip off the encasing [] if it exists
  45. -       if (!attrstr.length) {
  46. +       if (!predstr.length) {
  47.             return true;
  48.         }
  49. -       if (attrstr.front == '[' && attrstr.back == ']') {
  50. -           attrstr.popFront();
  51. -           attrstr.popBack();
  52. -       } else if (attrstr.front == '[' || attrstr.back == ']') {
  53. +       if (predstr.front == '[' && predstr.back == ']') {
  54. +           predstr.popFront();
  55. +           predstr.popBack();
  56. +       } else if (predstr.front == '[' || predstr.back == ']') {
  57.             // this seems to be malformed
  58. -           throw new XPathError("got malformed attribute match "~to!string(attrstr)~"\n");
  59. +           throw new XPathError("got malformed predicate match "~to!string(predstr)~"\n");
  60.         }
  61.         // rip apart the xpath predicate assuming it's node and attribute matches
  62. -       R[]attrlist;
  63. +       R[]predlist;
  64.         // basically, we're splitting on " and " and " or ", but while respecting []
  65.         int bcount = 0, i = 0;
  66.         ptrdiff_t lslice = 0;
  67. -       R tmpattrstr = attrstr;
  68. -       //foreach (i,c;attrstr) {
  69. -       while(!tmpattrstr.empty()) {
  70. -           auto c = tmpattrstr.front;
  71. -           if (c == '[') {
  72. +       //foreach (i,c;predstr) {
  73. +       R tmpstr = predstr.save;
  74. +       for( dchar quote = '\0'; !tmpstr.empty(); ) {
  75. +           auto c = tmpstr.front;
  76. +           if( quote != '\0' ) {
  77. +               if( quote == c ) {
  78. +                   quote = '\0';
  79. +               }
  80. +           } else if (c == '\'' || c == '"' ) {
  81. +               quote = c;
  82. +           } else if (c == '[') {
  83.                 bcount++;
  84.             } else if (c == ']') {
  85.                 bcount--;
  86.             } else if (bcount == 0 && c == ' ') {
  87.                 if (i != lslice) {
  88. -                   attrlist ~= attrstr[lslice..i];
  89. +                   predlist ~= predstr[lslice..i];
  90.                 }
  91.                 lslice = i+1;
  92.             }
  93.             i++;
  94. -           tmpattrstr.popFront();
  95. +           tmpstr.popFront();
  96.         }
  97.         // tack the last one on
  98. -       attrlist ~= attrstr[lslice..attrstr.length];
  99. +       predlist ~= predstr[lslice..predstr.length];
  100.         // length must be odd, otherwise the string is jank
  101. -       if (!(attrlist.length%2)) throw new XPathError("Encountered a janky predicate: "~to!string(attrstr));
  102. +       if (!(predlist.length%2)) throw new XPathError("Encountered a janky predicate: "~to!string(predstr));
  103.         // verify that odd numbers are "and" or "or"
  104. -       foreach (j,attr;attrlist) if (j%2 && attr != cast(R)"and" && attr != cast(R)"or") {
  105. +       foreach (j,attr;predlist) if (j%2 && attr != cast(R)"and" && attr != cast(R)"or") {
  106.             throw new XPathError("Encountered consecutive terms not separated by \"and\" or \"or\" starting at: "~to!string(attr));
  107.         } else if (!(j%2) && (attr == cast(R)"and" || attr == cast(R)"or")) {
  108. -           throw new XPathError("Encountered consecutive joining terms (\"and\" or \"or\") in: "~to!string(attrstr));
  109. +           throw new XPathError("Encountered consecutive joining terms (\"and\" or \"or\") in: "~to!string(predstr));
  110.         }
  111.         bool[]res;
  112. -       res.length = attrlist.length;
  113. +       res.length = predlist.length;
  114.         int numOrdTerms = 0;
  115. -       debug(xpath)foreach (attr;attrlist) {
  116. -           logline("Term: "~to!string(attr)~"\n");
  117. +       debug(xpath)foreach (pred;predlist) {
  118. +           logline("Term: "~to!string(pred)~"\n");
  119.         }
  120. -       foreach (j,attr;attrlist) if (!(j%2)) {
  121. -           debug(xpath)logline("matching on "~to!string(attr)~"\n");
  122. +       foreach (j,pred;predlist) if (!(j%2)) {
  123. +           debug(xpath)logline("matching on "~to!string(pred)~"\n");
  124. +           bool isattr   = false;      // is elem1 @attribute
  125. +           bool verbatim = false;      // is elem2 quoted string
  126. +           R elem1;            // Left of comparator
  127. +           R comparator;           // null, ">","<","=", ">=","<=","!="
  128. +           R elem2;            // right of comparator
  129. +
  130. +           if (pred.front == '@') {
  131. +               isattr = true;
  132. +               pred.popFront();
  133. +           }
  134. +           // TODO XXX check elem1/elem2 is an XPath func()
  135. +           elem1 = getWSToken(pred);
  136. +           pred = stripLeft(pred);
  137. +           // if there is still data in pred, it's time to look for a comparison operator
  138. +           if (pred.length) {
  139. +               // figure out what comparison needs to be done
  140. +               auto secelem = pred.save;
  141. +               if (!secelem.empty()) secelem.popFront();
  142. +               if (!secelem.empty() && (pred.front == '<' || pred.front == '>' || pred.front == '!') && secelem.front == '=') {
  143. +                   comparator = pred[0..2];
  144. +                   popN(pred, 2);
  145. +                   pred = stripLeft(pred);
  146. +               } else if (pred.front == '<' || pred.front == '>' || pred.front == '=') {
  147. +                   comparator = pred[0..1];
  148. +                   pred.popFront();
  149. +                   pred = stripLeft(pred);
  150. +               } else {
  151. +                   throw new XPathError("Could not determine comparator at: "~to!string(pred));
  152. +               }
  153. +               secelem = pred.save;
  154. +               if (!secelem.empty()) secelem.popFront();
  155. +               if (secelem.empty() && !isNumeric(to!string([pred.front]))) {
  156. +                   throw new XPathError("Badly formed XPath query: Non-numeric comparands must be quoted ("~to!string(pred)~")");
  157. +               }
  158. +               // strip off quotes if necessary
  159. +               if (pred.back == '"' && pred.front == '"') {
  160. +                   pred.popFront();
  161. +                   pred.popBack();
  162. +                   verbatim = true;
  163. +               } else if (pred.back == '"' || pred.front == '"') {
  164. +                   throw new XPathError("Badly formed XPath query: Missing quote ("~to!string(pred)~")");
  165. +               }
  166. +           }
  167. +           elem2 = pred.save;
  168.             // check to see if we're doing an attribute match
  169.             // there should be NO zero-length strings this far in
  170. -           if (attr.front == '@') {
  171. -               attr.popFront();
  172. -               R comparator;
  173. -               auto attrname = getWSToken(attr);
  174. -               bool verbatim = false;
  175. -               attr = stripLeft(attr);
  176. -               // if there is still data in attr, it's time to look for a comparison operator
  177. -               if (attr.length) {
  178. -                   // figure out what comparison needs to be done
  179. -                   auto secelem = attr.save;
  180. -                   if (!secelem.empty()) secelem.popFront();
  181. -                   if (!secelem.empty() && (attr.front == '<' || attr.front == '>' || attr.front == '!') && secelem.front == '=') {
  182. -                       comparator = attr[0..2];
  183. -                       popN(attr, 2);
  184. -                       attr = stripLeft(attr);
  185. -                   } else if (attr.front == '<' || attr.front == '>' || attr.front == '=') {
  186. -                       comparator = attr[0..1];
  187. -                       attr.popFront();
  188. -                       attr = stripLeft(attr);
  189. -                   } else {
  190. -                       throw new XPathError("Could not determine comparator at: "~to!string(attr));
  191. -                   }
  192. -                   secelem = attr.save;
  193. -                   if (!secelem.empty()) secelem.popFront();
  194. -                   if (secelem.empty() && !isNumeric(to!string([attr.front]))) {
  195. -                       throw new XPathError("Badly formed XPath query: Non-numeric comparands must be quoted ("~to!string(attr)~")");
  196. -                   }
  197. -                   // strip off quotes if necessary
  198. -                   if (attr.back == '"' && attr.front == '"') {
  199. -                       attr.popFront();
  200. -                       attr.popBack();
  201. -                       verbatim = true;
  202. -                   } else if (attr.back == '"' || attr.front == '"') {
  203. -                       throw new XPathError("Badly formed XPath query: Missing quote ("~to!string(attr)~")");
  204. -                   }
  205. -               }
  206. -               // make sure that if we pulled a comparator, there's something to compare on the other side
  207. -               if (comparator.length && !attr.length) throw new XPathError("Got a comparator without anything to compare");
  208. -               if (!hasAttribute(attrname)) {
  209. -                   debug(xpath)logline("could not find "~to!string(attrname)~"\n");
  210. +           if ( isattr ) {
  211. +               if (!hasAttribute(elem1)) {
  212. +                   debug(xpath)logline("could not find attr "~to!string(elem1)~"\n");
  213.                     res[j] = false;
  214.                     continue;
  215.                 }
  216. -               if (comparator.length) {
  217. -                   bool lres,neg = false,i1num = isNumeric(to!string(getAttribute(attrname))),i2num = isNumeric(to!string(attr));
  218. -                   double i1,i2;
  219. -                   // currently ignoring verbatim in this section of code
  220. -                   // we can't compare non-numerics without quotes
  221. -                   if (!verbatim && (!i1num || !i2num)) {
  222. -                       //res[j] = false;
  223. -                       //continue;
  224. -                       throw new XPathError("Badly formed XPath query: Non-numeric comparands must be quoted ("~to!string(attr)~")");
  225. -                   }
  226. -                   // get numeric equivalents
  227. -                   if (i1num) i1 = to!double(to!string(getAttribute(attrname)));
  228. -                   if (i2num) i2 = to!double(to!string(attr));
  229. -                   if (comparator.front == '<') {
  230. -                       if (i1num && i2num) {
  231. -                           lres = (i1 < i2);
  232. -                       } else {
  233. -                           lres = (getAttribute(attrname) < attr);
  234. -                       }
  235. -                   } else if (comparator.front == '>') {
  236. -                       if (i1num && i2num) {
  237. -                           lres = (i1 > i2);
  238. -                       } else {
  239. -                           lres = (getAttribute(attrname) > attr);
  240. -                       }
  241. -                   } else if (comparator.front == '!') neg = true;
  242. -                   // check to see if equality is also called for
  243. -                   if (comparator.back == '=') {
  244. -                       if (verbatim) {
  245. -                           if ((getAttribute(attrname) != attr && caseSen) || (icmp(getAttribute(attrname), attr) != 0 && !caseSen)) {
  246. -                               debug(xpath)logline("search value "~to!string(attr)~" did not match attribute value "~to!string(getAttribute(attrname))~"\n");
  247. -                               lres = false;
  248. -                           } else {
  249. -                               lres = true;
  250. -                           }
  251. -                       } else {
  252. -                           lres |= (i1 == i2);
  253. -                       }
  254. -                       if (neg) lres = !lres;
  255. -                   }
  256. -                   res[j] = lres;
  257. +               if( comparator.length == 0 ) {
  258. +                   // Just check for existance
  259. +                   res[j] = true;
  260.                     continue;
  261.                 }
  262. -               res[j] = true;
  263. -               continue;
  264. +               res[j] = compareXPathPredicate( elem1, comparator, elem2, getAttribute(elem1), caseSen );
  265.             }
  266. +           else if (true) {
  267. +               // assume elem1 is a tag
  268. +               //if (!comparator.length || !elem2.length) throw new XPathError("Is this really an Error?");
  269. +               foreach(child;getChildren) {
  270. +                   if( child.isCData || child.isXmlComment || child.isXmlPI || child.getName != elem1 )
  271. +                       continue;
  272. +              
  273. +                   if( compareXPathPredicate( elem1, comparator, elem2, child.getCData, caseSen )) {
  274. +                       res[j] = true;
  275. +                       break;
  276. +                   }
  277. +               }
  278. +           }              
  279.             // XXX take care of other types of matches other than attribute matches
  280. -       } else if (attr == cast(R)"or") {
  281. +       } else if (pred == cast(R)"or") {
  282.             numOrdTerms++;
  283.         }
  284.         // collect "and" terms into "or" groups
  285. @@ -1029,9 +1022,9 @@
  286.         ordTerms[0] = res[0];
  287.         debug(xpath)logline("res[0]="~to!(string)(res[0])~"\n");
  288.         numOrdTerms = 0; // we're using this as current position, now
  289. -       foreach (j,attr;attrlist) if (j%2) {
  290. +       foreach (j,attr;predlist) if (j%2) {
  291.             if (attr == cast(R)"and") {
  292. -               debug(xpath)logline("combining anded terms on ord term "~to!(string)(numOrdTerms)~" and i="~to!(string)(j)~" with res.length="~to!(string)(res.length)~" and attrlist.length="~to!(string)(attrlist.length)~"\n");
  293. +               debug(xpath)logline("combining anded terms on ord term "~to!(string)(numOrdTerms)~" and i="~to!(string)(j)~" with res.length="~to!(string)(res.length)~" and predlist.length="~to!(string)(predlist.length)~"\n");
  294.                 ordTerms[numOrdTerms] &= res[j+1];
  295.                 debug(xpath)logline("res["~to!(string)(j+1)~"]="~to!(string)(res[j+1])~"\n");
  296.             } else if (attr == cast(R)"or") {
  297. @@ -1047,7 +1040,59 @@
  298.         debug(xpath)logline("Ended up with "~to!(string)(ret)~"\n");
  299.         return ret;
  300.     }
  301. -  
  302. +
  303. +   private bool compareXPathPredicate( R elem1, R comparator, R elem2, R elem1value, bool caseSen )
  304. +   {
  305. +       // make sure that if we pulled a comparator, there's something to compare on the other side
  306. +       if (comparator.length && !elem2.length) throw new XPathError("Got a comparator without anything to compare");
  307. +       if (comparator.length) {
  308. +           bool lres,i1num = isNumeric(to!string(elem1value)),i2num = isNumeric(to!string(elem2));
  309. +           if (comparator.front == '<' || comparator.front == '>') {
  310. +               // Must be numeric
  311. +               if( !i2num )
  312. +                   throw new XPathError("Badly formed XPath query: comparator '"~to!string(comparator)~"' requires a numeric operand Not ("~to!string(elem2)~")");
  313. +               if( !i1num ) {
  314. +                   return false;
  315. +               }
  316. +          
  317. +               // get numeric equivalents
  318. +               double i1 = to!double(to!string(elem1value));
  319. +               double i2 = to!double(to!string(elem2));
  320. +
  321. +               if (comparator.front == '<') {
  322. +                   lres = i1 < i2;
  323. +               } else /*if (comparator.front == '>')*/ {
  324. +                   lres = i1 > i2;
  325. +               }
  326. +               // check to see if equality is also called for
  327. +               if (comparator.back == '=') {
  328. +                   lres |= (i1 == i2);
  329. +               }
  330. +           } else {
  331. +               bool neg = false;
  332. +               if (comparator.front == '!') neg = true;
  333. +
  334. +               if( !i1num || !i2num ) {
  335. +                   if ((elem1value != elem2 && caseSen) || (icmp(elem1value, elem2) != 0 && !caseSen)) {
  336. +                       debug(xpath)logline("search value "~to!string(elem2)~" did not match attribute value "~to!string(elem1value)~"\n");
  337. +                       lres = false;
  338. +                   } else {
  339. +                       lres = true;
  340. +                   }
  341. +               } else {
  342. +                   // get numeric equivalents
  343. +                   double i1 = to!double(to!string(elem1value));
  344. +                   double i2 = to!double(to!string(elem2));
  345. +                   lres = (i1 == i2);
  346. +               }
  347. +               if (neg) lres = !lres;
  348. +           }
  349. +           return lres;
  350. +       }
  351. +       return false;
  352. +   }
  353. +
  354. +
  355.     private bool isDeepPath(R xpath) {
  356.         // check to see if we're currently searching a deep path
  357.         auto secelem = xpath.save;
  358. @@ -1440,6 +1485,7 @@
  359.    *--------------------------
  360.    */
  361.  public int prealloc = 50;
  362. +///ditto
  363.  class XmlDocument(R=string) : XmlNode!R {
  364.     // this should inherit the reset and toXml that we want
  365.     protected XmlNode!R[]xmlNodes;
  366. @@ -1628,6 +1674,21 @@
  367.     string xmlstring = "<message responseID=\"1234abcd\" text=\"weather 12345\" type=\"message\" order=\"5\"><flags>triggered</flags><flags>targeted</flags></message>";
  368.     runTests(xmlstring);
  369.     runTests(cast(custring)xmlstring);
  370. +
  371. +   string xmlstring2 =
  372. +   `<table class="table1">
  373. +   <tr>         <th>URL </th><td><a href="path1/path2">Link 1.1</a></td></tr>
  374. +   <tr ab="two"><th>Head</th><td>Text 1.2</td></tr>
  375. +   <tr ab="4">  <th>Head</th><td>Text 1.3</td></tr>
  376. +   </table>
  377. +   <table class="table2">
  378. +   <tr>         <th>URL </th><td><a href="path1/path2">Link 2.1</a></td></tr>
  379. +   <tr ab="six"><th>Head</th><td>Text 2.2</td></tr>
  380. +   <tr ab="9">  <th>Head</th><td>Text 2.3</td></tr>
  381. +   </table>`;
  382. +
  383. +   runTests2(xmlstring2);
  384. +   runTests2(cast(custring)xmlstring2);
  385.  }
  386.  
  387.  version(XML_main) {
  388. @@ -1675,12 +1736,55 @@
  389.         assert(searchlist.length == 2 && searchlist[0].getName == cast(R)"flags");
  390.         searchlist = xml.parseXPath(cast(R)"/message[@order=5]/flags");
  391.         assert(searchlist.length == 2 && searchlist[0].getName == cast(R)"flags");
  392. -  
  393. +
  394. +       logline("kxml.xml XPath ??? tests\n");
  395. +       searchlist = xml.parseXPath(cast(R)`//@text`);
  396. +       assert(searchlist.length == 1 && searchlist[0].getCData == cast(R)"weather 12345");
  397. +       searchlist = xml.parseXPath(cast(R)`/message[flags="triggered" and flags="targeted"]/@order`);
  398. +       assert(searchlist.length == 1 && searchlist[0].getCData == cast(R)"5");
  399. +       searchlist = xml.parseXPath(cast(R)`/message[@order<6 and flags="triggered"]/flags`);
  400. +       assert(searchlist.length == 2 && searchlist[0].getName == cast(R)"flags");
  401. +       searchlist = xml.parseXPath(cast(R)`/message[@order<6 and flags="fail"]/flags`);
  402. +       assert(searchlist.length == 0);
  403. +
  404. +
  405.         /*logline("kxml.xml XPath subnode match test\n");
  406.         searchlist = xml.parseXPath("/message[flags@tweak]");
  407.         assert(searchlist.length == 2 && searchlist[0].getName == "flags");*/
  408.     }
  409.  
  410. +   void runTests2(R)(R xmlstring) {
  411. +       logline("Running More tests\n");
  412. +       auto xml = readDocument(xmlstring);
  413. +
  414. +       logline("kxml.xml XPath no-match tests\n");
  415. +       auto
  416. +       searchlist = xml.parseXPath(cast(R)`//ab`);
  417. +       assert(searchlist.length == 0);
  418. +       searchlist = xml.parseXPath(cast(R)`//ab=9`);       // Should this throw?
  419. +       assert(searchlist.length == 0);
  420. +       searchlist = xml.parseXPath(cast(R)`//td="Text2.2"`);   // Should this throw?
  421. +       assert(searchlist.length == 0);
  422. +       searchlist = xml.parseXPath(cast(R)`//tr[ab<=7]/td`);
  423. +       assert(searchlist.length == 0);
  424. +
  425. +       logline("kxml.xml XPath attr tests\n");
  426. +       searchlist = xml.parseXPath(cast(R)`//@ab`);
  427. +       assert(searchlist.length == 4);
  428. +       searchlist = xml.parseXPath(cast(R)`//@ab<=7`);
  429. +       assert(searchlist.length == 1);
  430. +       searchlist = xml.parseXPath(cast(R)`//table[@class!="table2"]//@ab`);
  431. +       assert(searchlist.length == 2);
  432. +//     searchlist = xml.parseXPath(cast(R)`//@class!="table2"//@ab`);  // Should this work?
  433. +//     assert(searchlist.length == 2);
  434. +
  435. +       logline("kxml.xml XPath predicate tests\n");
  436. +       searchlist = xml.parseXPath(cast(R)`//tr[@ab<=7]/td`);
  437. +       assert(searchlist.length == 1 && searchlist[0].getCData == cast(R)"Text 1.3");
  438. +       searchlist = xml.parseXPath(cast(R)`//tr[@ab>=9 and th="Head"]/td`);
  439. +       assert(searchlist.length == 1 && searchlist[0].getCData == cast(R)"Text 2.3");
  440. +   }
  441. +
  442.     struct custring {
  443.         string data;
  444.         void opAssign(custring assgn) {data = assgn.data;}
Advertisement
Add Comment
Please, Sign In to add comment