/* 
 * The contents of this file are subject to the Mozilla Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 * 
 * The Original Code is the Sablotron XSLT Processor.
 * 
 * The Initial Developer of the Original Code is Ginger Alliance Ltd.
 * Portions created by Ginger Alliance are Copyright (C) 2000 Ginger
 * Alliance Ltd. All Rights Reserved.
 * 
 * Contributor(s):
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL"), in which case the provisions of the GPL are applicable 
 * instead of those above.  If you wish to allow use of your 
 * version of this file only under the terms of the GPL and not to
 * allow others to use your version of this file under the MPL,
 * indicate your decision by deleting the provisions above and
 * replace them with the notice and other provisions required by
 * the GPL.  If you do not delete the provisions above, a recipient
 * may use your version of this file under either the MPL or the
 * GPL.
 */

#include "base.h"
#include "proc.h"
#include "expr.h"
#include "tree.h"
#include "context.h"
#include "vars.h"
// #include "xmlparse.h" - moved to parser.h
#include "parser.h"
#include "guard.h"
#include "key.h"

// GP: clean

/*****************************************************************
R u l e I t e m   methods
*****************************************************************/

RuleItem::RuleItem(XSLElement *arule,double aprio, QName &_name,
                   QName *_mode)
:
rule(arule), priority(aprio), name(_name), mode(_mode)
{
  match = rule -> atts.find(XSLA_MATCH);
};


//
//  ~RuleItem
//  has to dispose of the mode
//
RuleItem::~RuleItem()
{
    cdelete(mode);
}


/*****************************************************************
R u l e S L i s t   methods
*****************************************************************/

RuleSList::RuleSList()
:
SList<RuleItem*>(LIST_SIZE_LARGE)
{};

RuleSList::~RuleSList()
{
    freeall(FALSE);
}

int RuleSList::compare(int first, int second, void *data)
{
    return fcomp((*this)[first] -> priority, (*this)[second] -> priority);
};


XSLElement *RuleSList::findByName(const Tree& t, const QName &what) const
{
    int theNumber = number();
    for (int i=0; i < theNumber; i++)
        if (t.cmpQNames((*this)[i] -> name, what))
            return (*this)[i] -> rule;
    return NULL;
}

/*****************************************************************

  DataLineItem

*****************************************************************/

DataLineItem::DataLineItem(Sit S_)
: situation(S_)
{
}

DataLineItem::~DataLineItem()
{
    // the following close should be checked for error!!
    if (_dataline && _dataline -> mode != DLMODE_CLOSED)
        _dataline -> close(situation);
    cdelete(_dataline);
    if (!_preparsedTree)
        cdelete(_tree);
}

/*****************************************************************

  DataLinesList

*****************************************************************/

int DataLinesList::findNum(Str &absoluteURI, Bool _isXSL,
    DLAccessMode _mode)
{
    int theNumber = number();
    for (int i = 0; i < theNumber; i++)
    {
        DataLineItem* item = (*this)[i];
        if ((item->_dataline -> fullUri == absoluteURI) &&
            (item->_isXSL == _isXSL) && 
            (item-> _dataline -> mode == _mode || 
	         item-> _dataline -> mode == DLMODE_CLOSED))
        return i;
    }
    return -1;
}

Tree *DataLinesList::getTree(Str &absoluteURI, Bool _isXSL, 
    DLAccessMode _mode)
{
    int n;
    if ((n = findNum(absoluteURI, _isXSL, _mode)) != -1)
        return (*this)[n] -> _tree;
    else
        return NULL;
}

eFlag DataLinesList::addLine(Sit S, DataLine *d, Tree *t, Bool isXSL,
    Bool preparsedTree /* = FALSE */)
{
    DataLineItem *item = new DataLineItem(S);
    item -> _dataline = d;
    item -> _tree = t;
    item -> _isXSL = isXSL;
    item -> _preparsedTree = preparsedTree;
    append(item);
    return OK;
}

