Initial revision
[TestXSLT.git] / libsablot / src / engine / proc.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 #include "base.h"
34 #include "proc.h"
35 #include "expr.h"
36 #include "tree.h"
37 #include "context.h"
38 #include "vars.h"
39 // #include "xmlparse.h" - moved to parser.h
40 #include "parser.h"
41 #include "guard.h"
42 #include "key.h"
43 #include "domprovider.h"
44
45 #ifdef HAVE_CONFIG_H
46 #include <config.h>
47 #endif
48
49 #ifdef SABLOT_DEBUGGER
50 #include "debugger.h"
51 #endif
52
53 // GP: clean
54
55 /*****************************************************************
56 R u l e I t e m   methods
57 *****************************************************************/
58
59 RuleItem::RuleItem(XSLElement *arule,double aprio, QName &_name,
60                    QName *_mode)
61 :
62 rule(arule), priority(aprio), name(_name), mode(_mode)
63 {
64   match = rule -> atts.find(XSLA_MATCH);
65 };
66
67
68 //
69 //  ~RuleItem
70 //  has to dispose of the mode
71 //
72 RuleItem::~RuleItem()
73 {
74     cdelete(mode);
75 }
76
77
78 /*****************************************************************
79 R u l e S L i s t   methods
80 *****************************************************************/
81
82 RuleSList::RuleSList()
83 :
84 SList<RuleItem*>(LIST_SIZE_LARGE)
85 {};
86
87 RuleSList::~RuleSList()
88 {
89     freeall(FALSE);
90 }
91
92 int RuleSList::compare(int first, int second, void *data)
93 {
94     return fcomp((*this)[first] -> priority, (*this)[second] -> priority);
95 };
96
97
98 XSLElement* RuleSList::findByName(const Tree& t, const QName &what) const
99 {
100     int theNumber = number();
101     for (int i=0; i < theNumber; i++)
102         if (t.cmpQNames((*this)[i] -> name, what))
103             return (*this)[i] -> rule;
104     return NULL;
105 }
106
107 /*****************************************************************
108
109   DataLineItem
110
111 *****************************************************************/
112
113 DataLineItem::DataLineItem(Sit S_)
114 : situation(S_)
115 {
116 }
117
118 DataLineItem::~DataLineItem()
119 {
120     // the following close should be checked for error!!
121     if (_dataline && _dataline -> mode != DLMODE_CLOSED)
122         _dataline -> close(situation);
123     cdelete(_dataline);
124     if (!_preparsedTree)
125         cdelete(_tree);
126 }
127
128 /************************************************************
129
130 OutputDocumentList
131
132 ************************************************************/
133
134 eFlag OutputDocumentList::finish(Sit S)
135 {
136   for (int i = 0; i < number(); i++)
137     E( (*this)[i] -> finish(S) );
138   return OK;
139 }
140
141 /*****************************************************************
142
143   DataLinesList
144
145 *****************************************************************/
146
147 int DataLinesList::findNum(Str &absoluteURI, Bool _isXSL,
148     DLAccessMode _mode)
149 {
150     int theNumber = number();
151     for (int i = 0; i < theNumber; i++)
152     {
153         DataLineItem* item = (*this)[i];
154         if ((item->_dataline -> fullUri == absoluteURI) &&
155             (item->_isXSL == _isXSL) && 
156             (item-> _dataline -> mode == _mode || 
157                  item-> _dataline -> mode == DLMODE_CLOSED))
158         return i;
159     }
160     return -1;
161 }
162
163 Tree* DataLinesList::getTree(Str &absoluteURI, Bool _isXSL, 
164     DLAccessMode _mode)
165 {
166     int n;
167     if ((n = findNum(absoluteURI, _isXSL, _mode)) != -1)
168         return (*this)[n] -> _tree;
169     else
170         return NULL;
171 }
172
173 eFlag DataLinesList::addLine(Sit S, DataLine *d, Tree *t, Bool isXSL,
174     Bool preparsedTree /* = FALSE */)
175 {
176     DataLineItem *item = new DataLineItem(S);
177     item -> _dataline = d;
178     item -> _tree = t;
179     item -> _isXSL = isXSL;
180     item -> _preparsedTree = preparsedTree;
181     append(item);
182     return OK;
183 }
184
185 /*****************************************************************
186 *                                                                *
187 *   P r o c e s s o r   methods                                  *
188 *                                                                *
189 *****************************************************************/
190
191 // disable the MSVC warning "'this' used in the initializer list"
192
193 #ifdef WIN32
194 #pragma warning( disable : 4355 )
195 #endif
196
197 Processor::Processor() 
198
199   theArena(PROC_ARENA_SIZE)
200 #ifdef ENABLE_JS
201   , jscontexts(this)
202 #endif
203
204 #ifdef WIN32
205 #pragma warning( default : 4355 )
206 #endif
207
208 {
209 //    pushNewHandler();
210     theSchemeHandler = NULL;
211     theMessageHandler = NULL;
212     theSAXHandler = NULL;
213     theMiscHandler = NULL;
214     theEncHandler = NULL;
215     theSchemeUserData = theMessageUserData = 
216         theSAXUserData = theMiscUserData = theEncUserData = NULL;    
217     instanceData = NULL;
218     instanceSituation = NULL;
219     addedFlag = FALSE;
220     vars = NULL;
221     input = styleSheet = NULL;
222     keys = NULL;
223     nsUnique = 1;
224     hasMasterSituation = FALSE;
225     runsOnExternal = FALSE;
226 };
227
228 void Processor::prepareForRun()
229 {
230     // do this in open():
231     // vars = new VarsList();
232     input = styleSheet = NULL;
233     decimals().initialize();
234 }
235
236 void Processor::freeNonArgDatalines()
237 {
238     int i = 0;
239     while(i < datalines.number())
240     {
241         if (datalines[i] -> _dataline -> scheme != URI_ARG)
242             {
243             datalines.freerm(i, FALSE);
244             }
245         else
246         {
247             // removing the tree here because the arena gets disposed
248             // in cleanupAfterRun(). So only the arg buffers remain.
249                 // only remove the tree if not preparsed
250                     if (!datalines[i] -> _preparsedTree)
251                 cdelete(datalines[i] -> _tree);
252                 // don't move the following line out of the else!
253             i++;                
254         };
255     }
256     addedFlag = FALSE;
257 }
258
259 void Processor::cleanupAfterRun(Situation *Sp)
260 {
261 /*
262 These deletes are now subsumed in "datalines.freeall()":
263     cdelete(input);
264     cdelete(result);
265     cdelete(styleSheet);
266 */
267
268   styleSheet = input = NULL;
269     cdelete(vars);
270     cdelete(keys);
271     decimals().freeall(FALSE);
272 #ifdef ENABLE_JS
273     jscontexts.freeall(FALSE);
274 #endif
275
276     outputDocuments.freeall(FALSE);
277
278     freeNonArgDatalines();
279     if (Sp && !Sp -> isError())
280     {
281         assert(modes.isEmpty());
282         assert(outputters_.isEmpty());
283     }
284     else
285     {
286         modes.freeall(FALSE);
287         outputters_.freeall(FALSE);
288     };
289     if (Sp)
290         Sp -> clear();
291     // hope this works:
292     //DETACH: move to Tree
293     // dictionary.destroy();
294     theArena.dispose();
295     runsOnExternal = FALSE;
296 }
297
298 Processor::~Processor()
299 {
300 //    popNewHandler();
301     baseURIMappings.freeall(FALSE);
302     // FIXME: MUST clear theRecoder but got no situation
303 //    theRecoder.clear();
304 };
305
306 eFlag Processor::open(Sit S, const char *sheetURI, const char *inputURI)
307 {
308     Str temp;
309     DStr theBase;
310
311     my_getcwd(theBase);
312     theBase = findBaseURI(S, Str("file://") + theBase);
313
314     E( readTreeFromURI(S, styleSheet, temp = sheetURI, theBase, TRUE) );
315     //styleSheet -> speakDebug();
316
317 #ifdef SABLOT_DEBUGGER
318     if (debugger) debugger -> setBreakpoints(S, styleSheet);
319 #endif
320
321     runsOnExternal = !inputURI;
322     if (inputURI)
323       E( readTreeFromURI(S, input, temp = inputURI, theBase, FALSE) );
324     vars = new VarsList (*styleSheet);
325     keys = new KeySet;
326     return OK;
327 }
328
329 eFlag Processor::run(Sit S, const char* resultURI, NodeHandle doc /*= NULL */)
330 {
331     Str temp;
332     DStr theBase;
333     my_getcwd(theBase);
334     theBase = findBaseURI(S, Str("file://") + theBase);
335
336     //strip spaces from the document
337     if (input) E( stripTree(S, *input) )
338
339     Log1(S, L1_EXECUTING, styleSheet -> getURI());
340     double time_was = getMillisecs();
341     E( pushOutputterForURI(S, temp = resultURI, theBase) );
342     E( outputter() -> eventBeginOutput(S) );
343
344     inputRoot = !nhNull(doc) ? doc : (NodeHandle)&(NZ(input) -> getRoot());
345     GP( Context ) c = new Context(inputRoot);
346     (*c).set(inputRoot);
347
348     /*
349         in vars, global prebindings go to call level 0,
350         global bindings to call level 1
351     */
352     vars -> startCall();
353     // start by resolving all globals
354     //E( resolveGlobals(S, c) ); It has been done twice (XSLElement::execute)
355     E( styleSheet -> getRoot().execute(S, c, FALSE) );
356     vars -> endCall();
357     c.del();
358
359     E( outputter() -> eventTrailingNewline(S) );
360     E( outputter() -> eventEndOutput(S) );
361
362     //done all outputetrs
363     E( outputDocuments.finish(S) );
364         
365     // report info about the output document to the MiscHandler
366     void *miscUserData;
367     OutputDefinition *outDef = &(styleSheet -> outputDef);
368     MiscHandler *miscHlr = getMiscHandler(&miscUserData);
369     if (miscHlr)
370         miscHlr -> documentInfo(
371             miscUserData,
372             this,   // processor
373             outDef -> getValueStr(XSLA_MEDIA_TYPE), 
374             outDef -> getValueStr(XSLA_ENCODING));
375
376     E( popOutputter(S) );
377     
378     //check statuses
379     //this is not needed, 
380     //cleanupAfterRun takes care in the case of error
381     //assert(!outputters_.number());
382
383     Log1(S, L1_EXECUTION_DONE, getMillisecsDiff(time_was));
384     return OK;
385 }
386
387
388
389 /*================================================================
390 getVarBinding 
391     finds the current binding for the variable with the given name.
392     The binding is an expression. If none is present, returns NULL.
393 ================================================================*/
394
395 Expression* Processor::getVarBinding(QName &which)
396 {
397     return vars -> getBinding(which);
398 }
399
400 /*================================================================
401 execute
402 both flavours DEALLOCATE the context and set the pointer to NULL
403 ================================================================*/
404
405 eFlag Processor::execute(Sit S, Vertex *IP, Context *&c, Bool resolvingGlobals)
406 {
407     while (!c -> isFinished())
408     {
409       c -> setCurrentNode(c -> current());
410         if (IP) 
411           {
412             E( IP -> execute(S, c, resolvingGlobals) );
413           }
414         else
415           {
416             E( execApplyTemplates(S, c, resolvingGlobals) );
417           }
418         c -> shift();
419     };
420     cdelete(c);
421     return OK;
422 }
423
424 eFlag Processor::execute(Sit S, VertexList &IPlist, Context *&c, Bool resolvingGlobals)
425 {
426     // we may need to remove some variable bindings on each pass
427     // through a for loop
428     Vertex *theParent = IPlist.number()? IPlist[0] -> parent : NULL;
429     XSLElement *theForParent;
430     if (theParent && isXSLElement(theParent) && toX(theParent) -> op == XSL_FOR_EACH)
431         theForParent = toX(theParent);
432     else
433         theForParent = NULL;
434     
435     while (c -> current())
436     {
437         c -> setCurrentNode(c -> current());
438         E( IPlist.execute(S, c, resolvingGlobals) );
439         c -> shift();
440         if (theForParent)
441             theForParent -> removeBindings(S);
442     };
443     cdelete(c);
444     return OK;
445 }
446
447 eFlag Processor::execApplyTemplates(Sit S, Context *c, Bool resolvingGlobals)
448 {
449     XSLElement *rule;
450     E( NZ(styleSheet) -> findBestRule(S, rule, c, getCurrentMode(), FALSE) );
451     if (!rule)
452         E( builtinRule(S, c, resolvingGlobals) )
453     else
454         E( rule -> execute(S, c, resolvingGlobals) );
455     return OK;
456 }
457
458 eFlag Processor::execApplyImports(Sit S, Context *c, SubtreeInfo *subtree,
459                                   Bool resolvingGlobals)
460 {
461     XSLElement *rule;
462     E( NZ(styleSheet) -> 
463        findBestRule(S, rule, c, getCurrentMode(), TRUE, subtree) );
464     if (rule)
465         E( rule -> execute(S, c, FALSE) );
466     return OK;
467 }
468
469 eFlag Processor::builtinRule(Sit S, Context *c, Bool resolvingGlobals)
470 {
471   // may assume v to be a physical vertex
472   //Vertex *v = toPhysical(c -> current());
473   NodeHandle v = c -> current();
474     
475   switch (S.dom().getNodeType(v)) {
476   case DOCUMENT_NODE:
477   case ELEMENT_NODE:
478     //apply-templates
479     {
480       GP( Expression ) e = new Expression(styleSheet->getRoot(), EXF_LOCPATH);
481       (*e).setLS(AXIS_CHILD, EXNODE_NODE);
482       GP( Context ) newc;
483       newc.assign(c);
484       E( (*e).createContext(S, newc) );
485       newc.unkeep();
486       E( execute(S, NULL, newc, resolvingGlobals) );
487       newc.keep(); // GP: it's NULL by this time anyway
488       e.del();
489     }; break;
490   case TEXT_NODE:
491   case ATTRIBUTE_NODE:
492     //copy contents to output
493     {
494       const char *val = S.dom().getNodeValue(v);
495       E( outputter() -> eventData(S, Str(val)) );
496       S.dom().freeValue(v, (char*)val);
497     }; break;
498   case PROCESSING_INSTRUCTION_NODE:
499   case COMMENT_NODE:
500     {};
501   };
502
503   /*
504   switch(v -> vt & VT_BASE)
505   {
506   case VT_ROOT:
507   case VT_ELEMENT:
508     //apply-templates
509     {
510       GP( Expression ) e = new Expression(*toE(v), EXF_LOCPATH);
511       (*e).setLS(AXIS_CHILD, EXNODE_NODE);
512       GP( Context ) newc;
513       newc.assign(c);
514       E( (*e).createContext(S, newc) );
515       newc.unkeep();
516       E( execute(S, NULL, newc, resolvingGlobals) );
517       newc.keep(); // GP: it's NULL by this time anyway
518       e.del();
519     }; break;
520     case VT_TEXT:
521   case VT_ATTRIBUTE:
522     //copy contents to output
523     {
524       DStr temp;
525       E( v -> value(S, temp, c) );
526       E( outputter() -> eventData(S, temp) );
527     }; break;
528   case VT_PI:
529   case VT_COMMENT:
530     {};
531   };
532   */
533   return OK;
534 };
535
536
537 /*................................................................
538 getCurrentMode()
539 RETURN: pointer to the current mode which is a QName
540 ................................................................*/
541
542 QName* Processor::getCurrentMode()
543 {
544     return (modes.number() ? modes.last() : NULL);
545 }
546
547 /*................................................................
548 pushMode()
549 called before every mode-changing apply-templates
550 ARGS:
551 m       the mode to be placed on top of the mode stack
552 ................................................................*/
553
554 void Processor::pushMode(QName *m)
555 {
556     modes.append(m);
557 }
558
559 /*................................................................
560 popMode()
561 called after every mode-changing apply-templates
562 removes the current mode from the mode stack FREEING it
563 ................................................................*/
564
565 void Processor::popMode()
566 {
567     modes.freelast(FALSE);
568 }
569
570 eFlag Processor::addLineNoTree(Sit S, DataLine *&d, Str &absolute, Bool isXSL)
571 {
572     GP( DataLine ) d_;
573     M( S, d_ = new DataLine );
574     E( (*d_).open(S, absolute, DLMODE_READ, &argList) );
575     E( datalines.addLine(S, d_, NULL, isXSL) );
576     d = d_.keep();
577     return OK;
578 }
579
580 eFlag Processor::addLineTreeOnly(Sit S, DataLine *&d, Str &absolute, Bool isXSL,
581     Tree *t)
582 {
583     GP( DataLine ) d_;
584     M( S, d_ = new DataLine );
585     E( (*d_).setURIAndClose(S, absolute) );
586     E( datalines.addLine(S, d_, t, isXSL, /* preparsedTree = */ TRUE) );
587     d = d_.keep();
588     return OK;
589 }
590
591 eFlag Processor::addLineParse(Sit S, Tree *& newTree, Str &absolute, 
592                               Bool isXSL, Bool ignoreErr /* = FALSE */)
593 {
594     GP( DataLine ) d = new DataLine;
595     E( (*d).open(S, absolute, DLMODE_READ, &argList, ignoreErr) );
596     GP( Tree ) newTree_ = new Tree(absolute, isXSL );
597     //E( (*newTree_).parse(S, d) );
598     eFlag status = (*newTree_).parse(S, d);
599     E( (*d).close(S) );
600     //hold parse errors after dataline is closed
601     E( status );
602     E( datalines.addLine(S, d.keep(), newTree = newTree_.keep(), isXSL) );
603     return OK;
604 }
605
606 const Str& Processor::findBaseURI(Sit S, const Str& unmappedBase)
607 {
608     Str scheme, rest;
609     uri2SchemePath(S, unmappedBase, scheme, rest);
610     Str *mapping = baseURIMappings.find(scheme);
611     if (mapping) return *mapping;
612     mapping = baseURIMappings.find(""/**theEmptyString*/);
613     if (mapping) return *mapping;
614     return unmappedBase;
615 }
616
617 //
618 //          Processor::baseForVertex
619 //
620 //  returns the *translated* base URI in effect for a given vertex
621 //
622
623 const Str& Processor::baseForVertex(Sit S, Element *v)
624 {
625     return findBaseURI(S, NZ(v) -> getSubtreeInfo() -> getBaseURI());
626 }
627
628
629 //
630 //          Processor::readTreeFromURI
631 //
632 //  reads in a tree from URI 'location' resolved against 'base' which is 
633 //  a *translated* base (one for which the 'hard base URIs' have been taken 
634 //  into account)
635 //
636
637 eFlag Processor::readTreeFromURI(Sit S, Tree*& newTree, const Str& location, 
638                                  const Str& base, Bool isXSL, 
639                                  Bool ignoreErr /* = FALSE */)
640 {
641     Str
642         absolute;
643     makeAbsoluteURI(S, location, base, absolute);
644     newTree = datalines.getTree(absolute, isXSL, DLMODE_READ);
645     if (!newTree)
646         E( addLineParse(S, newTree, absolute, isXSL, ignoreErr) );
647     return OK;
648 }
649
650 eFlag Processor::createOutputterForURI(Sit S, Str& location, Str& base,
651                                        OutputterObj*& result,
652                                        OutputDefinition *outDef /*=null*/)
653 {
654   Str absolute;
655   makeAbsoluteURI(S, location, base, absolute);
656   if (datalines.getTree(absolute, FALSE, DLMODE_WRITE))
657     Err1(S, E1_URI_WRITE, absolute);
658   GP( DataLine ) d;
659   if ( !(absolute == "arg:/null") ) { 
660     M( S, d = new DataLine );
661     E( (*d).open(S, absolute, DLMODE_WRITE, &argList) );
662     // FIXME: the NULL tree in the following
663     E( datalines.addLine(S, d.keep(), NULL, FALSE) );
664   }else{ //physical output not required
665     d = NULL;
666   };
667   GP( OutputterObj ) newOut;
668   M( S, newOut = new OutputterObj );
669
670   if (!outDef) outDef = &(styleSheet -> outputDef);
671   E( (*newOut).setOptions(S, d, outDef) );
672   if (theSAXHandler)
673     E( (*newOut).setOptionsSAX(S, theSAXHandler, theSAXUserData, 
674                                SAXOUTPUT_AS_PHYSICAL) );
675
676   result = newOut.keep();
677
678   return OK;
679 }
680
681 eFlag Processor::createOutputDocument(Sit S, Str& href,
682                                       OutputDocument *&resultDoc, 
683                                       OutputDefinition *def)
684 {
685
686   //OutputterObj *newOut;
687   //E( createOutputterForURI(S, href, base, newOut, def) );
688   
689   //GP(OutputDocument) doc = new OutputDocument(newOut, def);
690   GP(OutputDocument) doc = new OutputDocument(href, def);
691
692   resultDoc = doc.keep();
693   outputDocuments.append(resultDoc);
694
695   return OK;
696 }
697
698 eFlag Processor::startDocument(Sit S, OutputDocument *doc)
699 {
700   switch (doc -> getState()) {
701   case OUTDOC_ACTIVE:
702     {
703       OutputterObj *out = NZ( doc -> getOutputter());
704       E( pushOutputter(S, out) );
705     }; break;
706   case OUTDOC_NEW:
707     {
708       ////////////////////////////////////////
709       //create the outputter
710       Str &href = doc -> getHref();
711       DStr base = "";
712       OutputterObj *current = outputter();
713       PhysicalOutputLayerObj *phys;
714       if (current && (phys = current->getPhysical()) && phys->getDataLine())
715         {
716           base = phys -> getDataLine() -> fullUri;
717         }
718       if (base == "" || base == "file://stdout" || base == "file://stderr")
719         {
720           DStr cwd;
721           my_getcwd(cwd);
722           base = "file:";
723           base += cwd;
724           S.message(MT_LOG, L2_SUBDOC_BASE, href, base);
725         }
726       S.message(MT_LOG, L2_SUBDOC, href, base);
727
728       OutputterObj *newOut;
729       E( createOutputterForURI(S, href, base, newOut, doc->getDefinition()) );
730       E( pushOutputter(S, doc -> setOutputter(newOut)) );
731       E( outputter() -> eventBeginOutput(S) );
732
733       doc -> setState(OUTDOC_ACTIVE);
734     }; break;
735   case OUTDOC_FINISHED:
736     assert(! "Couldn't write the document twice"); break;
737   }
738   return OK;
739 }
740
741 eFlag Processor::finishDocument(Sit S, OutputDocument *doc, Bool canClose)
742 {
743   switch (doc -> getState()) {
744   case OUTDOC_NEW:
745   case OUTDOC_FINISHED:
746     assert(!"Could not finish unopened/finished document"); break;
747   case OUTDOC_ACTIVE:
748     {
749       if (canClose)
750         {
751           E( outputter() -> eventTrailingNewline(S) );
752           E( outputter() -> eventEndOutput(S) );
753           doc -> setState(OUTDOC_FINISHED);
754         }
755       E( popOutputterNoFree(S) );
756     }; break;
757   }
758   return OK;
759 }
760
761 eFlag Processor::pushOutputterForURI(Sit S, Str& location, Str& base, 
762                                      OutputDefinition *outDef /*=NULL*/)
763 {
764   OutputterObj *newOut;
765
766   E( createOutputterForURI(S, location, base, newOut, outDef) );
767
768   outputters_.append(newOut);
769   return OK;
770 }
771
772 eFlag Processor::pushTreeConstructer(Sit S, TreeConstructer *& newTC, Tree *t,
773                                      SAXOutputType ot)
774 {
775     newTC = NULL;
776     GP( TreeConstructer ) newTC_ = new TreeConstructer(S);
777     GP( OutputterObj ) newTCSource = new OutputterObj;
778     //E( (*newTCSource).setOptions(S, NULL, &(styleSheet -> outputDef)) ); //_PH_
779     M( S, newTC_+0 );
780     outputters_.append(newTCSource);
781     E( (*newTC_).parseUsingSAX(S, t, *newTCSource, ot) );
782     newTCSource.keep();
783     newTC = newTC_.keep();
784     return OK;
785 }
786
787 eFlag Processor::pushOutputter(Sit S, OutputterObj* out_)
788 {
789     outputters_.append(out_);
790     return OK;
791 }
792
793 eFlag Processor::popOutputter(Sit S)
794 {
795     outputters_.freelast(FALSE);
796     return OK;
797 }
798
799 eFlag Processor::popOutputterNoFree(Sit S)
800 {
801     outputters_.deppend();
802     return OK;
803 }
804
805 eFlag Processor::popTreeConstructer(Sit S, TreeConstructer *theTC)
806 {
807     popOutputter(S);
808     delete theTC;
809     return OK;
810 }
811
812 /*
813 eFlag Processor::parse(Sit S, Tree *t, DataLine *d)
814 {
815     Log1(S, L1_PARSING, t -> name);
816     double time_was = getMillisecs();
817     TreeConstructer tc(S);
818     eFlag retval = tc.parseDataLineUsingExpat(S, t, d);
819     if (!retval)
820     {
821         Log1(S, L1_PARSE_DONE, getMillisecsDiff(time_was));
822     }
823     return retval;
824 }
825 */
826
827 eFlag Processor::getArg(Sit S, const char* name, char*& buffer, Bool isUTF16)
828 {
829     Str temp, *value = argList.find(temp = (char*)name);
830     if (!value)
831         Err1(S, E1_ARG_NOT_FOUND,(char*) name);
832     buffer = (char*) *value;
833     return OK;
834 }
835
836 //
837 //
838 //
839 //      plugin handler stuff
840 //
841 //
842 //
843
844 eFlag Processor::setHandler(Sit S, HandlerType type, void *handler, void *userData)
845 {
846     void **whereHandler, **whereUserData;
847     switch(type)
848     {
849     case HLR_SCHEME: 
850         {
851             whereHandler = (void **)&theSchemeHandler;
852             whereUserData = &theSchemeUserData;
853         }; break;
854     case HLR_MESSAGE: 
855         {
856             whereHandler = (void **)&theMessageHandler;
857             whereUserData = &theMessageUserData;
858         }; break;
859     case HLR_SAX: 
860         {
861             whereHandler = (void **)&theSAXHandler;
862             whereUserData = &theSAXUserData;
863         }; break;
864     case HLR_MISC: 
865         {
866             whereHandler = (void **)&theMiscHandler;
867             whereUserData = &theMiscUserData;
868         }; break;
869     case HLR_ENC:
870         {
871             whereHandler = (void **)&theEncHandler;
872             whereUserData = &theEncUserData;
873         }; break;
874     default: 
875         Err1(S, E1_INVALID_HLR_TYPE, (int) type);
876     }
877     if (*whereHandler)
878     {
879         if (handler)
880             Warn1(S, W1_HLR_REGISTERED, hlrTypeNames[type])
881         else
882         {
883             *whereHandler = NULL;
884             *whereUserData = NULL;
885         }
886     }
887     else
888     {
889         if (handler)
890         {
891             *whereHandler = handler;
892             *whereUserData = userData;
893         }
894         else
895             Warn1(S, W1_HLR_NOT_REGISTERED, hlrTypeNames[type])
896     }
897     return OK;
898 }
899
900
901 SchemeHandler* Processor::getSchemeHandler(void **udata)
902 {
903     if (udata)
904         *udata = theSchemeUserData;
905     return theSchemeHandler;
906 }
907
908 MessageHandler* Processor::getMessageHandler(void **udata)
909 {
910     if (udata)
911         *udata = theMessageUserData;
912     return theMessageHandler;
913 }
914
915 SAXHandler* Processor::getSAXHandler(void **udata)
916 {
917     if (udata)
918         *udata = theSAXUserData;
919     return theSAXHandler;
920 }
921
922 MiscHandler* Processor::getMiscHandler(void **udata)
923 {
924     if (udata)
925         *udata = theMiscUserData;
926     return theMiscHandler;
927 }
928
929 EncHandler* Processor::getEncHandler(void **udata)
930 {
931     if (udata)
932         *udata = theEncUserData;
933     return theEncHandler;
934 }
935
936 void* Processor::getHandlerUserData(HandlerType type, void *handler)
937 {
938     switch(type)
939     {
940     case HLR_SCHEME: return theSchemeUserData;
941     case HLR_MESSAGE: return theMessageUserData;
942     case HLR_MISC: return theMiscUserData;
943     default: return theSAXUserData;
944     }
945 }
946
947 void Processor::setHardEncoding(const Str& hardEncoding_)
948 {
949     hardEncoding = hardEncoding_;
950 }
951
952 const Str& Processor::getHardEncoding() const
953 {
954     return hardEncoding;
955 };
956
957 /*****************************************************************
958 copyArg
959   called if the result document's location uses the arg:
960   scheme. Returns information about the associated named buffer.
961   if not found, returns -1 in argOrdinal and NULL in newCopy.
962 ARGS
963   argName       the name of the arg
964 RETURNS
965   *argOrdinal    the ordinal number of this arg. This is the number
966                 of the call to useArg() which defined the arg.
967   newCopy       pointer to a new copy of the arg (allocated via
968                 malloc())
969 *****************************************************************/
970
971 void Processor::copyArg(Sit S, const Str& argName, int* argOrdinal,
972     char*& newCopy)
973 {
974     Str absolute;
975     int lineNo;
976     if ((makeAbsoluteURI(S, (Str&)argName, "arg:/", absolute) != URI_ARG)
977         || (lineNo = datalines.findNum(absolute, FALSE, DLMODE_WRITE)) == -1)
978     {
979         newCopy = NULL;
980         *argOrdinal = -1;
981         return;
982     }
983     DynBlock *block = NZ( datalines[lineNo] -> _dataline -> getOutBuffer() );
984     newCopy = block -> compactToBuffer(); // GP: OK (no exit route)
985
986     //  set *argOrdinal
987     *argOrdinal = argList.findNum((char *)absolute + 4);    // skip 'arg:'
988
989 }
990
991 eFlag Processor::useArg(Sit S, const char *name, const char *val)
992 {
993     assert(name);
994     DStr nameStr;
995     if (*name != '/')
996         nameStr = "/";
997     nameStr += name;
998     if (argList.find(nameStr))
999         Err1(S, E1_DUPLICATE_ARG, nameStr);
1000     StrStr *p = new StrStr;
1001     p -> key = nameStr;
1002     if (val)
1003         p -> value = val;
1004     else
1005         p -> value.empty();
1006     argList.append(p);
1007     addedFlag = TRUE;
1008
1009     return OK;
1010 }
1011
1012 eFlag Processor::useTree(Sit S, const char *name, Tree *t)
1013 {
1014     assert(name);
1015     DStr nameStr;
1016     if (*name != '/')
1017         nameStr = "/";
1018     nameStr += name;
1019         // to check for duplicate trees
1020         E( useArg(S, name, NULL) );
1021         
1022         Str absolute;
1023         DataLine *d;
1024         makeAbsoluteURI(S, nameStr, "arg:", absolute);
1025         E( addLineTreeOnly(S, d, absolute, t -> XSLTree, t) );
1026     addedFlag = TRUE;
1027     return OK;
1028 }
1029
1030 eFlag Processor::addGlobalParam(Sit S, const char *name, const char *val)
1031 {
1032     assert(name);
1033     if (!val) val = (char*)"";
1034     globalParamsList.appendConstruct(name, val);
1035     return OK;
1036 }
1037
1038 eFlag Processor::useGlobalParam(Sit S, const char *name, const char *val)
1039 {
1040     assert(name);
1041     QName q;
1042     q.setLocal(NZ(styleSheet) -> unexpand(name));
1043     Expression *expr = new Expression(styleSheet -> getRoot(), EXF_ATOM);
1044     Str aux = val;
1045     expr -> setAtom(aux);
1046     vars -> addPrebinding(S, q, expr);
1047     return OK;
1048 }
1049
1050 eFlag Processor::useGlobalParams(Sit S)
1051 {
1052     while (globalParamsList.number())
1053     {
1054         StrStr& item = *(globalParamsList.last());
1055         E( useGlobalParam(S, item.key, item.value) );
1056             globalParamsList.freelast(FALSE);       
1057     }
1058     return OK;
1059 }
1060
1061 void Processor::setHardBaseURI(const char* hardBase)
1062 {
1063   addBaseURIMapping(""/**theEmptyString*/, (const Str) hardBase);
1064 }
1065
1066 void Processor::addBaseURIMapping(const Str& scheme, const Str& mapping)
1067 {
1068     int ndx = baseURIMappings.findNum(scheme);
1069     if (ndx != -1)
1070         baseURIMappings.freerm(ndx, FALSE);
1071     if (!mapping.isEmpty())
1072         baseURIMappings.appendConstruct(scheme, mapping);
1073 }
1074
1075 eFlag Processor::freeResultArgs(Sit S)
1076 {
1077     datalines.freeall(FALSE);
1078     argList.freeall(FALSE);
1079     addedFlag = FALSE;
1080     return OK;
1081 }
1082
1083 SabArena* Processor::getArena() 
1084 {
1085     return &theArena;
1086 }
1087
1088 eFlag Processor::prefixIsAliasTarget(Sit S, const Str& prefix, Bool& result)
1089 {
1090   result = FALSE;
1091   //we may enter during the tree copy as well
1092   if (styleSheet)
1093     {
1094       Phrase p = NZ(styleSheet) -> unexpand(prefix);
1095       for (int i = 0; i < styleSheet -> aliases().number(); i++)
1096         {
1097           if (styleSheet -> aliases()[i] -> getValue() == p)
1098             {
1099               result = TRUE;
1100               break;
1101             }
1102         }
1103     }
1104   return OK;
1105 }
1106
1107 void Processor::getAliasedName(EQName & name, Bool & aliased)
1108 {
1109   Str myUri = name.getUri();
1110   for (int i = 0; i < styleSheet -> aliases().number(); i++)
1111     {
1112       const Str & aliasUri = 
1113         styleSheet -> expand(styleSheet -> aliases()[i]->getKey());
1114       if (aliasUri && aliasUri == myUri)
1115         {
1116           Phrase newUri = styleSheet -> aliases()[i] -> getValue();
1117           Phrase newPrefix = styleSheet -> aliases()[i] -> getPrefix();
1118           name.setUri(styleSheet -> expand(newUri));
1119           //name.setPrefix(styleSheet -> expand(newPrefix));
1120           aliased = true;
1121           break;
1122         };              
1123     }
1124 }
1125
1126 /*
1127 Str Processor::getAliasedName(const EQName& name, 
1128                               NamespaceStack& currNamespaces,
1129                               Bool expatLike)
1130 {
1131    DStr s;
1132    Str newPrefixStr;
1133    Str newUri;
1134    //Bool defaultNS = !name.hasPrefix();
1135
1136    if (1 || name.hasPrefix())
1137    {
1138       Phrase newPrefix = UNDEF_PHRASE;
1139       Str myUri = name.getUri();
1140       int i;
1141       Bool aliasFound = FALSE;
1142       if (styleSheet)
1143       {
1144           for (i = 0; i < styleSheet -> aliases().number(); i++)
1145           {
1146             const Str* aliasUri = 
1147               currNamespaces.getUri(styleSheet -> expand(
1148                                  styleSheet -> aliases()[i]->getKey()));
1149             if (aliasUri && *aliasUri == myUri)
1150               {
1151                 newPrefix = styleSheet -> aliases()[i] -> getValue();
1152                 aliasFound = TRUE;
1153               };                
1154           }
1155       };
1156       
1157       if (newPrefix == UNDEF_PHRASE)
1158         {
1159           if (! aliasFound && name.hasPrefix()) 
1160             newPrefixStr = name.getPrefix();
1161         } 
1162       else 
1163         {
1164           newPrefixStr = styleSheet -> expand(newPrefix);
1165           //s += newPrefixStr;
1166           //we should proceed w/o this 
1167           //currNamespaces.appendConstruct(name.getPrefix(),name.getUri(),TRUE);
1168           //currNamespaces.appendConstruct(newPrefixStr,
1169           //*currNamespaces.getUri(newPrefixStr));
1170         };
1171       //if (!s.isEmpty()) // could be empty e.g. for the default namespace
1172       //  s += ":";
1173    };
1174    if (expatLike)
1175      {
1176        s  = name.getUri();
1177        s += THE_NAMESPACE_SEPARATOR;
1178        s += name.getLocal();
1179        s += THE_NAMESPACE_SEPARATOR;
1180        s += newPrefixStr;
1181      }
1182    else 
1183      {
1184        s = newPrefixStr;
1185        if (! s.isEmpty()) s += ":";
1186        s += name.getLocal();
1187      }
1188    return s;
1189 }
1190 */
1191
1192 eFlag Processor::addKey(Sit S, const EQName& ename,
1193                         Expression& match, Expression &use)
1194 {
1195   
1196   //E( keys -> addKey(S, ename, toSXP_Document(*input), match, use) );
1197   E( NZ(keys) -> addKey(S, ename, inputRoot, match, use) );
1198   return OK;
1199 }
1200
1201 eFlag Processor::getKeyNodes(Sit S, const EQName& ename, 
1202                              const Str& value, Context& result, 
1203                              SXP_Document doc) const
1204 {
1205     E( NZ(keys) -> getNodes(S, ename, doc, value, result) );
1206     return OK;
1207 }
1208
1209 eFlag Processor::makeKeysForDoc(Sit S, SXP_Document doc)
1210
1211     E( NZ(keys) -> makeKeysForDoc(S, doc) );
1212     return OK;
1213 }
1214
1215
1216 void Processor::report(Sit S, MsgType type, MsgCode code, 
1217     const Str &arg1, const Str &arg2) const
1218 {
1219     S.message(type, code, arg1, arg2);
1220 }
1221
1222 void Processor::initForSXP(Tree *baseTree)
1223 {
1224   input = NULL;
1225   styleSheet = baseTree;
1226   runsOnExternal = TRUE;
1227   if (!vars)
1228     vars = new VarsList(*styleSheet);
1229 }
1230
1231 void Processor::cleanupAfterSXP()
1232 {
1233   vars -> freeall(FALSE);
1234   styleSheet = NULL;
1235   runsOnExternal = FALSE;
1236 }
1237
1238 Bool Processor::supportsFunction(Str &uri, Str &name) 
1239 {
1240 #ifdef ENABLE_JS
1241   JSContextItem *item = jscontexts.find(uri, FALSE);  
1242   if (item) {
1243     for (int i = 0; i < item -> names.number(); i++) {
1244       if (*(item -> names[i]) == name) return TRUE;
1245     }
1246   }
1247   return FALSE;
1248 #else
1249   return FALSE;
1250 #endif
1251 }
1252
1253 #ifdef ENABLE_JS
1254 eFlag Processor::evaluateJavaScript(Sit S, Str &uri, DStr &script) 
1255 {
1256   //printf("===> evaluating uri %s: %s\n", (char*)uri, (char*)script);
1257
1258   JSContextItem *item = jscontexts.find(uri, TRUE);
1259   assert(item);
1260   if (item && item -> cx) {
1261     if (! SJSEvaluate(*item, script ) )
1262       {
1263         Str msg = "'";
1264         msg = msg + (item -> errInfo.message ? item -> errInfo.message : "");
1265         if (item -> errInfo.token) {
1266           msg = msg + "' token: '";
1267           msg = msg + (item -> errInfo.token ? item -> errInfo.token : "");
1268         }
1269         msg = msg + "'";
1270         Err2( S, E_JS_EVAL_ERROR, 
1271               Str(item -> errInfo.line), msg );
1272       }
1273   }
1274   
1275   return OK;
1276 }
1277
1278 eFlag Processor::callJSFunction(Sit S, Context *c, Str &uri, Str &name, 
1279                                   ExprList &atoms, Expression &retxpr)
1280 {
1281   if (supportsFunction(uri, name)) {
1282     JSContextItem *item = jscontexts.find(uri, FALSE);
1283     if (item) {
1284       if (! SJSCallFunction(S, c, *item, name, atoms, retxpr) )
1285         {
1286           Str msg = "'";
1287           msg = msg + (item -> errInfo.message ? item -> errInfo.message : "");
1288           if (item -> errInfo.token) {
1289             msg = msg + "' token: '";
1290             msg = msg + (item -> errInfo.token ? item -> errInfo.token : "");
1291           }
1292           msg = msg + "'";
1293           Err2( S, E_JS_EVAL_ERROR, 
1294                 Str(item -> errInfo.line), msg );
1295         };
1296     }
1297   }
1298
1299   return OK;
1300 }
1301 #endif
1302
1303 Str Processor::getNextNSPrefix()
1304 {
1305   char ret[10];
1306   sprintf(ret, "ns_%d", nsUnique++);
1307   return Str(ret);
1308 }
1309
1310 eFlag Processor::resolveGlobal(Sit S, Context *c, QName &name, XSLElement *varElem /* = NULL */)    
1311
1312   // if !varElem, we need to ask styleSheet's toplevel var directory
1313   /*
1314   if (!varElem)
1315     varElem = styleSheet -> findVarInDirectory(name);
1316   else
1317     {
1318       // set the variable name from the element
1319       E( varElem -> setLogical(S, name, NZ(varElem -> atts.find(XSLA_NAME)) -> cont, FALSE) );
1320     }
1321   */
1322   
1323   //we have either the name or the element, if both, the element wins
1324   if (varElem) 
1325       E( varElem->setLogical(S, name, 
1326                              NZ(varElem->atts.find(XSLA_NAME))->cont, FALSE) );
1327   //this element must be the same as the varElem if available
1328   XSLElement *aux = styleSheet -> findVarInDirectory(name);
1329   if (varElem)
1330     {
1331       //it should happen only if the caller is XSLElement::execute
1332       //and we have some overriden (import) declaration
1333       //in such a case we simply skip the processing
1334       if (varElem != aux) return OK;
1335     }
1336   else
1337     { 
1338       varElem = aux;
1339     }
1340
1341   if (varElem)
1342     {
1343       // find the var record for several references
1344       VarBindings* record = vars -> find(name);
1345       // maybe it has a binding defined from a forward reference?
1346       //but it shouldn't happen now, due the ** condition
1347       if (record && vars -> getBinding(record))
1348         return OK;
1349       else
1350         {
1351           // maybe it is an "open toplevel" (currently processed)
1352           if (record && vars -> isOpenGlobal(record))
1353             {
1354               Str fullName;
1355               styleSheet -> expandQStr(name, fullName);
1356               Err1(S, E1_VAR_CIRCULAR_REF, fullName);
1357             }
1358           else
1359             {
1360               // process it normally
1361               E( vars -> openGlobal(S, name, record) );
1362               // varElem's expression will automatically evaluate with "resolvingGlobal"
1363               E( varElem -> execute(S, c, /* resolvingGlobals = */ TRUE) );
1364               E( vars -> closeGlobal(S, record) );              
1365             }
1366         }
1367     }
1368   else
1369     {
1370       // this is not a global
1371       Str fullName;
1372       styleSheet -> expandQStr(name, fullName);
1373       Err1(S, E1_VAR_NOT_FOUND, fullName);
1374     }
1375   return OK;
1376 }
1377
1378 /*
1379 eFlag Processor::resolveGlobals(Sit S, Context *c)
1380 {
1381      return styleSheet -> resolveGlobals(S, c, this);
1382 }
1383 */
1384
1385 eFlag Processor::stripElement(Sit S, Daddy *e)
1386 {
1387   if (isElement(e) && ! toE(e) -> preserveSpace)
1388     {
1389       EQName ename;
1390       e -> getOwner().expandQ(e -> getName(), ename);
1391       int sprec, pprec;
1392       double sprio, pprio;
1393       Bool s = styleSheet -> findStrippedName(ename, sprec, sprio);
1394       Bool p = styleSheet -> findPreservedName(ename, pprec, pprio);
1395
1396       if (s && (!p || sprec < pprec || sprio > pprio))
1397         {
1398           e -> contents.strip();
1399         }
1400     }
1401
1402   //process recursively
1403   for (int i = 0; i < e -> contents.number(); i++)
1404     {
1405       if (isElement(e -> contents[i]))
1406         E( stripElement(S, toE(e -> contents[i])) );
1407     }
1408   return OK;
1409 }
1410
1411 eFlag Processor::stripTree(Sit S, Tree &tree)
1412 {
1413   //strip is forbidden
1414   if (S.hasFlag(SAB_DISABLE_STRIPPING)) return OK;
1415
1416   //never strip the tree twice
1417   if (tree.stripped) return OK;
1418
1419   //skip if no definition in the stylesheet
1420   if (!styleSheet -> hasAnyStrippedName() && 
1421       ! styleSheet -> hasAnyPreservedName())
1422     return OK;
1423
1424   //do it
1425   Daddy &d = tree.getRoot();
1426   E( stripElement(S, &d) );
1427
1428   tree.stripped = TRUE;
1429
1430   return OK;
1431 }
1432
1433 void Processor::pushInBinding(Bool val)
1434 {
1435   inBinding.append(val);
1436 }
1437
1438 void Processor::popInBinding()
1439 {
1440   inBinding.deppend();
1441 }
1442
1443 Bool Processor::isInBinding()
1444 {
1445   return inBinding.number() && inBinding.last();
1446 }
1447
1448 eFlag Processor::pushDocumentDefinition(Sit S, OutputDefinition *def,
1449                                         OutputterObj*& out)
1450 {
1451   assert(def);
1452   Bool change = !documentDefinitions.number() || 
1453     documentDefinitions.last() != def;
1454
1455   documentDefinitions.append(def);
1456   
1457   if (change)
1458     {
1459       Str href = "file:///home/pavel/ga/sablot-ext/bin/inner.xml";
1460       Str base = "";
1461       E( pushOutputterForURI(S, href, base, def) );
1462       E( outputter() -> eventBeginOutput(S) );
1463     }
1464
1465   out = outputter();
1466   return OK;
1467 }
1468
1469 eFlag Processor::popDocumentDefinition(Sit S)
1470 {
1471   assert(documentDefinitions.number());
1472   OutputDefinition *last = documentDefinitions.last();
1473   documentDefinitions.deppend();
1474   //
1475   if (!documentDefinitions.number() || documentDefinitions.last() != last)
1476     {
1477       E( outputter() -> eventTrailingNewline(S) );
1478       E( outputter() -> eventEndOutput(S) );
1479       E( popOutputter(S) );
1480     }
1481   return OK;
1482 }