1 /*
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/
6 *
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.
11 *
12 * The Original Code is the Sablotron XSLT Processor.
13 *
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.
17 *
18 * Contributor(s): Marc Lehmann <pcg@goof.com>
19 *
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
30 * GPL.
31 */
32
33 #include "expr.h"
34 #include "verts.h"
35 #include <float.h>
36 #include <math.h>
37 #include "context.h"
38 #include "tree.h"
39 #include "utf8.h"
40 #include "domprovider.h"
41 #include "guard.h"
42
43 // GP: clean.....
44
45 // period is not here as it can also start a number
46 char tokenShort[]=
47 ".. :: // != <= >= "
48 "( ) [ ] @ , "
49 "| + - = < > "
50 "/ * "
51 "\x0\x0\x0";
52
53 ExToken tokenShortX[]=
54 {
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,
58 TOK_SLASH, TOK_STAR
59 };
60
61 /*================================================================
62 function info table
63 ================================================================*/
64
65 struct FuncInfoItem
66 {
67 const char *name;
68 ExFunctor func;
69 ExType type;
70 }
71 funcInfoTable[] =
72 {
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},
81
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},
93
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},
100
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},
107
108 // XSLT core
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},
116
117 // XSLT extensions
118 {"function-available",EXFF_FUNCTION_AVAILABLE,EX_BOOLEAN},
119 {"element-available",EXFF_ELEMENT_AVAILABLE,EX_BOOLEAN},
120 {NULL, EXFF_NONE, EX_UNKNOWN}
121 };
122
123 struct ExtFuncInfoItem
124 {
125 const char *name;
126 ExFunctor func;
127 ExType type;
128 }
129 extFuncInfoTable[] =
130 {
131 {"evaluate",EXFF_EVAL,EX_NODESET},
132 {NULL, EXFF_NONE, EX_UNKNOWN}
133 };
134
getFuncName(ExFunctor functor)135 Str getFuncName(ExFunctor functor)
136 {
137 return funcInfoTable[functor - EXF_FUNCTION - 1].name;
138 };
139
140
141
142 /**********************************************************
143 TokenItem
144 **********************************************************/
145
speak(DStr & s,SpeakMode mode)146 void TokenItem::speak(DStr &s, SpeakMode mode)
147 {
148 switch(tok)
149 {
150 case TOK_VAR: // remove leading $
151 s.nadd(firstc + 1, len - 1);
152 break;
153 case TOK_LITERAL:// remove enclosing quotes or dquotes
154 s.nadd(firstc + 1, len - 2);
155 break;
156 default:
157 s.nadd(firstc,len);
158 };
159 // s += '\0'; - thrown out
160 };
161
162 /**********************************************************
163
164 Tokenizer
165
166 **********************************************************/
167
Tokenizer(Expression & owner_)168 Tokenizer::Tokenizer(Expression &owner_)
169 :
170 items(LIST_SIZE_EXPR_TOKENS), owner(owner_)
171 {
172 };
173
~Tokenizer()174 Tokenizer::~Tokenizer()
175 {
176 items.freeall(FALSE);
177 }
178
tokenize(Sit S,const Str & astring)179 eFlag Tokenizer::tokenize(Sit S, const Str &astring)
180 {
181 char *p;
182 TokenItem item;
183 string = astring;
184 p = (char *) string;
185
186 E( getToken(S, p, item, TOK_NONE) );
187 ExToken prevToken = item.tok;
188 while ((item.tok != TOK_END) && (item.tok != TOK_NONE))
189 {
190 items.append(new TokenItem(item));
191 E( getToken(S, p, item, prevToken) );
192 prevToken = item.tok;
193 };
194
195 if (item.tok == TOK_NONE)
196 {
197 DStr itemStr;
198 item.speak(itemStr, SM_OFFICIAL);
199 Err1(S, ET_BAD_TOKEN, itemStr);
200 }
201 else
202 items.append(new TokenItem(item));
203
204 return OK;
205 }
206
207 /*================================================================
208 namerTable
209 a table of tokens which have the effect that the following
210 name is recognized as a NCName (rather than operator name) and * is
211 recognized as a wildcard (rather than multiplication operator).
212 The table should end with TOK_NONE (which is of this type too).
213 ================================================================*/
214
215 static ExToken namerTable[] = {
216 TOK_ATSIGN, TOK_DCOLON, TOK_LPAREN, TOK_LBRACKET,
217 // operators:
218 TOK_OR, TOK_AND, TOK_EQ, TOK_NEQ, TOK_LT, TOK_GT, TOK_LE, TOK_GE,
219 TOK_PLUS, TOK_MINUS, TOK_MINUS1, TOK_MULT, TOK_DIV, TOK_MOD, TOK_VERT,
220 // slashes are operators too but not for us
221 TOK_SLASH, TOK_DSLASH, TOK_COMMA,
222 // TOK_NONE (terminator)
223 TOK_NONE};
224
225 /*================================================================
226 Bool isNamer()
227 returns True iff the token is contained in the namerTable table.
228 ================================================================*/
229
isNamer(ExToken tok)230 static Bool isNamer(ExToken tok)
231 {
232 int i;
233 if (tok == TOK_NONE) return TRUE;
234 for (i = 0; (namerTable[i] != tok) &&
235 (namerTable[i] != TOK_NONE); i++);
236 return (Bool) (namerTable[i] == tok);
237 }
238
239 /*================================================================
240 ExToken tryShort()
241 looks up a few characters at p in the tokenShort table containing
242 the (up-to-3-char) symbols
243 RETURNS the token identified, or TOK_NONE if no match is found
244 ================================================================*/
245
tryShort(char * & p,ExToken prevToken)246 ExToken Tokenizer::tryShort(char*& p, ExToken prevToken)
247 {
248 int i;
249 char* t;
250 ExToken result;
251
252 for (i=0, t=tokenShort; *t; i++,t+=3)
253 if (*p==*t)
254 if ((t[1] == ' ') || (t[1] == p[1])) break;
255 if (*t)
256 {
257 p += ((t[1] == ' ')? 1 : 2);
258 result = tokenShortX[i];
259 if (result == TOK_STAR)
260 result = (isNamer(prevToken)? TOK_NAME : TOK_MULT);
261 if ((result == TOK_MINUS) && isNamer(prevToken))
262 result = TOK_MINUS1;
263 }
264 else result = TOK_NONE;
265 return result;
266 }
267
268 /*================================================================
269 eFlag lookToken()
270 sets 'ret' to the following token, but does not change the pointer p
271 ================================================================*/
272
lookToken(Sit S,ExToken & ret,char * p,ExToken prevToken)273 eFlag Tokenizer::lookToken(Sit S, ExToken &ret, char* p, ExToken prevToken)
274 {
275 // getToken_() changes p but this is passed by value so
276 // remains unchanged
277
278 E( getToken_(S, ret, p, prevToken) );
279 return OK;
280 }
281
282 /*================================================================
283 Bool findChar
284 sets p to address FOLLOWING the next occurence of c in p
285 (to skip a character reference, use findChar(p,';'))
286 RETURNS false iff the next occurence was not found
287 ================================================================*/
288
findChar(char * & p,char c)289 static Bool findChar(char*& p, char c)
290 {
291 while (*p && (*p != c)) p++;
292 if (*p)
293 {
294 p++;
295 return TRUE;
296 }
297 else
298 return FALSE;
299 }
300
301 /*================================================================
302 findSame
303 sets p to address FOLLOWING the next occurence of *p in p
304 RETURNS false iff another occurence was not found
305 ================================================================*/
306
findSame(char * & p)307 static Bool findSame(char*& p)
308 {
309 char first = *p++;
310 return findChar(p, first);
311 };
312
getToken_(Sit S,ExToken & ret,char * & p,ExToken prevToken)313 eFlag Tokenizer::getToken_(Sit S, ExToken &ret, char*& p, ExToken prevToken)
314 {
315 ExToken tok;
316 char c;
317
318 skipWhite(p);
319 if (!*p)
320 {
321 ret = TOK_END;
322 return OK;
323 }
324 else
325 {
326 // the following may also translate * into TOK_NAME
327 if ((tok = tryShort(p, prevToken)) != TOK_NONE)
328 {
329 ret = tok;
330 return OK;
331 };
332 switch (c = *p)
333 {
334 case '$':
335 {
336 // call getName with prevToken=TOK_NONE
337 // to ensure that e.g. 'and' in '$and' is treated as a name
338 E( getName(S, ret, ++p, TOK_NONE) );
339 if (ret != TOK_NONE)
340 ret = TOK_VAR;
341 else
342 Err(S, ET_BAD_VAR);
343 };
344 break;
345 case '\"':
346 case '\'':
347 if(!findSame(p))
348 Err(S, ET_INFINITE_LITERAL)
349 else
350 ret = TOK_LITERAL;
351 break;
352 case '&': sabassert(0); //DBG: do not process entity references so far
353 break;
354 case '.':
355 if (utf8IsDigit(utf8CharCode(p+1)))
356 {
357 E( getNumber(S, p) );
358 ret = TOK_NUMBER;
359 }
360 else {
361 p++;
362 ret = TOK_PERIOD;
363 };
364 break;
365 default:
366 {
367 if (utf8IsDigit(utf8CharCode(p)))
368 {
369 E( getNumber(S, p) );
370 ret = TOK_NUMBER;
371 }
372 else
373 {
374 if (utf8IsLetter(utf8CharCode(p)) || (*p == '_') || (*p == ':'))
375 {
376 // the following call finds TOK_NAME, TOK_FNAME,
377 // TOK_AXISNAME,
378 // as well as TOK_AND etc. (based on prev token)
379 E( getName(S, ret, p, prevToken) );
380 }
381 else
382 {
383 Str temp;
384 temp.nset(p, 1);
385 Err1(S, ET_BAD_TOKEN, temp); //unknown token
386 }
387 }
388 }; //default
389 }; //switch
390 }; //else
391 return OK;
392 }; //getToken_
393
getToken(Sit S,char * & p,TokenItem & item,ExToken prevToken)394 eFlag Tokenizer::getToken(Sit S, char*& p, TokenItem& item, ExToken prevToken)
395 {
396 ExToken t;
397 skipWhite(p);
398 item.firstc = p;
399 E( getToken_(S, t, p, prevToken) );
400 item.len = (long)(p - item.firstc);
401 item.tok = t;
402 return OK;
403 }
404
getNumber(Sit S,char * & p)405 eFlag Tokenizer::getNumber(Sit S, char*& p)
406 {
407 Bool wasDot = FALSE;
408 while ((*p) && (utf8IsDigit(utf8CharCode(p))) || (*p == '.'))
409 {
410 if (*p == '.')
411 if (wasDot)
412 Err(S, ET_BAD_NUMBER)
413 else wasDot = TRUE;
414 p += utf8SingleCharLength(p);
415 };
416 return OK;
417 };
418
419 /*================================================================
420 getWordOp
421 checks whether the sequence at p of given length is an operator name
422 RETURNS the appropriate token if so; TOK_NONE otherwise
423 ================================================================*/
424
getWordOp(char * p,int length)425 static ExToken getWordOp(char *p, int length)
426 {
427 if (length > 3) return TOK_NONE;
428 if (length < 2) length = 2;
429 if (!strncmp(p,"or",length)) return TOK_OR;
430 if (length < 3) length = 3;
431 if (!strncmp(p,"and",length)) return TOK_AND;
432 if (!strncmp(p,"div",length)) return TOK_DIV;
433 if (!strncmp(p,"mod",length)) return TOK_MOD;
434 return TOK_NONE;
435 }
436
isNodeTest(char * p,int length)437 static Bool isNodeTest(char *p, int length)
438 {
439 const char *q;
440 int qlen;
441 for (int i = 0; (q = exNodeTypeNames[i]) != NULL; i++)
442 {
443 if (!strncmp(q,p,
444 (length < (qlen = strlen(q))? qlen : length)))
445 break;
446 };
447 return (Bool)(q != NULL);
448 }
449
450 #define nameCharExtended(CH, PTR) ((CH = utf8CharCode(PTR))!= 0) &&\
451 (utf8IsNameChar(CH) || strchr(".-_:*",CH))
452
nameLength(char * from)453 int nameLength(char* from)
454 {
455 char *q = from;
456 int length = 0;
457 unsigned long c;
458 while(nameCharExtended(c,q)) {
459 q += utf8SingleCharLength(q);
460 length++;
461 }
462 return length;
463 }
464
getName(Sit S,ExToken & ret,char * & p,ExToken prevToken)465 eFlag Tokenizer::getName(Sit S, ExToken &ret, char*& p, ExToken prevToken)
466 {
467 char *former = p;
468 unsigned long c;
469 BOOL wasColon = FALSE;
470
471 if ((!utf8IsLetter(utf8CharCode(p))) && (*p != '_'))
472 {
473 ret = TOK_NONE;
474 return OK;
475 }
476
477 while (nameCharExtended(c,p))
478 {
479 if (c == ':')
480 {
481 if (wasColon)
482 {
483 // identify the bad qname;
484 Str theName;
485 theName.nset(former, nameLength(former));
486 Err1(S, E1_EXTRA_COLON, theName);
487 }
488 else
489 {
490 switch(*(p+1))
491 {
492 case ':':
493 {
494 ret = TOK_AXISNAME;
495 return OK;
496 };
497 case '*':
498 {
499 ret = TOK_NAME;
500 p += 2;
501 return OK;
502 };
503 default:
504 wasColon = TRUE;
505 };
506 };
507 }
508 else if (c == '*')
509 {
510 if ((p - former) && *(p - 1) != ':') // the first condition could be dropped
511 {
512 ret = TOK_NAME;
513 return OK;
514 }
515 }
516 p += utf8SingleCharLength (p);
517 }
518
519 if (!wasColon && !isNamer(prevToken))
520 {
521 if ((ret = getWordOp(former, (int) (p - former))) != TOK_NONE)
522 return OK;
523 };
524
525 ExToken next;
526
527 // look at following token with prev=TOK_NAME
528 E( lookToken(S, next,p,TOK_NAME) );
529 switch(next)
530 {
531 case TOK_DCOLON:
532 ret = TOK_AXISNAME;
533 break;
534 case TOK_LPAREN:
535 {
536 if (isNodeTest(former, (int) (p - former)))
537 ret = TOK_NTNAME;
538 else
539 ret = TOK_FNAME;
540 }; break;
541 default:
542 ret = TOK_NAME;
543 };
544 return OK;
545 };
546
547 /*================================================================
548 int findTop()
549 finds the first top-level occurence of 'token' starting with
550 position 'from'. If there is none, return value points at TOK_END.
551 ================================================================*/
552
findTop(ExToken token,int from)553 int Tokenizer::findTop(ExToken token, int from)
554 {
555 int level = 0;
556 ExToken ct;
557 int i;
558 for (i = from;
559 ((ct = items[i] -> tok) != TOK_END) && (level || (ct != token));
560 i++)
561 {
562 if ((ct == TOK_LPAREN) || (ct == TOK_LBRACKET))
563 level++;
564 if ((ct == TOK_RPAREN) || (ct == TOK_RBRACKET))
565 level--;
566 }
567 return i;
568 }
569
570
571 /*================================================================
572 eFlag getDelim()
573 given a position in 'pos', finds the corresponding next token.
574 If the left token is ( or [, looks for the matching right paren/bracket,
575 Otherwise looks for the occurence of the same token.
576 Returns pos pointing at the matching token, or at TOK_END if there
577 is none. (In case of failed reverse search, returns -1.)
578 ================================================================*/
579
getDelim(Sit S,int & pos,Bool reverse)580 eFlag Tokenizer::getDelim(Sit S, int &pos, Bool reverse /*=FALSE*/)
581 {
582 ExToken first, second, tok;
583 int level = 0,
584 i = pos;
585
586 switch(first = items[pos] -> tok)
587 {
588 case TOK_LBRACKET:
589 second = TOK_RBRACKET;
590 break;
591 case TOK_LPAREN:
592 second = TOK_RPAREN;
593 break;
594 case TOK_RBRACKET:
595 second = TOK_LBRACKET;
596 break;
597 case TOK_RPAREN:
598 second = TOK_LPAREN;
599 break;
600 default:
601 second = first;
602 }
603
604 i += reverse? -1 : 1;
605
606 while ((i >= 0) && ((tok = items[i] -> tok) != TOK_END))
607 {
608 if (tok == second)
609 {
610 if (!level)
611 {
612 pos = i;
613 return OK;
614 }
615 else level--;
616 }
617 else if (tok == first) level++;
618 i += reverse? -1 : 1;
619 }
620 pos = i;
621 return OK;
622 }
623
624 /*================================================================
625 stripParens()
626 given the left and right end positions of a tokenizer fragment,
627 shifts them inwards to strip any outer parentheses.
628 ================================================================*/
629
stripParens(Sit S,int & left,int & right)630 eFlag Tokenizer::stripParens(Sit S, int &left, int &right)
631 {
632 int left0 = left;
633 if (items[right]->tok == TOK_END)
634 right--;
635 // sabassert(left <= right);
636 while ((items[left]->tok == TOK_LPAREN)
637 && (items[right]->tok == TOK_RPAREN))
638 {
639 left0 = left;
640 E( getDelim(S, left0) );
641 if (left0 == right)
642 {
643 left++;
644 right--;
645 }
646 else break;
647 };
648 return OK;
649 }
650
report(Sit S,MsgType type,MsgCode code,const Str & arg1,const Str & arg2)651 void Tokenizer::report(Sit S, MsgType type, MsgCode code, const Str& arg1, const Str& arg2)
652 {
653 owner.report(S, type, code, arg1, arg2);
654 }
655
656
657 /*****************************************************************
658 * *
659 L o c S t e p
660 * *
661 *****************************************************************/
662
LocStep(Element & ownerV_,ExAxis _axis,ExNodeType _ntype)663 LocStep::LocStep(Element& ownerV_, ExAxis _axis /*=AXIS_NONE*/,
664 ExNodeType _ntype /*=EXNODE_NONE*/)
665 : preds(LIST_SIZE_1), ownerV(ownerV_)
666 {
667 set(_axis, _ntype);
668 positional = FALSE;
669 badPreds = 0;
670 }
671
~LocStep()672 LocStep::~LocStep()
673 {
674 preds.freeall(FALSE);
675 }
676
set(ExAxis _axis,ExNodeType _ntype)677 void LocStep::set(ExAxis _axis, ExNodeType _ntype)
678 {
679 ax = _axis;
680 ntype = _ntype;
681 }
682
speak(Sit S,DStr & strg,SpeakMode mode)683 void LocStep::speak(Sit S, DStr &strg, SpeakMode mode)
684 {
685 if (!(mode & SM_CONTENTS)) return;
686 switch(ax)
687 {
688 case AXIS_CHILD:
689 // case AXIS_DESC_OR_SELF:
690 case AXIS_ROOT:
691 break;
692 case AXIS_ATTRIBUTE:
693 strg += '@';
694 break;
695 default:
696 {
697 strg += axisNames[ax];
698 strg += "::";
699 };
700 };
701 if((ntype != EXNODE_NONE) //&& (ax != AXIS_DESC_OR_SELF)
702 && (ax != AXIS_ROOT))
703 {
704 strg += exNodeTypeNames[ntype];
705 strg += "()";
706 }
707 else
708 {
709 Str fullName;
710 getOwnerElement().getOwner().expandQStr(ntest,fullName);
711 // ntest.speak(S, strg,mode);
712 strg += fullName;
713 }
714 int i, predsNumber = preds.number();
715 for (i = 0; i < predsNumber; i++)
716 {
717 strg += '[';
718 preds[i] -> speak(S, strg,mode);
719 strg += ']';
720 };
721 };
722
parse(Sit S,Tokenizer & tokens,int & pos,Bool defaultToo)723 eFlag LocStep::parse(Sit S, Tokenizer& tokens, int& pos,
724 Bool defaultToo /* = FALSE */)
725 {
726 int right;
727 int &i = pos;
728 DStr temp;
729 ExToken tok;
730
731 tok = tokens.items[i++]->tok;
732 if (tok == TOK_END)
733 Err(S, ET_EMPTY_PATT);
734 switch (tok)
735 {
736 case TOK_PERIOD:
737 ax = AXIS_SELF;
738 ntype = EXNODE_NODE;
739 return OK;
740 break;
741 case TOK_DPERIOD:
742 ax = AXIS_PARENT;
743 ntype = EXNODE_NODE;
744 return OK;
745 break;
746 case TOK_ATSIGN:
747 {
748 ax = AXIS_ATTRIBUTE;
749 tok = tokens.items[i++]->tok;
750 };
751 break;
752 case TOK_STAR:
753 {
754 ax = AXIS_CHILD;
755 };
756 break;
757 case TOK_AXISNAME:
758 {
759 tokens.items[i-1] -> speak(temp, SM_OFFICIAL);
760 if ((ax = (ExAxis) lookup(temp, axisNames)) == AXIS_NONE)
761 Err1(S, E1_UNKNOWN_AXIS, temp);
762 i++;
763 tok = tokens.items[i++] -> tok;
764 };
765 break;
766 case TOK_NAME:
767 case TOK_NTNAME:
768 {
769 // if (!defaultAx)
770 // Err(S, ET_EXPR_SYNTAX);
771 ax = AXIS_CHILD;
772 };
773 break;
774 default:
775 Err(S, ET_EXPR_SYNTAX); // axis name or node-test expected
776 };
777
778 // axis has been determined; tok must now be a name or a node-test
779 temp.empty();
780 if ((tok != TOK_NAME) && (tok != TOK_NTNAME))
781 Err(S, ET_EXPR_SYNTAX); // axis name or node-test expected
782 tokens.items[i-1] -> speak(temp,SM_OFFICIAL);
783 ntype = EXNODE_NONE;
784
785 if (tok == TOK_NTNAME)
786 {
787 ntype = (ExNodeType) lookup(temp, exNodeTypeNames);
788 if (tokens.items[i++]->tok != TOK_LPAREN)
789 Err(S, ET_LPAREN_EXP);
790
791 //pi test may hav one literal param
792 if (ntype == EXNODE_PI && tokens.items[i]->tok == TOK_LITERAL) {
793 DStr pilit;
794 tokens.items[i++]->speak(pilit, SM_CONTENTS);
795 piname = pilit;
796 }
797
798 if (tokens.items[i++]->tok != TOK_RPAREN)
799 {
800 Err(S, ET_RPAREN_EXP);
801 }
802 }
803 else
804 {
805 // set the QName from prefix:uri using
806 // the namespace declarations in 'ownerE'
807 // (passed through from the containing attribute)
808 // DEFAULT: without using the default namespace
809 E( getOwnerElement().setLogical(S, ntest, temp, defaultToo) );
810 }
811
812 while ((tokens.items[i] -> tok) == TOK_LBRACKET)
813 {
814 badPreds = 0;
815 E( tokens.getDelim(S, right = i) );
816 if (tokens.items[right] -> tok == TOK_END)
817 Err(S, ET_RBRACKET_EXP)
818 else
819 {
820 GP( Expression ) ex = new Expression(getOwnerElement());
821 E( (*ex).parse(S, tokens,i+1,right-1) );
822 // find out about the use of last() and position()
823 int exPositionalType = (*ex).optimizePositional(0);
824 if (exPositionalType)
825 {
826 positional = TRUE;
827 if (exPositionalType == 2)
828 badPreds++;
829 // find sufficient position bounds
830 (*ex).optimizePositionBounds();
831 }
832 preds.append(ex.keep());
833 };
834 i = right + 1;
835 };
836
837 return OK; // pointing at the first char that does not start a predicate
838
839 }; // end LocStep::parse()
840
841
matchesWithoutPreds(Sit S,NodeHandle v)842 Bool LocStep::matchesWithoutPreds(Sit S, NodeHandle v)
843 {
844 // removed the following:
845 // sabassert(v);
846 // (because the parent-of-root calls etc.)
847 if (nhNull(v))
848 return FALSE;
849 // ANDed with VT_BASE for ordinary vertices
850 SXP_NodeType ty = S.dom().getNodeType(v);
851 switch(ntype)
852 {
853 case EXNODE_NODE:
854 break;
855 case EXNODE_TEXT:
856 if (ty != TEXT_NODE)
857 return FALSE;
858 break;
859 case EXNODE_PI:
860 if (ty != PROCESSING_INSTRUCTION_NODE)
861 return FALSE;
862 break;
863 case EXNODE_COMMENT:
864 if (ty != COMMENT_NODE)
865 return FALSE;
866 break;
867 case EXNODE_NONE:
868 if ((ty == TEXT_NODE) || (ty == COMMENT_NODE) ||
869 (ty == DOCUMENT_NODE) || (ty == PROCESSING_INSTRUCTION_NODE))
870 return FALSE;
871 break;
872 };
873
874 switch(ax)
875 {
876 case AXIS_ROOT:
877 return (Bool) (ty == DOCUMENT_NODE);
878 break;
879 case AXIS_ATTRIBUTE:
880 if (ty != ATTRIBUTE_NODE) return FALSE;
881 break;
882 case AXIS_NAMESPACE:
883 if (ty != NAMESPACE_NODE) return FALSE;
884 break;
885 case AXIS_CHILD:
886 case AXIS_DESCENDANT:
887 case AXIS_DESC_OR_SELF:
888 case AXIS_ANCESTOR:
889 case AXIS_ANC_OR_SELF:
890 case AXIS_FOLL_SIBLING:
891 case AXIS_PREC_SIBLING:
892 case AXIS_FOLLOWING:
893 case AXIS_PRECEDING:
894 switch (ty)
895 {
896 case ATTRIBUTE_NODE:
897 case NAMESPACE_NODE:
898 return FALSE;
899 case DOCUMENT_NODE:
900 switch(ax)
901 {
902 case AXIS_DESC_OR_SELF:
903 case AXIS_ANCESTOR:
904 case AXIS_ANC_OR_SELF:
905 break;
906 default:
907 return FALSE;
908 };
909 };
910 break;
911 case AXIS_SELF:
912 {
913 if (ntype == EXNODE_NONE && ty != ELEMENT_NODE) return false;
914 }; break;
915 case AXIS_PARENT:
916 break;
917 default: sabassert(0); //should be handled in parse
918 };
919
920 //if (ntype != EXNODE_NONE)
921 // return TRUE;
922 switch (ntype)
923 {
924 case EXNODE_NONE:
925 break;
926 case EXNODE_PI:
927 {
928 if (! S.domExternal(v) && ! (piname == ""))
929 {
930 ProcInstr *pi = toPI(v);
931 EQName ename;
932 pi->getOwner().expandQ(pi->name, ename);
933 return ename.getLocal() == piname;
934 }
935 else
936 return TRUE;
937 };
938 default:
939 return TRUE;
940 }
941
942 //exnode_none are processed here
943 if (S.domExternal(v))
944 {
945 const char *uri = S.dom().getNodeNameURI(v);
946 const char *local = S.dom().getNodeNameLocal(v);
947 Bool ret = getOwnerElement().getOwner()
948 .cmpQNameStrings(ntest, uri, local);
949 S.dom().freeName(v, (char*)uri);
950 S.dom().freeName(v, (char*)local);
951 return ret;
952 }
953 else
954 {
955 const QName &hisname = toV(v) -> getName();
956 return getOwnerElement().getOwner()
957 .cmpQNamesForeign(ntest, toV(v) -> dict(), hisname);
958 }
959 }
960
961
shift(Sit S,NodeHandle & v,NodeHandle baseV)962 eFlag LocStep::shift(Sit S, NodeHandle &v, NodeHandle baseV)
963 {
964 NodeHandle w = NULL; // the result
965 switch(ax)
966 {
967 case AXIS_ATTRIBUTE:
968 {
969 sabassert(S.domExternal(v) || nhNull(v) ||
970 isElement(baseV) && baseV == toV(v) -> parent);
971 // i.e. v==NULL and baseV isn't daddy
972 if (S.dom().getNodeType(baseV) != ELEMENT_NODE) break;
973 for (w = (!nhNull(v) ?
974 S.dom().getNextAttrNS(v) :
975 S.dom().getAttributeNo(baseV, 0));
976 !nhNull(w) && !matchesWithoutPreds(S, w);
977 w = S.dom().getNextAttrNS(w));
978 }; break;
979
980 case AXIS_CHILD:
981 {
982 sabassert(S.domExternal(v) || nhNull(v) ||
983 isDaddy(baseV) && baseV == toV(v) -> parent);
984 if (S.dom().getNodeType(baseV) != ELEMENT_NODE &&
985 S.dom().getNodeType(baseV) != DOCUMENT_NODE) break;
986 for (w = !nhNull(v) ?
987 S.dom().getNextSibling(v) :
988 S.dom().getFirstChild(baseV);
989 !nhNull(w) && !matchesWithoutPreds(S, w);
990 w = S.dom().getNextSibling(w));
991 }; break;
992
993 case AXIS_NAMESPACE:
994 {
995 sabassert(S.domExternal(v) || nhNull(v) ||
996 isElement(baseV) && baseV == toV(v) -> parent);
997 if (S.dom().getNodeType(baseV) != ELEMENT_NODE) break;
998 for (w = !nhNull(v) ?
999 S.dom().getNextAttrNS(v) :
1000 S.dom().getNamespaceNo(baseV, 0);
1001 !nhNull(w) && !matchesWithoutPreds(S, w);
1002 w = S.dom().getNextAttrNS(w));
1003 }; break;
1004
1005 case AXIS_ROOT: // technically not an axis
1006 {
1007 if (nhNull(v))
1008 {
1009 w = S.dom().getOwnerDocument(baseV);
1010 if (nhNull(w)) // w must have been the root itself
1011 w = baseV;
1012 }
1013 }; break;
1014
1015 case AXIS_SELF:
1016 {
1017 if (nhNull(v) && matchesWithoutPreds(S, baseV))
1018 w = baseV;
1019 }; break;
1020
1021 case AXIS_PARENT:
1022 {
1023 if (nhNull(v) && matchesWithoutPreds(S, S.dom().getParent(baseV)))
1024 w = S.dom().getParent(baseV);
1025 }; break;
1026
1027 case AXIS_ANCESTOR:
1028 case AXIS_ANC_OR_SELF:
1029 {
1030 if (!nhNull(v))
1031 w = S.dom().getParent(v);
1032 else
1033 w = (ax == AXIS_ANCESTOR) ? S.dom().getParent(baseV) : baseV;
1034 for (; !nhNull(w) && !matchesWithoutPreds(S, w);
1035 w = S.dom().getParent(w));
1036 }; break;
1037
1038 case AXIS_FOLL_SIBLING:
1039 {
1040 switch(S.dom().getNodeType(baseV))
1041 {
1042 case DOCUMENT_NODE:
1043 case NAMESPACE_NODE:
1044 case ATTRIBUTE_NODE:
1045 break;
1046 default:
1047 for (w = S.dom().getNextSibling(!nhNull(v) ? v : baseV);
1048 !nhNull(w) && !matchesWithoutPreds(S, w);
1049 w = S.dom().getNextSibling(w));
1050 }
1051 }; break;
1052
1053 case AXIS_PREC_SIBLING:
1054 {
1055 switch(S.dom().getNodeType(baseV))
1056 {
1057 case DOCUMENT_NODE:
1058 case NAMESPACE_NODE:
1059 case ATTRIBUTE_NODE:
1060 break;
1061 default:
1062 for (w = S.dom().getPreviousSibling(!nhNull(v) ? v : baseV);
1063 !nhNull(w) && !matchesWithoutPreds(S, w);
1064 w = S.dom().getPreviousSibling(w));
1065 }
1066 }; break;
1067
1068 case AXIS_DESCENDANT:
1069 case AXIS_DESC_OR_SELF:
1070 {
1071 if (nhNull(v))
1072 {
1073 if (ax == AXIS_DESC_OR_SELF && matchesWithoutPreds(S, baseV))
1074 {
1075 w = baseV;
1076 break;
1077 }
1078 else
1079 v = baseV;
1080 }
1081
1082 // find next descendant
1083 do
1084 {
1085 if ((S.dom().getNodeType(v) == ELEMENT_NODE ||
1086 (S.dom().getNodeType(v) == DOCUMENT_NODE)) &&
1087 S.dom().getChildCount(v))
1088 v = S.dom().getFirstChild(v);
1089 else
1090 {
1091 while (v != baseV)
1092 {
1093 SXP_Node v0 = v;
1094 v = S.dom().getNextSibling(v);
1095 if (!nhNull(v))
1096 break;
1097 else
1098 v = S.dom().getParent(v0);
1099 }
1100 };
1101 if (v != baseV && matchesWithoutPreds(S, v))
1102 {
1103 w = v;
1104 break;
1105 };
1106 }
1107 while(v != baseV);
1108 }; break;
1109 case AXIS_FOLLOWING:
1110 {
1111 do {
1112 if (!nhNull(v) && S.dom().getChildCount(v))
1113 w = S.dom().getChildNo(v,0);
1114 else
1115 {
1116 if (nhNull(v))
1117 v = baseV;
1118 while (!nhNull(v) && nhNull(S.dom().getNextSibling(v)))
1119 v = S.dom().getParent(v);
1120 if (!nhNull(v))
1121 w = S.dom().getNextSibling(v);
1122 else
1123 w = NULL;
1124 }
1125 v = w;
1126 } while (!nhNull(w) && !matchesWithoutPreds(S, w));
1127 }; break;
1128 case AXIS_PRECEDING:
1129 {
1130 do {
1131 v = !nhNull(v) ? v : baseV;
1132 w = S.dom().getPreviousSibling(v);
1133 if (!nhNull(w)) {
1134 int childCount;
1135 while (0 != (childCount = S.dom().getChildCount(w)))
1136 w = S.dom().getChildNo(w,childCount - 1);
1137 } else {
1138 w = S.dom().getParent(v);
1139 //eliminate ancestors of baseV
1140 for (v = S.dom().getParent(baseV);
1141 !nhNull(v) && v != w;
1142 v = S.dom().getParent(v));
1143 //we're in the ancestor line
1144 if (!nhNull(v)) {
1145 while (!nhNull(v) && !(S.dom().getPreviousSibling(v)))
1146 v = S.dom().getParent(v);
1147 if (!nhNull(v))
1148 w = S.dom().getPreviousSibling(v);
1149 else
1150 w = NULL;
1151 }
1152 }
1153 v = w;
1154 } while (!nhNull(w) && !matchesWithoutPreds(S, w));
1155 }; break;
1156 default:
1157 sabassert(0);
1158 };
1159 v = w;
1160 return OK;
1161 };
1162
report(Sit S,MsgType type,MsgCode code,const Str & arg1,const Str & arg2)1163 void LocStep::report(Sit S, MsgType type, MsgCode code, const Str& arg1, const Str& arg2)
1164 {
1165 ownerV.report(S, type, code, arg1, arg2);
1166 }
1167
1168
1169
1170 /**********************************************************
1171 N u m b e r
1172 **********************************************************/
1173
Number()1174 Number::Number()
1175 {
1176 *this = 0.0;
1177 }
1178
Number(double y)1179 Number::Number(double y)
1180 {
1181 *this = y;
1182 }
1183
1184 Number&
operator =(double y)1185 Number::operator= (double y)
1186 {
1187 x = y;
1188 return *this;
1189 };
1190
1191 Number&
operator =(const Str & s)1192 Number::operator= (const Str &s)
1193 {
1194 char *endptr, *startptr = s;
1195 skipWhite(startptr);
1196 if (*startptr)
1197 {
1198 x = strtod(startptr, &endptr);
1199 if (endptr) {
1200 skipWhite(endptr);
1201 if (*endptr)
1202 setNaN();
1203 }
1204 }
1205 else
1206 setNaN();
1207 return *this;
1208 };
1209
operator double() const1210 Number::operator double() const
1211 {
1212 return x;
1213 }
1214
operator ==(double y)1215 Bool Number::operator== (double y)
1216 {
1217 if (isNaN() || isnan__(y))
1218 return FALSE;
1219 if (isInf() || isinf__(y))
1220 return isInf() && isinf__(y) && !((x > 0) ^ (y > 0));
1221 double d = x - y;
1222 return (Bool)((d < EPS) && (d > -EPS));
1223 }
1224
operator ==(Number & y)1225 Bool Number::operator== (Number& y)
1226 {
1227 if (isNaN()) return FALSE;
1228 return (Bool)(operator== ((double) y));
1229 }
1230
1231
operator <(double y)1232 Bool Number::operator< (double y)
1233 {
1234 return (Bool)(x < y);
1235 }
1236
operator <(Number & y)1237 Bool Number::operator< (Number& y)
1238 {
1239 return (Bool)(x < (double) y);
1240 }
1241
operator >(double y)1242 Bool Number::operator> (double y)
1243 {
1244 return (Bool)(x > y);
1245 }
1246
operator >(Number & y)1247 Bool Number::operator> (Number& y)
1248 {
1249 return (Bool)(x > (double) y);
1250 }
1251
isNaN()1252 Bool Number::isNaN()
1253 {
1254 return isnan__(x);
1255 }
1256
isInf()1257 Bool Number::isInf()
1258 {
1259 return isinf__(x);
1260 };
1261
setNaN()1262 void Number::setNaN()
1263 {
1264 // divide by zero using a variable, to fool too clever compilers
1265 // 'volatile' suggested by Dirk Siebnich
1266 volatile int zero = 0;
1267 x = 0.0 / zero;
1268 }
1269
round()1270 int Number::round()
1271 {
1272 if (isNaN() || isInf())
1273 return 0;
1274 else return (int)(floor(x + 0.5)); // FIXME: ignoring the 'negative zero'
1275 }
1276
1277
1278 //________________________________________________________________
1279
1280 /*****************************************************************
1281 | |
1282 JS - External
1283 | |
1284 *****************************************************************/
1285
1286 #ifdef ENABLE_JS
External(void * prv,void * val)1287 External::External(void *prv, void *val)
1288 {
1289 priv = new JSExternalPrivate(prv, val);
1290 }
1291
External(External & other)1292 External::External(External& other)
1293 : priv(NULL)
1294 {
1295 assign(other);
1296 }
1297
1298
~External()1299 External::~External()
1300 {
1301 decref();
1302 }
1303
getValue()1304 void* External::getValue()
1305 {
1306 return priv->value;
1307 };
1308
assign(const External & other)1309 void External::assign(const External& other)
1310 {
1311 if (priv) decref();
1312 priv = other.priv; priv->refcnt++;
1313 }
1314
decref()1315 void External::decref()
1316 {
1317 if (priv)
1318 {
1319 priv->refcnt--;
1320 if (!priv->refcnt)
1321 {
1322 delete priv;
1323 priv = NULL;
1324 }
1325 }
1326 }
1327 #endif
1328
1329 /*****************************************************************
1330 | |
1331 E x p r e s s i o n
1332 | |
1333 *****************************************************************/
1334
Expression(Element & owner_,ExFunctor _functor)1335 Expression::Expression(Element &owner_,
1336 ExFunctor _functor /* = EXF_NONE */
1337 )
1338 : args(1), owner(owner_)
1339 {
1340 functor = _functor;
1341 switch(functor)
1342 {
1343 case EXF_LOCSTEP:
1344 {
1345 step = new LocStep(owner_);
1346 type = EX_NODESET;
1347 }; break;
1348 case EXF_LOCPATH:
1349 {
1350 type = EX_NODESET;
1351 }; break;
1352 case EXF_STRINGSEQ:
1353 type = EX_STRING;
1354 break;
1355 default:
1356 {
1357 type = EX_UNKNOWN;
1358 }; break;
1359 };
1360 hasPath = FALSE;
1361 isPattern = FALSE;
1362 pTree = NULL;
1363 // the following sets patomnodeset, note that e.g. patomnumber
1364 // is with it in a union
1365 patomnodeset = NULL;
1366 usesLast = FALSE;
1367 positional = FALSE;
1368 optimizePositionFrom = optimizePositionTo = 0; // see header
1369 }
1370
setLS(ExAxis _axis,ExNodeType _ntype)1371 void Expression::setLS(ExAxis _axis, ExNodeType _ntype)
1372 {
1373 sabassert(functor == EXF_LOCPATH);
1374 Expression *ls = new Expression(getOwnerElement(), EXF_LOCSTEP); // GP: OK
1375 args.append(ls);
1376 ls -> step -> set(_axis, _ntype);
1377 }
1378
1379 //
1380 // Expression destructor
1381 //
~Expression()1382 Expression::~Expression()
1383 {
1384 clearContent();
1385 }
1386
1387 #define deleteZ( PTR ) { cdelete( PTR ); }
1388
1389 //
1390 // clearContent()
1391 // called when setting the expression to a new value
1392 // to dispose of any existing contents.
1393 //
clearContent()1394 void Expression::clearContent()
1395 {
1396 args.freeall(FALSE);
1397 switch(functor)
1398 {
1399 case EXF_ATOM:
1400 {
1401 switch(type)
1402 {
1403 case EX_NODESET:
1404 deleteZ ( patomnodeset );
1405 break;
1406 case EX_STRING:
1407 deleteZ ( patomstring );
1408 break;
1409 case EX_NUMBER:
1410 deleteZ ( patomnumber );
1411 break;
1412 #ifdef ENABLE_JS
1413 case EX_EXTERNAL:
1414 deleteZ( patomexternal);
1415 break;
1416 #endif
1417 };
1418 }; break;
1419 case EXF_LOCSTEP:
1420 deleteZ ( step );
1421 break;
1422 case EXF_VAR:
1423 case EXF_OTHER_FUNC:
1424 deleteZ ( pName );
1425 break;
1426 }
1427 cdelete( pTree );
1428 };
1429
1430
1431
1432 /*================================================================
1433 speak
1434 writes the expression to string 'strg'. Formatting is specified
1435 by 'mode'.
1436 ================================================================*/
1437
speak(Sit S,DStr & strg,SpeakMode mode)1438 eFlag Expression::speak(Sit S, DStr &strg, SpeakMode mode)
1439 {
1440 int i, argsNumber = args.number();
1441 switch(functor)
1442 {
1443 case EXF_ATOM:
1444 {
1445 Str temp;
1446 E( tostring(S, temp) );
1447 strg += temp;
1448 }; break;
1449 case EXF_LOCSTEP:
1450 {
1451 step -> speak(S, strg, mode);
1452 }; break;
1453 case EXF_LOCPATH:
1454 {
1455 for(i = 0; i < argsNumber; i++)
1456 {
1457 args[i] -> speak(S, strg, mode);
1458 if (i < argsNumber-1)
1459 strg += "/";
1460 else if ((argsNumber == 1) &&
1461 (args[0] -> step -> ax == AXIS_ROOT))
1462 strg += "/";
1463 }
1464 }; break;
1465 default:
1466 {
1467 strg += (DStr("\nfunctor ") + (int) functor + "\n--------ARGS:\n");
1468 for (i = 0; i < argsNumber; i++)
1469 {
1470 strg += DStr("(") + (i+1) + ") ";
1471 args[i] -> speak(S, strg, mode);
1472 strg += "\n";
1473 };
1474 strg += "--------ARGS end\n";
1475 }
1476 };
1477 return OK;
1478 }
1479
1480 /*================================================================
1481 matches
1482 returns TRUE iff the current vertex of c satisfies the
1483 Expression's condition.
1484 PROBLEM:
1485 perhaps this should also return an error in case the expression is
1486 not of type nodeset?
1487 ================================================================*/
1488
matchesPattern(Sit S,Context * c,Bool & result)1489 eFlag Expression::matchesPattern(Sit S, Context *c, Bool &result)
1490 {
1491 sabassert(type == EX_NODESET);
1492 if (functor == EXF_LOCPATH)
1493 {
1494 E(matchesSinglePath(S, c -> current(), args.number() - 1, result));
1495 return OK;
1496 }
1497 if (functor == EXFO_UNION)
1498 {
1499 int j, argsNumber = args.number();
1500 for (j = 0; j < argsNumber; j++)
1501 {
1502 E( args[j] -> matchesPattern(S, c, result) );
1503 if (result)
1504 RetOK( result, TRUE );
1505 };
1506 }
1507 RetOK(result, FALSE);
1508 }
1509
trueFor(Sit S,Context * c,Bool & result)1510 eFlag Expression::trueFor(Sit S, Context *c, Bool& result)
1511 {
1512 Expression ex(getOwnerElement());
1513 E( eval(S, ex, c) );
1514 switch(ex.type)
1515 {
1516 case EX_NUMBER:
1517 result = (Bool) (ex.tonumber(S) == (double) (c -> getPosition() + 1));
1518 break;
1519 default:
1520 result = ex.tobool();
1521 }
1522 return OK;
1523 }
1524
1525
tobool()1526 Bool Expression::tobool()
1527 {
1528 sabassert(functor == EXF_ATOM);
1529 switch(type)
1530 {
1531 case EX_NUMBER:
1532 return (Bool) !(*patomnumber == 0.0 || patomnumber -> isNaN());
1533 break;
1534 case EX_STRING:
1535 return (Bool) !(patomstring -> isEmpty());
1536 break;
1537 case EX_BOOLEAN:
1538 return atombool;
1539 break;
1540 case EX_NODESET:
1541 return (Bool) !!(patomnodeset -> getSize());
1542 break;
1543 default: sabassert(0);
1544 };
1545 return FALSE; //just to return something
1546 }
1547
tostring(Sit S,Str & strg)1548 eFlag Expression::tostring(Sit S, Str& strg)
1549 {
1550 sabassert(functor == EXF_ATOM);
1551 switch(type)
1552 {
1553 case EX_NUMBER:
1554 if (patomnumber -> isNaN())
1555 strg = (char*)"NaN";
1556 else
1557 {
1558 if (!patomnumber -> isInf())
1559 strg = (double)(*patomnumber);
1560 else if (*patomnumber > 0.0)
1561 strg = (char*)"+Infinity";
1562 else strg = (char*)"-Infinity";
1563 }
1564 break;
1565 case EX_STRING:
1566 strg = *patomstring;
1567 break;
1568 case EX_BOOLEAN:
1569 strg = (atombool ? (char *)"true" : (char *)"false");
1570 break;
1571 case EX_NODESET:
1572 if (!patomnodeset -> getSize())
1573 strg = (char*)"";
1574 else
1575 {
1576 DStr temp;
1577 S.dom().constructStringValue(patomnodeset -> current(), temp);
1578 strg = temp;
1579 }
1580 break;
1581 case EX_EXTERNAL:
1582 {
1583 strg = (char*)"[External Object]";
1584 }; break;
1585 default: sabassert(0);
1586 };
1587 return OK;
1588 }
1589
tostringRef() const1590 const Str& Expression::tostringRef() const
1591 {
1592 sabassert((functor == EXF_ATOM) && (type == EX_STRING));
1593 return (*NZ(patomstring));
1594 }
1595
tonumber(Sit S)1596 Number Expression::tonumber(Sit S)
1597 {
1598 sabassert(functor == EXF_ATOM);
1599 Number n;
1600 switch(type)
1601 {
1602 case EX_NUMBER:
1603 n = *patomnumber;
1604 break;
1605 case EX_STRING:
1606 n = *patomstring;
1607 break;
1608 case EX_BOOLEAN:
1609 n = (atombool ? 1.0 : 0.0);
1610 break;
1611 case EX_NODESET:
1612 {
1613 // to avoid the following, tostring() must return const Str&:
1614 Str strg;
1615 tostring(S, strg);
1616 n = strg;
1617 // but note that changing it to n = tostringRef() failed
1618 };
1619 break;
1620 default: sabassert(0);
1621 };
1622 return n;
1623 }
1624
1625 #ifdef ENABLE_JS
toexternal(Sit S)1626 External& Expression::toexternal(Sit S)
1627 {
1628 sabassert((functor == EXF_ATOM) && (type == EX_EXTERNAL));
1629 return *patomexternal;
1630 /*
1631 sabassert(functor == EXF_ATOM);
1632 //External e;
1633 switch(type)
1634 {
1635 case EX_EXTERNAL:
1636 {
1637 e.assign(*patomexternal);
1638 //e.setValue(patomexternal -> getValue());
1639 }; break;
1640 default: sabassert(0);
1641 };
1642 //return e;
1643 return NULL;
1644 */
1645 }
1646 #endif
1647
tonodeset()1648 Context& Expression::tonodeset()
1649 {
1650 sabassert((functor == EXF_ATOM) && (type == EX_NODESET));
1651 return *(patomnodeset -> copy());
1652 }
1653
tonodesetRef()1654 const Context& Expression::tonodesetRef()
1655 {
1656 sabassert((functor == EXF_ATOM) && (type == EX_NODESET));
1657 return *patomnodeset;
1658 }
1659
patternOK(Sit S)1660 eFlag Expression::patternOK(Sit S)
1661 {
1662 int i,
1663 argsNumber = args.number();
1664 // sabassert(functor == EXFO_UNION || functor == EXF_LOCPATH);
1665
1666 if ( containsFunctor(EXFF_CURRENT) )
1667 Err(S, E_BAD_PATTERN);
1668
1669 switch(functor)
1670 {
1671 case EXF_LOCPATH:
1672 {
1673 for (i = 0; i < argsNumber; i++)
1674 {
1675 LocStep *ls = args[i] -> step;
1676 switch (ls -> ax)
1677 {
1678 case AXIS_CHILD:
1679 case AXIS_ATTRIBUTE:
1680 case AXIS_ROOT:
1681 break;
1682 case AXIS_DESC_OR_SELF:
1683 if (ls -> ntype != EXNODE_NODE)
1684 Err(S, E_BAD_PATTERN);
1685 break;
1686 default:
1687 Err(S, E_BAD_AXIS_IN_PATTERN);
1688 }
1689 }
1690 }; break;
1691 case EXFO_UNION:
1692 {
1693 for (i=0; i < argsNumber; i++)
1694 E(args[i] -> patternOK(S));
1695 }; break;
1696 default:
1697 Err(S, E_BAD_PATTERN);
1698 // sabassert(!"patternOK()");
1699 };
1700 return OK;
1701 }
1702
parse(Sit S,const DStr & string,Bool _isPattern,Bool defaultToo)1703 eFlag Expression::parse(Sit S, const DStr &string,
1704 Bool _isPattern /* = FALSE */,
1705 Bool defaultToo /* = FALSE */)
1706 {
1707 isPattern = _isPattern;
1708 Tokenizer t(*this);
1709 E( t.tokenize(S, string) );
1710 E( parse(S, t, 0, t.items.number() - 1, defaultToo) );
1711 if (_isPattern)
1712 E( patternOK(S) );
1713 return OK;
1714 }
1715
1716 /*================================================================
1717 Bool isOp()
1718 returns True if the given token is an operator, in which case
1719 'precedence' is set to its precedence
1720 ================================================================*/
1721
isOp(ExToken token,int & precedence)1722 Bool Expression::isOp(ExToken token, int &precedence)
1723 {
1724 Bool is = TRUE;
1725 switch(token)
1726 {
1727 case TOK_OR:
1728 precedence = 0;
1729 break;
1730 case TOK_AND:
1731 precedence = 1;
1732 break;
1733 case TOK_EQ:
1734 case TOK_NEQ:
1735 precedence = 2;
1736 break;
1737 case TOK_LT:
1738 case TOK_GT:
1739 case TOK_LE:
1740 case TOK_GE:
1741 precedence = 3;
1742 break;
1743 case TOK_PLUS:
1744 case TOK_MINUS:
1745 precedence = 4;
1746 break;
1747 case TOK_MULT:
1748 case TOK_DIV:
1749 case TOK_MOD:
1750 precedence = 5;
1751 break;
1752 case TOK_MINUS1:
1753 precedence = 6;
1754 break;
1755 case TOK_VERT:
1756 precedence = 7;
1757 break;
1758 default:
1759 {
1760 is = FALSE;
1761 precedence = -1;
1762 };
1763 };
1764 return is;
1765 }
1766
1767 /*================================================================
1768 void getFunctionInfo()
1769 returns function code and type for the function with given name
1770 if no such builtin function, returns EXFF_NONE
1771 ================================================================*/
1772
getFunctionInfo(const Str & s,ExFunctor & code,ExType & type)1773 void getFunctionInfo(const Str &s, ExFunctor &code, ExType &type)
1774 {
1775 char *p = (char *) s;
1776 int i;
1777
1778 for (i = 0; funcInfoTable[i].name; i++)
1779 {
1780 if (!strcmp(funcInfoTable[i].name,p))
1781 break;
1782 };
1783 code = funcInfoTable[i].func;
1784 type = funcInfoTable[i].type;
1785 }
1786
1787 /*================================================================
1788 void getExternalFunctionInfo()
1789 returns function code and type for the function with given name
1790 if no such builtin function, returns EXFF_NONE
1791 ================================================================*/
1792
getExternalFunctionInfo(const Str & u,const Str & n,ExFunctor & code,ExType & type)1793 void getExternalFunctionInfo(const Str &u, const Str &n, ExFunctor &code, ExType &type)
1794 {
1795 char *p = (char *) n;
1796 char *u2 = (char *) u;
1797 int i;
1798 for (i = 0; extFuncInfoTable[i].name; i++)
1799 {
1800 if (!strcmp(extFuncInfoTable[i].name,p) &&
1801 ((!strcmp(theSabExtNamespace,u2)) || !strcmp(theEXSLTDynNamespace,u2)) )
1802 break;
1803 };
1804 code = extFuncInfoTable[i].func;
1805 type = extFuncInfoTable[i].type;
1806 }
1807
1808 struct OpItem
1809 {
1810 ExFunctor fu;
1811 ExType ty;
1812 int arity;
1813 }
1814 opTable[] =
1815 {
1816 {EXFO_OR, EX_BOOLEAN, 3},
1817 {EXFO_AND, EX_BOOLEAN, 3},
1818 {EXFO_EQ, EX_BOOLEAN, 2},
1819 {EXFO_NEQ, EX_BOOLEAN, 2},
1820 {EXFO_LT, EX_BOOLEAN, 2},
1821 {EXFO_GT, EX_BOOLEAN, 2},
1822 {EXFO_LE, EX_BOOLEAN, 2},
1823 {EXFO_GE, EX_BOOLEAN, 2},
1824 {EXFO_PLUS, EX_NUMBER, 2},
1825 {EXFO_MINUS2, EX_NUMBER, 2},
1826 {EXFO_MULT, EX_NUMBER, 2},
1827 {EXFO_MOD, EX_NUMBER, 2},
1828 {EXFO_DIV, EX_NUMBER, 2},
1829 {EXFO_MINUS1, EX_NUMBER, 1},
1830 {EXFO_UNION, EX_NODESET, 3}
1831 };
1832
1833 /*================================================================
1834 eFlag parseLP()
1835 ================================================================*/
1836
parseLP(Sit S,Tokenizer & tokens,int & pos,Bool dropRoot,Bool defaultToo)1837 eFlag Expression::parseLP(Sit S, Tokenizer& tokens, int &pos,
1838 Bool dropRoot, Bool defaultToo /*=FALSE*/)
1839 {
1840 sabassert(functor == EXF_LOCPATH);
1841 ExToken tok;
1842 BOOL getaway = FALSE;
1843 Expression *ls; // GP: OK (immediately appended)
1844 int& i = pos;
1845 Bool
1846 slashPending = FALSE,
1847 nameWas = FALSE,
1848 nameRecent = FALSE;
1849
1850 tok = tokens.items[i] -> tok;
1851 if (tok == TOK_END)
1852 Err(S, ET_EMPTY_PATT);
1853 if ((tok == TOK_SLASH) || (tok== TOK_DSLASH))
1854 {
1855 if (!dropRoot)
1856 {
1857 args.append(ls = new Expression(getOwnerElement(),EXF_LOCSTEP));
1858 ls -> step -> set(AXIS_ROOT,EXNODE_NODE);
1859 }
1860 if (tok == TOK_SLASH)
1861 i++;
1862 }
1863
1864 while (!getaway)
1865 {
1866 tok = tokens.items[i] -> tok;
1867 switch(tok)
1868 {
1869 case TOK_NAME:
1870 case TOK_NTNAME:
1871 case TOK_AXISNAME:
1872 case TOK_ATSIGN:
1873 case TOK_PERIOD:
1874 case TOK_DPERIOD:
1875 {
1876 if (nameRecent)
1877 Err(S, ET_EXPR_SYNTAX);
1878 args.append(ls = new Expression(getOwnerElement(),EXF_LOCSTEP));
1879 E( ls -> step -> parse(S, tokens, i, defaultToo) );
1880 slashPending = FALSE;
1881 nameWas = TRUE;
1882 //nameRecent = (tok == TOK_NAME || tok == TOK_NTNAME)
1883 nameRecent = TRUE;
1884 };
1885 break;
1886 case TOK_DSLASH:
1887 {
1888 args.append(ls = new Expression(getOwnerElement(),EXF_LOCSTEP));
1889 ls -> step -> set(AXIS_DESC_OR_SELF, EXNODE_NODE);
1890 };
1891 // no break here?
1892 case TOK_SLASH:
1893 {
1894 if (slashPending)
1895 Err(S, ET_EXPR_SYNTAX);
1896 slashPending = TRUE;
1897 i++;
1898 if (tokens.items[i] -> tok == TOK_END)
1899 Err(S, ET_EMPTY_PATT);
1900 nameRecent = FALSE;
1901 };
1902 break;
1903 case TOK_VERT:
1904 case TOK_END:
1905 default: getaway = TRUE;
1906 };
1907 };
1908 if ((slashPending && nameWas) || !args.number())
1909 Err(S, ET_EMPTY_PATT);
1910
1911 return OK;
1912 }
1913
1914
1915 /*================================================================
1916 eFlag parseBasic()
1917 parses the basic expression in tokenizer 't' between positions
1918 'from' and 'to' inclusive. The basic expression is guaranteed
1919 to contain no operators (except for / and //) nor outer
1920 parentheses.
1921 ================================================================*/
1922
parseBasic(Sit S,Tokenizer & t,int from,int to,Bool defaultToo)1923 eFlag Expression::parseBasic(Sit S, Tokenizer &t, int from, int to,
1924 Bool defaultToo /* = FALSE */)
1925 {
1926 GP( Expression ) e, lp;
1927 // find the start of the filtering predicates
1928 int fstart, fright, fleft;
1929 ExToken tok;
1930
1931 switch(t.items[from] -> tok)
1932 {
1933 case TOK_VAR:
1934 case TOK_LITERAL:
1935 case TOK_NUMBER:
1936 fstart = from + 1;
1937 break;
1938 case TOK_FNAME:
1939 {
1940 t.getDelim(S, fstart = from + 1);
1941 fstart++;
1942 };
1943 break;
1944 case TOK_LPAREN:
1945 {
1946 t.getDelim(S, fstart = from);
1947 fstart++;
1948 };
1949 break;
1950 default:
1951 fstart = -1;
1952 };
1953
1954 //#pragma Msg("adding '+1':")
1955 if ((fstart != -1) && (fstart <= to))
1956 {
1957 switch(t.items[fstart] -> tok)
1958 {
1959 case TOK_LBRACKET:
1960 case TOK_SLASH:
1961 case TOK_DSLASH:
1962 {
1963 // parse the filtered expression into args[0]
1964 e = new Expression(getOwnerElement()); // is a GP
1965 E( (*e).parse(S, t, from, fstart - 1));
1966 args.append(e.keep());
1967 //
1968 functor = EXF_FILTER;
1969 type = EX_NODESET;
1970 fleft = fstart;
1971 while (t.items[fleft] -> tok == TOK_LBRACKET)
1972 {
1973 t.getDelim(S, fright = fleft);
1974 if ((t.items[fright] -> tok == TOK_END) || (fright > to))
1975 Err(S, ET_RBRACKET_EXP);
1976 if (fleft + 1 == fright)
1977 Err(S, ET_EXPR_SYNTAX);
1978 E( (e = new Expression(getOwnerElement())) -> parse(S, t,
1979 fleft + 1, fright - 1, defaultToo) );
1980 args.append(e.keep());
1981 fleft = fright + 1;
1982 };
1983 if (((tok = t.items[fleft] -> tok) == TOK_SLASH)
1984 || (tok == TOK_DSLASH))
1985 {
1986 E( (lp = new Expression(getOwnerElement(), EXF_LOCPATH)) -> parseLP(
1987 S, t, fleft, TRUE, defaultToo) );
1988 hasPath = TRUE;
1989 args.append(lp.keep());
1990 };
1991 if (fleft != to + 1)
1992 Err(S, ET_EXPR_SYNTAX);
1993 return OK;
1994 };
1995 break;
1996 }
1997 };
1998
1999 DStr temp;
2000 tok = t.items[from] -> tok;
2001 t.items[from] -> speak(temp,SM_OFFICIAL);
2002 if ((tok == TOK_VAR) || (tok == TOK_LITERAL)
2003 || (tok == TOK_NUMBER))
2004 {
2005 switch(t.items[from] -> tok)
2006 {
2007 case TOK_VAR:
2008 {
2009 functor = EXF_VAR;
2010 type = EX_UNKNOWN;
2011 // GP: OK (member)
2012 E( getOwnerElement().setLogical(S,
2013 *(pName = new QName), temp, FALSE) );
2014 }; break;
2015 case TOK_LITERAL:
2016 {
2017 functor = EXF_ATOM;
2018 type = EX_STRING;
2019 patomstring = new Str(temp);
2020 }; break;
2021 case TOK_NUMBER:
2022 {
2023 functor = EXF_ATOM;
2024 type = EX_NUMBER;
2025 *(patomnumber = new Number) = temp;
2026 }; break;
2027 };
2028 if (to != from)
2029 Err(S, ET_EXPR_SYNTAX);
2030 }
2031 else
2032 {
2033 if (tok == TOK_FNAME)
2034 {
2035 ExFunctor funcNo;
2036 ExType funcType;
2037 getFunctionInfo(temp,funcNo,funcType);
2038 if (funcNo != EXFF_NONE)
2039 {
2040 functor = funcNo;
2041 type = funcType;
2042 }
2043 else
2044 {
2045 QName funcName;
2046 E( getOwnerElement().setLogical(S, funcName, temp, FALSE) );
2047 Str uri = getOwnerTree().expand(funcName.getUri());
2048 Str name = getOwnerTree().expand(funcName.getLocal());
2049 getExternalFunctionInfo(uri,name,funcNo,funcType);
2050 if (funcNo != EXFF_NONE) {
2051 functor = funcNo;
2052 type = funcType;
2053 }
2054 else {
2055
2056 functor = EXF_OTHER_FUNC;
2057 E( getOwnerElement().setLogical(S,
2058 *(pName = new QName), temp, FALSE) );
2059 type = EX_UNKNOWN;
2060 };
2061 }
2062 int i = from+1,
2063 j;
2064 sabassert(t.items[i] -> tok == TOK_LPAREN);
2065 i++;
2066 // original loop test:
2067 // while (t.items[j = t.findTop(TOK_COMMA,i)] -> tok != TOK_END)
2068 while (((j = t.findTop(TOK_COMMA,i)) <= to) && (t.items[j] -> tok != TOK_END))
2069 {
2070 switch(t.items[j-1] -> tok)
2071 {
2072 case TOK_COMMA:
2073 case TOK_LPAREN:
2074 Err(S, ET_EXPR_SYNTAX);
2075 };
2076 args.append(e = new Expression(getOwnerElement()));
2077 e.keep();
2078 E( (*e).parse(S, t,i,j-1, defaultToo) );
2079 i = j+1;
2080 };
2081
2082 if ((t.items[j = t.findTop(TOK_RPAREN,i)]->tok == TOK_END) ||
2083 (j > to))
2084 {
2085 Err(S, ET_RPAREN_EXP);
2086 }
2087 if(t.items[j-1] -> tok == TOK_COMMA)
2088 Err(S, ET_EXPR_SYNTAX);
2089 if (j > i) // if any args
2090 {
2091 args.append(e = new Expression(getOwnerElement()));
2092 e.keep();
2093 E( (*e).parse(S, t,i,j-1, defaultToo) );
2094 }
2095 if (to != j)
2096 Err(S, ET_EXPR_SYNTAX);
2097 } // end "tok == TOK_FNAME"
2098 else
2099 { // it must be a LocPath
2100 type = EX_NODESET;
2101 functor = EXF_LOCPATH;
2102 int howfar = from;
2103 E( parseLP(S, t, howfar, FALSE, defaultToo) );
2104 if (howfar != to + 1)
2105 Err(S, ET_EXPR_SYNTAX);
2106 }
2107 }
2108 return OK;
2109 }
2110
2111 /*================================================================
2112 eFlag parse()
2113 translates the given token list into an expression (a tree of
2114 'Expression' objects plus some leaves).
2115 INPUT
2116 t a tokenizer whose tokenize() method has been called
2117 from,to first and last position in the token list the parsing
2118 applies to (i.e. a complex expression will parse the
2119 subexpressions with the same tokenizer but different
2120 limits)
2121 ================================================================*/
2122
parse(Sit S,Tokenizer & t,int from,int to,Bool defaultToo)2123 eFlag Expression::parse(Sit S, Tokenizer& t, int from, int to,
2124 Bool defaultToo /* = FALSE */)
2125 // isOp, skipParens
2126 {
2127 int i;
2128 ExToken
2129 token,
2130 mintoken = TOK_NONE;
2131 int precedence,
2132 minprec = 999,
2133 minndx = -1,
2134 leftmost = 0,
2135 arity;
2136
2137 if (from > to)
2138 Err(S, ET_EXPR_SYNTAX);
2139
2140 t.stripParens(S, from,to);
2141 // search from right to left (left-associativity)
2142 for (i = to; i >= from; i--)
2143 {
2144 switch(token = t.items[i] -> tok)
2145 {
2146 case TOK_RPAREN:
2147 case TOK_RBRACKET:
2148 {
2149 // reverse search:
2150 E( t.getDelim(S, i,TRUE) ); // i is decremented at loop end
2151 if (i == -1)
2152 Err(S, ET_LPARCKET_EXP);
2153 };
2154 break;
2155 default:
2156 {
2157 if (isOp(token, precedence) && (precedence < minprec))
2158 {
2159 minprec = precedence;
2160 minndx = leftmost = i;
2161 mintoken = token;
2162 // if (token == TOK_OR) break;
2163 }
2164 else
2165 if (token == mintoken)
2166 leftmost = i;
2167 };
2168 };
2169 };
2170
2171 //minndx now points to the rightmost lowest-precedence operator
2172 // leftmost points at its leftmost occurence
2173
2174 if (minndx == -1)
2175 E( parseBasic(S, t, from, to, defaultToo) )
2176 else
2177 {
2178 int tablendx = t.items[minndx] -> tok - TOKGROUP_OPERATORS;
2179 functor = opTable[tablendx].fu;
2180 type = opTable[tablendx].ty;
2181 arity = opTable[tablendx].arity;
2182 Expression *e = new Expression(getOwnerElement()); // GP: OK
2183
2184 args.append(e);
2185 switch(arity)
2186 {
2187 case 1:
2188 {
2189 if (minndx != from)
2190 Err(S, ET_EXPR_SYNTAX)
2191 else
2192 E( e -> parse(S, t,from+1,to, defaultToo) );
2193 };
2194 break;
2195 case 2:
2196 {
2197 E( e -> parse(S, t,from,minndx - 1, defaultToo) );
2198 args.append(e = new Expression(getOwnerElement())); // GP: OK
2199 E( e -> parse(S, t,minndx + 1, to, defaultToo) );
2200 };
2201 break;
2202 default:
2203 {
2204 E( e -> parse(S, t,from,leftmost - 1, defaultToo) );
2205 int another = leftmost,
2206 lastone = leftmost;
2207 // tom 24-10-00
2208 // the following fails for "x and not(x and x)"
2209 // t.getDelim(another);
2210 // changing it to:
2211 another = t.findTop(t.items[another]->tok, another+1);
2212 while((another <= to) && (t.items[another]->tok != TOK_END))
2213 {
2214 args.append(e = new Expression(getOwnerElement())); // GP: OK
2215 E( e -> parse(S, t, lastone + 1, another - 1, defaultToo));
2216 lastone = another;
2217
2218 // tom 14-11-00
2219 // t.getDelim(another); failed too, for "x and x and (x and x)"
2220 another = t.findTop(t.items[another]->tok, another+1);
2221 };
2222 args.append(e = new Expression(getOwnerElement())); // GP: OK
2223 E( e -> parse(S, t,lastone + 1, to, defaultToo) );
2224 };
2225 };
2226 }
2227 return OK;
2228 }
2229
2230
setAtom(Context * c)2231 void Expression::setAtom(Context *c)
2232 {
2233 clearContent();
2234 functor = EXF_ATOM;
2235 type = EX_NODESET;
2236 patomnodeset = c;
2237 }
2238
setAtom(const Number & n)2239 void Expression::setAtom(const Number& n)
2240 {
2241 clearContent();
2242 functor = EXF_ATOM;
2243 type = EX_NUMBER;
2244 *(patomnumber = new Number) = (Number&) n;
2245 }
2246
setAtom(Bool b)2247 void Expression::setAtom(Bool b)
2248 {
2249 clearContent();
2250 functor = EXF_ATOM;
2251 type = EX_BOOLEAN;
2252 atombool = b;
2253 }
2254
setAtom(const DStr & s)2255 void Expression::setAtom(const DStr &s)
2256 {
2257 clearContent();
2258 functor = EXF_ATOM;
2259 type = EX_STRING;
2260 patomstring = new Str(s);
2261 }
2262
2263 #ifdef ENABLE_JS
setAtom(const External & ext)2264 void Expression::setAtom(const External &ext)
2265 {
2266 clearContent();
2267 functor = EXF_ATOM;
2268 type = EX_EXTERNAL;
2269 patomexternal = new External();
2270 patomexternal -> assign(ext);
2271 }
2272 #endif
2273
2274 /*================================================================
2275 setFragment
2276 sets the expression to point to a 'result tree fragment' - a newly
2277 constructed tree - whose address it returns
2278 ================================================================*/
2279
setFragment()2280 Tree* Expression::setFragment()
2281 {
2282 functor = EXF_FRAGMENT;
2283 type = EX_NODESET;
2284 return pTree = new Tree("RTF", FALSE); // not an XSL tree
2285 }
2286
2287 #define funcIsOperator(f) ((EXFO_OR <= f) && (f <= EXFO_Z))
2288 #define funcIsBuiltin(f) ((EXF_FUNCTION <= f) && (f <= EXFF_NONE))
2289
eval(Sit S,Expression & retxpr,Context * c,Bool resolvingGlobals)2290 eFlag Expression::eval(Sit S, Expression &retxpr, Context *c, Bool resolvingGlobals /* = FALSE */)
2291 {
2292 sabassert(!isPattern && "evaluating pattern!");
2293 GP( Context ) newc;
2294
2295 switch(functor)
2296 {
2297 case EXF_ATOM:
2298 {
2299 //cannot use retxpr = *this !!!
2300 switch(type)
2301 {
2302 case EX_STRING:
2303 retxpr.setAtom(*patomstring);
2304 break;
2305 case EX_NUMBER:
2306 retxpr.setAtom(*patomnumber);
2307 break;
2308 case EX_NODESET:
2309 retxpr.setAtom(patomnodeset -> copy());
2310 break;
2311 case EX_BOOLEAN:
2312 retxpr.setAtom(atombool);
2313 break;
2314 #ifdef ENABLE_JS
2315 case EX_EXTERNAL:
2316 retxpr.setAtom(*patomexternal);
2317 break;
2318 #endif
2319 default: sabassert(0);
2320 }
2321 }; break;
2322 case EXF_VAR:
2323 {
2324 Expression *ex = NZ(S.getProcessor()) -> getVarBinding(*pName);
2325 if (!ex)
2326 {
2327 // if we're resolving globals, this may mean a forward reference
2328 if (resolvingGlobals)
2329 {
2330 E( S.getProcessor() -> resolveGlobal(S, c, *pName) );
2331 ex = S.getProcessor() -> getVarBinding(*pName);
2332 }
2333 else
2334 {
2335 Str fullName;
2336 getOwnerTree().expandQStr(*pName, fullName);
2337 Err1(S, E1_VAR_NOT_FOUND, fullName);
2338 }
2339 };
2340 E( ex -> eval(S, retxpr, c) );
2341 }; break;
2342 case EXF_LOCPATH:
2343 case EXFO_UNION:
2344 {
2345 sabassert(c && "context is null!");
2346 newc.assign(c);
2347 E( createContext(S, newc) );
2348 newc.unkeep();
2349 // assign newc directly without copying
2350 retxpr.setAtom((*newc).copy());
2351 newc.del();
2352 }; break;
2353 case EXF_OTHER_FUNC: // other function
2354 {
2355 Str ret;
2356 Str uri = getOwnerTree().expand(pName -> getUri());
2357 Str name = getOwnerTree().expand(pName -> getLocal());
2358
2359 if (! S.getProcessor() -> supportsFunction(uri, name) )
2360 {
2361 Str fullName;
2362 getOwnerTree().expandQStr(*pName, fullName);
2363 Err1(S, ET_FUNC_NOT_SUPPORTED,fullName);
2364 }
2365 int i,
2366 argsNumber = args.number();
2367 // ExprList atoms(LIST_SIZE_1);
2368 GPD( ExprList ) atoms = new ExprList(LIST_SIZE_1);
2369 atoms.autodelete();
2370 GP( Expression ) ex;
2371 for (i = 0; i < argsNumber; i++)
2372 {
2373 ex = new Expression(getOwnerElement());
2374 E( args[i]->eval(S, *ex, c, resolvingGlobals) );
2375 (*atoms).append(ex.keep());
2376 };
2377
2378 #ifdef ENABLE_JS
2379 E( S.getProcessor()->callJSFunction(S, c, uri, name, *atoms, retxpr) );
2380 #endif
2381 }; break;
2382 case EXF_FILTER:
2383 {
2384 sabassert(c && "context is null!");
2385 newc.assign(c);
2386 E( createContext(S, newc, c -> getPosition()) );
2387 newc.unkeep();
2388 retxpr.setAtom((*newc).copy());
2389 newc.del();
2390 }; break;
2391 case EXF_STRINGSEQ:
2392 {
2393 DStr result;
2394 Expression temp(getOwnerElement());
2395 int i,
2396 argsNumber = args.number();
2397 for (i = 0; i < argsNumber; i++)
2398 {
2399 E( args[i] -> eval(S, temp, c, resolvingGlobals) );
2400 Str tempStr;
2401 E( temp.tostring(S, tempStr) );
2402 result += tempStr;
2403 };
2404 retxpr.setAtom(result);
2405 }; break;
2406 case EXF_FRAGMENT:
2407 {
2408 newc = new Context(NULL); //current nde not needed _cn_
2409 (*newc).set(&(pTree -> getRoot()));
2410 retxpr.setAtom((*newc).copy());
2411 newc.del();
2412 }; break;
2413 default:
2414 {
2415 int i,
2416 argsNumber = args.number();
2417 // ExprList atoms(LIST_SIZE_1);
2418 GPD( ExprList ) atoms = new ExprList(LIST_SIZE_1);
2419 atoms.autodelete();
2420 GP( Expression ) ex;
2421 for (i = 0; i < argsNumber; i++)
2422 {
2423 ex = new Expression(getOwnerElement());
2424 E( args[i]->eval(S, *ex, c, resolvingGlobals) );
2425 (*atoms).append(ex.keep());
2426 };
2427 if (funcIsOperator(functor))
2428 E( callOp(S, retxpr, *atoms) ) //an operator
2429 else
2430 if (funcIsBuiltin(functor))
2431 E( callFunc(S, retxpr, *atoms, c) ) //a core XPath function
2432 else
2433 {
2434 Str fullName;
2435 getOwnerTree().expandQStr(*pName, fullName);
2436 Err1( S, ET_FUNC_NOT_SUPPORTED, fullName );
2437 }
2438 // atoms autodeleted
2439 };
2440 };
2441 return OK;
2442 }
2443
2444
2445
2446 template<class T>
hardCompare(ExFunctor op,T b1,T b2)2447 Bool hardCompare(ExFunctor op, T b1, T b2)
2448 {
2449 Str p,q;
2450 switch(op)
2451 {
2452 case EXFO_EQ: return (Bool) (b1 == b2); break;
2453 case EXFO_NEQ: return (Bool) !(b1 == b2); break;
2454 case EXFO_LT: return (Bool) (b1 < b2); break;
2455 case EXFO_GT: return (Bool) (b2 < b1); break;
2456 case EXFO_LE: return (Bool) ((b1 < b2) || (b1 == b2)); break;
2457 case EXFO_GE: return (Bool) ((b2 < b1) || (b1 == b2)); break;
2458 default: sabassert(0);
2459 }
2460 return FALSE; //just to return something
2461 }
2462
_invertOp(ExFunctor op)2463 ExFunctor _invertOp(ExFunctor op)
2464 {
2465 switch(op)
2466 {
2467 case EXFO_EQ: return EXFO_EQ; break;
2468 case EXFO_NEQ: return EXFO_NEQ; break;
2469 case EXFO_LT: return EXFO_GT; break;
2470 case EXFO_GT: return EXFO_LT; break;
2471 case EXFO_LE: return EXFO_GE; break;
2472 case EXFO_GE: return EXFO_LE; break;
2473 default: sabassert(!"_invertOp"); return EXF_NONE; // to return something
2474 }
2475 }
2476
atomicCompare(ExFunctor op,const Str & str1,const Str & str2,Number * num2)2477 Bool atomicCompare(ExFunctor op, const Str& str1, const Str& str2,
2478 Number* num2)
2479 {
2480 switch(op) {
2481 case EXFO_EQ:
2482 case EXFO_NEQ:
2483 {
2484 return hardCompare(op, str1, str2);
2485 }; break;
2486 case EXFO_LT:
2487 case EXFO_GT:
2488 case EXFO_LE:
2489 case EXFO_GE:
2490 {
2491 Number n1, n2;
2492 n1 = str1;
2493 if (num2) n2 = *num2;
2494 else n2 = str2;
2495 return hardCompare(op, n1, n2);
2496 }; break;
2497 default:
2498 {
2499 sabassert(!"atomicCompare");
2500 }
2501 }
2502 return FALSE; //just to return something
2503 }
2504
compareCC(Sit S,ExFunctor op,const Context & c1,const Context & c2)2505 Bool Expression::compareCC(Sit S, ExFunctor op, const Context &c1, const Context &c2)
2506 {
2507 DStr str1, str2;
2508 GP( Context )
2509 c1prime = ((Context&) c1).copy(),
2510 c2prime = ((Context&) c2).copy();
2511 Bool resulting = FALSE;
2512 (*c1prime).reset();
2513 while ((*c1prime).current())
2514 {
2515 str1.empty();
2516 S.dom().constructStringValue((*c1prime).current(), str1);
2517 (*c2prime).reset();
2518 while((*c2prime).current())
2519 {
2520 str2.empty();
2521 S.dom().constructStringValue((*c2prime).current(), str2);
2522 if ( atomicCompare(op, str1, str2, NULL) )
2523 {
2524 resulting = TRUE;
2525 break;
2526 }
2527 (*c2prime).shift();
2528 };
2529 (*c1prime).shift();
2530 };
2531 // would be done automatically:
2532 c1prime.del();
2533 c2prime.del();
2534 return resulting;
2535 }
2536
compareCS(Sit S,ExFunctor op,const Context & c1,const Str & str2)2537 Bool Expression::compareCS(Sit S, ExFunctor op, const Context &c1, const Str &str2)
2538 {
2539 DStr str1;
2540 Bool resulting = FALSE;
2541 GP( Context ) c = ((Context&) c1).copy();
2542 //some optimization on str to num conversion
2543 Number* num2 = NULL;
2544 if (op != EXFO_EQ && op != EXFO_NEQ)
2545 {
2546 num2 = new Number();
2547 *num2 = str2;
2548 }
2549
2550 (*c).reset();
2551 while((*c).current())
2552 {
2553 str1.empty();
2554 S.dom().constructStringValue((*c).current(), str1);
2555 if ( atomicCompare(op, str1, str2, num2) )
2556 {
2557 resulting = TRUE;
2558 break;
2559 }
2560 (*c).shift();
2561 };
2562 c.del();
2563 if (num2) delete num2;
2564 return resulting;
2565 }
2566
compareCN(Sit S,ExFunctor op,const Context & c1,const Number & n2)2567 Bool Expression::compareCN(Sit S, ExFunctor op, const Context &c1, const Number& n2)
2568 {
2569 Number n1;
2570 DStr s1;
2571 GP( Context ) c = ((Context&) c1).copy();
2572 Bool resulting = FALSE;
2573
2574 (*c).reset();
2575 while((*c).current())
2576 {
2577 s1.empty();
2578 S.dom().constructStringValue((*c).current(), s1);
2579 n1 = s1;
2580 if (hardCompare(op, n1, (Number)n2))
2581 {
2582 resulting = TRUE;
2583 break;
2584 }
2585 (*c).shift();
2586 };
2587 c.del();
2588 return resulting;
2589 }
2590
compare(Sit S,Bool & result,Expression & other,ExFunctor op)2591 eFlag Expression::compare(Sit S, Bool &result, Expression &other, ExFunctor op)
2592 // Both *this and other are assumed to be ATOMS.
2593 {
2594 sabassert(functor == EXF_ATOM);
2595 sabassert(other.functor == EXF_ATOM);
2596
2597 ExType histype = other.type;
2598
2599 //check external object
2600 if (type == EX_EXTERNAL || histype == EX_EXTERNAL)
2601 {
2602 Err(S, E_INVALID_OPERAND_TYPE);
2603 }
2604
2605 //
2606 if (type == EX_NODESET)
2607 {
2608 if (other.type == EX_BOOLEAN)
2609 result = hardCompare(op, tobool(), other.tobool());
2610 else
2611 {
2612 Context& mynodeset = tonodeset(); // GP: OK
2613 switch(other.type)
2614 {
2615 case EX_NODESET:
2616 result = compareCC(S, op, mynodeset, other.tonodesetRef());
2617 break;
2618 case EX_STRING:
2619 {
2620 Str temp;
2621 E( other.tostring(S, temp) );
2622 result = compareCS(S, op, mynodeset, temp);
2623 }; break;
2624 case EX_NUMBER:
2625 result = compareCN(S, op, mynodeset, other.tonumber(S));
2626 break;
2627 default: sabassert(0);
2628 };
2629 delete &mynodeset;
2630 }
2631 }
2632 else
2633 {
2634 if (histype == EX_NODESET)
2635 {
2636 E( other.compare(S, result, *this, _invertOp(op)) );
2637 }
2638 else
2639 { // none of the two are nodesets
2640 switch (op) {
2641 case EXFO_EQ:
2642 case EXFO_NEQ:
2643 {
2644 if (type == EX_BOOLEAN || histype == EX_BOOLEAN)
2645 result = hardCompare(op, tobool(), other.tobool());
2646 else
2647 {
2648 if (type == EX_NUMBER || histype == EX_NUMBER)
2649 result = hardCompare(op, tonumber(S), other.tonumber(S));
2650 else
2651 {
2652 if (type == EX_STRING || histype == EX_STRING)
2653 {
2654 Str temp, otherTemp;
2655 E( tostring(S, temp) );
2656 E( other.tostring(S, otherTemp) );
2657 result = hardCompare(op, temp, otherTemp);
2658 }
2659 else
2660 sabassert(0);
2661 }
2662 }
2663 }; break;
2664 case EXFO_LT:
2665 case EXFO_GT:
2666 case EXFO_LE:
2667 case EXFO_GE:
2668 {
2669 result = hardCompare(op, tonumber(S), other.tonumber(S));
2670 }; break;
2671 }
2672 }
2673 }
2674 return OK;
2675 }
2676
callOp(Sit S,Expression & retxpr,ExprList & atoms)2677 eFlag Expression::callOp(Sit S, Expression& retxpr, ExprList& atoms)
2678 {
2679 int i,
2680 atomsNumber = atoms.number();
2681 switch(functor)
2682 {
2683 case EXFO_OR:
2684 case EXFO_AND:
2685 {
2686 sabassert(atomsNumber > 1);
2687 Bool result;
2688 result = atoms[0] -> tobool();
2689 for (i = 1; i < atomsNumber; i++)
2690 {
2691 if (functor == EXFO_OR)
2692 {
2693 if (atoms[i] -> tobool())
2694 {
2695 result = TRUE;
2696 break;
2697 }
2698 }
2699 else //EXFO_AND
2700 {
2701 if (!atoms[i] -> tobool())
2702 {
2703 result = FALSE;
2704 break;
2705 }
2706 };
2707 };
2708 retxpr.setAtom(result);
2709 }; break;
2710 case EXFO_EQ:
2711 case EXFO_NEQ:
2712 case EXFO_LT:
2713 case EXFO_LE:
2714 case EXFO_GT:
2715 case EXFO_GE:
2716 {
2717 sabassert(atomsNumber == 2);
2718 Bool result;
2719 E( atoms[0]->compare(S, result,*(atoms[1]),functor) );
2720 retxpr.setAtom(result);
2721 }; break;
2722 case EXFO_PLUS:
2723 case EXFO_MINUS2:
2724 case EXFO_MULT:
2725 case EXFO_DIV:
2726 case EXFO_MOD:
2727 {
2728 sabassert(atomsNumber > 1);
2729 double result;
2730 result = (double) (atoms[0] -> tonumber(S));
2731 for (i = 1; i < atomsNumber; i++)
2732 {
2733 switch(functor)
2734 {
2735 case EXFO_PLUS:
2736 result += atoms[i] -> tonumber(S);
2737 break;
2738 case EXFO_MINUS2:
2739 result -= atoms[i] -> tonumber(S);
2740 break;
2741 case EXFO_MULT:
2742 result *= atoms[i] -> tonumber(S);
2743 break;
2744 case EXFO_DIV:
2745 result /= atoms[i] -> tonumber(S);
2746 break;
2747 case EXFO_MOD:
2748 {
2749 double d = atoms[i] -> tonumber(S);
2750 double aux = result/d;
2751 aux = aux > 0 ? floor(aux) : ceil(aux);
2752 result = result - (aux * d);
2753 };
2754 break;
2755 };
2756 };
2757 //eliminate the -0 effect
2758 if (!result) result = 0;
2759 retxpr.setAtom(Number(result));
2760 }; break;
2761 case EXFO_MINUS1:
2762 {
2763 sabassert(atomsNumber == 1);
2764 retxpr.setAtom(Number(-(double)(atoms[0] -> tonumber(S))));
2765 }; break;
2766 };
2767 return OK;
2768 }
2769
2770 // this appends the list of nodes with given ID, possibly with multiplicities
appendNodesWithID(Sit S,Str & ids,Context * c,Context & result)2771 void appendNodesWithID(Sit S, Str& ids, Context *c, Context& result)
2772 {
2773 char *p = (char*) ids;
2774 Str singleID;
2775 NodeHandle singleNode;
2776 int tokenLen;
2777 for (skipWhite(p); *p; skipWhite(p))
2778 {
2779 singleID.nset(p, tokenLen = strcspn(p,theWhitespace));
2780 SXP_Document doc = S.dom().getOwnerDocument(c -> current());
2781 singleNode = S.dom().getNodeWithID(doc, singleID);
2782 if (singleNode)
2783 result.append(singleNode);
2784 p += tokenLen;
2785 };
2786 }
2787
2788 /*================================================================
2789 callFunc
2790 calls the built-in XPath or XSLT function as determined by
2791 'this -> functor'. Returns an expression in 'retxpr'.
2792 'atoms' is a list of atomized arguments, 'c' is the current context
2793 necessary for certain functions.
2794 ================================================================*/
2795
2796 #define checkArgsCount(x) if (atomsNumber != x)\
2797 Err(S, ET_BAD_ARGS_N);
2798 #define checkArgsCountMax(x) if (atomsNumber > x)\
2799 Err(S, ET_BAD_ARGS_N);
2800 #define checkArgsCountMin(x) if (atomsNumber < x)\
2801 Err(S, ET_BAD_ARGS_N);
2802 #define checkArgsCountBetween(x,y) if ((atomsNumber < x) || \
2803 (atomsNumber > y)) Err(S, ET_BAD_ARGS_N);
2804
2805 // only check for being a nodeset
2806 #define checkIsNodeset(x) if (atoms[x] -> type != EX_NODESET)\
2807 Err(S, ET_BAD_ARG_TYPE);
2808
2809 // Everything is a string, in a cosmic sense
2810 #define checkIsString(x)
2811 #define checkIsString2(x,y)
2812 #define checkIsNumber(x)
2813
2814 /*................
2815 firstOccurence
2816 Finds the first complete occurence of q in p; returns the 0-based starting
2817 position, or -1 if not found
2818 Now works for UTF-8 strings.
2819 ................*/
2820
firstOccurence(char * p,char * q)2821 int firstOccurence(char *p, char *q)
2822 {
2823 int i = 0,
2824 iCurr = 0,
2825 j = 0,
2826 pos = 0,
2827 charlen;
2828 while(p[i] && q[j])
2829 {
2830 charlen = utf8SingleCharLength(p + i);
2831 if (!strncmp(p + i, q + j, charlen))
2832 {
2833 i += charlen;
2834 j += charlen;
2835 }
2836 else
2837 {
2838 i = (iCurr += utf8SingleCharLength(p + iCurr));
2839 pos++;
2840 j = 0;
2841 }
2842 };
2843 if (q[j])
2844 return -1;
2845 else
2846 return pos;
2847 }
2848
2849 /*................
2850 getBetween
2851 Returns in s the portion of the source string between from and to inclusive.
2852 If to == -1 then copies the whole rest of the string.
2853 Now works for multibyte UTF-8 characters.
2854 ................*/
2855
getBetween(Str & s,char * source,int from,int to)2856 void getBetween(Str& s, char *source, int from, int to)
2857 {
2858 sabassert(source);
2859 char *start = NULL;
2860 int i;
2861 if (from < 0) from = 0;
2862 for (i = 0; *source && (i <= to || to == -1); i++)
2863 {
2864 if (i == from)
2865 {
2866 start = source;
2867 if (to == -1)
2868 // no need to go through the rest of the string
2869 break;
2870 }
2871 if (!(*source & 0x80))
2872 // optimize for ASCII chars
2873 source++;
2874 else
2875 source += utf8SingleCharLength(source);
2876 }
2877 if (!start)
2878 s.empty();
2879 else
2880 {
2881 if (to == -1)
2882 s = start;
2883 else
2884 s.nset(start,(int)(source - start));
2885 }
2886 }
2887
2888 /*................
2889 getCurrValue
2890 Returns the string value of the current node
2891 ................*/
2892
getCurrValue(Sit S,Str & s,Context * c)2893 eFlag getCurrValue(Sit S, Str &s, Context *c)
2894 {
2895 NodeHandle v;
2896 DStr temp;
2897 if (!!(v = c -> current()))
2898 S.dom().constructStringValue(v, temp);
2899 else
2900 s.empty();
2901 s = temp;
2902 return OK;
2903 }
2904
2905 // An auxiliary function which retrieves the document from a given URI,
2906 // regardless of whether DOM is external or not. Should be moved into S.dom().
2907
2908 // new model for processing on external documents: first if have the
2909 // processor and we're running on external, the DOMProvider is asked
2910
getDocument_(Sit S,NodeHandle & newroot,const Str & location,const Str & baseUri,Processor * proc)2911 eFlag Expression::getDocument_(Sit S, NodeHandle& newroot,
2912 const Str& location, const Str& baseUri,
2913 Processor* proc)
2914 {
2915 newroot = NULL;
2916 if ( proc && proc -> processingExternal() )
2917 {
2918 /* FIXME: this is incomplete */
2919 newroot = S.dom().retrieveDocument(location, baseUri);
2920 }
2921 //external not active or failed
2922 if ( nhNull(newroot) )
2923 {
2924 if ( proc )
2925 {
2926 Tree *newtree;
2927 Str uri, locBase;
2928 if (baseUri == (const char*)"")
2929 locBase = proc -> baseForVertex(S, &getOwnerElement());
2930 else
2931 locBase = baseUri;
2932 makeAbsoluteURI(S, location, locBase, uri);
2933
2934 //deny document fragments for the 'file' scheme
2935 const char* aux = (char*)uri;
2936 const char *colon = strchr(aux, ':');
2937 if ( colon
2938 && ( ((colon - aux == 4) && !strncmp(aux, "file", 4)) ||
2939 ((colon - aux == 3) && !strncmp(aux, "arg", 3))))
2940 {
2941 if ( strchr((const char*)uri, '#') )
2942 Err1(S, E_DOC_FRAGMENT, (char*)uri);
2943 }
2944 //if we found no colon, it's bad, but we do not care right here
2945
2946 Bool iserr =
2947 proc -> readTreeFromURI(S, newtree, uri,
2948 proc ->
2949 baseForVertex(S, &getOwnerElement()),
2950 FALSE,
2951 S.hasFlag(SAB_IGNORE_DOC_NOT_FOUND));
2952 if (!iserr) {
2953 newroot = (NodeHandle) &(newtree -> getRoot());
2954 //strip spaces
2955 proc -> stripTree(S, *(newtree));
2956 } else if (! S.hasFlag(SAB_IGNORE_DOC_NOT_FOUND))
2957 return NOT_OK; //propagate an error
2958 }
2959 else
2960 {
2961 Err1(S, E1_URI_OPEN, location);
2962 }
2963 }
2964 return OK;
2965 }
2966
2967
2968
callFunc(Sit S,Expression & retxpr,ExprList & atoms,Context * c)2969 eFlag Expression::callFunc(Sit S, Expression &retxpr, ExprList &atoms, Context *c)
2970 {
2971 NodeHandle v;
2972 int atomsNumber = atoms.number();
2973 switch(functor)
2974 {
2975 case EXFF_LAST:
2976 {
2977 checkArgsCount(0);
2978 retxpr.setAtom( Number(c -> getSize()) );
2979 }; break;
2980 case EXFF_POSITION:
2981 {
2982 checkArgsCount(0);
2983 retxpr.setAtom( Number(c -> getPosition() + 1) );
2984 }; break;
2985 case EXFF_COUNT:
2986 {
2987 checkArgsCount(1);
2988 checkIsNodeset(0);
2989 retxpr.setAtom(
2990 Number(atoms[0] -> tonodesetRef().getSize()) );
2991 }; break;
2992 case EXFF_LOCAL_NAME:
2993 case EXFF_NAMESPACE_URI:
2994 case EXFF_NAME:
2995 {
2996 checkArgsCountMax(1);
2997 Str strg;
2998 if (!atomsNumber)
2999 v = (c -> isFinished()) ? NULL : c -> current();
3000 else
3001 {
3002 checkIsNodeset(0);
3003 const Context& newc = atoms[0] -> tonodesetRef();
3004 v = (newc.isVoid()? NULL : newc.current());
3005 };
3006 if (v)
3007 {
3008 if (!S.domExternal(v))
3009 {
3010 const QName& q = toV(v) -> getName();
3011 switch(functor)
3012 {
3013 case EXFF_NAME:
3014 toV(v) -> getOwner().expandQStr(q, strg); break;
3015 case EXFF_LOCAL_NAME:
3016 strg = toV(v) -> getOwner().expand(q.getLocal()); break;
3017 case EXFF_NAMESPACE_URI:
3018 strg = toV(v) -> getOwner().expand(q.getUri()); break;
3019 }
3020 }
3021 else
3022 {
3023 const char *aux;
3024 switch(functor) {
3025 case EXFF_NAME:
3026 {
3027 strg = (aux = S.dom().getNodeName(v));
3028 S.dom().freeName(v, (char*)aux);
3029 } break;
3030 case EXFF_LOCAL_NAME:
3031 {
3032 strg = (aux = S.dom().getNodeNameLocal(v));
3033 S.dom().freeName(v, (char*)aux);
3034 } break;
3035 case EXFF_NAMESPACE_URI:
3036 {
3037 strg = (aux = S.dom().getNodeNameURI(v));
3038 S.dom().freeName(v, (char*)aux);
3039 } break;
3040 }
3041 }
3042 };
3043 retxpr.setAtom(strg);
3044 };
3045 break;
3046
3047 case EXFF_STRING:
3048 {
3049 Str string;
3050 checkArgsCountMax(1);
3051 if (!atomsNumber)
3052 E( getCurrValue(S, string, c) )
3053 else
3054 E( atoms[0] -> tostring(S, string) );
3055 retxpr.setAtom(string);
3056 }; break;
3057
3058 case EXFF_CONCAT:
3059 {
3060 checkArgsCountMin(2);
3061 DStr strg;
3062 for (int k = 0; k < atomsNumber; k++)
3063 {
3064 checkIsString(k);
3065 Str atomStr;
3066 E( atoms[k] -> tostring(S, atomStr) );
3067 strg += atomStr;
3068 };
3069 retxpr.setAtom(strg);
3070 }; break;
3071
3072 case EXFF_STARTS_WITH:
3073 {
3074 checkArgsCount(2);
3075 checkIsString2(0,1);
3076 Str a0Str, a1Str;
3077 E( atoms[0] -> tostring(S, a0Str) );
3078 E( atoms[1] -> tostring(S, a1Str) );
3079 retxpr.setAtom((Bool) !firstOccurence(
3080 a0Str, a1Str));
3081 }; break;
3082
3083 case EXFF_CONTAINS:
3084 {
3085 checkArgsCount(2);
3086 checkIsString2(0,1);
3087 Str a0Str, a1Str;
3088 E( atoms[0] -> tostring(S, a0Str) );
3089 E( atoms[1] -> tostring(S, a1Str) );
3090 retxpr.setAtom((Bool) (firstOccurence(
3091 a0Str, a1Str) != -1));
3092 }; break;
3093
3094 case EXFF_SUBSTRING_BEFORE:
3095 case EXFF_SUBSTRING_AFTER:
3096 {
3097 Str strg;
3098 Str theBigger, theSmaller;
3099 E( atoms[0] -> tostring(S, theBigger) );
3100 E( atoms[1] -> tostring(S, theSmaller) );
3101 checkArgsCount(2);
3102 checkIsString2(0,1);
3103 int where = firstOccurence(theBigger,theSmaller);
3104 if (where == -1)
3105 strg.empty();
3106 else
3107 {
3108 if (functor == EXFF_SUBSTRING_BEFORE)
3109 {
3110 if (where == 0)
3111 strg.empty();
3112 else
3113 getBetween(strg, theBigger, 0, where-1);
3114 }
3115 else
3116 getBetween(strg, theBigger,
3117 where + utf8StrLength(theSmaller), -1);
3118 };
3119 retxpr.setAtom(strg);
3120 }; break;
3121
3122 case EXFF_SUBSTRING:
3123 {
3124 checkArgsCountBetween(2,3);
3125 checkIsString(0); checkIsNumber(1);
3126 /* useless test causing a warning in MSVC:
3127
3128 if (atomsNumber == 3)
3129 checkIsNumber(2);
3130 */
3131 Str strg;
3132 Number from_ = atoms[1] -> tonumber(S) - 1;
3133 if (!from_.isNaN() && !from_.isInf())
3134 {
3135 int from = from_.round(),
3136 to = -1;
3137 if (atomsNumber > 2)
3138 {
3139 // use length in 3rd argument
3140 Number len = atoms[2] -> tonumber(S);
3141 if (len <= 0 || len.isNaN())
3142 to = -2;
3143 else if (!len.isInf())
3144 to = from + len.round() - 1; // otherwise it remains -1
3145 }
3146 Str a0Str;
3147 E( atoms[0] -> tostring(S, a0Str) );
3148 getBetween(strg, a0Str, from, to);
3149 }
3150 retxpr.setAtom(strg);
3151 }; break;
3152
3153 case EXFF_STRING_LENGTH:
3154 {
3155 checkArgsCountBetween(0,1);
3156 if (atomsNumber)
3157 {
3158 checkIsString(0);
3159 Str a0Str;
3160 E( atoms[0] -> tostring(S, a0Str) );
3161 retxpr.setAtom(Number(utf8StrLength(a0Str)));
3162 }
3163 else
3164 {
3165 Str string;
3166 E( getCurrValue(S, string, c) );
3167 retxpr.setAtom(Number(utf8StrLength(string)));
3168 }
3169 }; break;
3170
3171 case EXFF_NORMALIZE_SPACE:
3172 {
3173 checkArgsCountBetween(0,1);
3174 Str string;
3175 if (atomsNumber)
3176 {
3177 checkIsString(0);
3178 E( atoms[0] -> tostring(S, string) );
3179 }
3180 else
3181 E( getCurrValue(S, string, c) );
3182 char *p = (char*) string;
3183 DStr stripped;
3184 skipWhite(p);
3185 while(*p)
3186 {
3187 if (isWhite(*p))
3188 {
3189 skipWhite(p);
3190 if (*p)
3191 stripped += ' ';
3192 p--;
3193 }
3194 else
3195 stripped += *p;
3196 p++;
3197 }
3198 retxpr.setAtom(stripped);
3199 }; break;
3200
3201 case EXFF_TRANSLATE:
3202 {
3203 checkArgsCount(3);
3204 checkIsString2(0,1);
3205 checkIsString(2);
3206
3207 DStr resulting;
3208 Str baseStr, srcStr, destStr;
3209 int pos;
3210 E( atoms[0] -> tostring(S, baseStr) );
3211 E( atoms[1] -> tostring(S, srcStr) );
3212 E( atoms[2] -> tostring(S, destStr) );
3213 char
3214 // changing tostringCharPtr() to tostring():
3215 *p = baseStr,
3216 *src = srcStr,
3217 *dest = destStr,
3218 *destchar;
3219 while(*p)
3220 {
3221 pos = utf8Strchr(src, p);
3222 if (pos == -1) {
3223 destchar = p;
3224 }
3225 else if ((destchar = utf8StrIndex(dest,pos)) == NULL) {
3226 p += utf8SingleCharLength(p);
3227 continue;
3228 }
3229 resulting.nadd(destchar, utf8SingleCharLength(destchar));
3230 p += utf8SingleCharLength(p);
3231 };
3232 retxpr.setAtom(resulting);
3233 }; break;
3234
3235 case EXFF_BOOLEAN:
3236 {
3237 checkArgsCount(1);
3238 retxpr.setAtom(atoms[0] -> tobool());
3239 }; break;
3240
3241 case EXFF_NOT:
3242 {
3243 checkArgsCount(1);
3244 retxpr.setAtom((Bool)!(atoms[0] -> tobool()));
3245 }; break;
3246
3247 case EXFF_TRUE:
3248 {
3249 checkArgsCount(0);
3250 retxpr.setAtom(TRUE);
3251 }; break;
3252
3253 case EXFF_FALSE:
3254 {
3255 checkArgsCount(0);
3256 retxpr.setAtom(FALSE);
3257 }; break;
3258
3259 case EXFF_LANG:
3260 {
3261 checkArgsCount(1);
3262 checkIsString(0);
3263 // get the argument
3264 Str langQuery;
3265 E( atoms[0] -> tostring(S, langQuery) );
3266 NodeHandle w, att = NULL;
3267 int attCount, i;
3268 const char* langValue = NULL;
3269 for (w = c -> current(); w && !langValue; w = S.dom().getParent(w))
3270 {
3271 // find whether w has an xml:lang attribute
3272 if (!S.domExternal(w)) {
3273 QName searchName;
3274 searchName.setUri(getOwnerTree().unexpand(theXMLNamespace));
3275 searchName.setLocal(getOwnerTree().unexpand("lang"));
3276 int idx = toE(w) -> atts.findNdx(searchName);
3277 if (idx != -1) {
3278 langValue = toA( toE(w) -> atts[idx]) -> cont;
3279 }
3280 } else {
3281 attCount = S.dom().getAttributeCount(w);
3282 for (i = 0; i < attCount && !langValue; i++)
3283 {
3284 att = S.dom().getAttributeNo(w, i);
3285 const char *uri = S.dom().getNodeNameURI(att);
3286 const char *local = S.dom().getNodeNameLocal(att);
3287 if (!strcmp(uri, theXMLNamespace)
3288 && !strcmp(local, "lang"))
3289 langValue = S.dom().getNodeValue(att);
3290 S.dom().freeName(att, (char*)uri);
3291 S.dom().freeName(att, (char*)local);
3292 }
3293 }
3294 }
3295 if (langValue)
3296 {
3297 char *langQPtr = (char*) langQuery;
3298 int qlen;
3299
3300 // if strings equal except for possibly a suffix starting with -
3301 // then return TRUE
3302 if (!strncasecmp(langQPtr, langValue, qlen = langQuery.length())
3303 && (langValue[qlen] == 0 || langValue[qlen] == '-'))
3304 retxpr.setAtom(TRUE);
3305 else
3306 retxpr.setAtom(FALSE);
3307 if (att)
3308 S.dom().freeValue(att, (char*)langValue);
3309 }
3310 else
3311 retxpr.setAtom(FALSE);
3312 }; break;
3313
3314 case EXFF_NUMBER:
3315 {
3316 checkArgsCountMax(1);
3317 Number n;
3318 if (!atomsNumber)
3319 {
3320 Str string;
3321 E( getCurrValue(S, string, c) );
3322 n = string;
3323 }
3324 else
3325 n = atoms[0] -> tonumber(S);
3326 retxpr.setAtom(n);
3327 }; break;
3328
3329 case EXFF_SUM:
3330 {
3331 DStr string;
3332 Number n, sum = 0;
3333 checkArgsCount(1);
3334 checkIsNodeset(0);
3335 GP( Context ) newc = &(atoms[0] -> tonodeset());
3336 (*newc).reset();
3337 while (!(*newc).isFinished())
3338 {
3339 string.empty();
3340 S.dom().constructStringValue((*newc).current(), string);
3341 n = string;
3342 if (n.isNaN())
3343 {
3344 sum.setNaN();
3345 break;
3346 };
3347 sum = sum + n;
3348 (*newc).shift();
3349 };
3350 newc.del();
3351 retxpr.setAtom(sum);
3352 }; break;
3353
3354 case EXFF_FLOOR:
3355 case EXFF_CEILING:
3356 case EXFF_ROUND:
3357 {
3358 checkArgsCount(1);
3359 checkIsNumber(0);
3360 Number n = atoms[0] -> tonumber(S);
3361 switch(functor)
3362 {
3363 case EXFF_FLOOR:
3364 n = floor((double)n); break;
3365 case EXFF_CEILING:
3366 n = ceil((double)n); break;
3367 case EXFF_ROUND:
3368 n = floor((double)n + .5); break;
3369 };
3370 retxpr.setAtom(n);
3371 }; break;
3372
3373 case EXFF_DOCUMENT:
3374 {
3375 checkArgsCountMin(1);
3376 checkArgsCountMax(2);
3377 checkIsString(0);
3378 DStr location;
3379 Str baseUri;
3380 if (atomsNumber == 2)
3381 {
3382 checkIsNodeset(1);
3383 const Context& aux = atoms[1] -> tonodesetRef();
3384 if ( ! aux.isVoid() )
3385 {
3386 NodeHandle n = aux[0];
3387 if (! S.domExternal(n) )
3388 {
3389 baseUri = NZ(toV(n) -> subtree) -> getBaseURI();
3390 } else {
3391 baseUri = "";
3392 }
3393 } else {
3394 baseUri = "";
3395 }
3396 //atoms[1] -> tostring(S, baseUri);
3397 }
3398 else
3399 baseUri = "";
3400 NodeHandle newroot = NULL;
3401 Processor *proc = S.getProcessor();
3402 //sabassert(S.domExternal((void*)1) || proc); //_ph_ sxp
3403 GP( Context ) newc = new Context(c->getCurrentNode());
3404
3405 // Current node doesn't change
3406 //(*newc).setCurrentNode (c->getCurrentNode());
3407
3408 // GP: the context doesn't autodelete anything on error
3409 // since readTreeFromURI adds the trees to datalines list
3410 // All datalines removed on error (in cleanupAfterRun())
3411
3412 if (atoms[0] -> type == EX_NODESET)
3413 {
3414 const Context& ctxt = atoms[0] -> tonodesetRef();
3415 int ctxtNumber = ctxt.getSize();
3416 for (int k = 0; k < ctxtNumber; k++)
3417 {
3418 S.dom().constructStringValue(ctxt[k], location);
3419 E( getDocument_(S, newroot, location, baseUri, proc) );
3420 // check for duplicities and correct URI sorting!
3421 if (! nhNull(newroot) )
3422 {
3423 (*newc).append(newroot);
3424 if (proc && !S.domExternal(newroot)) // _ph_ spx
3425 E( proc -> makeKeysForDoc(S, newroot) );
3426 }
3427 };
3428 }
3429 else
3430 {
3431 E( atoms[0] -> tostring(S, location) );
3432 E( getDocument_(S, newroot, location, baseUri, proc) );
3433 if (! nhNull(newroot) )
3434 {
3435 (*newc).append(newroot);
3436 if (proc && !S.domExternal(newroot)) //_ph_ spx
3437 E( proc -> makeKeysForDoc(S, newroot) );
3438 }
3439 }
3440 retxpr.setAtom(newc.keep());
3441 }; break;
3442
3443 case EXFF_GENERATE_ID:
3444 {
3445 DStr s;
3446 switch(atomsNumber)
3447 {
3448 case 0:
3449 v = (c -> isFinished() ? NULL : c -> current());
3450 break;
3451 case 1:
3452 {
3453 checkIsNodeset(0);
3454 const Context& newc = atoms[0] -> tonodesetRef();
3455 v = (newc.isVoid()? NULL : newc.current());
3456 }; break;
3457 default:
3458 Err(S, ET_BAD_ARGS_N);
3459 };
3460 if (v)
3461 {
3462 s = "i__";
3463 if (S.domExternal(v))
3464 {
3465 // need cast to long then to int to avoid
3466 // compiler error on IBM AIX
3467 s += (int) (long) v;
3468 }
3469 else
3470 {
3471 s += (int) (long) &(toV(v) -> getOwner());
3472 s += "_";
3473 s += toV(v) -> stamp;
3474 }
3475 }
3476 retxpr.setAtom(s);
3477 }; break;
3478
3479 case EXFF_SYSTEM_PROPERTY:
3480 {
3481 checkArgsCount(1);
3482 checkIsString(0);
3483 QName q;
3484 Str a0Str;
3485 E( atoms[0] -> tostring(S, a0Str) );
3486 E( getOwnerElement().setLogical(S, q, a0Str, FALSE) );
3487 if (q.getUri() == getOwnerTree().stdPhrase(PHRASE_XSL_NAMESPACE))
3488 {
3489 const Str& localStr = getOwnerTree().expand(q.getLocal());
3490 if (localStr == (const char*) "version")
3491 retxpr.setAtom(Number(1.0));
3492 else if (localStr == (const char*) "vendor")
3493 retxpr.setAtom(Str("Ginger Alliance"));
3494 else if (localStr == (const char*) "vendor-url")
3495 retxpr.setAtom(Str("www.gingerall.com"));
3496 else
3497 retxpr.setAtom(Str(""));
3498 }
3499 else if (q.getUri() == getOwnerTree().stdPhrase(PHRASE_SABEXT_NAMESPACE))
3500 {
3501 const Str& localStr = getOwnerTree().expand(q.getLocal());
3502 if (localStr == (const char*) "version")
3503 retxpr.setAtom(Str(SAB_VERSION));
3504 else
3505 retxpr.setAtom(Str(""));
3506 }
3507 else
3508 retxpr.setAtom(Str(""));
3509 }; break;
3510
3511 case EXFF_EVAL:
3512 {
3513 Str string;
3514 checkArgsCount(1);
3515 E( atoms[0] -> tostring(S, string) );
3516 Expression *ex = new Expression(getOwnerElement());
3517 E( (*ex).parse(S,string,FALSE,FALSE) );
3518 E( (*ex).eval(S,retxpr,c) )
3519 }; break;
3520
3521 case EXFF_CURRENT:
3522 {
3523 Context *newc = new Context(NULL); //_cn_ no need for cn
3524 newc -> set ( NZ(c -> getCurrentNode()));
3525 //Context *origc = getOwnerElement().getOrigContext();
3526 //sabassert(origc);
3527 //newc -> set ( origc -> current());
3528 retxpr.setAtom (newc);
3529 }; break;
3530 case EXFF_KEY:
3531 {
3532 checkArgsCount(2);
3533 checkIsString(0);
3534 QName q;
3535 Str a0Str;
3536 E( atoms[0] -> tostring(S, a0Str) );
3537 getOwnerElement().setLogical(S, q, a0Str, FALSE);
3538 EQName ename;
3539 getOwnerTree().expandQ(q, ename);
3540 Processor *proc = NZ(S.getProcessor());
3541 GP( Context ) newc = new Context(NULL); //_cn_
3542 GP( Context ) newc2;
3543 Context auxContext(NULL);
3544 DStr oneString;
3545 if (atoms[1] -> type == EX_NODESET)
3546 {
3547 const Context& ctxt = atoms[1] -> tonodesetRef();
3548 int ctxtNumber = ctxt.getSize();
3549 for (int k = 0; k < ctxtNumber; k++)
3550 {
3551 S.dom().constructStringValue(ctxt[k], oneString);
3552 E( proc -> getKeyNodes(S, ename,
3553 oneString, auxContext,
3554 S.dom().getOwnerDocument(c -> current())) );
3555 newc2 = (*newc).swallow(S, &auxContext);
3556 newc.del();
3557 newc.assign(newc2.keep());
3558 };
3559 }
3560 else
3561 {
3562 E( atoms[1] -> tostring(S, oneString) );
3563 E( proc -> getKeyNodes(S, ename,
3564 oneString, *newc,
3565 S.dom().getOwnerDocument(c -> current())) );
3566 }
3567 retxpr.setAtom(newc.keep());
3568 }; break;
3569 case EXFF_FORMAT_NUMBER:
3570 {
3571 checkArgsCountBetween(2,3);
3572 Number num = atoms[0] -> tonumber(S);
3573 Str fmt;
3574 E( atoms[1] -> tostring(S, fmt) );
3575 EQName ename;
3576 if (atomsNumber == 3)
3577 {
3578 Str nameStr;
3579 E( atoms[2] -> tostring(S, nameStr) );
3580 QName q;
3581 getOwnerElement().setLogical(S, q, nameStr, FALSE);
3582 getOwnerTree().expandQ(q, ename);
3583 }
3584 Str result;
3585 E( NZ(S.getProcessor()) -> decimals().format(S, ename, num, fmt, result) );
3586 retxpr.setAtom(result);
3587 }; break;
3588
3589 case EXFF_ID:
3590 {
3591 checkArgsCount(1);
3592 GP( Context ) result = new Context(NULL);
3593 DStr ids;
3594 if (atoms[0] -> type == EX_NODESET)
3595 {
3596 const Context& ctxt = atoms[0] -> tonodesetRef();
3597 int ctxtNumber = ctxt.getSize();
3598 for (int k = 0; k < ctxtNumber; k++)
3599 {
3600 S.dom().constructStringValue(ctxt[k], ids);
3601 appendNodesWithID(S, ids, c, *result);
3602 };
3603 }
3604 else
3605 {
3606 E( atoms[0] -> tostring(S, ids) );
3607 appendNodesWithID(S, ids, c, *result);
3608 };
3609 E( (*result).sort(S) );
3610 (*result).uniquize();
3611 retxpr.setAtom(result.keep());
3612 }; break;
3613
3614 case EXFF_FUNCTION_AVAILABLE:
3615 {
3616 checkArgsCount(1);
3617 Str nameStr;
3618 QName funcName;
3619 ExFunctor funcNo;
3620 ExType funcType;
3621 E( atoms[0] -> tostring(S, nameStr));
3622 E( getOwnerElement().setLogical(S, funcName, nameStr, FALSE) );
3623 Str uri = getOwnerTree().expand(funcName.getUri());
3624 Str name = getOwnerTree().expand(funcName.getLocal());
3625 getExternalFunctionInfo(uri,name,funcNo,funcType);
3626 if (funcNo == EXFF_NONE) {
3627 retxpr.setAtom(S.getProcessor() -> supportsFunction(uri, name));
3628 } else {
3629 retxpr.setAtom(TRUE);
3630 };
3631 }; break;
3632
3633 case EXFF_ELEMENT_AVAILABLE:
3634 {
3635 checkArgsCount(1);
3636 Str nameStr;
3637 QName elName;
3638 E( atoms[0] -> tostring(S, nameStr));
3639 E( getOwnerElement().setLogical(S, elName, nameStr, FALSE) );
3640 Bool ret = ExtensionElement::elementAvailable(getOwnerTree(), elName);
3641 retxpr.setAtom(ret);
3642 }; break;
3643 case EXFF_UNPARSED_ENTITY_URI:
3644 {
3645 checkArgsCount(1);
3646 NodeHandle curr;
3647 NZ( curr = c -> current() );
3648 if (S.domExternal(curr))
3649 {
3650 retxpr.setAtom(""); //not supported for external docs
3651 //add check for SXPF_SUPPORTS_UNPARSED_ENTITIES
3652 }
3653 else
3654 {
3655 Str name;
3656 E( atoms[0] -> tostring(S, name) );
3657 Str *uri = toV(curr) -> getOwner().getUnparsedEntityUri(name);
3658 if (uri)
3659 retxpr.setAtom(*uri);
3660 else
3661 retxpr.setAtom("");
3662 }
3663 }; break;
3664
3665 default:
3666 Err1(S, ET_FUNC_NOT_SUPPORTED, getFuncName(functor));
3667 }
3668 return OK;
3669 }
3670
createLPContext(Sit S,Context * & c,int baseNdx,NodeHandle givenGlobalCurrent)3671 eFlag Expression::createLPContext(Sit S, Context *&c, int baseNdx, NodeHandle givenGlobalCurrent /* = NULL */)
3672 {
3673 sabassert(functor == EXF_LOCPATH);
3674 GP( Context ) theResult = new Context(c->getCurrentNode()); //_cn_ result has no cn
3675 Context info(givenGlobalCurrent ? givenGlobalCurrent : c -> getCurrentNode()); //_cn_
3676 //info.setCurrentNode(givenGlobalCurrent ? givenGlobalCurrent : c -> current());
3677 //(*theResult).setCurrentNode(c -> getCurrentNode());
3678 E( createLPContextLevel(S, 0, args.number(), c -> current(), info, theResult) );
3679 E( (*theResult).sort(S) );
3680 (*theResult).uniquize();
3681 c = theResult.keep();
3682 return OK;
3683 }
3684
3685 /*
3686 * createLPContextLevel
3687 * ranges over all nodes that satisfy the stepLevel-th step, calling self
3688 * recursively until the last one is reached. The vertices satisfying the last
3689 * step are added to theResult.
3690 * Base is passed from the preceding step and used for expression
3691 * evaluation. info holds the 'globally current' vertex.
3692 * The purpose of this routine is to generate a context without having to
3693 * also generate the intermediate contexts for each step. Also, some of the predicates
3694 * may be known to use last(), in which case we first have to compute the number
3695 * of nodes that reach such a predicate at all.
3696 */
3697
createLPContextLevel(Sit S,int stepLevel,int stepsCount,NodeHandle base,Context & info,Context * theResult)3698 eFlag Expression::createLPContextLevel(Sit S,
3699 int stepLevel, int stepsCount, NodeHandle base,
3700 Context &info, Context *theResult)
3701 {
3702 // GP: theResult will be freed on error since the caller (createLPContext) holds it in a GP
3703
3704 sabassert(functor == EXF_LOCPATH);
3705 int i, j, init,
3706 predsCount = args[stepLevel] -> step -> preds.number(),
3707 lastBad = -1; // last bad predicate, or the step itself
3708
3709
3710 // keep a stack of positions, one for each predicate IN THIS STEP
3711 List<int> reached(predsCount), // serves as position for next pred
3712 totalReached(predsCount); // serves as size for next (bad) pred
3713
3714 // there will be as many dry (size-counting) runs as there are bad preds
3715 Bool dryRun = TRUE,
3716 quitThisRound = FALSE, quitThisVertex = FALSE;
3717
3718 // i ranges over predicates. Value i==predsCount is the special last run
3719 for (i = 0; i <= predsCount; i++)
3720 {
3721 if (i == predsCount)
3722 // the last run, not a dry-run
3723 dryRun = FALSE;
3724 // if this is the last run, or if the current pred uses last(), compute
3725 // the context size
3726 if (!dryRun || args[stepLevel] -> step -> preds[i] -> usesLast)
3727 {
3728 // initialize the size arrays:
3729 // append base values for preds past the last bad one,
3730 // up to this bad one (incl.)
3731 for (init = 0; init <= lastBad; init++)
3732 reached[init] = 0;
3733 for (init = lastBad + 1; init <= i; init++)
3734 {
3735 reached.append(0);
3736 totalReached.append(-1); // -1 just for safety
3737 };
3738
3739 // locally current vertex
3740 NodeHandle locCurr = NULL;
3741
3742 quitThisRound = FALSE;
3743 do
3744 {
3745 // shift the locally current vertex
3746 //_speed_ this should be replaced with smart context
3747 //created with createContext on local step argument
3748 E( args[stepLevel] -> step -> shift(S, locCurr, base) );
3749 if (!nhNull(locCurr))
3750 {
3751 if ((lastBad < 0) || !dryRun) ++reached[0];
3752 quitThisVertex = FALSE;
3753 for (j = 0; j < i; j++)
3754 {
3755 Bool satisfies;
3756 info.deppendall();
3757 // set locCurr as current at position reached[j]-1 in the context
3758 info.setVirtual(locCurr, reached[j] - 1, totalReached[j]);
3759
3760 Expression *thisPred =
3761 args[stepLevel] -> step -> preds[j];
3762 // find whether we're in position bounds for this pred
3763 switch(thisPred -> inBounds(reached[j] - 1))
3764 {
3765 case 0:
3766 // within bounds
3767 {
3768 E( thisPred -> trueFor(S, &info, satisfies) );
3769 if (satisfies)
3770 ++reached[j + 1];
3771 else
3772 quitThisVertex = TRUE;
3773 }; break;
3774 case -1:
3775 // before start, move to another vertex
3776 quitThisVertex = TRUE;
3777 break;
3778 case 1:
3779 // past the end, bail out
3780 quitThisRound = TRUE;
3781 break;
3782 };
3783 if (quitThisVertex || quitThisRound)
3784 break;
3785 };
3786 if (j == i && !dryRun) // passed all preds
3787 {
3788 if (stepLevel < stepsCount - 1)
3789 E( createLPContextLevel(S,
3790 stepLevel + 1, stepsCount,
3791 locCurr, info, theResult))
3792 else
3793 theResult -> append(locCurr);
3794
3795 } // if ! dryRun
3796 } // if locCurr
3797 } while (!nhNull(locCurr) && !quitThisRound);
3798 // move all data collected to safe places
3799 for (init = lastBad + 1; init <= i; init++)
3800 totalReached[init] = reached[init];
3801 lastBad = i;
3802 } // if bad predicate
3803 } // for, over all preds
3804 return OK;
3805 }
3806
3807
createLPContextSum(Sit S,Context * & c,NodeHandle globalCurrent)3808 eFlag Expression::createLPContextSum(Sit S, Context *&c, NodeHandle globalCurrent /* = NULL */)
3809 {
3810 sabassert(functor == EXF_LOCPATH);
3811 GP( Context ) newc = new Context(c->getCurrentNode()); //_cn_ needed?
3812 Context
3813 *newc2, *returnedc;
3814 int cNumber = c -> getSize();
3815 for (int j = 0; j < cNumber; j++)
3816 {
3817 E( createLPContext(S, returnedc = c, j, globalCurrent) );
3818 newc2 = (*newc).swallow(S, returnedc);
3819 newc.del();
3820 newc = newc2;
3821 delete returnedc;
3822 /* tom 01/11/25 */
3823 c -> shift();
3824 }
3825 c = newc.keep();
3826 return OK;
3827 }
3828
3829
3830 /*................................................................
3831 createContext()
3832 creates a context for this expression, based on its functor.
3833 ................................................................*/
3834
3835 // GP: createContext is clean
3836 // if unsuccessful, returns NULL in c and performs no allocations
3837
3838
createContext(Sit S,Context * & c,int baseNdx)3839 eFlag Expression::createContext(Sit S, Context *& c, int baseNdx /* = -1 */)
3840 {
3841 GP( Context ) newc; // newc gets assigned c ONLY IN THE END
3842 Context *c_orig = c;
3843 newc.assign(c);
3844 c = NULL;
3845
3846 int i, j,
3847 argsNumber = args.number();
3848 if (baseNdx == -1)
3849 baseNdx = (*newc).getPosition();
3850 switch(functor)
3851 {
3852 case EXF_VAR:
3853 {
3854 Expression *deref = NULL;
3855 if (S.getProcessor())
3856 deref = S.getProcessor() -> getVarBinding(*pName);
3857 if (!deref)
3858 {
3859 Str fullName;
3860 getOwnerTree().expandQStr(*pName, fullName);
3861 Err1(S, E1_VAR_NOT_FOUND, fullName);
3862 }
3863 NodeHandle current_node = (*newc).getCurrentNode();
3864 E( deref -> createContext(S, newc, baseNdx) );
3865 newc.unkeep();
3866 (*newc).setCurrentNode(current_node);
3867 };
3868 break;
3869 case EXF_ATOM:
3870 {
3871 if (type != EX_NODESET)
3872 Err(S, ET_CONTEXT_FOR_BAD_EXPR);
3873 newc = patomnodeset -> copy();
3874 newc.unkeep();
3875 }; break;
3876 case EXFO_UNION:
3877 {
3878 sabassert(baseNdx != -1); // meaningful only for a locpath
3879 GP( Context ) csummand;
3880 Context *newc2; // GP: OK
3881 sabassert(argsNumber);
3882 E( args[0] -> createContext(S, newc, baseNdx) );
3883 newc.unkeep();
3884 for (i = 1; i < argsNumber; i++)
3885 {
3886 csummand.assign(c_orig);
3887 E( args[i] -> createContext(S, csummand, baseNdx) );
3888 newc2 = (*newc).swallow(S, csummand);
3889 csummand.del();
3890 newc.del();
3891 newc = newc2;
3892 }
3893 // clean up newc!
3894 (*newc).reset();
3895 }
3896 break;
3897 case EXF_LOCPATH:
3898 {
3899 E( createLPContext(S, newc, baseNdx) );
3900 newc.unkeep();
3901 }
3902 break;
3903 case EXF_FILTER:
3904 {
3905 sabassert(baseNdx != -1); // meaningful only for a locpath
3906 NodeHandle wasCurrent = (*newc).getCurrentNode();
3907 E( args[0] -> createContext(S, newc, baseNdx) );
3908 newc.unkeep();
3909 (*newc).setCurrentNode(wasCurrent);
3910
3911 GP( Context ) filteredc;
3912 for (i = 1; i < argsNumber - (int) hasPath; i++)
3913 {
3914 filteredc = new Context(c_orig -> getCurrentNode());
3915 (*newc).reset();
3916 Bool istrue;
3917 int newcNumber = (*newc).getSize();
3918 for (j = 0; j < newcNumber; j++)
3919 {
3920 E(args[i] -> trueFor(S, newc, istrue));
3921 if (istrue)
3922 (*filteredc).append((*newc)[j]);
3923 (*newc).shift();
3924 };
3925 newc.del();
3926 newc = filteredc.keep();
3927 if (!(*newc).getSize()) break;
3928 };
3929 if (hasPath)
3930 {
3931 filteredc.assign(newc);
3932 filteredc = newc; // a patch due to SGI MIPSpro compiler
3933 E( args[argsNumber-1] -> createLPContextSum(S, filteredc, (*newc).getCurrentNode()) );
3934 newc.del();
3935 newc = filteredc.keep();
3936 }
3937 }
3938 break;
3939 case EXF_LOCSTEP:
3940 {
3941 sabassert(step);
3942 sabassert(baseNdx != -1); // meaningful only for a locpath
3943
3944 /////////
3945 // E( step -> createContextNoPreds(newc = c, baseNdx) ); - done as follows:
3946 GP( Context ) newc2 = new Context(c_orig -> getCurrentNode());
3947 NodeHandle curr = NULL;
3948 do
3949 {
3950 //_speed_ here we should test the axis type
3951 // and use smart context if possible
3952 E( step -> shift(S, curr, (*newc)[baseNdx]) );
3953 if (!nhNull(curr))
3954 (*newc2).append(curr);
3955 }
3956 while (!nhNull(curr));
3957 /////////
3958
3959 GP( Context ) filteredc;
3960 int stepPredsNumber = step -> preds.number();
3961 for (i = 0; i < stepPredsNumber; i++)
3962 {
3963 filteredc = new Context(c_orig -> getCurrentNode());
3964 (*newc2).reset();
3965 Bool istrue;
3966 int newc2Number = (*newc2).getSize();
3967 for (j = 0; j < newc2Number; j++)
3968 {
3969 E( step -> preds[i] -> trueFor(S, newc2,istrue) );
3970 if (istrue)
3971 (*filteredc).append((*newc2)[j]);
3972 (*newc2).shift();
3973 };
3974 newc2.del();
3975 newc2 = filteredc.keep();
3976 if (!(*newc2).getSize()) break;
3977 };
3978 newc = newc2.keep();
3979 };
3980 break;
3981 case EXF_OTHER_FUNC:
3982 {
3983 Expression resolved(getOwnerElement());
3984 E( eval(S, resolved, newc) );
3985 E( resolved.createContext(S, newc, baseNdx) );
3986 newc.unkeep();
3987 }; break;
3988 default:
3989 if (funcIsBuiltin(functor))
3990 {
3991 Expression resolved(getOwnerElement());
3992 E( eval(S, resolved, newc) );
3993 E( resolved.createContext(S, newc, baseNdx) );
3994 newc.unkeep();
3995 }
3996 else
3997 Err(S, ET_CONTEXT_FOR_BAD_EXPR);
3998 };
3999 c = newc.keep();
4000 return OK;
4001 }
4002
4003
matchesSingleStep(Sit S,NodeHandle v,Bool & result)4004 eFlag Expression::matchesSingleStep(Sit S, NodeHandle v, Bool &result)
4005 {
4006 sabassert(functor == EXF_LOCSTEP);
4007 if (!NZ(step) -> matchesWithoutPreds(S, v))
4008 RetOK(result, FALSE);
4009 if (!step -> preds.number())
4010 RetOK(result, TRUE);
4011 if (!S.dom().getParent(v))
4012 RetOK(result, FALSE);
4013 if (!step -> positional)
4014 {
4015 GP( Context ) c = new Context(NULL); //_cn_ current() is not allowed in patterns
4016 (*c).set(v);
4017 Bool stillOK = TRUE;
4018 for (int i = 0; i < step -> preds.number() && stillOK; i++)
4019 E(step -> preds[i] -> trueFor(S, c,stillOK));
4020 c.del();
4021 RetOK(result,stillOK);
4022 }
4023 else // positional case
4024 {
4025 GP( Context ) c = new Context(NULL); //_cn_ current() is not allowed in patterns
4026 Context *newc; // GP: OK
4027 (*c).set(S.dom().getParent(v));
4028 E( createContext(S, newc = c+0, 0) );
4029 result = (newc -> contains(v));
4030 c.del();
4031 delete newc;
4032 }
4033 return OK;
4034 }
4035
4036
matchesSinglePath(Sit S,NodeHandle v,int lastIndex,Bool & result)4037 eFlag Expression::matchesSinglePath(Sit S, NodeHandle v, int lastIndex, Bool& result)
4038 {
4039 sabassert(functor == EXF_LOCPATH);
4040 int i;
4041 NodeHandle w = v;
4042 for (i = lastIndex; i >= 0; i--)
4043 {
4044 if (!w)
4045 RetOK(result, FALSE);
4046 switch(args[i] -> step -> ax)
4047 {
4048 case AXIS_ROOT:
4049 {
4050 if (i)
4051 sabassert(!"root not first");
4052 E( args[i] -> matchesSingleStep(S, w, result) );
4053 if (!result) RetOK(result, FALSE);
4054 };
4055 break;
4056 case AXIS_CHILD:
4057 case AXIS_ATTRIBUTE:
4058 {
4059 E( args[i] -> matchesSingleStep(S, w, result) );
4060 if (!result) RetOK(result, FALSE);
4061 w = S.dom().getParent(w);
4062 };
4063 break;
4064 case AXIS_DESC_OR_SELF:
4065 {
4066 E( args[i] -> matchesSingleStep(S, w, result) );
4067 if (!result) RetOK(result, FALSE);
4068 NodeHandle previous = w;
4069 while (previous)
4070 {
4071 E(matchesSinglePath(S, previous, i-1, result));
4072 if (result)
4073 return OK;
4074 else
4075 previous = S.dom().getParent(previous);
4076 };
4077 RetOK( result, FALSE );
4078 }
4079 break;
4080 default:
4081 sabassert(!"bad axis in pattern");
4082 }
4083 }
4084 result = TRUE;
4085 return OK;
4086 }
4087
4088 /*
4089 * optimizePositional()
4090 * called for a predicate of a locstep
4091 * returns 2 if the predicate uses the last() function
4092 * so the size of the context has to be determined
4093 * returns 1 if the predicate only uses position()
4094 * 0 if neither
4095 */
4096
optimizePositional(int level)4097 int Expression::optimizePositional(int level)
4098 {
4099 int result = 0;
4100
4101 switch(functor)
4102 {
4103 case EXFF_LAST:
4104 result = 2; break;
4105 case EXFF_POSITION:
4106 result = 1; break;
4107 case EXF_ATOM:
4108 case EXF_VAR:
4109 case EXF_LOCPATH:
4110 /* result = 0; */ break;
4111 case EXF_STRINGSEQ:
4112 case EXF_FRAGMENT:
4113 case EXF_LOCSTEP:
4114 sabassert(!"invalid predicate type");
4115 break;
4116 case EXF_FILTER: // can be e.g. "current()/@attr"
4117 default: // all the functions and operators, including EXF_OTHER_FUNC
4118 {
4119 int sub = 0;
4120 for (int i = 0; i < args.number(); i++)
4121 {
4122 if (!!(sub = args[i] -> optimizePositional(level + 1)))
4123 {
4124 result = sub;
4125 if (result == 2) break;
4126 }
4127 }
4128 }
4129 }
4130 //PH: when we're called w/o recursion,
4131 //we might be called for situation like foo[45],
4132 //so we should return 1, if the expression
4133 //is of the type of EX_NUMBER
4134 if (!level && type == EX_NUMBER && !result) result = 1;
4135
4136 usesLast = (result == 2);
4137 positional = (result >= 1);
4138 return result;
4139 }
4140
4141 /*
4142 * optimizePositionBounds()
4143 * called for a predicate of a locstep
4144 * returns the range of positions that need to be examined for a context
4145 * e.g. the predicate in foo[1] will return both set to 1.
4146 * the positions returned are 1-based, 0 means "no restriction"
4147 */
4148
optimizePositionBounds()4149 void Expression::optimizePositionBounds()
4150 {
4151 int from = 0, to = 0;
4152 switch(functor)
4153 {
4154 case EXF_ATOM:
4155 {
4156 if (type == EX_NUMBER)
4157 from = to = NZ(patomnumber) -> round(); // bad values like NaN return 0 which is OK.
4158 }; break;
4159 case EXFO_EQ:
4160 case EXFO_LE:
4161 case EXFO_LT:
4162 case EXFO_GE:
4163 case EXFO_GT:
4164 {
4165 if (args[0] -> functor == EXFF_POSITION &&
4166 args[1] -> functor == EXF_ATOM && args[1] -> type == EX_NUMBER)
4167 {
4168 int bound = args[1] -> patomnumber -> round();
4169 switch(functor)
4170 {
4171 case EXFO_EQ: from = to = bound; break;
4172 case EXFO_LE: to = bound; break;
4173 case EXFO_LT: to = bound - 1; break;
4174 case EXFO_GE: from = bound; break;
4175 case EXFO_GT: from = bound + 1; break;
4176 }
4177 }
4178 }; break;
4179 }
4180 optimizePositionFrom = from;
4181 optimizePositionTo = to;
4182 }
4183
inBounds(int position) const4184 int Expression::inBounds(int position) const
4185 {
4186 if (optimizePositionTo && position > optimizePositionTo-1)
4187 return 1;
4188 if (optimizePositionFrom && position < optimizePositionFrom-1)
4189 return -1;
4190 return 0;
4191 }
4192
getOwnerElement() const4193 Element& Expression::getOwnerElement() const
4194 {
4195 return owner;
4196 };
4197
getOwnerTree() const4198 Tree& Expression::getOwnerTree() const
4199 {
4200 return owner.getOwner();
4201 }
4202
4203
report(Sit S,MsgType type,MsgCode code,const Str & arg1,const Str & arg2)4204 void Expression::report(Sit S, MsgType type, MsgCode code, const Str& arg1, const Str& arg2)
4205 {
4206 getOwnerElement().report(S,type,code,arg1,arg2);
4207 }
4208
containsFunctor(ExFunctor func)4209 Bool Expression::containsFunctor(ExFunctor func)
4210 {
4211 if (functor == func)
4212 return TRUE;
4213 else if (functor == EXF_LOCSTEP)
4214 {
4215 for (int i = 0; i < step -> preds.number(); i++)
4216 {
4217 if (step -> preds[i] -> containsFunctor(func))
4218 return TRUE;
4219 }
4220 }
4221
4222 for (int i = 0; i < args.number(); i++)
4223 {
4224 if (args[i] -> containsFunctor(func))
4225 return TRUE;
4226 }
4227 return FALSE;
4228 }
4229