/*****************************************************************
*                                                                *
*   P r o c e s s o r   methods                                  *
*                                                                *
*****************************************************************/

// disable the MSVC warning "'this' used in the initializer list"

#ifdef WIN32
#pragma warning( disable : 4355 )
#endif

Processor::Processor() 
: 
theArena(PROC_ARENA_SIZE)

#ifdef WIN32
#pragma warning( default : 4355 )
#endif

{
//    pushNewHandler();
    theSchemeHandler = NULL;
    theMessageHandler = NULL;
    theSAXHandler = NULL;
    theMiscHandler = NULL;
    theEncHandler = NULL;
    theSchemeUserData = theMessageUserData = 
        theSAXUserData = theMiscUserData = theEncUserData = NULL;    
    instanceData = NULL;
    instanceSituation = NULL;
    addedFlag = FALSE;
    vars = NULL;
    input = styleSheet = NULL;
    keys = NULL;
    nsUnique = 1;
    hasMasterSituation = FALSE;
};

void Processor::prepareForRun()
{
    // do this in open():
    // vars = new VarsList();
    input = styleSheet = NULL;
    decimals().initialize();
}

void Processor::freeNonArgDatalines()
{
    int i = 0;
    while(i < datalines.number())
    {
        if (datalines[i] -> _dataline -> scheme != URI_ARG)
	    {
            datalines.freerm(i, FALSE);
	    }
        else
        {
            // removing the tree here because the arena gets disposed
            // in cleanupAfterRun(). So only the arg buffers remain.
	        // only remove the tree if not preparsed
		    if (!datalines[i] -> _preparsedTree)
                cdelete(datalines[i] -> _tree);
	        // don't move the following line out of the else!
            i++;        	
        };
    }
    addedFlag = FALSE;
}

void Processor::cleanupAfterRun(Situation *Sp)
{
/*
These deletes are now subsumed in "datalines.freeall()":
    cdelete(input);
    cdelete(result);
    cdelete(styleSheet);
*/
    cdelete(vars);
    cdelete(keys);
    decimals().freeall(FALSE);
    jscontexts.freeall(FALSE);

    freeNonArgDatalines();
    if (Sp && !Sp -> isError())
    {
        assert(modes.isEmpty());
        assert(outputters_.isEmpty());
    }
    else
    {
        modes.freeall(FALSE);
        outputters_.freeall(FALSE);
    };
    if (Sp)
	Sp -> clear();
    // hope this works:
    //DETACH: move to Tree
    // dictionary.destroy();
    theArena.dispose();
}

Processor::~Processor()
{
//    popNewHandler();
    baseURIMappings.freeall(FALSE);
    // FIXME: MUST clear theRecoder but got no situation
//    theRecoder.clear();
};

eFlag Processor::open(Sit S, char *sheetURI, char *inputURI)
{
    Str temp;
    DStr theBase;

    my_getcwd(theBase);
    theBase = findBaseURI(Str("file://") + theBase);

    E( readTreeFromURI(S, styleSheet, temp = sheetURI, theBase, TRUE) );
    E( readTreeFromURI(S, input, temp = inputURI, theBase, FALSE) );
    vars = new VarsList (*styleSheet);
    keys = new KeySet;
    return OK;
}

