2 * The contents of this file are subject to the Mozilla Public
3 * License Version 1.1 (the "License"); you may not use this file
4 * except in compliance with the License. You may obtain a copy of
5 * the License at http://www.mozilla.org/MPL/
7 * Software distributed under the License is distributed on an "AS
8 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9 * implied. See the License for the specific language governing
10 * rights and limitations under the License.
12 * The Original Code is the Sablotron XSLT Processor.
14 * The Initial Developer of the Original Code is Ginger Alliance Ltd.
15 * Portions created by Ginger Alliance are Copyright (C) 2000-2002
16 * Ginger Alliance Ltd. All Rights Reserved.
18 * Contributor(s): Marc Lehmann <pcg@goof.com>
20 * Alternatively, the contents of this file may be used under the
21 * terms of the GNU General Public License Version 2 or later (the
22 * "GPL"), in which case the provisions of the GPL are applicable
23 * instead of those above. If you wish to allow use of your
24 * version of this file only under the terms of the GPL and not to
25 * allow others to use your version of this file under the MPL,
26 * indicate your decision by deleting the provisions above and
27 * replace them with the notice and other provisions required by
28 * the GPL. If you do not delete the provisions above, a recipient
29 * may use your version of this file under either the MPL or the
40 #include "domprovider.h"
45 // period is not here as it can also start a number
53 ExToken tokenShortX[]=
55 TOK_DPERIOD, TOK_DCOLON, TOK_DSLASH, TOK_NEQ, TOK_LE, TOK_GE,
56 TOK_LPAREN, TOK_RPAREN, TOK_LBRACKET, TOK_RBRACKET, TOK_ATSIGN, TOK_COMMA,
57 TOK_VERT, TOK_PLUS, TOK_MINUS, TOK_EQ, TOK_LT, TOK_GT,
61 /*================================================================
63 ================================================================*/
73 // XPath functions - NODESET category
74 {"last",EXFF_LAST,EX_NUMBER},
75 {"position",EXFF_POSITION,EX_NUMBER},
76 {"count",EXFF_COUNT,EX_NUMBER},
77 {"id",EXFF_ID,EX_NODESET},
78 {"local-name",EXFF_LOCAL_NAME,EX_STRING},
79 {"namespace-uri",EXFF_NAMESPACE_URI,EX_STRING},
80 {"name",EXFF_NAME,EX_STRING},
82 // XPath - STRING category
83 {"string",EXFF_STRING,EX_STRING},
84 {"concat",EXFF_CONCAT,EX_STRING},
85 {"starts-with",EXFF_STARTS_WITH,EX_BOOLEAN},
86 {"contains",EXFF_CONTAINS,EX_BOOLEAN},
87 {"substring-before",EXFF_SUBSTRING_BEFORE,EX_STRING},
88 {"substring-after",EXFF_SUBSTRING_AFTER,EX_STRING},
89 {"substring",EXFF_SUBSTRING,EX_STRING},
90 {"string-length",EXFF_STRING_LENGTH,EX_NUMBER},
91 {"normalize-space",EXFF_NORMALIZE_SPACE,EX_STRING},
92 {"translate",EXFF_TRANSLATE,EX_STRING},
94 // XPath - BOOLEAN category
95 {"boolean",EXFF_BOOLEAN,EX_BOOLEAN},
96 {"not",EXFF_NOT,EX_BOOLEAN},
97 {"true",EXFF_TRUE,EX_BOOLEAN},
98 {"false",EXFF_FALSE,EX_BOOLEAN},
99 {"lang",EXFF_LANG,EX_BOOLEAN},
101 // XPath - NUMBER category
102 {"number", EXFF_NUMBER, EX_NUMBER},
103 {"sum", EXFF_SUM, EX_NUMBER},
104 {"floor", EXFF_FLOOR, EX_NUMBER},
105 {"ceiling", EXFF_CEILING, EX_NUMBER},
106 {"round", EXFF_ROUND, EX_NUMBER},
109 {"document",EXFF_DOCUMENT,EX_NODESET},
110 {"key",EXFF_KEY,EX_NODESET},
111 {"format-number",EXFF_FORMAT_NUMBER, EX_STRING},
112 {"current",EXFF_CURRENT, EX_NODESET},
113 {"unparsed-entity-uri",EXFF_UNPARSED_ENTITY_URI, EX_STRING},
114 {"generate-id",EXFF_GENERATE_ID,EX_STRING},
115 {"system-property",EXFF_SYSTEM_PROPERTY, EX_STRING},
118 {"function-available",EXFF_FUNCTION_AVAILABLE,EX_BOOLEAN},
119 {"element-available",EXFF_ELEMENT_AVAILABLE,EX_BOOLEAN},
120 {NULL, EXFF_NONE, EX_UNKNOWN}
123 Str getFuncName(ExFunctor functor)
125 return funcInfoTable[functor - EXF_FUNCTION - 1].name;
128 /**********************************************************
130 **********************************************************/
132 void TokenItem::speak(DStr &s, SpeakMode mode)
136 case TOK_VAR: // remove leading $
137 s.nadd(firstc + 1, len - 1);
139 case TOK_LITERAL:// remove enclosing quotes or dquotes
140 s.nadd(firstc + 1, len - 2);
145 // s += '\0'; - thrown out
148 /**********************************************************
152 **********************************************************/
154 Tokenizer::Tokenizer(Expression &owner_)
156 items(LIST_SIZE_EXPR_TOKENS), owner(owner_)
160 Tokenizer::~Tokenizer()
162 items.freeall(FALSE);
165 eFlag Tokenizer::tokenize(Sit S, const Str &astring)
172 E( getToken(S, p, item, TOK_NONE) );
173 ExToken prevToken = item.tok;
174 while ((item.tok != TOK_END) && (item.tok != TOK_NONE))
176 items.append(new TokenItem(item));
177 E( getToken(S, p, item, prevToken) );
178 prevToken = item.tok;
181 if (item.tok == TOK_NONE)
184 item.speak(itemStr, SM_OFFICIAL);
185 Err1(S, ET_BAD_TOKEN, itemStr);
188 items.append(new TokenItem(item));
193 /*================================================================
195 a table of tokens which have the effect that the following
196 name is recognized as a NCName (rather than operator name) and * is
197 recognized as a wildcard (rather than multiplication operator).
198 The table should end with TOK_NONE (which is of this type too).
199 ================================================================*/
201 static ExToken namerTable[] = {
202 TOK_ATSIGN, TOK_DCOLON, TOK_LPAREN, TOK_LBRACKET,
204 TOK_OR, TOK_AND, TOK_EQ, TOK_NEQ, TOK_LT, TOK_GT, TOK_LE, TOK_GE,
205 TOK_PLUS, TOK_MINUS, TOK_MINUS1, TOK_MULT, TOK_DIV, TOK_MOD, TOK_VERT,
206 // slashes are operators too but not for us
207 TOK_SLASH, TOK_DSLASH, TOK_COMMA,
208 // TOK_NONE (terminator)
211 /*================================================================
213 returns True iff the token is contained in the namerTable table.
214 ================================================================*/
216 static Bool isNamer(ExToken tok)
219 if (tok == TOK_NONE) return TRUE;
220 for (i = 0; (namerTable[i] != tok) &&
221 (namerTable[i] != TOK_NONE); i++);
222 return (Bool) (namerTable[i] == tok);
225 /*================================================================
227 looks up a few characters at p in the tokenShort table containing
228 the (up-to-3-char) symbols
229 RETURNS the token identified, or TOK_NONE if no match is found
230 ================================================================*/
232 ExToken Tokenizer::tryShort(char*& p, ExToken prevToken)
238 for (i=0, t=tokenShort; *t; i++,t+=3)
240 if ((t[1] == ' ') || (t[1] == p[1])) break;
243 p += ((t[1] == ' ')? 1 : 2);
244 result = tokenShortX[i];
245 if (result == TOK_STAR)
246 result = (isNamer(prevToken)? TOK_NAME : TOK_MULT);
247 if ((result == TOK_MINUS) && isNamer(prevToken))
250 else result = TOK_NONE;
254 /*================================================================
256 sets 'ret' to the following token, but does not change the pointer p
257 ================================================================*/
259 eFlag Tokenizer::lookToken(Sit S, ExToken &ret, char* p, ExToken prevToken)
261 // getToken_() changes p but this is passed by value so
264 E( getToken_(S, ret, p, prevToken) );
268 /*================================================================
270 sets p to address FOLLOWING the next occurence of c in p
271 (to skip a character reference, use findChar(p,';'))
272 RETURNS false iff the next occurence was not found
273 ================================================================*/
275 static Bool findChar(char*& p, char c)
277 while (*p && (*p != c)) p++;
287 /*================================================================
289 sets p to address FOLLOWING the next occurence of *p in p
290 RETURNS false iff another occurence was not found
291 ================================================================*/
293 static Bool findSame(char*& p)
296 return findChar(p, first);
299 eFlag Tokenizer::getToken_(Sit S, ExToken &ret, char*& p, ExToken prevToken)
312 // the following may also translate * into TOK_NAME
313 if ((tok = tryShort(p, prevToken)) != TOK_NONE)
322 // call getName with prevToken=TOK_NONE
323 // to ensure that e.g. 'and' in '$and' is treated as a name
324 E( getName(S, ret, ++p, TOK_NONE) );
334 Err(S, ET_INFINITE_LITERAL)
338 case '&': assert(0); //DBG: do not process entity references so far
341 if (utf8IsDigit(utf8CharCode(p+1)))
343 E( getNumber(S, p) );
353 if (utf8IsDigit(utf8CharCode(p)))
355 E( getNumber(S, p) );
360 if (utf8IsLetter(utf8CharCode(p)) || (*p == '_') || (*p == ':'))
362 // the following call finds TOK_NAME, TOK_FNAME,
364 // as well as TOK_AND etc. (based on prev token)
365 E( getName(S, ret, p, prevToken) );
371 Err1(S, ET_BAD_TOKEN, temp); //unknown token
380 eFlag Tokenizer::getToken(Sit S, char*& p, TokenItem& item, ExToken prevToken)
385 E( getToken_(S, t, p, prevToken) );
386 item.len = (long)(p - item.firstc);
391 eFlag Tokenizer::getNumber(Sit S, char*& p)
394 while ((*p) && (utf8IsDigit(utf8CharCode(p))) || (*p == '.'))
398 Err(S, ET_BAD_NUMBER)
400 p += utf8SingleCharLength(p);
405 /*================================================================
407 checks whether the sequence at p of given length is an operator name
408 RETURNS the appropriate token if so; TOK_NONE otherwise
409 ================================================================*/
411 static ExToken getWordOp(char *p, int length)
413 if (length > 3) return TOK_NONE;
414 if (length < 2) length = 2;
415 if (!strncmp(p,"or",length)) return TOK_OR;
416 if (length < 3) length = 3;
417 if (!strncmp(p,"and",length)) return TOK_AND;
418 if (!strncmp(p,"div",length)) return TOK_DIV;
419 if (!strncmp(p,"mod",length)) return TOK_MOD;
423 static Bool isNodeTest(char *p, int length)
427 for (int i = 0; (q = exNodeTypeNames[i]) != NULL; i++)
430 (length < (qlen = strlen(q))? qlen : length)))
433 return (Bool)(q != NULL);
436 #define nameCharExtended(CH, PTR) ((CH = utf8CharCode(PTR))!= 0) &&\
437 (utf8IsNameChar(CH) || strchr(".-_:*",CH))
439 int nameLength(char* from)
444 while(nameCharExtended(c,q)) {
445 q += utf8SingleCharLength(q);
451 eFlag Tokenizer::getName(Sit S, ExToken &ret, char*& p, ExToken prevToken)
455 BOOL wasColon = FALSE;
457 if ((!utf8IsLetter(utf8CharCode(p))) && (*p != '_'))
463 while (nameCharExtended(c,p))
469 // identify the bad qname;
471 theName.nset(former, nameLength(former));
472 Err1(S, E1_EXTRA_COLON, theName);
496 if ((p - former) && *(p - 1) != ':') // the first condition could be dropped
502 p += utf8SingleCharLength (p);
505 if (!wasColon && !isNamer(prevToken))
507 if ((ret = getWordOp(former, (int) (p - former))) != TOK_NONE)
513 // look at following token with prev=TOK_NAME
514 E( lookToken(S, next,p,TOK_NAME) );
522 if (isNodeTest(former, (int) (p - former)))
533 /*================================================================
535 finds the first top-level occurence of 'token' starting with
536 position 'from'. If there is none, return value points at TOK_END.
537 ================================================================*/
539 int Tokenizer::findTop(ExToken token, int from)
545 ((ct = items[i] -> tok) != TOK_END) && (level || (ct != token));
548 if ((ct == TOK_LPAREN) || (ct == TOK_LBRACKET))
550 if ((ct == TOK_RPAREN) || (ct == TOK_RBRACKET))
557 /*================================================================
559 given a position in 'pos', finds the corresponding next token.
560 If the left token is ( or [, looks for the matching right paren/bracket,
561 Otherwise looks for the occurence of the same token.
562 Returns pos pointing at the matching token, or at TOK_END if there
563 is none. (In case of failed reverse search, returns -1.)
564 ================================================================*/
566 eFlag Tokenizer::getDelim(Sit S, int &pos, Bool reverse /*=FALSE*/)
568 ExToken first, second, tok;
572 switch(first = items[pos] -> tok)
575 second = TOK_RBRACKET;
581 second = TOK_LBRACKET;
590 i += reverse? -1 : 1;
592 while ((i >= 0) && ((tok = items[i] -> tok) != TOK_END))
603 else if (tok == first) level++;
604 i += reverse? -1 : 1;
610 /*================================================================
612 given the left and right end positions of a tokenizer fragment,
613 shifts them inwards to strip any outer parentheses.
614 ================================================================*/
616 eFlag Tokenizer::stripParens(Sit S, int &left, int &right)
619 if (items[right]->tok == TOK_END)
621 // assert(left <= right);
622 while ((items[left]->tok == TOK_LPAREN)
623 && (items[right]->tok == TOK_RPAREN))
626 E( getDelim(S, left0) );
637 void Tokenizer::report(Sit S, MsgType type, MsgCode code, const Str& arg1, const Str& arg2)
639 owner.report(S, type, code, arg1, arg2);
643 /*****************************************************************
647 *****************************************************************/
649 LocStep::LocStep(Element& ownerV_, ExAxis _axis /*=AXIS_NONE*/,
650 ExNodeType _ntype /*=EXNODE_NONE*/)
651 : preds(LIST_SIZE_1), ownerV(ownerV_)
660 preds.freeall(FALSE);
663 void LocStep::set(ExAxis _axis, ExNodeType _ntype)
669 void LocStep::speak(Sit S, DStr &strg, SpeakMode mode)
671 if (!(mode & SM_CONTENTS)) return;
675 // case AXIS_DESC_OR_SELF:
683 strg += axisNames[ax];
687 if((ntype != EXNODE_NONE) //&& (ax != AXIS_DESC_OR_SELF)
688 && (ax != AXIS_ROOT))
690 strg += exNodeTypeNames[ntype];
696 getOwnerElement().getOwner().expandQStr(ntest,fullName);
697 // ntest.speak(S, strg,mode);
700 int i, predsNumber = preds.number();
701 for (i = 0; i < predsNumber; i++)
704 preds[i] -> speak(S, strg,mode);
709 eFlag LocStep::parse(Sit S, Tokenizer& tokens, int& pos,
710 Bool defaultToo /* = FALSE */)
717 tok = tokens.items[i++]->tok;
719 Err(S, ET_EMPTY_PATT);
735 tok = tokens.items[i++]->tok;
745 tokens.items[i-1] -> speak(temp, SM_OFFICIAL);
746 if ((ax = (ExAxis) lookup(temp, axisNames)) == AXIS_NONE)
747 Err1(S, E1_UNKNOWN_AXIS, temp);
749 tok = tokens.items[i++] -> tok;
756 // Err(S, ET_EXPR_SYNTAX);
761 Err(S, ET_EXPR_SYNTAX); // axis name or node-test expected
764 // axis has been determined; tok must now be a name or a node-test
766 if ((tok != TOK_NAME) && (tok != TOK_NTNAME))
767 Err(S, ET_EXPR_SYNTAX); // axis name or node-test expected
768 tokens.items[i-1] -> speak(temp,SM_OFFICIAL);
771 if (tok == TOK_NTNAME)
773 ntype = (ExNodeType) lookup(temp, exNodeTypeNames);
774 if (tokens.items[i++]->tok != TOK_LPAREN)
775 Err(S, ET_LPAREN_EXP);
777 //pi test may hav one literal param
778 if (ntype == EXNODE_PI && tokens.items[i]->tok == TOK_LITERAL) {
780 tokens.items[i++]->speak(pilit, SM_CONTENTS);
784 if (tokens.items[i++]->tok != TOK_RPAREN)
786 Err(S, ET_RPAREN_EXP);
791 // set the QName from prefix:uri using
792 // the namespace declarations in 'ownerE'
793 // (passed through from the containing attribute)
794 // DEFAULT: without using the default namespace
795 E( getOwnerElement().setLogical(S, ntest, temp, defaultToo) );
798 while ((tokens.items[i] -> tok) == TOK_LBRACKET)
801 E( tokens.getDelim(S, right = i) );
802 if (tokens.items[right] -> tok == TOK_END)
803 Err(S, ET_RBRACKET_EXP)
806 GP( Expression ) ex = new Expression(getOwnerElement());
807 E( (*ex).parse(S, tokens,i+1,right-1) );
808 // find out about the use of last() and position()
809 int exPositionalType = (*ex).optimizePositional(0);
810 if (exPositionalType)
813 if (exPositionalType == 2)
815 // find sufficient position bounds
816 (*ex).optimizePositionBounds();
818 preds.append(ex.keep());
823 return OK; // pointing at the first char that does not start a predicate
825 }; // end LocStep::parse()
828 Bool LocStep::matchesWithoutPreds(Sit S, NodeHandle v)
830 // removed the following:
832 // (because the parent-of-root calls etc.)
835 // ANDed with VT_BASE for ordinary vertices
836 SXP_NodeType ty = S.dom().getNodeType(v);
846 if (ty != PROCESSING_INSTRUCTION_NODE)
850 if (ty != COMMENT_NODE)
854 if ((ty == TEXT_NODE) || (ty == COMMENT_NODE) ||
855 (ty == DOCUMENT_NODE) || (ty == PROCESSING_INSTRUCTION_NODE))
863 return (Bool) (ty == DOCUMENT_NODE);
866 if (ty != ATTRIBUTE_NODE) return FALSE;
869 if (ty != NAMESPACE_NODE) return FALSE;
872 case AXIS_DESCENDANT:
873 case AXIS_DESC_OR_SELF:
875 case AXIS_ANC_OR_SELF:
876 case AXIS_FOLL_SIBLING:
877 case AXIS_PREC_SIBLING:
888 case AXIS_DESC_OR_SELF:
890 case AXIS_ANC_OR_SELF:
899 if (ntype == EXNODE_NONE && ty != ELEMENT_NODE) return false;
903 default: assert(0); //should be handled in parse
906 //if (ntype != EXNODE_NONE)
914 if (! S.domExternal(v) && ! (piname == ""))
916 ProcInstr *pi = toPI(v);
918 pi->getOwner().expandQ(pi->name, ename);
919 return ename.getLocal() == piname;
928 //exnode_none are processed here
929 if (S.domExternal(v))
931 const char *uri = S.dom().getNodeNameURI(v);
932 const char *local = S.dom().getNodeNameLocal(v);
933 Bool ret = getOwnerElement().getOwner()
934 .cmpQNameStrings(ntest, uri, local);
935 S.dom().freeName(v, (char*)uri);
936 S.dom().freeName(v, (char*)local);
941 const QName &hisname = toV(v) -> getName();
942 return getOwnerElement().getOwner()
943 .cmpQNamesForeign(ntest, toV(v) -> dict(), hisname);
948 eFlag LocStep::shift(Sit S, NodeHandle &v, NodeHandle baseV)
950 NodeHandle w = NULL; // the result
955 assert(S.domExternal(v) || nhNull(v) ||
956 isElement(baseV) && baseV == toV(v) -> parent);
957 // i.e. v==NULL and baseV isn't daddy
958 if (S.dom().getNodeType(baseV) != ELEMENT_NODE) break;
959 for (w = (!nhNull(v) ?
960 S.dom().getNextAttrNS(v) :
961 S.dom().getAttributeNo(baseV, 0));
962 !nhNull(w) && !matchesWithoutPreds(S, w);
963 w = S.dom().getNextAttrNS(w));
968 assert(S.domExternal(v) || nhNull(v) ||
969 isDaddy(baseV) && baseV == toV(v) -> parent);
970 if (S.dom().getNodeType(baseV) != ELEMENT_NODE &&
971 S.dom().getNodeType(baseV) != DOCUMENT_NODE) break;
972 for (w = !nhNull(v) ?
973 S.dom().getNextSibling(v) :
974 S.dom().getFirstChild(baseV);
975 !nhNull(w) && !matchesWithoutPreds(S, w);
976 w = S.dom().getNextSibling(w));
981 assert(S.domExternal(v) || nhNull(v) ||
982 isElement(baseV) && baseV == toV(v) -> parent);
983 if (S.dom().getNodeType(baseV) != ELEMENT_NODE) break;
984 for (w = !nhNull(v) ?
985 S.dom().getNextAttrNS(v) :
986 S.dom().getNamespaceNo(baseV, 0);
987 !nhNull(w) && !matchesWithoutPreds(S, w);
988 w = S.dom().getNextAttrNS(w));
991 case AXIS_ROOT: // technically not an axis
995 w = S.dom().getOwnerDocument(baseV);
996 if (nhNull(w)) // w must have been the root itself
1003 if (nhNull(v) && matchesWithoutPreds(S, baseV))
1009 if (nhNull(v) && matchesWithoutPreds(S, S.dom().getParent(baseV)))
1010 w = S.dom().getParent(baseV);
1014 case AXIS_ANC_OR_SELF:
1017 w = S.dom().getParent(v);
1019 w = (ax == AXIS_ANCESTOR) ? S.dom().getParent(baseV) : baseV;
1020 for (; !nhNull(w) && !matchesWithoutPreds(S, w);
1021 w = S.dom().getParent(w));
1024 case AXIS_FOLL_SIBLING:
1026 switch(S.dom().getNodeType(baseV))
1029 case NAMESPACE_NODE:
1030 case ATTRIBUTE_NODE:
1033 for (w = S.dom().getNextSibling(!nhNull(v) ? v : baseV);
1034 !nhNull(w) && !matchesWithoutPreds(S, w);
1035 w = S.dom().getNextSibling(w));
1039 case AXIS_PREC_SIBLING:
1041 switch(S.dom().getNodeType(baseV))
1044 case NAMESPACE_NODE:
1045 case ATTRIBUTE_NODE:
1048 for (w = S.dom().getPreviousSibling(!nhNull(v) ? v : baseV);
1049 !nhNull(w) && !matchesWithoutPreds(S, w);
1050 w = S.dom().getPreviousSibling(w));
1054 case AXIS_DESCENDANT:
1055 case AXIS_DESC_OR_SELF:
1059 if (ax == AXIS_DESC_OR_SELF && matchesWithoutPreds(S, baseV))
1068 // find next descendant
1071 if ((S.dom().getNodeType(v) == ELEMENT_NODE ||
1072 (S.dom().getNodeType(v) == DOCUMENT_NODE)) &&
1073 S.dom().getChildCount(v))
1074 v = S.dom().getFirstChild(v);
1080 v = S.dom().getNextSibling(v);
1084 v = S.dom().getParent(v0);
1087 if (v != baseV && matchesWithoutPreds(S, v))
1095 case AXIS_FOLLOWING:
1098 if (!nhNull(v) && S.dom().getChildCount(v))
1099 w = S.dom().getChildNo(v,0);
1104 while (!nhNull(v) && nhNull(S.dom().getNextSibling(v)))
1105 v = S.dom().getParent(v);
1107 w = S.dom().getNextSibling(v);
1112 } while (!nhNull(w) && !matchesWithoutPreds(S, w));
1114 case AXIS_PRECEDING:
1117 v = !nhNull(v) ? v : baseV;
1118 w = S.dom().getPreviousSibling(v);
1121 while (0 != (childCount = S.dom().getChildCount(w)))
1122 w = S.dom().getChildNo(w,childCount - 1);
1124 w = S.dom().getParent(v);
1125 //eliminate ancestors of baseV
1126 for (v = S.dom().getParent(baseV);
1127 !nhNull(v) && v != w;
1128 v = S.dom().getParent(v));
1129 //we're in the ancestor line
1131 while (!nhNull(v) && !(S.dom().getPreviousSibling(v)))
1132 v = S.dom().getParent(v);
1134 w = S.dom().getPreviousSibling(v);
1140 } while (!nhNull(w) && !matchesWithoutPreds(S, w));
1149 void LocStep::report(Sit S, MsgType type, MsgCode code, const Str& arg1, const Str& arg2)
1151 ownerV.report(S, type, code, arg1, arg2);
1156 /**********************************************************
1158 **********************************************************/
1165 Number::Number(double y)
1171 Number::operator= (double y)
1178 Number::operator= (const Str &s)
1180 char *endptr, *startptr = s;
1181 skipWhite(startptr);
1184 x = strtod(startptr, &endptr);
1196 Number::operator double() const
1201 Bool Number::operator== (double y)
1203 if (isNaN() || isnan__(y))
1205 if (isInf() || isinf__(y))
1206 return isInf() && isinf__(y) && !((x > 0) ^ (y > 0));
1208 return (Bool)((d < EPS) && (d > -EPS));
1211 Bool Number::operator== (Number& y)
1213 if (isNaN()) return FALSE;
1214 return (Bool)(operator== ((double) y));
1218 Bool Number::operator< (double y)
1220 return (Bool)(x < y);
1223 Bool Number::operator< (Number& y)
1225 return (Bool)(x < (double) y);
1228 Bool Number::operator> (double y)
1230 return (Bool)(x > y);
1233 Bool Number::operator> (Number& y)
1235 return (Bool)(x > (double) y);
1238 Bool Number::isNaN()
1243 Bool Number::isInf()
1248 void Number::setNaN()
1250 // divide by zero using a variable, to fool too clever compilers
1251 // 'volatile' suggested by Dirk Siebnich
1252 volatile int zero = 0;
1258 if (isNaN() || isInf())
1260 else return (int)(floor(x + 0.5)); // FIXME: ignoring the 'negative zero'
1264 //________________________________________________________________
1266 /*****************************************************************
1270 *****************************************************************/
1273 External::External(void *prv, void *val)
1275 priv = new JSExternalPrivate(prv, val);
1278 External::External(External& other)
1285 External::~External()
1290 void* External::getValue()
1295 void External::assign(const External& other)
1298 priv = other.priv; priv->refcnt++;
1301 void External::decref()
1315 /*****************************************************************
1319 *****************************************************************/
1321 Expression::Expression(Element &owner_,
1322 ExFunctor _functor /* = EXF_NONE */
1324 : args(1), owner(owner_)
1331 step = new LocStep(owner_);
1349 // the following sets patomnodeset, note that e.g. patomnumber
1350 // is with it in a union
1351 patomnodeset = NULL;
1354 optimizePositionFrom = optimizePositionTo = 0; // see header
1357 void Expression::setLS(ExAxis _axis, ExNodeType _ntype)
1359 assert(functor == EXF_LOCPATH);
1360 Expression *ls = new Expression(getOwnerElement(), EXF_LOCSTEP); // GP: OK
1362 ls -> step -> set(_axis, _ntype);
1366 // Expression destructor
1368 Expression::~Expression()
1373 #define deleteZ( PTR ) { cdelete( PTR ); }
1377 // called when setting the expression to a new value
1378 // to dispose of any existing contents.
1380 void Expression::clearContent()
1382 args.freeall(FALSE);
1390 deleteZ ( patomnodeset );
1393 deleteZ ( patomstring );
1396 deleteZ ( patomnumber );
1400 deleteZ( patomexternal);
1409 case EXF_OTHER_FUNC:
1418 /*================================================================
1420 writes the expression to string 'strg'. Formatting is specified
1422 ================================================================*/
1424 eFlag Expression::speak(Sit S, DStr &strg, SpeakMode mode)
1426 int i, argsNumber = args.number();
1432 E( tostring(S, temp) );
1437 step -> speak(S, strg, mode);
1441 for(i = 0; i < argsNumber; i++)
1443 args[i] -> speak(S, strg, mode);
1444 if (i < argsNumber-1)
1446 else if ((argsNumber == 1) &&
1447 (args[0] -> step -> ax == AXIS_ROOT))
1453 strg += (DStr("\nfunctor ") + (int) functor + "\n--------ARGS:\n");
1454 for (i = 0; i < argsNumber; i++)
1456 strg += DStr("(") + (i+1) + ") ";
1457 args[i] -> speak(S, strg, mode);
1460 strg += "--------ARGS end\n";
1466 /*================================================================
1468 returns TRUE iff the current vertex of c satisfies the
1469 Expression's condition.
1471 perhaps this should also return an error in case the expression is
1472 not of type nodeset?
1473 ================================================================*/
1475 eFlag Expression::matchesPattern(Sit S, Context *c, Bool &result)
1477 assert(type == EX_NODESET);
1478 if (functor == EXF_LOCPATH)
1480 E(matchesSinglePath(S, c -> current(), args.number() - 1, result));
1483 if (functor == EXFO_UNION)
1485 int j, argsNumber = args.number();
1486 for (j = 0; j < argsNumber; j++)
1488 E( args[j] -> matchesPattern(S, c, result) );
1490 RetOK( result, TRUE );
1493 RetOK(result, FALSE);
1496 eFlag Expression::trueFor(Sit S, Context *c, Bool& result)
1498 Expression ex(getOwnerElement());
1499 E( eval(S, ex, c) );
1503 result = (Bool) (ex.tonumber(S) == (double) (c -> getPosition() + 1));
1506 result = ex.tobool();
1512 Bool Expression::tobool()
1514 assert(functor == EXF_ATOM);
1518 return (Bool) !(*patomnumber == 0.0 || patomnumber -> isNaN());
1521 return (Bool) !(patomstring -> isEmpty());
1527 return (Bool) !!(patomnodeset -> getSize());
1531 return FALSE; //just to return something
1534 eFlag Expression::tostring(Sit S, Str& strg)
1536 assert(functor == EXF_ATOM);
1540 if (patomnumber -> isNaN())
1541 strg = (char*)"NaN";
1544 if (!patomnumber -> isInf())
1545 strg = (double)(*patomnumber);
1546 else if (*patomnumber > 0.0)
1547 strg = (char*)"+Infinity";
1548 else strg = (char*)"-Infinity";
1552 strg = *patomstring;
1555 strg = (atombool ? (char *)"true" : (char *)"false");
1558 if (!patomnodeset -> getSize())
1563 S.dom().constructStringValue(patomnodeset -> current(), temp);
1569 strg = (char*)"[External Object]";
1576 const Str& Expression::tostringRef() const
1578 assert((functor == EXF_ATOM) && (type == EX_STRING));
1579 return (*NZ(patomstring));
1582 Number Expression::tonumber(Sit S)
1584 assert(functor == EXF_ATOM);
1595 n = (atombool ? 1.0 : 0.0);
1599 // to avoid the following, tostring() must return const Str&:
1603 // but note that changing it to n = tostringRef() failed
1612 External Expression::toexternal(Sit S)
1614 assert(functor == EXF_ATOM);
1620 e.assign(*patomexternal);
1621 //e.setValue(patomexternal -> getValue());
1629 Context& Expression::tonodeset()
1631 assert((functor == EXF_ATOM) && (type == EX_NODESET));
1632 return *(patomnodeset -> copy());
1635 const Context& Expression::tonodesetRef()
1637 assert((functor == EXF_ATOM) && (type == EX_NODESET));
1638 return *patomnodeset;
1641 eFlag Expression::patternOK(Sit S)
1644 argsNumber = args.number();
1645 // assert(functor == EXFO_UNION || functor == EXF_LOCPATH);
1647 if ( containsFunctor(EXFF_CURRENT) )
1648 Err(S, E_BAD_PATTERN);
1654 for (i = 0; i < argsNumber; i++)
1656 LocStep *ls = args[i] -> step;
1660 case AXIS_ATTRIBUTE:
1663 case AXIS_DESC_OR_SELF:
1664 if (ls -> ntype != EXNODE_NODE)
1665 Err(S, E_BAD_PATTERN);
1668 Err(S, E_BAD_AXIS_IN_PATTERN);
1674 for (i=0; i < argsNumber; i++)
1675 E(args[i] -> patternOK(S));
1678 Err(S, E_BAD_PATTERN);
1679 // assert(!"patternOK()");
1684 eFlag Expression::parse(Sit S, const DStr &string,
1685 Bool _isPattern /* = FALSE */,
1686 Bool defaultToo /* = FALSE */)
1688 isPattern = _isPattern;
1690 E( t.tokenize(S, string) );
1691 E( parse(S, t, 0, t.items.number() - 1, defaultToo) );
1697 /*================================================================
1699 returns True if the given token is an operator, in which case
1700 'precedence' is set to its precedence
1701 ================================================================*/
1703 Bool Expression::isOp(ExToken token, int &precedence)
1748 /*================================================================
1749 void getFunctionInfo()
1750 returns function code and type for the function with given name
1751 if no such builtin function, returns EXFF_NONE
1752 ================================================================*/
1754 void getFunctionInfo(const Str &s, ExFunctor &code, ExType &type)
1756 char *p = (char *) s;
1759 for (i = 0; funcInfoTable[i].name; i++)
1761 if (!strcmp(funcInfoTable[i].name,p))
1764 code = funcInfoTable[i].func;
1765 type = funcInfoTable[i].type;
1776 {EXFO_OR, EX_BOOLEAN, 3},
1777 {EXFO_AND, EX_BOOLEAN, 3},
1778 {EXFO_EQ, EX_BOOLEAN, 2},
1779 {EXFO_NEQ, EX_BOOLEAN, 2},
1780 {EXFO_LT, EX_BOOLEAN, 2},
1781 {EXFO_GT, EX_BOOLEAN, 2},
1782 {EXFO_LE, EX_BOOLEAN, 2},
1783 {EXFO_GE, EX_BOOLEAN, 2},
1784 {EXFO_PLUS, EX_NUMBER, 2},
1785 {EXFO_MINUS2, EX_NUMBER, 2},
1786 {EXFO_MULT, EX_NUMBER, 2},
1787 {EXFO_MOD, EX_NUMBER, 2},
1788 {EXFO_DIV, EX_NUMBER, 2},
1789 {EXFO_MINUS1, EX_NUMBER, 1},
1790 {EXFO_UNION, EX_NODESET, 3}
1793 /*================================================================
1795 ================================================================*/
1797 eFlag Expression::parseLP(Sit S, Tokenizer& tokens, int &pos,
1798 Bool dropRoot, Bool defaultToo /*=FALSE*/)
1800 assert(functor == EXF_LOCPATH);
1802 BOOL getaway = FALSE;
1803 Expression *ls; // GP: OK (immediately appended)
1806 slashPending = FALSE,
1810 tok = tokens.items[i] -> tok;
1812 Err(S, ET_EMPTY_PATT);
1813 if ((tok == TOK_SLASH) || (tok== TOK_DSLASH))
1817 args.append(ls = new Expression(getOwnerElement(),EXF_LOCSTEP));
1818 ls -> step -> set(AXIS_ROOT,EXNODE_NODE);
1820 if (tok == TOK_SLASH)
1826 tok = tokens.items[i] -> tok;
1837 Err(S, ET_EXPR_SYNTAX);
1838 args.append(ls = new Expression(getOwnerElement(),EXF_LOCSTEP));
1839 E( ls -> step -> parse(S, tokens, i, defaultToo) );
1840 slashPending = FALSE;
1842 //nameRecent = (tok == TOK_NAME || tok == TOK_NTNAME)
1848 args.append(ls = new Expression(getOwnerElement(),EXF_LOCSTEP));
1849 ls -> step -> set(AXIS_DESC_OR_SELF, EXNODE_NODE);
1855 Err(S, ET_EXPR_SYNTAX);
1856 slashPending = TRUE;
1858 if (tokens.items[i] -> tok == TOK_END)
1859 Err(S, ET_EMPTY_PATT);
1865 default: getaway = TRUE;
1868 if ((slashPending && nameWas) || !args.number())
1869 Err(S, ET_EMPTY_PATT);
1875 /*================================================================
1877 parses the basic expression in tokenizer 't' between positions
1878 'from' and 'to' inclusive. The basic expression is guaranteed
1879 to contain no operators (except for / and //) nor outer
1881 ================================================================*/
1883 eFlag Expression::parseBasic(Sit S, Tokenizer &t, int from, int to,
1884 Bool defaultToo /* = FALSE */)
1886 GP( Expression ) e, lp;
1887 // find the start of the filtering predicates
1888 int fstart, fright, fleft;
1891 switch(t.items[from] -> tok)
1900 t.getDelim(S, fstart = from + 1);
1906 t.getDelim(S, fstart = from);
1914 //#pragma Msg("adding '+1':")
1915 if ((fstart != -1) && (fstart <= to))
1917 switch(t.items[fstart] -> tok)
1923 // parse the filtered expression into args[0]
1924 e = new Expression(getOwnerElement()); // is a GP
1925 E( (*e).parse(S, t, from, fstart - 1));
1926 args.append(e.keep());
1928 functor = EXF_FILTER;
1931 while (t.items[fleft] -> tok == TOK_LBRACKET)
1933 t.getDelim(S, fright = fleft);
1934 if ((t.items[fright] -> tok == TOK_END) || (fright > to))
1935 Err(S, ET_RBRACKET_EXP);
1936 if (fleft + 1 == fright)
1937 Err(S, ET_EXPR_SYNTAX);
1938 E( (e = new Expression(getOwnerElement())) -> parse(S, t,
1939 fleft + 1, fright - 1, defaultToo) );
1940 args.append(e.keep());
1943 if (((tok = t.items[fleft] -> tok) == TOK_SLASH)
1944 || (tok == TOK_DSLASH))
1946 E( (lp = new Expression(getOwnerElement(), EXF_LOCPATH)) -> parseLP(
1947 S, t, fleft, TRUE, defaultToo) );
1949 args.append(lp.keep());
1951 if (fleft != to + 1)
1952 Err(S, ET_EXPR_SYNTAX);
1960 tok = t.items[from] -> tok;
1961 t.items[from] -> speak(temp,SM_OFFICIAL);
1962 if ((tok == TOK_VAR) || (tok == TOK_LITERAL)
1963 || (tok == TOK_NUMBER))
1965 switch(t.items[from] -> tok)
1972 E( getOwnerElement().setLogical(S,
1973 *(pName = new QName), temp, FALSE) );
1979 patomstring = new Str(temp);
1985 *(patomnumber = new Number) = temp;
1989 Err(S, ET_EXPR_SYNTAX);
1993 if (tok == TOK_FNAME)
1997 getFunctionInfo(temp,funcNo,funcType);
1998 if (funcNo != EXFF_NONE)
2005 functor = EXF_OTHER_FUNC;
2006 E( getOwnerElement().setLogical(S,
2007 *(pName = new QName), temp, FALSE) );
2012 assert(t.items[i] -> tok == TOK_LPAREN);
2014 // original loop test:
2015 // while (t.items[j = t.findTop(TOK_COMMA,i)] -> tok != TOK_END)
2016 while (((j = t.findTop(TOK_COMMA,i)) <= to) && (t.items[j] -> tok != TOK_END))
2018 switch(t.items[j-1] -> tok)
2022 Err(S, ET_EXPR_SYNTAX);
2024 args.append(e = new Expression(getOwnerElement()));
2026 E( (*e).parse(S, t,i,j-1, defaultToo) );
2030 if ((t.items[j = t.findTop(TOK_RPAREN,i)]->tok == TOK_END) ||
2033 Err(S, ET_RPAREN_EXP);
2035 if(t.items[j-1] -> tok == TOK_COMMA)
2036 Err(S, ET_EXPR_SYNTAX);
2037 if (j > i) // if any args
2039 args.append(e = new Expression(getOwnerElement()));
2041 E( (*e).parse(S, t,i,j-1, defaultToo) );
2044 Err(S, ET_EXPR_SYNTAX);
2045 } // end "tok == TOK_FNAME"
2047 { // it must be a LocPath
2049 functor = EXF_LOCPATH;
2051 E( parseLP(S, t, howfar, FALSE, defaultToo) );
2052 if (howfar != to + 1)
2053 Err(S, ET_EXPR_SYNTAX);
2059 /*================================================================
2061 translates the given token list into an expression (a tree of
2062 'Expression' objects plus some leaves).
2064 t a tokenizer whose tokenize() method has been called
2065 from,to first and last position in the token list the parsing
2066 applies to (i.e. a complex expression will parse the
2067 subexpressions with the same tokenizer but different
2069 ================================================================*/
2071 eFlag Expression::parse(Sit S, Tokenizer& t, int from, int to,
2072 Bool defaultToo /* = FALSE */)
2078 mintoken = TOK_NONE;
2086 Err(S, ET_EXPR_SYNTAX);
2088 t.stripParens(S, from,to);
2089 // search from right to left (left-associativity)
2090 for (i = to; i >= from; i--)
2092 switch(token = t.items[i] -> tok)
2098 E( t.getDelim(S, i,TRUE) ); // i is decremented at loop end
2100 Err(S, ET_LPARCKET_EXP);
2105 if (isOp(token, precedence) && (precedence < minprec))
2107 minprec = precedence;
2108 minndx = leftmost = i;
2110 // if (token == TOK_OR) break;
2113 if (token == mintoken)
2119 //minndx now points to the rightmost lowest-precedence operator
2120 // leftmost points at its leftmost occurence
2123 E( parseBasic(S, t, from, to, defaultToo) )
2126 int tablendx = t.items[minndx] -> tok - TOKGROUP_OPERATORS;
2127 functor = opTable[tablendx].fu;
2128 type = opTable[tablendx].ty;
2129 arity = opTable[tablendx].arity;
2130 Expression *e = new Expression(getOwnerElement()); // GP: OK
2138 Err(S, ET_EXPR_SYNTAX)
2140 E( e -> parse(S, t,from+1,to, defaultToo) );
2145 E( e -> parse(S, t,from,minndx - 1, defaultToo) );
2146 args.append(e = new Expression(getOwnerElement())); // GP: OK
2147 E( e -> parse(S, t,minndx + 1, to, defaultToo) );
2152 E( e -> parse(S, t,from,leftmost - 1, defaultToo) );
2153 int another = leftmost,
2156 // the following fails for "x and not(x and x)"
2157 // t.getDelim(another);
2159 another = t.findTop(t.items[another]->tok, another+1);
2160 while((another <= to) && (t.items[another]->tok != TOK_END))
2162 args.append(e = new Expression(getOwnerElement())); // GP: OK
2163 E( e -> parse(S, t, lastone + 1, another - 1, defaultToo));
2167 // t.getDelim(another); failed too, for "x and x and (x and x)"
2168 another = t.findTop(t.items[another]->tok, another+1);
2170 args.append(e = new Expression(getOwnerElement())); // GP: OK
2171 E( e -> parse(S, t,lastone + 1, to, defaultToo) );
2179 void Expression::setAtom(Context *c)
2187 void Expression::setAtom(const Number& n)
2192 *(patomnumber = new Number) = (Number&) n;
2195 void Expression::setAtom(Bool b)
2203 void Expression::setAtom(const DStr &s)
2208 patomstring = new Str(s);
2212 void Expression::setAtom(const External &ext)
2217 patomexternal = new External();
2218 patomexternal -> assign(ext);
2222 /*================================================================
2224 sets the expression to point to a 'result tree fragment' - a newly
2225 constructed tree - whose address it returns
2226 ================================================================*/
2228 Tree* Expression::setFragment()
2230 functor = EXF_FRAGMENT;
2232 return pTree = new Tree("RTF", FALSE); // not an XSL tree
2235 #define funcIsOperator(f) ((EXFO_OR <= f) && (f <= EXFO_Z))
2236 #define funcIsBuiltin(f) ((EXF_FUNCTION <= f) && (f <= EXFF_NONE))
2238 eFlag Expression::eval(Sit S, Expression &retxpr, Context *c, Bool resolvingGlobals /* = FALSE */)
2240 assert(!isPattern && "evaluating pattern!");
2247 //cannot use retxpr = *this !!!
2251 retxpr.setAtom(*patomstring);
2254 retxpr.setAtom(*patomnumber);
2257 retxpr.setAtom(patomnodeset -> copy());
2260 retxpr.setAtom(atombool);
2264 retxpr.setAtom(*patomexternal);
2272 Expression *ex = NZ(S.getProcessor()) -> getVarBinding(*pName);
2275 // if we're resolving globals, this may mean a forward reference
2276 if (resolvingGlobals)
2278 E( S.getProcessor() -> resolveGlobal(S, c, *pName) );
2279 ex = S.getProcessor() -> getVarBinding(*pName);
2284 getOwnerTree().expandQStr(*pName, fullName);
2285 Err1(S, E1_VAR_NOT_FOUND, fullName);
2288 E( ex -> eval(S, retxpr, c) );
2293 assert(c && "context is null!");
2295 E( createContext(S, newc) );
2297 // assign newc directly without copying
2298 retxpr.setAtom((*newc).copy());
2301 case EXF_OTHER_FUNC: // other function
2304 Str uri = getOwnerTree().expand(pName -> getUri());
2305 Str name = getOwnerTree().expand(pName -> getLocal());
2307 if (! S.getProcessor() -> supportsFunction(uri, name) )
2310 getOwnerTree().expandQStr(*pName, fullName);
2311 Err1(S, ET_FUNC_NOT_SUPPORTED,fullName);
2314 argsNumber = args.number();
2315 // ExprList atoms(LIST_SIZE_1);
2316 GPD( ExprList ) atoms = new ExprList(LIST_SIZE_1);
2318 GP( Expression ) ex;
2319 for (i = 0; i < argsNumber; i++)
2321 ex = new Expression(getOwnerElement());
2322 E( args[i]->eval(S, *ex, c, resolvingGlobals) );
2323 (*atoms).append(ex.keep());
2327 E( S.getProcessor()->callJSFunction(S, c, uri, name, *atoms, retxpr) );
2332 assert(c && "context is null!");
2334 E( createContext(S, newc, c -> getPosition()) );
2336 retxpr.setAtom((*newc).copy());
2342 Expression temp(getOwnerElement());
2344 argsNumber = args.number();
2345 for (i = 0; i < argsNumber; i++)
2347 E( args[i] -> eval(S, temp, c, resolvingGlobals) );
2349 E( temp.tostring(S, tempStr) );
2352 retxpr.setAtom(result);
2356 newc = new Context(NULL); //current nde not needed _cn_
2357 (*newc).set(&(pTree -> getRoot()));
2358 retxpr.setAtom((*newc).copy());
2364 argsNumber = args.number();
2365 // ExprList atoms(LIST_SIZE_1);
2366 GPD( ExprList ) atoms = new ExprList(LIST_SIZE_1);
2368 GP( Expression ) ex;
2369 for (i = 0; i < argsNumber; i++)
2371 ex = new Expression(getOwnerElement());
2372 E( args[i]->eval(S, *ex, c, resolvingGlobals) );
2373 (*atoms).append(ex.keep());
2375 if (funcIsOperator(functor))
2376 E( callOp(S, retxpr, *atoms) ) //an operator
2378 if (funcIsBuiltin(functor))
2379 E( callFunc(S, retxpr, *atoms, c) ) //a core XPath function
2383 getOwnerTree().expandQStr(*pName, fullName);
2384 Err1( S, ET_FUNC_NOT_SUPPORTED, fullName );
2386 // atoms autodeleted
2395 Bool hardCompare(ExFunctor op, T b1, T b2)
2400 case EXFO_EQ: return (Bool) (b1 == b2); break;
2401 case EXFO_NEQ: return (Bool) !(b1 == b2); break;
2402 case EXFO_LT: return (Bool) (b1 < b2); break;
2403 case EXFO_GT: return (Bool) (b2 < b1); break;
2404 case EXFO_LE: return (Bool) ((b1 < b2) || (b1 == b2)); break;
2405 case EXFO_GE: return (Bool) ((b2 < b1) || (b1 == b2)); break;
2408 return FALSE; //just to return something
2411 ExFunctor _invertOp(ExFunctor op)
2415 case EXFO_EQ: return EXFO_EQ; break;
2416 case EXFO_NEQ: return EXFO_NEQ; break;
2417 case EXFO_LT: return EXFO_GT; break;
2418 case EXFO_GT: return EXFO_LT; break;
2419 case EXFO_LE: return EXFO_GE; break;
2420 case EXFO_GE: return EXFO_LE; break;
2421 default: assert(!"_invertOp"); return EXF_NONE; // to return something
2425 Bool atomicCompare(ExFunctor op, const Str& str1, const Str& str2,
2432 return hardCompare(op, str1, str2);
2441 if (num2) n2 = *num2;
2443 return hardCompare(op, n1, n2);
2447 assert(!"atomicCompare");
2450 return FALSE; //just to return something
2453 Bool Expression::compareCC(Sit S, ExFunctor op, const Context &c1, const Context &c2)
2457 c1prime = ((Context&) c1).copy(),
2458 c2prime = ((Context&) c2).copy();
2459 Bool resulting = FALSE;
2461 while ((*c1prime).current())
2464 S.dom().constructStringValue((*c1prime).current(), str1);
2466 while((*c2prime).current())
2469 S.dom().constructStringValue((*c2prime).current(), str2);
2470 if ( atomicCompare(op, str1, str2, NULL) )
2479 // would be done automatically:
2485 Bool Expression::compareCS(Sit S, ExFunctor op, const Context &c1, const Str &str2)
2488 Bool resulting = FALSE;
2489 GP( Context ) c = ((Context&) c1).copy();
2490 //some optimization on str to num conversion
2491 Number* num2 = NULL;
2492 if (op != EXFO_EQ && op != EXFO_NEQ)
2494 num2 = new Number();
2499 while((*c).current())
2502 S.dom().constructStringValue((*c).current(), str1);
2503 if ( atomicCompare(op, str1, str2, num2) )
2511 if (num2) delete num2;
2515 Bool Expression::compareCN(Sit S, ExFunctor op, const Context &c1, const Number& n2)
2519 GP( Context ) c = ((Context&) c1).copy();
2520 Bool resulting = FALSE;
2523 while((*c).current())
2526 S.dom().constructStringValue((*c).current(), s1);
2528 if (hardCompare(op, n1, (Number)n2))
2539 eFlag Expression::compare(Sit S, Bool &result, Expression &other, ExFunctor op)
2540 // Both *this and other are assumed to be ATOMS.
2542 assert(functor == EXF_ATOM);
2543 assert(other.functor == EXF_ATOM);
2545 ExType histype = other.type;
2547 //check external object
2548 if (type == EX_EXTERNAL || histype == EX_EXTERNAL)
2550 Err(S, E_INVALID_OPERAND_TYPE);
2554 if (type == EX_NODESET)
2556 if (other.type == EX_BOOLEAN)
2557 result = hardCompare(op, tobool(), other.tobool());
2560 Context& mynodeset = tonodeset(); // GP: OK
2564 result = compareCC(S, op, mynodeset, other.tonodesetRef());
2569 E( other.tostring(S, temp) );
2570 result = compareCS(S, op, mynodeset, temp);
2573 result = compareCN(S, op, mynodeset, other.tonumber(S));
2582 if (histype == EX_NODESET)
2584 E( other.compare(S, result, *this, _invertOp(op)) );
2587 { // none of the two are nodesets
2592 if (type == EX_BOOLEAN || histype == EX_BOOLEAN)
2593 result = hardCompare(op, tobool(), other.tobool());
2596 if (type == EX_NUMBER || histype == EX_NUMBER)
2597 result = hardCompare(op, tonumber(S), other.tonumber(S));
2600 if (type == EX_STRING || histype == EX_STRING)
2602 Str temp, otherTemp;
2603 E( tostring(S, temp) );
2604 E( other.tostring(S, otherTemp) );
2605 result = hardCompare(op, temp, otherTemp);
2617 result = hardCompare(op, tonumber(S), other.tonumber(S));
2625 eFlag Expression::callOp(Sit S, Expression& retxpr, ExprList& atoms)
2628 atomsNumber = atoms.number();
2634 assert(atomsNumber > 1);
2636 result = atoms[0] -> tobool();
2637 for (i = 1; i < atomsNumber; i++)
2639 if (functor == EXFO_OR)
2641 if (atoms[i] -> tobool())
2649 if (!atoms[i] -> tobool())
2656 retxpr.setAtom(result);
2665 assert(atomsNumber == 2);
2667 E( atoms[0]->compare(S, result,*(atoms[1]),functor) );
2668 retxpr.setAtom(result);
2676 assert(atomsNumber > 1);
2678 result = (double) (atoms[0] -> tonumber(S));
2679 for (i = 1; i < atomsNumber; i++)
2684 result += atoms[i] -> tonumber(S);
2687 result -= atoms[i] -> tonumber(S);
2690 result *= atoms[i] -> tonumber(S);
2693 result /= atoms[i] -> tonumber(S);
2697 double d = atoms[i] -> tonumber(S);
2698 double aux = result/d;
2699 aux = aux > 0 ? floor(aux) : ceil(aux);
2700 result = result - (aux * d);
2705 //eliminate the -0 effect
2706 if (!result) result = 0;
2707 retxpr.setAtom(Number(result));
2711 assert(atomsNumber == 1);
2712 retxpr.setAtom(Number(-(double)(atoms[0] -> tonumber(S))));
2718 // this appends the list of nodes with given ID, possibly with multiplicities
2719 void appendNodesWithID(Sit S, Str& ids, Context *c, Context& result)
2721 char *p = (char*) ids;
2723 NodeHandle singleNode;
2725 for (skipWhite(p); *p; skipWhite(p))
2727 singleID.nset(p, tokenLen = strcspn(p,theWhitespace));
2728 SXP_Document doc = S.dom().getOwnerDocument(c -> current());
2729 singleNode = S.dom().getNodeWithID(doc, singleID);
2731 result.append(singleNode);
2736 /*================================================================
2738 calls the built-in XPath or XSLT function as determined by
2739 'this -> functor'. Returns an expression in 'retxpr'.
2740 'atoms' is a list of atomized arguments, 'c' is the current context
2741 necessary for certain functions.
2742 ================================================================*/
2744 #define checkArgsCount(x) if (atomsNumber != x)\
2745 Err(S, ET_BAD_ARGS_N);
2746 #define checkArgsCountMax(x) if (atomsNumber > x)\
2747 Err(S, ET_BAD_ARGS_N);
2748 #define checkArgsCountMin(x) if (atomsNumber < x)\
2749 Err(S, ET_BAD_ARGS_N);
2750 #define checkArgsCountBetween(x,y) if ((atomsNumber < x) || \
2751 (atomsNumber > y)) Err(S, ET_BAD_ARGS_N);
2753 // only check for being a nodeset
2754 #define checkIsNodeset(x) if (atoms[x] -> type != EX_NODESET)\
2755 Err(S, ET_BAD_ARG_TYPE);
2757 // Everything is a string, in a cosmic sense
2758 #define checkIsString(x)
2759 #define checkIsString2(x,y)
2760 #define checkIsNumber(x)
2764 Finds the first complete occurence of q in p; returns the 0-based starting
2765 position, or -1 if not found
2766 Now works for UTF-8 strings.
2769 int firstOccurence(char *p, char *q)
2778 charlen = utf8SingleCharLength(p + i);
2779 if (!strncmp(p + i, q + j, charlen))
2786 i = (iCurr += utf8SingleCharLength(p + iCurr));
2799 Returns in s the portion of the source string between from and to inclusive.
2800 If to == -1 then copies the whole rest of the string.
2801 Now works for multibyte UTF-8 characters.
2804 void getBetween(Str& s, char *source, int from, int to)
2809 if (from < 0) from = 0;
2810 for (i = 0; *source && (i <= to || to == -1); i++)
2816 // no need to go through the rest of the string
2819 if (!(*source & 0x80))
2820 // optimize for ASCII chars
2823 source += utf8SingleCharLength(source);
2832 s.nset(start,(int)(source - start));
2838 Returns the string value of the current node
2841 eFlag getCurrValue(Sit S, Str &s, Context *c)
2845 if (!!(v = c -> current()))
2846 S.dom().constructStringValue(v, temp);
2853 // An auxiliary function which retrieves the document from a given URI,
2854 // regardless of whether DOM is external or not. Should be moved into S.dom().
2856 // new model for processing on external documents: first if have the
2857 // processor and we're running on external, the DOMProvider is asked
2859 eFlag Expression::getDocument_(Sit S, NodeHandle& newroot,
2860 const Str& location, const Str& baseUri,
2864 if ( proc && proc -> processingExternal() )
2866 /* FIXME: this is incomplete */
2867 newroot = S.dom().retrieveDocument(location, baseUri);
2869 //external not active or failed
2870 if ( nhNull(newroot) )
2876 if (baseUri == (const char*)"")
2877 locBase = proc -> baseForVertex(S, &getOwnerElement());
2880 makeAbsoluteURI(S, location, locBase, uri);
2882 //deny document fragments for the 'file' scheme
2883 const char* aux = (char*)uri;
2884 const char *colon = strchr(aux, ':');
2886 && ( ((colon - aux == 4) && !strncmp(aux, "file", 4)) ||
2887 ((colon - aux == 3) && !strncmp(aux, "arg", 3))))
2889 if ( strchr((const char*)uri, '#') )
2890 Err1(S, E_DOC_FRAGMENT, (char*)uri);
2892 //if we found no colon, it's bad, but we do not care right here
2895 proc -> readTreeFromURI(S, newtree, uri,
2897 baseForVertex(S, &getOwnerElement()),
2899 S.hasFlag(SAB_IGNORE_DOC_NOT_FOUND));
2901 newroot = (NodeHandle) &(newtree -> getRoot());
2903 proc -> stripTree(S, *(newtree));
2904 } else if (! S.hasFlag(SAB_IGNORE_DOC_NOT_FOUND))
2905 return NOT_OK; //propagate an error
2909 Err1(S, E1_URI_OPEN, location);
2917 eFlag Expression::callFunc(Sit S, Expression &retxpr, ExprList &atoms, Context *c)
2920 int atomsNumber = atoms.number();
2926 retxpr.setAtom( Number(c -> getSize()) );
2931 retxpr.setAtom( Number(c -> getPosition() + 1) );
2937 Number(atoms[0] -> tonodesetRef().getSize()) );
2939 case EXFF_LOCAL_NAME:
2940 case EXFF_NAMESPACE_URI:
2943 checkArgsCountMax(1);
2946 v = (c -> isFinished()) ? NULL : c -> current();
2950 const Context& newc = atoms[0] -> tonodesetRef();
2951 v = (newc.isVoid()? NULL : newc.current());
2955 if (!S.domExternal(v))
2957 const QName& q = toV(v) -> getName();
2961 toV(v) -> getOwner().expandQStr(q, strg); break;
2962 case EXFF_LOCAL_NAME:
2963 strg = toV(v) -> getOwner().expand(q.getLocal()); break;
2964 case EXFF_NAMESPACE_URI:
2965 strg = toV(v) -> getOwner().expand(q.getUri()); break;
2974 strg = (aux = S.dom().getNodeName(v));
2975 S.dom().freeName(v, (char*)aux);
2977 case EXFF_LOCAL_NAME:
2979 strg = (aux = S.dom().getNodeNameLocal(v));
2980 S.dom().freeName(v, (char*)aux);
2982 case EXFF_NAMESPACE_URI:
2984 strg = (aux = S.dom().getNodeNameURI(v));
2985 S.dom().freeName(v, (char*)aux);
2990 retxpr.setAtom(strg);
2997 checkArgsCountMax(1);
2999 E( getCurrValue(S, string, c) )
3001 E( atoms[0] -> tostring(S, string) );
3002 retxpr.setAtom(string);
3007 checkArgsCountMin(2);
3009 for (int k = 0; k < atomsNumber; k++)
3013 E( atoms[k] -> tostring(S, atomStr) );
3016 retxpr.setAtom(strg);
3019 case EXFF_STARTS_WITH:
3022 checkIsString2(0,1);
3024 E( atoms[0] -> tostring(S, a0Str) );
3025 E( atoms[1] -> tostring(S, a1Str) );
3026 retxpr.setAtom((Bool) !firstOccurence(
3033 checkIsString2(0,1);
3035 E( atoms[0] -> tostring(S, a0Str) );
3036 E( atoms[1] -> tostring(S, a1Str) );
3037 retxpr.setAtom((Bool) (firstOccurence(
3038 a0Str, a1Str) != -1));
3041 case EXFF_SUBSTRING_BEFORE:
3042 case EXFF_SUBSTRING_AFTER:
3045 Str theBigger, theSmaller;
3046 E( atoms[0] -> tostring(S, theBigger) );
3047 E( atoms[1] -> tostring(S, theSmaller) );
3049 checkIsString2(0,1);
3050 int where = firstOccurence(theBigger,theSmaller);
3055 if (functor == EXFF_SUBSTRING_BEFORE)
3060 getBetween(strg, theBigger, 0, where-1);
3063 getBetween(strg, theBigger,
3064 where + utf8StrLength(theSmaller), -1);
3066 retxpr.setAtom(strg);
3069 case EXFF_SUBSTRING:
3071 checkArgsCountBetween(2,3);
3072 checkIsString(0); checkIsNumber(1);
3073 /* useless test causing a warning in MSVC:
3075 if (atomsNumber == 3)
3079 Number from_ = atoms[1] -> tonumber(S) - 1;
3080 if (!from_.isNaN() && !from_.isInf())
3082 int from = from_.round(),
3084 if (atomsNumber > 2)
3086 // use length in 3rd argument
3087 Number len = atoms[2] -> tonumber(S);
3088 if (len <= 0 || len.isNaN())
3090 else if (!len.isInf())
3091 to = from + len.round() - 1; // otherwise it remains -1
3094 E( atoms[0] -> tostring(S, a0Str) );
3095 getBetween(strg, a0Str, from, to);
3097 retxpr.setAtom(strg);
3100 case EXFF_STRING_LENGTH:
3102 checkArgsCountBetween(0,1);
3107 E( atoms[0] -> tostring(S, a0Str) );
3108 retxpr.setAtom(Number(utf8StrLength(a0Str)));
3113 E( getCurrValue(S, string, c) );
3114 retxpr.setAtom(Number(utf8StrLength(string)));
3118 case EXFF_NORMALIZE_SPACE:
3120 checkArgsCountBetween(0,1);
3125 E( atoms[0] -> tostring(S, string) );
3128 E( getCurrValue(S, string, c) );
3129 char *p = (char*) string;
3145 retxpr.setAtom(stripped);
3148 case EXFF_TRANSLATE:
3151 checkIsString2(0,1);
3155 Str baseStr, srcStr, destStr;
3157 E( atoms[0] -> tostring(S, baseStr) );
3158 E( atoms[1] -> tostring(S, srcStr) );
3159 E( atoms[2] -> tostring(S, destStr) );
3161 // changing tostringCharPtr() to tostring():
3168 pos = utf8Strchr(src, p);
3172 else if ((destchar = utf8StrIndex(dest,pos)) == NULL) {
3173 p += utf8SingleCharLength(p);
3176 resulting.nadd(destchar, utf8SingleCharLength(destchar));
3177 p += utf8SingleCharLength(p);
3179 retxpr.setAtom(resulting);
3185 retxpr.setAtom(atoms[0] -> tobool());
3191 retxpr.setAtom((Bool)!(atoms[0] -> tobool()));
3197 retxpr.setAtom(TRUE);
3203 retxpr.setAtom(FALSE);
3212 E( atoms[0] -> tostring(S, langQuery) );
3213 NodeHandle w, att = NULL;
3215 const char* langValue = NULL;
3216 for (w = c -> current(); w && !langValue; w = S.dom().getParent(w))
3218 // find whether w has an xml:lang attribute
3219 if (!S.domExternal(w)) {
3221 searchName.setUri(getOwnerTree().unexpand(theXMLNamespace));
3222 searchName.setLocal(getOwnerTree().unexpand("lang"));
3223 int idx = toE(w) -> atts.findNdx(searchName);
3225 langValue = toA( toE(w) -> atts[idx]) -> cont;
3228 attCount = S.dom().getAttributeCount(w);
3229 for (i = 0; i < attCount && !langValue; i++)
3231 att = S.dom().getAttributeNo(w, i);
3232 const char *uri = S.dom().getNodeNameURI(att);
3233 const char *local = S.dom().getNodeNameLocal(att);
3234 if (!strcmp(uri, theXMLNamespace)
3235 && !strcmp(local, "lang"))
3236 langValue = S.dom().getNodeValue(att);
3237 S.dom().freeName(att, (char*)uri);
3238 S.dom().freeName(att, (char*)local);
3244 char *langQPtr = (char*) langQuery;
3247 // if strings equal except for possibly a suffix starting with -
3249 if (!strncasecmp(langQPtr, langValue, qlen = langQuery.length())
3250 && (langValue[qlen] == 0 || langValue[qlen] == '-'))
3251 retxpr.setAtom(TRUE);
3253 retxpr.setAtom(FALSE);
3255 S.dom().freeValue(att, (char*)langValue);
3258 retxpr.setAtom(FALSE);
3263 checkArgsCountMax(1);
3268 E( getCurrValue(S, string, c) );
3272 n = atoms[0] -> tonumber(S);
3282 GP( Context ) newc = &(atoms[0] -> tonodeset());
3284 while (!(*newc).isFinished())
3287 S.dom().constructStringValue((*newc).current(), string);
3298 retxpr.setAtom(sum);
3307 Number n = atoms[0] -> tonumber(S);
3311 n = floor((double)n); break;
3313 n = ceil((double)n); break;
3315 n = floor((double)n + .5); break;
3322 checkArgsCountMin(1);
3323 checkArgsCountMax(2);
3327 if (atomsNumber == 2)
3330 const Context& aux = atoms[1] -> tonodesetRef();
3331 if ( ! aux.isVoid() )
3333 NodeHandle n = aux[0];
3334 if (! S.domExternal(n) )
3336 baseUri = NZ(toV(n) -> subtree) -> getBaseURI();
3343 //atoms[1] -> tostring(S, baseUri);
3347 NodeHandle newroot = NULL;
3348 Processor *proc = S.getProcessor();
3349 //assert(S.domExternal((void*)1) || proc); //_ph_ sxp
3350 GP( Context ) newc = new Context(c->getCurrentNode());
3352 // Current node doesn't change
3353 //(*newc).setCurrentNode (c->getCurrentNode());
3355 // GP: the context doesn't autodelete anything on error
3356 // since readTreeFromURI adds the trees to datalines list
3357 // All datalines removed on error (in cleanupAfterRun())
3359 if (atoms[0] -> type == EX_NODESET)
3361 const Context& ctxt = atoms[0] -> tonodesetRef();
3362 int ctxtNumber = ctxt.getSize();
3363 for (int k = 0; k < ctxtNumber; k++)
3365 S.dom().constructStringValue(ctxt[k], location);
3366 E( getDocument_(S, newroot, location, baseUri, proc) );
3367 // check for duplicities and correct URI sorting!
3368 if (! nhNull(newroot) )
3370 (*newc).append(newroot);
3371 if (proc && !S.domExternal(newroot)) // _ph_ spx
3372 E( proc -> makeKeysForDoc(S, newroot) );
3378 E( atoms[0] -> tostring(S, location) );
3379 E( getDocument_(S, newroot, location, baseUri, proc) );
3380 if (! nhNull(newroot) )
3382 (*newc).append(newroot);
3383 if (proc && !S.domExternal(newroot)) //_ph_ spx
3384 E( proc -> makeKeysForDoc(S, newroot) );
3387 retxpr.setAtom(newc.keep());
3390 case EXFF_GENERATE_ID:
3396 v = (c -> isFinished() ? NULL : c -> current());
3401 const Context& newc = atoms[0] -> tonodesetRef();
3402 v = (newc.isVoid()? NULL : newc.current());
3405 Err(S, ET_BAD_ARGS_N);
3410 if (S.domExternal(v))
3412 // need cast to long then to int to avoid
3413 // compiler error on IBM AIX
3414 s += (int) (long) v;
3418 s += (int) (long) &(toV(v) -> getOwner());
3420 s += toV(v) -> stamp;
3426 case EXFF_SYSTEM_PROPERTY:
3432 E( atoms[0] -> tostring(S, a0Str) );
3433 E( getOwnerElement().setLogical(S, q, a0Str, FALSE) );
3434 if (q.getUri() == getOwnerTree().stdPhrase(PHRASE_XSL_NAMESPACE))
3436 const Str& localStr = getOwnerTree().expand(q.getLocal());
3437 if (localStr == (const char*) "version")
3438 retxpr.setAtom(Number(1.0));
3439 else if (localStr == (const char*) "vendor")
3440 retxpr.setAtom(Str("Ginger Alliance"));
3441 else if (localStr == (const char*) "vendor-url")
3442 retxpr.setAtom(Str("www.gingerall.com"));
3444 retxpr.setAtom(Str(""));
3446 else if (q.getUri() == getOwnerTree().stdPhrase(PHRASE_SABEXT_NAMESPACE))
3448 const Str& localStr = getOwnerTree().expand(q.getLocal());
3449 if (localStr == (const char*) "version")
3450 retxpr.setAtom(Str(SAB_VERSION));
3452 retxpr.setAtom(Str(""));
3455 retxpr.setAtom(Str(""));
3460 Context *newc = new Context(NULL); //_cn_ no need for cn
3461 newc -> set ( NZ(c -> getCurrentNode()));
3462 //Context *origc = getOwnerElement().getOrigContext();
3464 //newc -> set ( origc -> current());
3465 retxpr.setAtom (newc);
3473 E( atoms[0] -> tostring(S, a0Str) );
3474 getOwnerElement().setLogical(S, q, a0Str, FALSE);
3476 getOwnerTree().expandQ(q, ename);
3477 Processor *proc = NZ(S.getProcessor());
3478 GP( Context ) newc = new Context(NULL); //_cn_
3479 GP( Context ) newc2;
3480 Context auxContext(NULL);
3482 if (atoms[1] -> type == EX_NODESET)
3484 const Context& ctxt = atoms[1] -> tonodesetRef();
3485 int ctxtNumber = ctxt.getSize();
3486 for (int k = 0; k < ctxtNumber; k++)
3488 S.dom().constructStringValue(ctxt[k], oneString);
3489 E( proc -> getKeyNodes(S, ename,
3490 oneString, auxContext,
3491 S.dom().getOwnerDocument(c -> current())) );
3492 newc2 = (*newc).swallow(S, &auxContext);
3494 newc.assign(newc2.keep());
3499 E( atoms[1] -> tostring(S, oneString) );
3500 E( proc -> getKeyNodes(S, ename,
3502 S.dom().getOwnerDocument(c -> current())) );
3504 retxpr.setAtom(newc.keep());
3506 case EXFF_FORMAT_NUMBER:
3508 checkArgsCountBetween(2,3);
3509 Number num = atoms[0] -> tonumber(S);
3511 E( atoms[1] -> tostring(S, fmt) );
3513 if (atomsNumber == 3)
3516 E( atoms[2] -> tostring(S, nameStr) );
3518 getOwnerElement().setLogical(S, q, nameStr, FALSE);
3519 getOwnerTree().expandQ(q, ename);
3522 E( NZ(S.getProcessor()) -> decimals().format(S, ename, num, fmt, result) );
3523 retxpr.setAtom(result);
3529 GP( Context ) result = new Context(NULL);
3531 if (atoms[0] -> type == EX_NODESET)
3533 const Context& ctxt = atoms[0] -> tonodesetRef();
3534 int ctxtNumber = ctxt.getSize();
3535 for (int k = 0; k < ctxtNumber; k++)
3537 S.dom().constructStringValue(ctxt[k], ids);
3538 appendNodesWithID(S, ids, c, *result);
3543 E( atoms[0] -> tostring(S, ids) );
3544 appendNodesWithID(S, ids, c, *result);
3546 E( (*result).sort(S) );
3547 (*result).uniquize();
3548 retxpr.setAtom(result.keep());
3551 case EXFF_FUNCTION_AVAILABLE:
3556 E( atoms[0] -> tostring(S, nameStr));
3557 E( getOwnerElement().setLogical(S, funcName, nameStr, FALSE) );
3558 Str uri = getOwnerTree().expand(funcName.getUri());
3559 Str name = getOwnerTree().expand(funcName.getLocal());
3560 retxpr.setAtom(S.getProcessor() -> supportsFunction(uri, name));
3563 case EXFF_ELEMENT_AVAILABLE:
3568 E( atoms[0] -> tostring(S, nameStr));
3569 E( getOwnerElement().setLogical(S, elName, nameStr, FALSE) );
3570 Bool ret = ExtensionElement::elementAvailable(getOwnerTree(), elName);
3571 retxpr.setAtom(ret);
3573 case EXFF_UNPARSED_ENTITY_URI:
3577 NZ( curr = c -> current() );
3578 if (S.domExternal(curr))
3580 retxpr.setAtom(""); //not supported for external docs
3581 //add check for SXPF_SUPPORTS_UNPARSED_ENTITIES
3586 E( atoms[0] -> tostring(S, name) );
3587 Str *uri = toV(curr) -> getOwner().getUnparsedEntityUri(name);
3589 retxpr.setAtom(*uri);
3596 Err1(S, ET_FUNC_NOT_SUPPORTED, getFuncName(functor));
3601 eFlag Expression::createLPContext(Sit S, Context *&c, int baseNdx, NodeHandle givenGlobalCurrent /* = NULL */)
3603 assert(functor == EXF_LOCPATH);
3604 GP( Context ) theResult = new Context(c->getCurrentNode()); //_cn_ result has no cn
3605 Context info(givenGlobalCurrent ? givenGlobalCurrent : c -> getCurrentNode()); //_cn_
3606 //info.setCurrentNode(givenGlobalCurrent ? givenGlobalCurrent : c -> current());
3607 //(*theResult).setCurrentNode(c -> getCurrentNode());
3608 E( createLPContextLevel(S, 0, args.number(), c -> current(), info, theResult) );
3609 E( (*theResult).sort(S) );
3610 (*theResult).uniquize();
3611 c = theResult.keep();
3616 * createLPContextLevel
3617 * ranges over all nodes that satisfy the stepLevel-th step, calling self
3618 * recursively until the last one is reached. The vertices satisfying the last
3619 * step are added to theResult.
3620 * Base is passed from the preceding step and used for expression
3621 * evaluation. info holds the 'globally current' vertex.
3622 * The purpose of this routine is to generate a context without having to
3623 * also generate the intermediate contexts for each step. Also, some of the predicates
3624 * may be known to use last(), in which case we first have to compute the number
3625 * of nodes that reach such a predicate at all.
3628 eFlag Expression::createLPContextLevel(Sit S,
3629 int stepLevel, int stepsCount, NodeHandle base,
3630 Context &info, Context *theResult)
3632 // GP: theResult will be freed on error since the caller (createLPContext) holds it in a GP
3634 assert(functor == EXF_LOCPATH);
3636 predsCount = args[stepLevel] -> step -> preds.number(),
3637 lastBad = -1; // last bad predicate, or the step itself
3640 // keep a stack of positions, one for each predicate IN THIS STEP
3641 List<int> reached(predsCount), // serves as position for next pred
3642 totalReached(predsCount); // serves as size for next (bad) pred
3644 // there will be as many dry (size-counting) runs as there are bad preds
3646 quitThisRound = FALSE, quitThisVertex = FALSE;
3648 // i ranges over predicates. Value i==predsCount is the special last run
3649 for (i = 0; i <= predsCount; i++)
3651 if (i == predsCount)
3652 // the last run, not a dry-run
3654 // if this is the last run, or if the current pred uses last(), compute
3656 if (!dryRun || args[stepLevel] -> step -> preds[i] -> usesLast)
3658 // initialize the size arrays:
3659 // append base values for preds past the last bad one,
3660 // up to this bad one (incl.)
3661 for (init = 0; init <= lastBad; init++)
3663 for (init = lastBad + 1; init <= i; init++)
3666 totalReached.append(-1); // -1 just for safety
3669 // locally current vertex
3670 NodeHandle locCurr = NULL;
3672 quitThisRound = FALSE;
3675 // shift the locally current vertex
3676 //_speed_ this should be replaced with smart context
3677 //created with createContext on local step argument
3678 E( args[stepLevel] -> step -> shift(S, locCurr, base) );
3679 if (!nhNull(locCurr))
3681 if ((lastBad < 0) || !dryRun) ++reached[0];
3682 quitThisVertex = FALSE;
3683 for (j = 0; j < i; j++)
3687 // set locCurr as current at position reached[j]-1 in the context
3688 info.setVirtual(locCurr, reached[j] - 1, totalReached[j]);
3690 Expression *thisPred =
3691 args[stepLevel] -> step -> preds[j];
3692 // find whether we're in position bounds for this pred
3693 switch(thisPred -> inBounds(reached[j] - 1))
3698 E( thisPred -> trueFor(S, &info, satisfies) );
3702 quitThisVertex = TRUE;
3705 // before start, move to another vertex
3706 quitThisVertex = TRUE;
3709 // past the end, bail out
3710 quitThisRound = TRUE;
3713 if (quitThisVertex || quitThisRound)
3716 if (j == i && !dryRun) // passed all preds
3718 if (stepLevel < stepsCount - 1)
3719 E( createLPContextLevel(S,
3720 stepLevel + 1, stepsCount,
3721 locCurr, info, theResult))
3723 theResult -> append(locCurr);
3727 } while (!nhNull(locCurr) && !quitThisRound);
3728 // move all data collected to safe places
3729 for (init = lastBad + 1; init <= i; init++)
3730 totalReached[init] = reached[init];
3732 } // if bad predicate
3733 } // for, over all preds
3738 eFlag Expression::createLPContextSum(Sit S, Context *&c, NodeHandle globalCurrent /* = NULL */)
3740 assert(functor == EXF_LOCPATH);
3741 GP( Context ) newc = new Context(c->getCurrentNode()); //_cn_ needed?
3744 int cNumber = c -> getSize();
3745 for (int j = 0; j < cNumber; j++)
3747 E( createLPContext(S, returnedc = c, j, globalCurrent) );
3748 newc2 = (*newc).swallow(S, returnedc);
3760 /*................................................................
3762 creates a context for this expression, based on its functor.
3763 ................................................................*/
3765 // GP: createContext is clean
3766 // if unsuccessful, returns NULL in c and performs no allocations
3769 eFlag Expression::createContext(Sit S, Context *& c, int baseNdx /* = -1 */)
3771 GP( Context ) newc; // newc gets assigned c ONLY IN THE END
3772 Context *c_orig = c;
3777 argsNumber = args.number();
3779 baseNdx = (*newc).getPosition();
3784 Expression *deref = NULL;
3785 if (S.getProcessor())
3786 deref = S.getProcessor() -> getVarBinding(*pName);
3790 getOwnerTree().expandQStr(*pName, fullName);
3791 Err1(S, E1_VAR_NOT_FOUND, fullName);
3793 NodeHandle current_node = (*newc).getCurrentNode();
3794 E( deref -> createContext(S, newc, baseNdx) );
3796 (*newc).setCurrentNode(current_node);
3801 if (type != EX_NODESET)
3802 Err(S, ET_CONTEXT_FOR_BAD_EXPR);
3803 newc = patomnodeset -> copy();
3808 assert(baseNdx != -1); // meaningful only for a locpath
3809 GP( Context ) csummand;
3810 Context *newc2; // GP: OK
3812 E( args[0] -> createContext(S, newc, baseNdx) );
3814 for (i = 1; i < argsNumber; i++)
3816 csummand.assign(c_orig);
3817 E( args[i] -> createContext(S, csummand, baseNdx) );
3818 newc2 = (*newc).swallow(S, csummand);
3829 E( createLPContext(S, newc, baseNdx) );
3835 assert(baseNdx != -1); // meaningful only for a locpath
3836 NodeHandle wasCurrent = (*newc).getCurrentNode();
3837 E( args[0] -> createContext(S, newc, baseNdx) );
3839 (*newc).setCurrentNode(wasCurrent);
3841 GP( Context ) filteredc;
3842 for (i = 1; i < argsNumber - (int) hasPath; i++)
3844 filteredc = new Context(c_orig -> getCurrentNode());
3847 int newcNumber = (*newc).getSize();
3848 for (j = 0; j < newcNumber; j++)
3850 E(args[i] -> trueFor(S, newc, istrue));
3852 (*filteredc).append((*newc)[j]);
3856 newc = filteredc.keep();
3857 if (!(*newc).getSize()) break;
3861 filteredc.assign(newc);
3862 filteredc = newc; // a patch due to SGI MIPSpro compiler
3863 E( args[argsNumber-1] -> createLPContextSum(S, filteredc, (*newc).getCurrentNode()) );
3865 newc = filteredc.keep();
3872 assert(baseNdx != -1); // meaningful only for a locpath
3875 // E( step -> createContextNoPreds(newc = c, baseNdx) ); - done as follows:
3876 GP( Context ) newc2 = new Context(c_orig -> getCurrentNode());
3877 NodeHandle curr = NULL;
3880 //_speed_ here we should test the axis type
3881 // and use smart context if possible
3882 E( step -> shift(S, curr, (*newc)[baseNdx]) );
3884 (*newc2).append(curr);
3886 while (!nhNull(curr));
3889 GP( Context ) filteredc;
3890 int stepPredsNumber = step -> preds.number();
3891 for (i = 0; i < stepPredsNumber; i++)
3893 filteredc = new Context(c_orig -> getCurrentNode());
3896 int newc2Number = (*newc2).getSize();
3897 for (j = 0; j < newc2Number; j++)
3899 E( step -> preds[i] -> trueFor(S, newc2,istrue) );
3901 (*filteredc).append((*newc2)[j]);
3905 newc2 = filteredc.keep();
3906 if (!(*newc2).getSize()) break;
3908 newc = newc2.keep();
3911 case EXF_OTHER_FUNC:
3913 Expression resolved(getOwnerElement());
3914 E( eval(S, resolved, newc) );
3915 E( resolved.createContext(S, newc, baseNdx) );
3919 if (funcIsBuiltin(functor))
3921 Expression resolved(getOwnerElement());
3922 E( eval(S, resolved, newc) );
3923 E( resolved.createContext(S, newc, baseNdx) );
3927 Err(S, ET_CONTEXT_FOR_BAD_EXPR);
3934 eFlag Expression::matchesSingleStep(Sit S, NodeHandle v, Bool &result)
3936 assert(functor == EXF_LOCSTEP);
3937 if (!NZ(step) -> matchesWithoutPreds(S, v))
3938 RetOK(result, FALSE);
3939 if (!step -> preds.number())
3940 RetOK(result, TRUE);
3941 if (!S.dom().getParent(v))
3942 RetOK(result, FALSE);
3943 if (!step -> positional)
3945 GP( Context ) c = new Context(NULL); //_cn_ current() is not allowed in patterns
3947 Bool stillOK = TRUE;
3948 for (int i = 0; i < step -> preds.number() && stillOK; i++)
3949 E(step -> preds[i] -> trueFor(S, c,stillOK));
3951 RetOK(result,stillOK);
3953 else // positional case
3955 GP( Context ) c = new Context(NULL); //_cn_ current() is not allowed in patterns
3956 Context *newc; // GP: OK
3957 (*c).set(S.dom().getParent(v));
3958 E( createContext(S, newc = c+0, 0) );
3959 result = (newc -> contains(v));
3967 eFlag Expression::matchesSinglePath(Sit S, NodeHandle v, int lastIndex, Bool& result)
3969 assert(functor == EXF_LOCPATH);
3972 for (i = lastIndex; i >= 0; i--)
3975 RetOK(result, FALSE);
3976 switch(args[i] -> step -> ax)
3981 assert(!"root not first");
3982 E( args[i] -> matchesSingleStep(S, w, result) );
3983 if (!result) RetOK(result, FALSE);
3987 case AXIS_ATTRIBUTE:
3989 E( args[i] -> matchesSingleStep(S, w, result) );
3990 if (!result) RetOK(result, FALSE);
3991 w = S.dom().getParent(w);
3994 case AXIS_DESC_OR_SELF:
3996 E( args[i] -> matchesSingleStep(S, w, result) );
3997 if (!result) RetOK(result, FALSE);
3998 NodeHandle previous = w;
4001 E(matchesSinglePath(S, previous, i-1, result));
4005 previous = S.dom().getParent(previous);
4007 RetOK( result, FALSE );
4011 assert(!"bad axis in pattern");
4019 * optimizePositional()
4020 * called for a predicate of a locstep
4021 * returns 2 if the predicate uses the last() function
4022 * so the size of the context has to be determined
4023 * returns 1 if the predicate only uses position()
4027 int Expression::optimizePositional(int level)
4040 /* result = 0; */ break;
4044 assert(!"invalid predicate type");
4046 case EXF_FILTER: // can be e.g. "current()/@attr"
4047 default: // all the functions and operators, including EXF_OTHER_FUNC
4050 for (int i = 0; i < args.number(); i++)
4052 if (!!(sub = args[i] -> optimizePositional(level + 1)))
4055 if (result == 2) break;
4060 //PH: when we're called w/o recursion,
4061 //we might be called for situation like foo[45],
4062 //so we should return 1, if the expression
4063 //is of the type of EX_NUMBER
4064 if (!level && type == EX_NUMBER && !result) result = 1;
4066 usesLast = (result == 2);
4067 positional = (result >= 1);
4072 * optimizePositionBounds()
4073 * called for a predicate of a locstep
4074 * returns the range of positions that need to be examined for a context
4075 * e.g. the predicate in foo[1] will return both set to 1.
4076 * the positions returned are 1-based, 0 means "no restriction"
4079 void Expression::optimizePositionBounds()
4081 int from = 0, to = 0;
4086 if (type == EX_NUMBER)
4087 from = to = NZ(patomnumber) -> round(); // bad values like NaN return 0 which is OK.
4095 if (args[0] -> functor == EXFF_POSITION &&
4096 args[1] -> functor == EXF_ATOM && args[1] -> type == EX_NUMBER)
4098 int bound = args[1] -> patomnumber -> round();
4101 case EXFO_EQ: from = to = bound; break;
4102 case EXFO_LE: to = bound; break;
4103 case EXFO_LT: to = bound - 1; break;
4104 case EXFO_GE: from = bound; break;
4105 case EXFO_GT: from = bound + 1; break;
4110 optimizePositionFrom = from;
4111 optimizePositionTo = to;
4114 int Expression::inBounds(int position) const
4116 if (optimizePositionTo && position > optimizePositionTo-1)
4118 if (optimizePositionFrom && position < optimizePositionFrom-1)
4123 Element& Expression::getOwnerElement() const
4128 Tree& Expression::getOwnerTree() const
4130 return owner.getOwner();
4134 void Expression::report(Sit S, MsgType type, MsgCode code, const Str& arg1, const Str& arg2)
4136 getOwnerElement().report(S,type,code,arg1,arg2);
4139 Bool Expression::containsFunctor(ExFunctor func)
4141 if (functor == func)
4143 else if (functor == EXF_LOCSTEP)
4145 for (int i = 0; i < step -> preds.number(); i++)
4147 if (step -> preds[i] -> containsFunctor(func))
4152 for (int i = 0; i < args.number(); i++)
4154 if (args[i] -> containsFunctor(func))