added Info.plist
[TestXSLT.git] / libsablot / src / engine / context.cpp
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):
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 /*****************************************************************
34
35     c   o   n   t   e   x   t   .   c   p   p
36
37 *****************************************************************/
38
39 #include "context.h"
40 #include "tree.h"
41 #include "utf8.h"
42 #include <locale.h>
43 #include "key.h"
44 #include "domprovider.h"
45 #include "guard.h"  // GP: clean
46
47 /*****************************************************************
48     language name aliases
49     primarily for different language names in Windows setlocale
50     (unix setlocale respects ISO 639 which is also the standard
51     for language codes in XML docs)
52     SO FAR FOR TESTING PURPOSES
53
54     each entry contains 2 strings with space-separated codes
55     - first with the ISO codes (may appear in XML)
56     - second with the setlocale-specific codes (needn't be ISO outside unix)
57 *****************************************************************/
58
59 struct LangAlias
60 {
61     const char 
62         *namesISO, *namesLocale;
63 };
64
65 LangAlias langAliases[] =
66 {
67     {"en",                      "en_US"},
68     {"de",                      "de_DE"},
69     {"fr",                      "fr_FR"},
70     {"it",                      "it_IT"},
71     {"cze ces cs cs_CZ",        "cs_CZ czech"},
72     {"sk slk slo sk_SK",        "sk_SK slovak"},
73     {NULL, NULL}
74 };
75
76 /*****************************************************************
77     C L i s t   methods
78 *****************************************************************/
79
80 CList::CList()
81 :
82 SList<NodeHandle>(LIST_SIZE_LARGE)
83 {
84     sortDefs = NULL;
85     refCount = 1;
86     currLevel = 0;
87     wcsValues = FALSE;
88 }
89
90 // GP: added method to kill values
91
92 CList::~CList()
93 {
94     values.freeall(TRUE);
95 }
96
97 void CList::incRefCount()
98 {
99     refCount++;
100 }
101
102 int CList::decRefCount()
103 {
104     return --refCount;
105 }
106
107 //  compareWithoutDocOrd
108 //  assumes that the 'values' entries have been created
109 //  compares 2 vertices based on just the values, not their relative document order
110
111 Bool CList::tagChanged(int i, int j)
112 {
113   return tags[i] != tags[j];
114 }
115
116 int CList::compareWithoutDocOrd(int i, int j)
117 {
118     assert(sortDefs && currLevel < sortDefs -> number());
119     assert(i < values.number() && j < values.number());
120
121     SortDef *def = (*sortDefs)[currLevel];
122     // *** use case-order and lang from def!!!
123     int result;
124     if (def -> asText)
125       {
126         if (wcsValues) 
127           {
128             result = wcscmp__((char*)values[i], (char*)values[j]);
129           }
130         else
131           {
132             result = strcmp((char*)values[i], (char*)values[j]);
133           }
134       }
135     else
136       {
137         Number n1, n2;
138         n1 = (char*)values[i];
139         n2 = (char*)values[j];
140         if (n1 < n2)
141             result = -1;
142         else 
143             result = (n2 < n1) ? 1 : 0;
144       }
145     return def -> ascend ? result : -result;
146 }
147
148
149 //  compare assumes that the 'values' entries have been created
150 int CList::compare(int i, int j, void *data)
151 {
152     if (sortDefs)
153     {
154         int result = compareWithoutDocOrd(i, j);
155         if (result)
156             return result;
157     }
158     // sort in document order
159     NodeHandle v1 = block[i], v2 = block[j];
160     assert(v1 && v2);
161     return ((DOMProvider*)data) -> compareNodes(v1, v2);
162 }
163
164 Bool hasWord(const char *sentence, const char *w)
165 {
166     const char *p;
167     Str currWord;
168     int len;
169     for (p = sentence; *p; p += len + strspn(p, " "))
170     {
171         currWord.nset(p, len = strcspn(p, " "));
172         if (currWord.eqNoCase(w)) return TRUE;
173     }
174     return FALSE;
175 }
176
177 char* setLang(const Str& src)
178 {
179     char *result;
180     const char *p;
181     Str dest;
182     int len;
183     if (NULL != (result = setlocale(LC_COLLATE, src))) 
184         return result;
185     
186     LangAlias *a = langAliases;
187     while(a -> namesISO && !hasWord(a -> namesISO, src)) a++;
188     if (a -> namesISO)
189     {
190         for (p = a -> namesLocale; *p; p += len + strspn(p, " "))
191         {
192             dest.nset(p, len = strcspn(p, " "));
193             if (NULL != (result = setlocale(LC_COLLATE, dest))) 
194                 return result;
195         }
196     }
197     return FALSE;
198 }
199
200 /*!!! _ph_ remove */
201 void __dump(CList& lst, int idx1, int idx2)
202 {
203   printf("\n--- DUMP - BEGIN (%d, %d) ---\n", idx1, idx2);
204   for (int i = 0; i < lst.number(); i++)
205     {
206       if (isElement(lst[i]))
207         {
208           Element* e = toE(lst[i]);
209           for (int j = 0; j < e -> atts.number(); j++)
210             {
211               printf("%2s", (char*)((Attribute*)e -> atts[j]) -> cont);
212             }
213           printf("\n");
214         }
215     }
216   printf("--- DUMP - END ---\n\n");
217 }
218
219 eFlag CList::sort(Sit S, XSLElement *caller, Context *ctxt, SortDefList* sortDefs_ /* = NULL */)
220 {
221   // GP: on error, locale is reset
222   // checked that all callers destroy the context on error
223   int currTag = 0;
224   assert(caller || !sortDefs_);
225   Str theLang;
226   sortDefs = sortDefs_;
227   if (sortDefs)
228     {
229       if (!setLang((*sortDefs)[0] -> lang))
230         {
231           Warn1(S, W1_LANG, (*sortDefs)[0] -> lang);
232           setlocale(LC_COLLATE, "C");
233         }
234       // this is to reset the locale if there's an error
235       E_DO( makeValues(S, 0, number() - 1, 0, caller, ctxt), 
236             setlocale(LC_COLLATE, "C") );
237     }
238   currLevel = 0;
239   SList<NodeHandle>::sort(0, number() - 1, &(S.dom()));
240   for (int i = 1; sortDefs && (i < sortDefs -> number()); i++)
241     {
242       if (!setLang((*sortDefs)[i] -> lang))
243         {
244           Warn1(S, W1_LANG, (*sortDefs)[i] -> lang);
245           setlocale(LC_COLLATE, "C");
246         }
247       int j0 = 0;
248       currLevel = i - 1;
249       for (int j = 1; j <= number(); j++)
250         if (j == number() || compareWithoutDocOrd(j0,j) || 
251             tagChanged(j0, j))
252           {
253             if (j > j0 + 1)
254               {
255                 currLevel = i;
256                 E_DO( makeValues(S, j0, j-1, i, caller, ctxt), 
257                       setlocale(LC_COLLATE, "C"));
258                 // printf("Sorting %d:%d to %d\n",i,j0,j-1);
259                 SList<NodeHandle>::sort(j0, j-1, &(S.dom()));
260                 //set tags for the next level
261                 currTag++;
262                 for (int aux = j0; aux < j; aux++) tags[aux] = currTag;
263                 // reset currLevel for next compareWithoutDocOrd()
264                 currLevel = i - 1;
265               };
266             j0 = j;
267           }
268     }
269   if (sortDefs)
270     {
271       // *** unset the temp locale as necessary
272       setlocale(LC_ALL, "C");
273       values.freeall(TRUE);
274       tags.deppendall();
275     }
276   // reset the current element of parent context
277   ctxt -> setPosition(0);
278   return OK;
279 }
280
281 void CList::swap(int i, int j)
282 {
283     SList<NodeHandle>::swap(i, j);
284     if (sortDefs)
285       {
286         values.swap(i, j);
287         tags.swap(i, j);
288       }
289 }
290
291 eFlag CList::makeValues(Sit S, int from, int to, int level, 
292                         XSLElement *caller, Context *ctxt)
293 {
294     assert(ctxt);
295     wcsValues = FALSE;
296     if (!sortDefs) return OK;
297     
298     // we may assume that the node handles are actually pointers now
299     assert(level < sortDefs -> number());
300     SortDef *def = (*sortDefs)[level];
301     DStr d;
302     //GP( Str ) strg;
303     //char *strg;
304     GP(GChar) strg;
305     Expression *e, result(*caller, EXF_ATOM);
306     for (int i = from; i <= to; i++)
307     {
308         ctxt -> setPosition(i);
309         //strg = new Str;
310         e = def -> sortExpr;
311         if (!e)
312         {
313             E( toPhysical((*this)[i]) -> value(S, d, ctxt) );
314             if (def -> asText)
315               {
316 #               if defined(SAB_WCS_COLLATE)
317                 strg.assign( utf8xfrm(d) );
318                 wcsValues = TRUE;
319 #               else
320                 strg.assign( d.cloneData() );
321 #               endif
322               }
323             else
324                 strg.assign(d.cloneData() );
325         }
326         else
327         {
328             E( e -> eval(S, result, ctxt) );
329
330             if (def -> asText)
331                 {
332 #               if defined(SAB_WCS_COLLATE)
333                 Str temp;
334                 E( result.tostring(S, temp) );
335                 strg.assign( utf8xfrm(temp) );
336                 wcsValues = TRUE;
337 #               else
338                 DStr aux;
339                 E(result.tostring(S,aux));
340                 strg.assign( aux.cloneData() );
341 #               endif
342             }
343             else {
344               DStr aux;
345               E(result.tostring(S, aux));
346               strg.assign( aux.cloneData() );
347             }
348         }
349         if (!level)
350           {
351             values.append(strg.keep());
352             tags.append(0);
353           }
354         else
355           {
356             cdeleteArr(values[i]);
357             values[i] = strg.keep();
358           }
359     }
360     return OK;
361 }
362
363 void CList::report(Sit S, MsgType type, MsgCode code, const Str& arg1, const Str& arg2)
364 {
365     S.message(type, code, arg1, arg2);
366 }
367
368
369 /*****************************************************************
370
371     C o n t e x t    methods
372
373 ******************************************************************/
374
375 Context::Context(NodeHandle current, int isForKey_ /* = FALSE */)
376 {
377     isForKey = isForKey_;
378     array = isForKey? new KList : new CList;
379     currentNode = current;
380     position = -1;
381     virtualPosition = 0;
382     virtualSize = -1;
383 }
384
385 Context::~Context()
386 {
387     assert(array);
388     if (!array -> decRefCount())
389         delete array;
390 }
391
392 int Context::getPosition() const
393 {
394     return position + virtualPosition;
395 }
396
397 int Context::getSize() const
398 {
399     if (virtualSize != -1)
400         return virtualSize;
401     else
402         return array -> number();
403 }
404
405 NodeHandle Context::current() const
406 {
407     if (!isFinished())
408         return (*array)[position];
409     else
410         return NULL;
411 };
412
413 NodeHandle Context::getCurrentNode() const
414 {
415   return currentNode;
416   /*
417     if (currentNode)
418         return currentNode;
419     else
420         return current();
421   */
422 }
423
424 void Context::setCurrentNode(NodeHandle v)
425 {
426     currentNode = v;
427 }
428
429 void Context::reset()
430 {
431     if (!array -> number())
432         position = -1;
433     else
434         position = 0;
435 }
436
437 Bool Context::isFinished() const
438 {
439     return (Bool) !((position >= 0) && (position < array -> number()));
440 }
441
442 Bool Context::isVoid() const
443 {
444     return (Bool) !(array -> number());
445 }
446
447 NodeHandle Context::shift()
448 {
449     if ((position >= 0) && (position < array -> number() - 1))
450         return (*this)[++position];
451     position = -1;
452     return NULL;
453 }
454
455 void Context::set(NodeHandle v)
456 {
457     array -> append(v);
458     reset();
459 }
460
461 void Context::setVirtual(NodeHandle v, int virtualPosition_, int virtualSize_)
462 {
463     assert(!array -> number() && "setVirtual() on nonvoid context");
464     array -> append(v);
465     virtualSize = virtualSize_;
466     virtualPosition = virtualPosition_;
467     reset();
468 }
469
470 NodeHandle Context::operator[] (int n) const
471 {
472     return (*array)[n];
473 }
474
475 void Context::deppendall()
476 {
477     if (!array -> decRefCount())
478         delete array;
479     array = isForKey? new KList : new CList;
480     position = -1;
481 }
482
483 void Context::append(NodeHandle v)
484 {
485     array -> append(v);
486     reset();
487 }
488
489 void Context::deppend()
490 {
491     array -> deppend();
492     if (position >= array -> number())
493         position = array -> number() - 1;
494 }
495
496 Context* Context::copy()
497 {
498     Context *newc = new Context(currentNode);
499     delete NZ(newc -> array);
500     newc -> array = array;
501     newc -> virtualPosition = virtualPosition;
502     newc -> virtualSize = virtualSize;
503     array -> incRefCount();
504     newc -> reset();
505     return newc;
506 }
507
508 void Context::swap(int i, int j)
509 {
510     array -> swap(i, j);
511 }
512
513 Context* Context::swallow(Sit S, Context *other)
514 {
515     Context *result = new Context(currentNode); // GP: OK since no E()
516     int i = 0, j = 0,
517         iLimit = array -> number(), jLimit = other -> array -> number();
518     NodeHandle v, w;
519     while ((i < iLimit) && (j < jLimit))
520     {
521         v = (*array)[i];
522         w = (*other)[j];
523         switch(S.dom().compareNodes(v, w))
524         {
525         case 0: 
526             j++; break;
527         case -1:
528         {
529             result -> append(v);
530             i++;
531         }; break;
532         case 1:
533         {
534             result -> append(w);
535             j++;
536         }; break;
537         }
538     }
539         
540     while (i < iLimit)
541         result -> append((*array)[i++]);
542     while (j < jLimit)
543         result -> append((*(other -> array))[j++]);
544     deppendall();
545     other -> deppendall();
546     return result;
547 }
548
549 Bool Context::contains(NodeHandle v) const
550 {
551     int i;
552     for (i = 0; i < array -> number(); i++)
553     {
554         if (v == (*array)[i])
555             return TRUE;
556     }
557     return FALSE;
558 }
559
560 void Context::uniquize()
561 {
562     int i;
563     for (i = array -> number() - 2; i >= 0; i--)
564         if ((*array)[i] == (*array)[i+1])
565             array -> rm(i);
566 }
567
568 CList* Context::getArrayForDOM()
569 {
570     return array;
571 }
572
573 KList* Context::getKeyArray()
574 {
575     assert(isForKey);
576     return cast(KList*, array);
577 }