eFlag Processor::run(Sit S, char* resultURI)
{
    Str temp;
    DStr theBase;
    my_getcwd(theBase);
    theBase = findBaseURI(Str("file://") + theBase);

    Log1(S, L1_EXECUTING, styleSheet -> getURI());
    double time_was = getMillisecs();
    E( pushOutputterForURI(S, temp = resultURI, theBase) );
    E( outputter() -> eventBeginOutput(S) );

    GP( Context ) c = new Context(&(input -> getRoot()));
    (*c).set(&(input -> getRoot()));

    /*
        in vars, global prebindings go to call level 0,
        global bindings to call level 1
    */
    vars -> startCall();
    // start by resolving all globals
    E( resolveGlobals(S, c) );
    E( styleSheet -> getRoot().execute(S, c, FALSE) );
    vars -> endCall();
    c.del();

    E( outputter() -> eventTrailingNewline(S) );
    E( outputter() -> eventEndOutput(S) );
        
    // report info about the output document to the MiscHandler
    void *miscUserData;
    OutputDefinition *outDef = &(styleSheet -> outputDef);
    MiscHandler *miscHlr = getMiscHandler(&miscUserData);
    if (miscHlr)
        miscHlr -> documentInfo(
            miscUserData,
            this,   // processor
            outDef -> getValueStr(XSLA_MEDIA_TYPE), 
            outDef -> getValueStr(XSLA_ENCODING));

    E( popOutputter(S) );
    Log1(S, L1_EXECUTION_DONE, getMillisecsDiff(time_was));
    return OK;
}



/*================================================================
getVarBinding 
    finds the current binding for the variable with the given name.
    The binding is an expression. If none is present, returns NULL.
================================================================*/

Expression *Processor::getVarBinding(QName &which)
{
    return vars -> getBinding(which);
}

/*================================================================
execute
both flavours DEALLOCATE the context and set the pointer to NULL
================================================================*/

eFlag Processor::execute(Sit S, Vertex *IP, Context *&c, Bool resolvingGlobals)
{
    while (!c -> isFinished())
    {
      c -> setCurrentNode(c -> current());
        if (IP) 
	  {
            E( IP -> execute(S, c, resolvingGlobals) );
	  }
        else
	  {
            E( execApplyTemplates(S, c, resolvingGlobals) );
	  }
        c -> shift();
    };
    cdelete(c);
    return OK;
}

eFlag Processor::execute(Sit S, VertexList &IPlist, Context *&c, Bool resolvingGlobals)
{
    // we may need to remove some variable bindings on each pass
    // through a for loop
    Vertex *theParent = IPlist.number()? IPlist[0] -> parent : NULL;
    XSLElement *theForParent;
    if (theParent && isXSLElement(theParent) && toX(theParent) -> op == XSL_FOR_EACH)
        theForParent = toX(theParent);
    else
        theForParent = NULL;
    
    while (c -> current())
    {
        c -> setCurrentNode(c -> current());
        E( IPlist.execute(S, c, resolvingGlobals) );
        c -> shift();
        if (theForParent)
            theForParent -> removeBindings(S);
    };
    cdelete(c);
    return OK;
}

eFlag Processor::execApplyTemplates(Sit S, Context *c, Bool resolvingGlobals)
{
    XSLElement *rule;
    E( NZ(styleSheet) -> findBestRule(S, rule, c, getCurrentMode(), FALSE) );
    if (!rule)
        E( builtinRule(S, c, resolvingGlobals) )
    else
        E( rule -> execute(S, c, resolvingGlobals) );
    return OK;
}

eFlag Processor::execApplyImports(Sit S, Context *c, Bool resolvingGlobals)
{
    XSLElement *rule;
    E( NZ(styleSheet) -> findBestRule(S, rule, c, getCurrentMode(), TRUE) );
    if (rule)
        E( rule -> execute(S, c, FALSE) );
    return OK;
}

eFlag Processor::builtinRule(Sit S, Context *c, Bool resolvingGlobals)
{
    // may assume v to be a physical vertex
    Vertex *v = toPhysical(c -> current());
    
    switch(v -> vt & VT_BASE)
    {
    case VT_ROOT:
    case VT_ELEMENT:
        //apply-templates
        {
            GP( Expression ) e = new Expression(*toE(v), EXF_LOCPATH);
            (*e).setLS(AXIS_CHILD, EXNODE_NODE);
            GP( Context ) newc;
            newc.assign(c);
            E( (*e).createContext(S, newc) );
            newc.unkeep();
            E( execute(S, NULL, newc, resolvingGlobals) );
            newc.keep(); // GP: it's NULL by this time anyway
            e.del();
        }; break;
    case VT_TEXT:
    case VT_ATTRIBUTE:
        //copy contents to output
        {
            DStr temp;
            E( v -> value(S, temp, c) );
            E( outputter() -> eventData(S, temp) );
        }; break;
    case VT_PI:
    case VT_COMMENT:
        {};
    };
    return OK;
};


