Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --- a/xml.d 2012-04-30 15:13:36.189695809 +0100
- +++ b/xml.d 2012-04-30 17:53:29.657357062 +0100
- @@ -842,14 +842,14 @@
- debug(xpath)logline("Got xpath "~to!string(xpath)~" in node "~to!string(getName)~"\n");
- R truncxpath;
- auto nextnode = getNextNode(xpath,truncxpath);
- - R attrmatch;
- + R predmatch;
- // XXX need to be able to split the attribute match off even when it doesn't have [] around it
- ptrdiff_t offset = nextnode.length - find(nextnode, cast(R)"[").length;
- if (offset != nextnode.length) {
- // rip out attribute string
- - attrmatch = nextnode[offset..nextnode.length];
- + predmatch = nextnode[offset..nextnode.length];
- nextnode = nextnode[0..offset];
- - debug(xpath)logline("Found attribute chunk: "~to!string(attrmatch)~"\n");
- + debug(xpath)logline("Found predicate chunk: "~to!string(predmatch)~"\n");
- }
- debug(xpath)logline("Looking for "~to!string(nextnode)~"\n");
- XmlNode[]retarr;
- @@ -858,7 +858,7 @@
- // we were searching for nodes, and this is one
- debug(xpath)logline("Found a node we want! name is: "~to!string(getName)~"\n");
- retarr ~= this;
- - } else foreach(child;getChildren) if (!child.isCData && !child.isXmlComment && !child.isXmlPI && child.matchXPathPredicate(attrmatch,caseSensitive)) {
- + } else foreach(child;getChildren) if (!child.isCData && !child.isXmlComment && !child.isXmlPI && child.matchXPathPredicate(predmatch,caseSensitive)) {
- if (!nextnode.length || (caseSensitive && child.getName == nextnode) || (!caseSensitive && !icmp(child.getName(), nextnode))) {
- // child that matches the search string, pass on the truncated string
- debug(xpath)logline("Sending "~to!string(truncxpath)~" to "~to!string(child.getName)~"\n");
- @@ -875,141 +875,163 @@
- return retarr;
- }
- - private bool matchXPathPredicate(R attrstr,bool caseSen) {
- - debug(xpath)logline("matching attribute string "~to!string(attrstr)~"\n");
- + private bool matchXPathPredicate(R predstr,bool caseSen) {
- + debug(xpath)logline("matching predicate string "~to!string(predstr)~"\n");
- // strip off the encasing [] if it exists
- - if (!attrstr.length) {
- + if (!predstr.length) {
- return true;
- }
- - if (attrstr.front == '[' && attrstr.back == ']') {
- - attrstr.popFront();
- - attrstr.popBack();
- - } else if (attrstr.front == '[' || attrstr.back == ']') {
- + if (predstr.front == '[' && predstr.back == ']') {
- + predstr.popFront();
- + predstr.popBack();
- + } else if (predstr.front == '[' || predstr.back == ']') {
- // this seems to be malformed
- - throw new XPathError("got malformed attribute match "~to!string(attrstr)~"\n");
- + throw new XPathError("got malformed predicate match "~to!string(predstr)~"\n");
- }
- // rip apart the xpath predicate assuming it's node and attribute matches
- - R[]attrlist;
- + R[]predlist;
- // basically, we're splitting on " and " and " or ", but while respecting []
- int bcount = 0, i = 0;
- ptrdiff_t lslice = 0;
- - R tmpattrstr = attrstr;
- - //foreach (i,c;attrstr) {
- - while(!tmpattrstr.empty()) {
- - auto c = tmpattrstr.front;
- + R tmppredstr = predstr;
- + //foreach (i,c;predstr) {
- + while(!tmppredstr.empty()) {
- + auto c = tmppredstr.front;
- if (c == '[') {
- bcount++;
- } else if (c == ']') {
- bcount--;
- } else if (bcount == 0 && c == ' ') {
- if (i != lslice) {
- - attrlist ~= attrstr[lslice..i];
- + predlist ~= predstr[lslice..i];
- }
- lslice = i+1;
- }
- i++;
- - tmpattrstr.popFront();
- + tmppredstr.popFront();
- }
- // tack the last one on
- - attrlist ~= attrstr[lslice..attrstr.length];
- + predlist ~= predstr[lslice..predstr.length];
- // length must be odd, otherwise the string is jank
- - if (!(attrlist.length%2)) throw new XPathError("Encountered a janky predicate: "~to!string(attrstr));
- + if (!(predlist.length%2)) throw new XPathError("Encountered a janky predicate: "~to!string(predstr));
- // verify that odd numbers are "and" or "or"
- - foreach (j,attr;attrlist) if (j%2 && attr != cast(R)"and" && attr != cast(R)"or") {
- + foreach (j,attr;predlist) if (j%2 && attr != cast(R)"and" && attr != cast(R)"or") {
- throw new XPathError("Encountered consecutive terms not separated by \"and\" or \"or\" starting at: "~to!string(attr));
- } else if (!(j%2) && (attr == cast(R)"and" || attr == cast(R)"or")) {
- - throw new XPathError("Encountered consecutive joining terms (\"and\" or \"or\") in: "~to!string(attrstr));
- + throw new XPathError("Encountered consecutive joining terms (\"and\" or \"or\") in: "~to!string(predstr));
- }
- bool[]res;
- - res.length = attrlist.length;
- + res.length = predlist.length;
- int numOrdTerms = 0;
- - debug(xpath)foreach (attr;attrlist) {
- - logline("Term: "~to!string(attr)~"\n");
- + debug(xpath)foreach (pred;predlist) {
- + logline("Term: "~to!string(pred)~"\n");
- }
- - foreach (j,attr;attrlist) if (!(j%2)) {
- - debug(xpath)logline("matching on "~to!string(attr)~"\n");
- + foreach (j,pred;predlist) if (!(j%2)) {
- + debug(xpath)logline("matching on "~to!string(pred)~"\n");
- + bool isattr = false; // is elem1 @attribute
- + bool verbatim = false; // is elem2 quoted string
- + R elem1; // Left of comparator
- + R comparator; // null, ">","<","=", ">=","<=","!="
- + R elem2; // right of comparator
- +
- + if (pred.front == '@') {
- + isattr = true;
- + pred.popFront();
- + }
- + // TODO XXX check elem1/elem2 is an XPath func()
- + elem1 = getWSToken(pred);
- + pred = stripLeft(pred);
- + // if there is still data in pred, it's time to look for a comparison operator
- + if (pred.length) {
- + // figure out what comparison needs to be done
- + auto secelem = pred.save;
- + if (!secelem.empty()) secelem.popFront();
- + if (!secelem.empty() && (pred.front == '<' || pred.front == '>' || pred.front == '!') && secelem.front == '=') {
- + comparator = pred[0..2];
- + popN(pred, 2);
- + pred = stripLeft(pred);
- + } else if (pred.front == '<' || pred.front == '>' || pred.front == '=') {
- + comparator = pred[0..1];
- + pred.popFront();
- + pred = stripLeft(pred);
- + } else {
- + throw new XPathError("Could not determine comparator at: "~to!string(pred));
- + }
- + secelem = pred.save;
- + if (!secelem.empty()) secelem.popFront();
- + if (secelem.empty() && !isNumeric(to!string([pred.front]))) {
- + throw new XPathError("Badly formed XPath query: Non-numeric comparands must be quoted ("~to!string(pred)~")");
- + }
- + // strip off quotes if necessary
- + if (pred.back == '"' && pred.front == '"') {
- + pred.popFront();
- + pred.popBack();
- + verbatim = true;
- + } else if (pred.back == '"' || pred.front == '"') {
- + throw new XPathError("Badly formed XPath query: Missing quote ("~to!string(pred)~")");
- + }
- + }
- + elem2 = pred.save;
- + debug(xpath) {
- + logline("Reconstructing ");
- + logline( isattr ? "@" : "");
- + logline( elem1.length ? ""~to!string(elem1) : "");
- + logline( comparator.length ? ""~to!string(comparator): "");
- + logline( elem2.length ? ""~to!string(elem2) : "");
- + logline(" verbatim is "~to!string(verbatim)~"\n");
- + }
- +
- // check to see if we're doing an attribute match
- // there should be NO zero-length strings this far in
- - if (attr.front == '@') {
- - attr.popFront();
- - R comparator;
- - auto attrname = getWSToken(attr);
- - bool verbatim = false;
- - attr = stripLeft(attr);
- - // if there is still data in attr, it's time to look for a comparison operator
- - if (attr.length) {
- - // figure out what comparison needs to be done
- - auto secelem = attr.save;
- - if (!secelem.empty()) secelem.popFront();
- - if (!secelem.empty() && (attr.front == '<' || attr.front == '>' || attr.front == '!') && secelem.front == '=') {
- - comparator = attr[0..2];
- - popN(attr, 2);
- - attr = stripLeft(attr);
- - } else if (attr.front == '<' || attr.front == '>' || attr.front == '=') {
- - comparator = attr[0..1];
- - attr.popFront();
- - attr = stripLeft(attr);
- - } else {
- - throw new XPathError("Could not determine comparator at: "~to!string(attr));
- - }
- - secelem = attr.save;
- - if (!secelem.empty()) secelem.popFront();
- - if (secelem.empty() && !isNumeric(to!string([attr.front]))) {
- - throw new XPathError("Badly formed XPath query: Non-numeric comparands must be quoted ("~to!string(attr)~")");
- - }
- - // strip off quotes if necessary
- - if (attr.back == '"' && attr.front == '"') {
- - attr.popFront();
- - attr.popBack();
- - verbatim = true;
- - } else if (attr.back == '"' || attr.front == '"') {
- - throw new XPathError("Badly formed XPath query: Missing quote ("~to!string(attr)~")");
- - }
- - }
- + if ( isattr ) {
- + auto attrname = elem1;
- // make sure that if we pulled a comparator, there's something to compare on the other side
- - if (comparator.length && !attr.length) throw new XPathError("Got a comparator without anything to compare");
- + if (comparator.length && !pred.length) throw new XPathError("Got a comparator without anything to compare");
- if (!hasAttribute(attrname)) {
- debug(xpath)logline("could not find "~to!string(attrname)~"\n");
- res[j] = false;
- continue;
- }
- if (comparator.length) {
- - bool lres,neg = false,i1num = isNumeric(to!string(getAttribute(attrname))),i2num = isNumeric(to!string(attr));
- - double i1,i2;
- - // currently ignoring verbatim in this section of code
- - // we can't compare non-numerics without quotes
- - if (!verbatim && (!i1num || !i2num)) {
- - //res[j] = false;
- - //continue;
- - throw new XPathError("Badly formed XPath query: Non-numeric comparands must be quoted ("~to!string(attr)~")");
- - }
- - // get numeric equivalents
- - if (i1num) i1 = to!double(to!string(getAttribute(attrname)));
- - if (i2num) i2 = to!double(to!string(attr));
- - if (comparator.front == '<') {
- - if (i1num && i2num) {
- - lres = (i1 < i2);
- - } else {
- - lres = (getAttribute(attrname) < attr);
- + bool lres,i1num = isNumeric(to!string(getAttribute(attrname))),i2num = isNumeric(to!string(elem2));
- + if (comparator.front == '<' || comparator.front == '>') {
- + // Must be numeric
- + if( !i2num )
- + throw new XPathError("Badly formed XPath query: comparator '"~to!string(comparator)~"' requires a numeric operand Not ("~to!string(elem2)~")");
- + if( !i1num ) {
- + res[j] = false;
- + continue;
- }
- - } else if (comparator.front == '>') {
- - if (i1num && i2num) {
- - lres = (i1 > i2);
- - } else {
- - lres = (getAttribute(attrname) > attr);
- +
- + // get numeric equivalents
- + double i1 = to!double(to!string(getAttribute(attrname)));
- + double i2 = to!double(to!string(elem2));
- +
- + if (comparator.front == '<') {
- + lres = i1 < i2;
- + } else /*if (comparator.front == '>')*/ {
- + lres = i1 > i2;
- }
- - } else if (comparator.front == '!') neg = true;
- - // check to see if equality is also called for
- - if (comparator.back == '=') {
- - if (verbatim) {
- - if ((getAttribute(attrname) != attr && caseSen) || (icmp(getAttribute(attrname), attr) != 0 && !caseSen)) {
- - debug(xpath)logline("search value "~to!string(attr)~" did not match attribute value "~to!string(getAttribute(attrname))~"\n");
- + // check to see if equality is also called for
- + if (comparator.back == '=') {
- + lres |= (i1 == i2);
- + }
- + } else {
- + bool neg = false;
- + if (comparator.front == '!') neg = true;
- +
- + if( !i1num || !i2num ) {
- + if ((getAttribute(attrname) != elem2 && caseSen) || (icmp(getAttribute(attrname), elem2) != 0 && !caseSen)) {
- + debug(xpath)logline("search value "~to!string(elem2)~" did not match attribute value "~to!string(getAttribute(attrname))~"\n");
- lres = false;
- } else {
- lres = true;
- }
- } else {
- - lres |= (i1 == i2);
- + // get numeric equivalents
- + double i1 = to!double(to!string(getAttribute(attrname)));
- + double i2 = to!double(to!string(elem2));
- + lres = (i1 == i2);
- }
- if (neg) lres = !lres;
- }
- @@ -1019,8 +1041,13 @@
- res[j] = true;
- continue;
- }
- + else if (true) {
- + // assume elem1 is a tag
- + //foreach
- +
- + }
- // XXX take care of other types of matches other than attribute matches
- - } else if (attr == cast(R)"or") {
- + } else if (pred == cast(R)"or") {
- numOrdTerms++;
- }
- // collect "and" terms into "or" groups
- @@ -1029,9 +1056,9 @@
- ordTerms[0] = res[0];
- debug(xpath)logline("res[0]="~to!(string)(res[0])~"\n");
- numOrdTerms = 0; // we're using this as current position, now
- - foreach (j,attr;attrlist) if (j%2) {
- + foreach (j,attr;predlist) if (j%2) {
- if (attr == cast(R)"and") {
- - 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");
- + 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");
- ordTerms[numOrdTerms] &= res[j+1];
- debug(xpath)logline("res["~to!(string)(j+1)~"]="~to!(string)(res[j+1])~"\n");
- } else if (attr == cast(R)"or") {
- @@ -1440,6 +1467,7 @@
- *--------------------------
- */
- public int prealloc = 50;
- +/// ditto
- class XmlDocument(R=string) : XmlNode!R {
- // this should inherit the reset and toXml that we want
- protected XmlNode!R[]xmlNodes;
Advertisement
Add Comment
Please, Sign In to add comment