/*................................................................
getCurrentMode()
RETURN: pointer to the current mode which is a QName
................................................................*/

QName *Processor::getCurrentMode()
{
    return (modes.number() ? modes.last() : NULL);
}

/*................................................................
pushMode()
called before every mode-changing apply-templates
ARGS:
m       the mode to be placed on top of the mode stack
................................................................*/

void Processor::pushMode(QName *m)
{
    modes.append(m);
}

/*................................................................
popMode()
called after every mode-changing apply-templates
removes the current mode from the mode stack FREEING it
................................................................*/

void Processor::popMode()
{
    modes.freelast(FALSE);
}

eFlag Processor::addLineNoTree(Sit S, DataLine *&d, Str &absolute, Bool isXSL)
{
    GP( DataLine ) d_;
    M( S, d_ = new DataLine );
    E( (*d_).open(S, absolute, DLMODE_READ, &argList) );
    E( datalines.addLine(S, d_, NULL, isXSL) );
    d = d_.keep();
    return OK;
}

eFlag Processor::addLineTreeOnly(Sit S, DataLine *&d, Str &absolute, Bool isXSL,
    Tree *t)
{
    GP( DataLine ) d_;
    M( S, d_ = new DataLine );
    E( (*d_).setURIAndClose(S, absolute) );
    E( datalines.addLine(S, d_, t, isXSL, /* preparsedTree = */ TRUE) );
    d = d_.keep();
    return OK;
}

eFlag Processor::addLineParse(Sit S, Tree *& newTree, Str &absolute, Bool isXSL)
{
    GP( DataLine ) d = new DataLine;
    E( (*d).open(S, absolute, DLMODE_READ, &argList) );
    GP( Tree ) newTree_ = new Tree(absolute, isXSL );
    //E( (*newTree_).parse(S, d) );
    eFlag status = (*newTree_).parse(S, d);
    E( (*d).close(S) );
    //hold parse errors after dataline is closed
    E( status );
    E( datalines.addLine(S, d.keep(), newTree = newTree_.keep(), isXSL) );
    return OK;
}

const Str& Processor::findBaseURI(const Str& unmappedBase)
{
    Str scheme, rest;
    uri2SchemePath(unmappedBase, scheme, rest);
    Str *mapping = baseURIMappings.find(scheme);
    if (mapping) return *mapping;
    mapping = baseURIMappings.find(""/**theEmptyString*/);
    if (mapping) return *mapping;
    return unmappedBase;
}

//
//          Processor::baseForVertex
//
//  returns the *translated* base URI in effect for a given vertex
//

const Str& Processor::baseForVertex(Element *v)
{
    return findBaseURI(NZ(v) -> getSubtreeInfo() -> getBaseURI());
}


//
//          Processor::readTreeFromURI
//
//  reads in a tree from URI 'location' resolved against 'base' which is 
//  a *translated* base (one for which the 'hard base URIs' have been taken 
//  into account)
//

eFlag Processor::readTreeFromURI(Sit S, Tree*& newTree, const Str& location, const Str& base,
      Bool isXSL)
{
    Str
        absolute;
    makeAbsoluteURI(location, base, absolute);
    newTree = datalines.getTree(absolute, isXSL, DLMODE_READ);
    if (!newTree)
        E( addLineParse(S, newTree, absolute, isXSL) );
    return OK;
}

eFlag Processor::pushOutputterForURI(Sit S, Str& location, Str& base)
{
    Str absolute;
    makeAbsoluteURI(location, base, absolute);
    if (datalines.getTree(absolute, FALSE, DLMODE_WRITE))
        Err1(S, E1_URI_WRITE, absolute);
    GP( DataLine ) d;
    if ( !(absolute == "arg:/null") ) { 
      M( S, d = new DataLine );
      E( (*d).open(S, absolute, DLMODE_WRITE, &argList) );
      // FIXME: the NULL tree in the following
      E( datalines.addLine(S, d.keep(), NULL, FALSE) );
    }else{ //physical output not required
      d = NULL;
    };
    GP( OutputterObj ) newOut;
    M( S, newOut = new OutputterObj );
    E( (*newOut).setOptions(S, d, &(styleSheet -> outputDef)) );
    if (theSAXHandler)
        E( (*newOut).setOptionsSAX(S, theSAXHandler, theSAXUserData, 
				   SAXOUTPUT_AS_PHYSICAL) );

    outputters_.append(newOut.keep());
    return OK;
}

eFlag Processor::pushTreeConstructer(Sit S, TreeConstructer *& newTC, Tree *t)
{
    newTC = NULL;
    GP( TreeConstructer ) newTC_ = new TreeConstructer(S);
    GP( OutputterObj ) newTCSource = new OutputterObj;
    //E( (*newTCSource).setOptions(S, NULL, &(styleSheet -> outputDef)) ); //_PH_
    M( S, newTC_+0 );
    outputters_.append(newTCSource);
    E( (*newTC_).parseUsingSAX(S, t, *newTCSource) );
    newTCSource.keep();
    newTC = newTC_.keep();
    return OK;
}

eFlag Processor::pushOutputter(Sit S, OutputterObj* out_)
{
    outputters_.append(out_);
    return OK;
}

eFlag Processor::popOutputter(Sit S)
{
    outputters_.freelast(FALSE);
    return OK;
}

eFlag Processor::popTreeConstructer(Sit S, TreeConstructer *theTC)
{
    popOutputter(S);
    delete theTC;
    return OK;
}

/*
eFlag Processor::parse(Sit S, Tree *t, DataLine *d)
{
    Log1(S, L1_PARSING, t -> name);
    double time_was = getMillisecs();
    TreeConstructer tc(S);
    eFlag retval = tc.parseDataLineUsingExpat(S, t, d);
    if (!retval)
    {
        Log1(S, L1_PARSE_DONE, getMillisecsDiff(time_was));
    }
    return retval;
}
*/

eFlag Processor::getArg(Sit S, const char* name, char*& buffer, Bool isUTF16)
{
    Str temp, *value = argList.find(temp = (char*)name);
    if (!value)
        Err1(S, E1_ARG_NOT_FOUND,(char*) name);
    buffer = (char*) *value;
    return OK;
}

//
//
//
//      plugin handler stuff
//
//
//

eFlag Processor::setHandler(Sit S, HandlerType type, void *handler, void *userData)
{
    void **whereHandler, **whereUserData;
    switch(type)
    {
    case HLR_SCHEME: 
        {
            whereHandler = (void **)&theSchemeHandler;
            whereUserData = &theSchemeUserData;
        }; break;
    case HLR_MESSAGE: 
        {
            whereHandler = (void **)&theMessageHandler;
            whereUserData = &theMessageUserData;
        }; break;
    case HLR_SAX: 
        {
            whereHandler = (void **)&theSAXHandler;
            whereUserData = &theSAXUserData;
        }; break;
    case HLR_MISC: 
        {
            whereHandler = (void **)&theMiscHandler;
            whereUserData = &theMiscUserData;
        }; break;
    case HLR_ENC:
        {
            whereHandler = (void **)&theEncHandler;
            whereUserData = &theEncUserData;
        }; break;
    default: 
        Err1(S, E1_INVALID_HLR_TYPE, (int) type);
    }
    if (*whereHandler)
    {
        if (handler)
            Warn1(S, W1_HLR_REGISTERED, hlrTypeNames[type])
        else
        {
            *whereHandler = NULL;
            *whereUserData = NULL;
        }
    }
    else
    {
        if (handler)
        {
            *whereHandler = handler;
            *whereUserData = userData;
        }
        else
            Warn1(S, W1_HLR_NOT_REGISTERED, hlrTypeNames[type])
    }
    return OK;
}


SchemeHandler *Processor::getSchemeHandler(void **udata)
{
    if (udata)
        *udata = theSchemeUserData;
    return theSchemeHandler;
}

MessageHandler *Processor::getMessageHandler(void **udata)
{
    if (udata)
        *udata = theMessageUserData;
    return theMessageHandler;
}

SAXHandler *Processor::getSAXHandler(void **udata)
{
    if (udata)
        *udata = theSAXUserData;
    return theSAXHandler;
}

MiscHandler *Processor::getMiscHandler(void **udata)
{
    if (udata)
        *udata = theMiscUserData;
    return theMiscHandler;
}

EncHandler *Processor::getEncHandler(void **udata)
{
    if (udata)
        *udata = theEncUserData;
    return theEncHandler;
}

void *Processor::getHandlerUserData(HandlerType type, void *handler)
{
    switch(type)
    {
    case HLR_SCHEME: return theSchemeUserData;
    case HLR_MESSAGE: return theMessageUserData;
    case HLR_MISC: return theMiscUserData;
    default: return theSAXUserData;
    }
}

void Processor::setHardEncoding(const Str& hardEncoding_)
{
    hardEncoding = hardEncoding_;
}

const Str& Processor::getHardEncoding() const
{
    return hardEncoding;
};

/*****************************************************************
copyArg
  called if the result document's location uses the arg:
  scheme. Returns information about the associated named buffer.
  if not found, returns -1 in argOrdinal and NULL in newCopy.
ARGS
  argName       the name of the arg
RETURNS
  *argOrdinal    the ordinal number of this arg. This is the number
                of the call to useArg() which defined the arg.
  newCopy       pointer to a new copy of the arg (allocated via
                malloc())
*****************************************************************/

void Processor::copyArg(const Str& argName, int* argOrdinal,
    char*& newCopy)
{
    Str absolute;
    int lineNo;
    if ((makeAbsoluteURI((Str&)argName, "arg:/", absolute) != URI_ARG)
        || (lineNo = datalines.findNum(absolute, FALSE, DLMODE_WRITE)) == -1)
    {
        newCopy = NULL;
        *argOrdinal = -1;
        return;
    }
    DynBlock *block = NZ( datalines[lineNo] -> _dataline -> getOutBuffer() );
    newCopy = block -> compactToBuffer(); // GP: OK (no exit route)

    //  set *argOrdinal
    *argOrdinal = argList.findNum((char *)absolute + 4);    // skip 'arg:'

}

eFlag Processor::useArg(Sit S, char *name, char *val)
{
    assert(name);
    DStr nameStr;
    if (*name != '/')
        nameStr = "/";
    nameStr += name;
    if (argList.find(nameStr))
        Err1(S, E1_DUPLICATE_ARG, nameStr);
    StrStr *p = new StrStr;
    p -> key = nameStr;
    if (val)
        p -> value = val;
    else
        p -> value.empty();
    argList.append(p);
    addedFlag = TRUE;

    return OK;
}

eFlag Processor::useTree(Sit S, char *name, Tree *t)
{
    assert(name);
    DStr nameStr;
    if (*name != '/')
        nameStr = "/";
    nameStr += name;
	// to check for duplicate trees
	E( useArg(S, name, NULL) );
	
	Str absolute;
	DataLine *d;
	makeAbsoluteURI(nameStr, "arg:", absolute);
	E( addLineTreeOnly(S, d, absolute, t -> XSLTree, t) );
    addedFlag = TRUE;
    return OK;
}

eFlag Processor::addGlobalParam(Sit S, char *name, char *val)
{
    assert(name);
    if (!val) val = (char*)"";
    globalParamsList.appendConstruct(name, val);
    return OK;
}

eFlag Processor::useGlobalParam(Sit S, char *name, char *val)
{
    assert(name);
    QName q;
    q.setLocal(NZ(styleSheet) -> unexpand(name));
    Expression *expr = new Expression(styleSheet -> getRoot(), EXF_ATOM);
    Str aux = val;
    expr -> setAtom(aux);
    vars -> addPrebinding(S, q, expr);
    return OK;
}

eFlag Processor::useGlobalParams(Sit S)
{
    while (globalParamsList.number())
    {
        StrStr& item = *(globalParamsList.last());
        E( useGlobalParam(S, item.key, item.value) );
	    globalParamsList.freelast(FALSE);	    
    }
    return OK;
}

void Processor::setHardBaseURI(const char* hardBase)
{
  addBaseURIMapping(""/**theEmptyString*/, (const Str) hardBase);
}

void Processor::addBaseURIMapping(const Str& scheme, const Str& mapping)
{
    int ndx = baseURIMappings.findNum(scheme);
    if (ndx != -1)
        baseURIMappings.freerm(ndx, FALSE);
    if (!mapping.isEmpty())
        baseURIMappings.appendConstruct(scheme, mapping);
}

eFlag Processor::freeResultArgs(Sit S)
{
    datalines.freeall(FALSE);
    argList.freeall(FALSE);
    addedFlag = FALSE;
    return OK;
}

Arena *Processor::getArena() 
{
    return &theArena;
}

Str Processor::getAliasedName(const EQName& name, NamespaceStack& currNamespaces)
{
   DStr s;
   Bool defaultNS = !name.hasPrefix();

   if (1 || name.hasPrefix())
   {
      Phrase newPrefix = UNDEF_PHRASE;
      Str myUri = name.getUri();
      int i;
      Bool aliasFound = FALSE;
      if (styleSheet)
      {
          for (i = 0; i < styleSheet -> aliases().number(); i++)
          {
	    const Str* aliasUri = 
	      currNamespaces.getUri(styleSheet -> expand(
					 styleSheet -> aliases()[i]->getKey()));
	    if (aliasUri && *aliasUri == myUri)
              {
		newPrefix = styleSheet -> aliases()[i] -> getValue();
		aliasFound = TRUE;
              };	        
	  }
      };
      
      if (newPrefix == UNDEF_PHRASE)
      {
        if (! aliasFound && name.hasPrefix()) s += name.getPrefix();
      } 
      else 
      {
        Str newPrefixStr = styleSheet -> expand(newPrefix);
        s += newPrefixStr;
	//we should proceed w/o this 
        //currNamespaces.appendConstruct(name.getPrefix(),name.getUri(),TRUE);
	//currNamespaces.appendConstruct(newPrefixStr,
        //                               *currNamespaces.getUri(newPrefixStr));
      };
      if (!s.isEmpty()) // could be empty e.g. for the default namespace
        s += ":";
   };
   s += name.getLocal();
   return s;
}

eFlag Processor::addKey(Sit S, const EQName& ename,
    Expression& match, Expression &use)
{
    E( keys -> addKey(S, ename, toSXP_Document(*input), match, use) );
    return OK;
}

eFlag Processor::getKeyNodes(Sit S, const EQName& ename, 
			     const Str& value, Context& result, 
			     SXP_Document doc) const
{
    E( keys -> getNodes(S, ename, doc, value, result) );
    return OK;
}

eFlag Processor::makeKeysForDoc(Sit S, SXP_Document doc)
{ 
    E( keys -> makeKeysForDoc(S, doc) );
    return OK;
}


void Processor::report(Sit S, MsgType type, MsgCode code, 
    const Str &arg1, const Str &arg2) const
{
    S.message(type, code, arg1, arg2);
}

void Processor::initForSXP(Tree *baseTree)
{
    cdelete(vars);
    input = NULL;
    styleSheet = baseTree;
    vars = new VarsList(*styleSheet);
}

void Processor::cleanupAfterSXP()
{
    cdelete(vars);
    styleSheet = NULL;
}


eFlag Processor::evaluateJavaScript(Sit S, Str &uri, DStr &script) 
{
  //printf("===> evaluating uri %s: %s\n", (char*)uri, (char*)script);

  JSContextItem *item = jscontexts.find(uri, TRUE);
  assert(item);
  if (item && item -> cx) {
    if (! SJSEvaluate(*item, script ) )
      {
	Str msg = "'";
	msg = msg + (item -> errInfo.message ? item -> errInfo.message : "");
	if (item -> errInfo.token) {
	  msg = msg + "' token: '";
	  msg = msg + (item -> errInfo.token ? item -> errInfo.token : "");
	}
	msg = msg + "'";
	Err2( S, E_JS_EVAL_ERROR, 
	      Str(item -> errInfo.line), msg );
      }
  }
  
  return OK;
}


Bool Processor::supportsFunction(Str &uri, Str &name) 
{
  JSContextItem *item = jscontexts.find(uri, FALSE);  
  if (item) {
    for (int i = 0; i < item -> names.number(); i++) {
      if (*(item -> names[i]) == name) return TRUE;
    }
  }
  return FALSE;
}

eFlag Processor::callJSFunction(Sit S, Context *c, Str &uri, Str &name, 
				  ExprList &atoms, Expression &retxpr)
{
  if (supportsFunction(uri, name)) {
    JSContextItem *item = jscontexts.find(uri, FALSE);
    if (item) {
      if (! SJSCallFunction(S, c, *item, name, atoms, retxpr) )
	{
	  Str msg = "'";
	  msg = msg + (item -> errInfo.message ? item -> errInfo.message : "");
	  if (item -> errInfo.token) {
	    msg = msg + "' token: '";
	    msg = msg + (item -> errInfo.token ? item -> errInfo.token : "");
	  }
	  msg = msg + "'";
	  Err2( S, E_JS_EVAL_ERROR, 
		Str(item -> errInfo.line), msg );
	};
    }
  }

  return OK;
}

Str Processor::getNextNSPrefix()
{
  char ret[10];
  sprintf(ret, "ns_%d", nsUnique++);
  return Str(ret);
}

eFlag Processor::resolveGlobal(Sit S, Context *c, QName &name, XSLElement *varElem /* = NULL */)    
{ 
    // if !varElem, we need to ask styleSheet's toplevel var directory
    if (!varElem)
	varElem = styleSheet -> findVarInDirectory(name);
    else
    {
	// set the variable name from the element
	E( varElem -> setLogical(S, name, NZ(varElem -> atts.find(XSLA_NAME)) -> cont, FALSE) );
}
    if (varElem)
    {
	// find the var record for several references
	VarBindings* record = vars -> find(name);
	// maybe it has a binding defined from a forward reference?
	if (record && vars -> getBinding(record))
	    return OK;
	else
	{
	    // maybe it is an "open toplevel" (currently processed)
	    if (record && vars -> isOpenGlobal(record))
	    {
		Str fullName;
		styleSheet -> expandQStr(name, fullName);
		Err1(S, E1_VAR_CIRCULAR_REF, fullName);
	    }
	    else
	    {
		// process it normally
		E( vars -> openGlobal(S, name, record) );
		// varElem's expression will automatically evaluate with "resolvingGlobal"
		E( varElem -> execute(S, c, /* resolvingGlobals = */ TRUE) );
		E( vars -> closeGlobal(S, record) );		
	    }
	}
    }
    else
    {
	// this is not a global
	Str fullName;
	styleSheet -> expandQStr(name, fullName);
	Err1(S, E1_VAR_NOT_FOUND, fullName);
    }
    return OK;
}

eFlag Processor::resolveGlobals(Sit S, Context *c)
{
     return styleSheet -> resolveGlobals(S, c, this);
}
