/* 
 * 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 "tree.h"
#include "proc.h"
#include "guard.h"
#include "key.h"

eFlag StylesheetStructure::findBestRule(
    Sit S, 
    XSLElement *&ret, 
    Context *c,
    QName *currMode,
    Bool importsOnly)
{
    int i;
    ret = NULL;
    // if we don't process imports only, look at the rules in this
    // structure
    if (!importsOnly)
    {
        int rulesNumber = rulesList.number();
	int bestRule = -1;
	double bestPrio = 0;
	Expression *pattern = NULL;
	QName *thisMode;
	XSLElement *therule;
	for (i = 0; i < rulesNumber; i++)
	{
	    if (
		(bestRule != -1) &&
		(fcomp(rulesList[i] -> priority, bestPrio) == -1)  
		// current < best
		)
		break;
	    therule = rulesList[i] -> rule;
	    Attribute *a = rulesList[i] -> match;
	    if (a) 
		pattern = a -> expr;
	    else 
		continue;

	    thisMode = rulesList[i] -> mode;
	    if ((!thisMode) ^ (!currMode))
		continue;
	    if (thisMode && !((*thisMode) == (*currMode)))
		continue;
	    // else both thisMode and currMode are NULL which is OK
	    if (pattern)
	    {
		Bool result;
		E( pattern -> matchesPattern(S, c, result) );
		if (result)
		{
		    bestRule = i;
		    bestPrio = rulesList[i] -> priority;
		}
	    };
	};
	if (bestRule != -1) 
	    ret = rulesList[bestRule] -> rule; // else remains NULL
    }
    
    // if no match was found, look at the imported stylesheets
    if (!ret)
    {
	int importCount = importChildren.number();
	for (i = 0; i < importCount && !ret; i++)
	{
	    // call findBestRule recursively, setting importsOnly to FALSE
	    E( importChildren[i] -> findBestRule(
		S, ret, c, currMode, FALSE));
	}
    }
    return OK;
};

XSLElement* StylesheetStructure::findRuleByName(Tree &t, QName &q)
{
    XSLElement* ret = rulesList.findByName(t,q);
    int importCount = importChildren.number(),
	i;
    for (i = 0; !ret && i < importCount; i++)
	ret = importChildren[i] -> rulesList.findByName(t,q);
    return ret;
}

//
//
//  SubtreeList
//
//


//
//
//  VarDirectory
//
//

VarDirectory::VarDirectory()
{
}

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

XSLElement* VarDirectory::find(QName& name)
{
    int i = findNdx(name);
    return (i == -1)? NULL : (*this)[i] -> getElement();
}

eFlag VarDirectory::insert(Sit S, QName &name, XSLElement* var)
{
    int i = findNdx(name);
    if (i == -1)
	append(new VarDirectoryItem(name, var));
    else
    {
	// if precedences are equal, raise error
	int 
	    oldPrec = (*this)[i] -> getElement() -> getImportPrecedence(),
	    newPrec = var -> getImportPrecedence();
	// due to immediate resolution of xsl:import, new must be <= old
	assert(newPrec <= oldPrec);
	(*this)[i] -> setElement(var);
	if (newPrec == oldPrec)
	{
	    Str fullName;
	    var -> getOwner().expandQStr(name, fullName);
	    Err1(S, E1_MULT_ASSIGNMENT, fullName);
	}
    }
    return OK;
}


int VarDirectory::findNdx(const QName& name)
{
    int i;
    for (i = 0; i < number(); i++)
    {
	if ( (*this)[i] -> getName() == name )
	    return i;
    }
    return -1;
}

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

  Attribute Sets

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

//
//
//  AttSetMember implementation
//
//

void AttSetMember::set(XSLElement *newAttDef)
{
    int oldPrec = attDef ? 
	attDef -> getImportPrecedence() : -1;
    int newPrec = newAttDef -> getImportPrecedence();
    if (oldPrec == newPrec && !redefinition)
	redefinition = newAttDef;
    if (newPrec <= oldPrec || oldPrec == -1)
	attDef = newAttDef;
    if (newPrec < oldPrec)
	redefinition = NULL;
}

//
//
//  AttSet
//
//

AttSet::AttSet(QName &name_)
: name(name_)
{
};

AttSet::~AttSet()
{
    freeall(FALSE);
    //don't free this list items, these are copies of 
    //pointers stored with xsl:attribute-set element
    //usedSets.freeall(FALSE); 
    usedSets.deppendall(); 
}

eFlag AttSet::checkRedefinitions(Sit S)
{
    for (int i = 0; i < number(); i++)
	if ((*this)[i] -> getRedefinition())
	{
	    XSLElement *redef = (*this)[i] -> getRedefinition(); 
	    Str fullNameAtt, fullNameThis;
	    redef -> getOwner().expandQStr((*this)[i] -> getAttName(), fullNameAtt);
	    redef -> getOwner().expandQStr(getName(), fullNameThis);
	    S.setCurrVDoc(redef);
	    Warn2(S, W2_ATTSET_REDEF, fullNameAtt, fullNameThis);
	}
    return OK;
}

eFlag AttSet::execute(Sit S, Context *c, Tree& sheet, QNameList& history, Bool resolvingGlobals)
{
  if (history.findNdx(name) != -1)
    {
      Str fullName;
      sheet.expandQStr(name, fullName);
      Err1(S, E1_CIRCULAR_ASET_REF, fullName);
    }
  history.append(&name);
  int i;
  for (i = 0; i < usedSets.number(); i++)
    E( sheet.attSets().executeAttSet(S, *(usedSets[i]), c, 
				     sheet, history, resolvingGlobals) );
  history.deppend();

  for (i = 0; i < number(); i++)
    {
      XSLElement *def = (*this)[i] -> getAttributeDef();
      E( def -> execute(S, c, resolvingGlobals) );
    }
  return OK;
}

int AttSet::findNdx(QName &attName)
{
    for (int i = 0; i < number(); i++)
	if ((*this)[i] -> getAttName() == attName)
	    return i;
    return -1;
}

void AttSet::insertAttributeDef(XSLElement *attDef, QName &attName)
{
    int ndx = findNdx(name);
    if (ndx == -1)
    {
	append(new AttSetMember(attName));
	ndx = number() - 1;
    }
    (*this)[ndx] -> set(attDef);
}

void AttSet::insertUses(QName &usedSet)
{ 
    if (usedSets.findNdx(usedSet) == -1) 
	usedSets.append(&usedSet); 
}


//
//
//  AttSetList
//
//

AttSetList::AttSetList()
:
PList<AttSet*>(LIST_SIZE_LARGE)
{};

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

eFlag AttSetList::checkRedefinitions(Sit S)
{
    for (int i = 0; i < number(); i++)
	E( (*this)[i] -> checkRedefinitions(S) );
    return OK;
}

AttSet *AttSetList::insert(QName &name)
{
    int ndx = findNdx(name);
    AttSet *ptr = NULL;
    if (ndx == -1)
	append(ptr = new AttSet(name));
    else
	ptr = (*this)[ndx];
    return ptr;
}

eFlag AttSetList::executeAttSet(Sit S, QName &name, Context *c, Tree &sheet, QNameList& history, Bool resolvingGlobals)
{
    int ndx = findNdx(name);
    if (ndx == -1)
    {
	Str fullName;
	sheet.expandQStr(name, fullName);
	Err1(S, E1_NONEX_ASET_NAME, fullName);
    }
    E( (*this)[ndx] -> execute(S, c, sheet, history, resolvingGlobals) );
    return OK;
}

int AttSetList::findNdx(const QName &what) const
{
    int count = number();
    for (int i = 0; i < count; i++)
        if ((*this)[i] -> getName() == what)
	    return i;
    return -1;
}

//
//
//  AliasItem implementation
//
//

void AliasItem::set(Phrase newKey, Phrase newValue, int newPrecedence, XSLElement *source)
{
    assert(newPrecedence >= 0);
    if (key == UNDEF_PHRASE) key = newKey;
    if (precedence == newPrecedence && value != newValue && !redefinition)
	redefinition = source;
    if (newPrecedence <= precedence || precedence == -1)
    {
	value = newValue;
	precedence = newPrecedence;
    }
    if (newPrecedence < precedence)
	redefinition = NULL;
}

//
//
//  AliasList implementation
//
//

int AliasList::findNdx(Phrase key_) const
{
    int i, count = number();
    for (i = 0; (i < count) && !(key_ == ((*this)[i] -> getKey())); i++);
    return (i < count) ? i : -1;
}

Phrase AliasList::find(Phrase key_) const
{
    int ndx = findNdx(key_);
    return (ndx != -1) ? (*this)[ndx] -> getValue() : PHRASE_NOT_FOUND;
}

void AliasList::insertAlias(Phrase key, Phrase value, int precedence, XSLElement *source)
{
    AliasItem *newItem;
    int ndx = findNdx(key);
    if (ndx == -1)
	append(newItem = new AliasItem);
    else
	newItem = (*this)[ndx];
    newItem -> set(key, value, precedence, source);
}

eFlag AliasList::checkRedefinitions(Sit S, Tree &sheet)
{
    for (int i = 0; i < number(); i++)
	if ((*this)[i] -> getRedefinition())
	{
	    S.setCurrVDoc((*this)[i] -> getRedefinition());
	    Str fullName;
	    sheet.expand((*this)[i] -> getKey());
	    Warn1(S, W1_ALIAS_REDEF, fullName);
	}
    return OK;
}

/****************************************
T r e e   methods
****************************************/

Tree::Tree(const Str &aname, BOOL aXSLTree)
    : theArena(TREE_ARENA_SIZE), theDictionary(&theArena, TREE_DICT_LOGSIZE),
      structure(0)
{
    // theDictionary.initialize();
    QName &rootname = (QName&) getTheEmptyQName();
    root = new(&theArena) RootNode(*this, rootname);
    XSLTree = aXSLTree;
    stackTop = &getRoot();
    pendingTextNode = NULL;
    getRoot().stamp = 0;
    vcount = 1;
    stripped = 0;
    QName dummyName;
    theDummyElement = new(&theArena) Element(*this, dummyName);
    initDict();

    // append entry for 'the whole tree' to subtrees
    subtrees.push(new SubtreeInfo(
	aname, XSL_NONE, &structure));
};

Tree::~Tree()
{
    getRoot().~RootNode();
    // this is just in case there's a processing error:
    pendingNS.freeall(FALSE);
    preservedElements.freeall(FALSE);
    strippedElements.freeall(FALSE);
    delete theDummyElement;
    // clear subtree information
    subtrees.freeall(FALSE);
    aliasesList.freeall(FALSE);
}

void Tree::initDict()
{
    theDictionary.initialize();
    theDictionary.insert("", stdPhrases[PHRASE_EMPTY]);
    theDictionary.insert("xsl", stdPhrases[PHRASE_XSL]);
    theDictionary.insert(theXSLTNamespace, stdPhrases[PHRASE_XSL_NAMESPACE]);
    theDictionary.insert(theXMLNamespace, stdPhrases[PHRASE_XML_NAMESPACE]);
    theDictionary.insert("*", stdPhrases[PHRASE_STAR]);
    theDictionary.insert("xmlns", stdPhrases[PHRASE_XMLNS]);
    theDictionary.insert("lang", stdPhrases[PHRASE_LANG]);
}

Element &Tree::dummyElement() const
{
    return *theDummyElement;
}

void Tree::flushPendingText()
{
    if (pendingTextNode)
        pendingTextNode -> cont = pendingText;
    pendingText.empty();
    pendingTextNode = NULL;
}
    
eFlag Tree::appendVertex(Sit S, Vertex *v)
{
    assert(stackTop && isDaddy(stackTop));
    assert(!isText(v) || !pendingTextNode);
    if (!isText(v))
        flushPendingText();
    E( cast(Daddy*,stackTop) -> newChild(S, v) ); //sets parent too
    if (isDaddy(v))
        stackTop = v;
    v -> stamp = vcount++;
    // set the subtree information for vertex
    v -> setSubtreeInfo(subtrees.getCurrent());
    return OK;
};

void Tree::dropCurrentElement(Vertex *v)
{
    assert(stackTop && isElement(stackTop));
    assert(stackTop == v);
    assert(!pendingTextNode);
    stackTop = v -> parent;
    delete v;
    toE(stackTop) -> contents.deppend();
}

Vertex *Tree::appendText(Sit S, char *string, int len)
{
    Vertex *txt = NULL;
    if (!pendingTextNode)
    {
        // the initializing text does not matter
        txt = new(&getArena()) Text(*this, string, len);
        appendVertex(S, txt);
        pendingTextNode = toText(txt);
    }
    pendingText.nadd(string,len);
    return txt;
}

Vertex *Tree::popVertex()
{
    Vertex *v = NZ( stackTop );
    stackTop = v -> parent;
    return v;
}

eFlag Tree::parseFinished(Sit S)
{
    flushPendingText();
    return OK;
}

HashTable& Tree::dict() 
{
    return theDictionary;
}

Arena& Tree::getArena()
{
    return theArena;
}

const QName& Tree::getTheEmptyQName() const
{
    return theEmptyQName;
}


Bool Tree::cmpQNames(const QName &first, const QName &second) const
{
/*
    printf("comparing names (%s,%s,%s) and (%s,%s,%s)\n",
        (char*)(((Tree*)this)->expand(first.getPrefix())),
	    (char*)(((Tree*)this)->expand(first.getUri())),
	    (char*)(((Tree*)this)->expand(first.getLocal())),
        (char*)(((Tree*)this)->expand(second.getPrefix())),
	    (char*)(((Tree*)this)->expand(second.getUri())),
	    (char*)(((Tree*)this)->expand(second.getLocal()))
	);
*/	
    if (first.getLocal() == stdPhrase(PHRASE_STAR))
        return (Bool)(first.getPrefix() == UNDEF_PHRASE || 
            first.getUri() == second.getUri());
    else
        return (Bool) (first.getUri() == second.getUri()
	        && first.getLocal() == second.getLocal());
}

Bool Tree::cmpQNamesForeign(const QName &q, const HashTable& dictForeign, const QName &qForeign)
{
/*
    printf("comparing names (%s,%s,%s) and (%s,%s,%s)\n",
        (char*)(((Tree*)this)->expand(q.getPrefix())),
	    (char*)(((Tree*)this)->expand(q.getUri())),
	    (char*)(((Tree*)this)->expand(q.getLocal())),
        (char*)(dictForeign.getKey(qForeign.getPrefix())),
	    (char*)(dictForeign.getKey(qForeign.getUri())),
	    (char*)(dictForeign.getKey(qForeign.getLocal()))
	);
*/	

    if (q.getLocal() == stdPhrase(PHRASE_STAR))
    {
        return (Bool)(q.getPrefix() == UNDEF_PHRASE || 
            (dict().getKey(q.getUri()) == dictForeign.getKey(qForeign.getUri())));
	}
    else
    {
        return (Bool) 
	        (dict().getKey(q.getUri()) == dictForeign.getKey(qForeign.getUri()) &&
            dict().getKey(q.getLocal()) == dictForeign.getKey(qForeign.getLocal()));		
	}
}

Bool Tree::cmpQNameStrings(const QName &q, const Str& uri, const Str& local)
{
    if (q.getLocal() == stdPhrase(PHRASE_STAR))
        return (Bool)(
	    q.getUri() == UNDEF_PHRASE || dict().getKey(q.getUri()) == uri);
    else
    {
        return (Bool) 
  	        (dict().getKey(q.getUri()) == uri &&
            dict().getKey(q.getLocal()) == local);		
	}
}

void Tree::expandQ(const QName& q, EQName& expanded)
{
    expanded.setLocal(expand(q.getLocal()));
    expanded.setUri(expand(q.getUri()));
    expanded.setPrefix(expand(q.getPrefix()));
}

void Tree::expandQStr(const QName& q, Str& expName)
{
    EQName expanded;
    expandQ(q, expanded);
    expanded.getname(expName);
}

const Str& Tree::expand(Phrase ph)
{
    return dict().getKey(ph);
}

Phrase Tree::unexpand(const Str& strg)
{
    Phrase result;
    dict().insert(strg, result);
    return result;
}

eFlag Tree::serialize(Sit S, char *& result)
{
    OutputterObj out;
    OutputDefinition def;
    GP( DataLine ) targetLine = new DataLine;
    // set default options for output
    EQName xmlMethod;
    xmlMethod.setLocal((char*)"xml");
    E( def.setItemEQName(S, XSLA_METHOD, xmlMethod, NULL, OUTPUT_PRECEDENCE_WEAKEST) );
    E( def.setDefaults(S) ); 
    E( (*targetLine).open(S, (const char*)"arg:/dummy_", DLMODE_WRITE, NULL) );
    out.setOptions(S, targetLine, &def);
    E( getRoot().serialize(S, out) );
    result = (*targetLine).getOutBuffer() -> compactToBuffer();
    E( (*targetLine).close(S) );
    targetLine.del();
    return OK;
}

eFlag Tree::serializeNode(Sit S, Element *v, char *& result)
{
    OutputterObj out;
    OutputDefinition def;
    GP( DataLine ) targetLine = new DataLine;
    // set default options for output
    EQName xmlMethod;
    xmlMethod.setLocal((char*)"xml");
    E( def.setItemEQName(S, XSLA_METHOD, xmlMethod, NULL, OUTPUT_PRECEDENCE_WEAKEST) );
    E( def.setDefaults(S) ); 
    E( (*targetLine).open(S, (const char*)"arg:/dummy_", DLMODE_WRITE, NULL) );
    out.setOptions(S, targetLine, &def);
    E( (*v).serializeSubtree(S, out) );
    result = (*targetLine).getOutBuffer() -> compactToBuffer();
    E( (*targetLine).close(S) );
    targetLine.del();
    return OK;
}

void Tree::makeStamps()
{
    int stamp_ = 0;
    getRoot().makeStamps(stamp_);
    vcount = stamp_;
}

Bool getWhDelimString(char *&list, Str& firstPart)
{
    skipWhite(list);
    if (!*list) return FALSE;
    char *list_was = list;
    for(; *list && !isWhite(*list); list++);
    firstPart.nset(list_was, (int)(list - list_was));
    return TRUE;
}

/*****************************************************************
Tree::processVertexAfterParse()
Performs any necessary actions on a vertex immediately after it is
parsed into the tree. The vertex is still 'current'. Typically, this
function calls popVertex.
ARGS:
    v   the vertex to be processed
The function operates only on XSL vertices, namely:
xsl:include - replaces the vertex by the newly constructed tree
xsl:output  - files the information into the tree's structures
...
Other vertices are just popped off the stack.
*****************************************************************/

eFlag Tree::processVertexAfterParse(Sit S, Vertex *v, TreeConstructer* tc)
{
  //be careful with this test, it might be moved deeper inside this 
  //function if needed

  if (v -> vt & VT_TOP_FOREIGN) 
    {
      popVertex();
      return OK;
    }

  XSL_OP theOp;
  if (isXSLElement(v))
    {
      XSLElement *x = toX(v);
      switch(theOp = x -> op)
        {
	  //catch xsl:use-attribute-sets
	case XSL_ELEMENT:
	case XSL_COPY: 
	  {
	    E( extractUsedSets(S, toE(v)) );
	    popVertex();
	  }; break;
	case XSL_IMPORT:
	  {
	    // first check for ill-positioned xsl:import,
	    // then continue as for include
	    if (v -> parent)
	      {
		Vertex *w;
		int pos = v -> ordinal - 1;
		for (; pos >= 0; pos--)
		  {
		    if (isElement(w = toD(v -> parent) -> contents[pos]) &&
			(!isXSL(w) || toX(w) -> op != XSL_IMPORT))
		      Err2(S, E_ELEM_CONTAINS_ELEM, 
			   xslOpNames[XSL_STYLESHEET], 
			   xslOpNames[XSL_IMPORT]);
		  }
	      }
	  }; // no break
	case XSL_INCLUDE:
	  {
	    Attribute *a = NZ( x -> atts.find(XSLA_HREF) );
	    GP( Tree ) srcTree; 
	    const Str& base = S.findBaseURI(a -> getSubtreeInfo() ->
					    getBaseURI());		
	    
	    Str absolute;
	    makeAbsoluteURI(a -> cont, base, absolute);
	    if (S.getProcessor())
	      {
		E( S.getProcessor() -> readTreeFromURI(S, srcTree, 
						       a -> cont, base, 
						       FALSE) );
		srcTree.keep();
	      }
	    else
	      {
		//Str absolute;
		//makeAbsoluteURI(a -> cont, base, absolute);
		srcTree = new Tree(absolute, FALSE);
		DataLine d;
		E( d.open(S, absolute, DLMODE_READ, /* argList = */ NULL) );
		E( (*srcTree).parse(S, &d) );
		E( d.close(S) );
	      }
	    
	    Element *theSheet=(*srcTree).findStylesheet((*srcTree).getRoot());
	    if (!theSheet)
	      Warn1(S, W_NO_STYLESHEET, (char*)(a -> cont));
	    dropCurrentElement(v);
	    
	    OutputterObj source;
	    // we start a subtree to record where the nodes come from
	    // when including, we use the old structure
	    // when importing, Tree creates a new one
	    E( startSubtree(S, (*srcTree).getURI(), theOp) );
	    E( tc -> parseUsingSAXForAWhile(S, source, absolute) );
	    if (theSheet)
	      E( theSheet -> contents.copy(S, source) );
	    E( tc -> parseUsingSAXForAWhileDone(S, source) );
	    // end the subtree
	    E( endSubtree(S) );		        
	  }; break;
	case XSL_OUTPUT:
	  {
	    int i, attsNumber = x -> atts.number();
	    Attribute *theAtt;
	    for (i = 0; i < attsNumber; i++)
	      {
		theAtt = toA(x -> atts[i]);
		switch(theAtt -> op)
		  {
		  case XSLA_METHOD:
		    {
		      QName q;
		      EQName eq;
		      E( x -> setLogical(S, 
					 q, theAtt -> cont, FALSE) );
		      expandQ(q, eq);
		      E( outputDef.setItemEQName(S, XSLA_METHOD, 
						 eq, v, 
						 v -> getImportPrecedence()) );
		    }; break;
		  case XSLA_CDATA_SECT_ELEMS:
		    {
		      QName q;
		      Bool someRemains;
		      Str listPart;
		      char *p = theAtt -> cont;
		      do
			{
			  someRemains = getWhDelimString(p, listPart);
			  if (someRemains)
			    {
			      E( x -> setLogical(S, 
						 q, listPart, TRUE) );
			      EQName expanded;
			      expandQ(q, expanded);
			      E( outputDef.setItemEQName(S, 
							 XSLA_CDATA_SECT_ELEMS,
							 expanded, v, 
							 v -> getImportPrecedence()) );
			    };
			}
		      while (someRemains);
		    }; break;
		  default:
		    {
		      E( outputDef.setItemStr(S, theAtt -> op, theAtt -> cont, 
					      theAtt, 
					      theAtt -> getImportPrecedence()) );
		      
		    };
		  };
	      }
	    popVertex();
	  }; break;
	case XSL_NAMESPACE_ALIAS:
	  {
	    Phrase style,result;
	    Attribute *sp = NZ( x -> atts.find(XSLA_STYLESHEET_PREFIX) );
	    Attribute *rp = NZ( x -> atts.find(XSLA_RESULT_PREFIX) );
	    if (sp -> cont == "#default") style = UNDEF_PHRASE;
	    else dict().insert(sp -> cont, style);
	    if (rp -> cont == "#default") result = UNDEF_PHRASE;
	    else dict().insert(rp -> cont, result);
	    aliases().insertAlias(style, result, v -> getImportPrecedence(), x);
	    popVertex();
	  }; break;
	case XSL_TEMPLATE:
	  {
	    E( insertRule(S, x) );
	    popVertex();
	  }; break;
	case XSL_ATTRIBUTE_SET:
	  {
	    QName name;
	    
	    E( x -> setLogical(S, name,
			       NZ( x -> atts.find(XSLA_NAME)) -> cont, 
			       FALSE) );
	    AttSet *ptr = attSets().insert(name);
	    E( extractUsedSets(S, toE(v)) );
	    if (x -> attSetNames(FALSE))
	      {
		for (int i = 0; i < x -> attSetNames(FALSE) -> number(); i++)
		 ptr -> insertUses(*(x -> attSetNames(FALSE) -> operator[] (i)));
	      }
	    XSLElement *son;
	    for (int i = 0; i < x -> contents.number(); i++)
	      {
		assert(isXSLElement(x -> contents[i]) && 
		       toX(x -> contents[i]) -> op == XSL_ATTRIBUTE);
		son = toX(x -> contents[i]);
		E( son -> setLogical(S, name, 
				     NZ( son -> atts.find(XSLA_NAME)) -> cont,
				     FALSE) );
		ptr -> insertAttributeDef(son, name);
	      }
	    popVertex();
	  }; break;
	case XSL_STYLESHEET:
	case XSL_TRANSFORM:
	  {
	    E( excludeNamespaces(S, toE(v), TRUE) );
	    /* E( extensionNamespaces(S, toE(v)) ); */
	    popVertex();
	  }; break;
	case XSL_VARIABLE:
	case XSL_PARAM:
	  {
	    // only look at top-levels
	    Vertex *par = v -> parent;
	    if (par && isXSLElement(par) && 
		(toX(par) -> op == XSL_STYLESHEET || 
		 toX(par) -> op == XSL_TRANSFORM))
	      {
		// is top-level -> insert into directory, 
		//with error if there already is an entry 
		// with the same import precedence
		// find name first
		QName name;
		E( x -> setLogical(S, name, 
				   NZ( x -> atts.find(XSLA_NAME)) -> cont, 
				   FALSE) );
		E( toplevelVars.insert(S, name, x) );
	      }
	    popVertex();
	  }; break;
	default:
	  popVertex();
	}
	//the literal output element may have some xsl features
    }
    else {
      if (XSLTree) {
	Bool isRoot = (getRoot().contents.number()) > 0 && 
	  (v == getRoot().contents[0]);
	E( excludeNamespaces(S, toE(v), isRoot) );
	
	E( extractUsedSets(S, toE(v)) );
	popVertex();
      }
      else {
	popVertex();
      }
    }
    return OK;
}

eFlag Tree::parsePrefixes(Sit S, UriList &ul, NSList &namespaces, Str &tokens)
{
  char *p, *q;

  //ul.deppendall();
  q = (char*)tokens;
  skipWhite(q);
  p = q;
  int i = strcspn(q, theWhitespace);
  while (*q && i) {
    q += i;
    char save = *q;
    *q = 0;

    Str prefix = p;
    int idx = namespaces.findNdx(prefix == "#default" ? UNDEF_PHRASE : 
				 unexpand(prefix));
    if (idx == -1)
      Err1(S, E_EX_NAMESPACE_UNKNOWN, p);

    ul.append(new Str(expand(toNS(namespaces[idx]) -> uri)));
    *q = save;
    skipWhite(q);
    p = q;
    i = strcspn(q, theWhitespace);
  }
  return OK;
}

eFlag Tree::excludeNamespaces(Sit S, Element *e, Bool isRoot) {
  //exclude result prefixes
  Attribute *a = e -> atts.find(XSLA_EXCL_RES_PREFIXES);
  if (a) E( parsePrefixes(S, *(e->excludedNS(TRUE)), e -> namespaces, 
			a -> cont) );
  //exclude extension element prefixes
  a = e -> atts.find(XSLA_EXT_ELEM_PREFIXES);
  if (a) E( parsePrefixes(S, *(e->excludedNS(TRUE)), e -> namespaces, 
			a -> cont) );
  if (isRoot) {
    //add xml prefix
    Str xml = theXMLNamespace;
    Str xsl = theXSLTNamespace;
    Bool forceXSL = e -> excludedNS(TRUE) -> findNdx(xsl) != -1;
    
    e -> excludedNS(TRUE) -> addUri(xml);
    e -> excludedNS(TRUE) -> addUri(xsl);
    
    //if the target alias is bound to xslt nemespace,
    //we remove theXSLTNamespace from the exclusion list to
    //eliminate specification leak 
    //(xslt namespace sould never be output)
    for (int i = 0; i < aliases().number(); i++) {
      Phrase res = (aliases()[i]) -> getValue();
      NmSpace *nm;
      if ((nm = e -> namespaces.find(res)) && 
	  (expand(nm -> uri) == theXSLTNamespace) && !forceXSL)
	{
	  e -> excludedNS(TRUE) -> removeUri(xsl);
	}
    }
  }
  return OK;
}

eFlag Tree::extensionNamespaces(Sit S, Element *e) {
  //exclude extension element prefixes
  Attribute *a = e -> atts.find(XSLA_EXT_ELEM_PREFIXES);
  if (a) {
    E( parsePrefixes(S, *(e->extensionNS(TRUE)), e -> namespaces, 
		     a -> cont) );
  }
  return OK;
}

eFlag Tree::extractUsedSets(Sit S, Element *e) 
{
    Attribute *a = e -> atts.find(XSLA_USE_ATTR_SETS); 
    if (a) 
    {
	QNameList *names = e -> attSetNames(TRUE);
	names -> freeall(FALSE);
	char *p, *q;

	q = (char*) (a -> cont);
	skipWhite(q);
	p = q;
	int i = strcspn(q, theWhitespace);
	while (*q && i) {
	    q += i;
	    char save = *q;
	    *q = 0;

	    Str token = p;
	    GP( QName ) name = new QName;
	    E( e -> setLogical(S, *name, token, FALSE) );

	    names -> append( name.keep() );
	    *q = save;
	    skipWhite(q);
	    p = q;
	    i = strcspn(q, theWhitespace);
	}

    }
    return OK;
}

/*****************************************************************
findStylesheet()
finds a xsl:stylesheet child of the given daddy. Returns NULL 
if not found.
*****************************************************************/

Element *Tree::findStylesheet(Daddy& d)
{
    Vertex *w;
    int dContentsNumber = d.contents.number();
    for (int i = 0; i < dContentsNumber; i++)
    {
        if (isElement(w = d.contents[i]))
        {
            const QName& wName = toE(w) -> name;
	        Tree& owner = w -> getOwner();
		    Str localStr;
//            if (!strcmp(wName.getUri(), theXSLTNamespace) && /* _PH_ */
            if (wName.getUri() == owner.stdPhrase(PHRASE_XSL_NAMESPACE) &&
               ((localStr = owner.expand(wName.getLocal()) == 
	               xslOpNames[XSL_STYLESHEET]) ||
               (localStr == xslOpNames[XSL_TRANSFORM])))
            return toE(w);
        }
    };
    return NULL;
}

double Tree::defaultPriorityLP(Expression *lpath)
{
    assert(lpath && lpath -> functor == EXF_LOCPATH);
    assert(lpath -> args.number());
    if ((lpath -> args.number() > 1) || lpath -> args[0] -> step -> preds.number())
        return .5;
    else
    {
        switch (lpath -> args[0] -> step -> ntype)
        {
        case EXNODE_COMMENT:
        case EXNODE_TEXT:
        case EXNODE_PI:  //// pi('literal') should have priority 0.5
        case EXNODE_NODE:
            return -0.5;
            break;
        case EXNODE_NONE:
            {
                QName &qn = lpath -> args[0] -> step -> ntest;
                if (qn.getLocal() != lpath -> getOwnerTree().stdPhrase(PHRASE_STAR))
                    return 0.0;
                else
                {   
                    if (qn.getPrefix() == UNDEF_PHRASE)
                        return -0.5;
                    else
                        return -0.25;
                };
            }; break;
        default:
            return 0.5;
        };
    };
    return 0;   // BCC thinks we don't return enough
}

double Tree::defaultPriority(XSLElement *tmpl)
{
    Expression *e = tmpl -> getAttExpr(XSLA_MATCH);
    if (!e) 
        return PRIORITY_NOMATCH;
    switch(e -> functor)
    {
    case EXF_LOCPATH:
        {
            return defaultPriorityLP(e);
        }; break;
    case EXFO_UNION:
        {
            double max=0, priority;
            BOOL first = TRUE;
            int eArgsNumber = e -> args.number();
            for (int i=0; i < eArgsNumber; i++)
            {
                priority = defaultPriorityLP(e -> args[i]);
                if (first || (priority > max)) 
                    max = priority;
                first = FALSE;
            };
            return max;
        }; break;
    default:
        {
            assert(!"expression not a union or LP");
            return 0;   // dummy
        }
    };
    return 0;       // dummy for BCC
}

eFlag Tree::parse(Sit S, DataLine *d)
{
    Log1(S, L1_PARSING, getURI());
    double time_was = getMillisecs();
    TreeConstructer tc(S);
    eFlag retval = tc.parseDataLineUsingExpat(S, this, d);
    if (!retval)
    {
        Log1(S, L1_PARSE_DONE, getMillisecsDiff(time_was));
    }
    return retval;
}

eFlag Tree::getMatchingList(Sit S, Expression& match, Context& result)
{
    E( getRoot().getMatchingList(S, match, result) );
    return OK;
}

Bool Tree::isExtensionUri(Phrase uri) 
{
  Str search = expand(uri);
  for (Vertex *v = stackTop; v; v = v -> parent) 
    {
      UriList *ulist;
      if (isElement(v) && (ulist = toE(v) -> extensionNS(FALSE)) &&
	  (ulist -> findNdx(search) != -1)) {
	return TRUE;
      }
    }
  return FALSE;
}

eFlag Tree::insertRule(Sit S, XSLElement *tmpl)
{
    double prio;
    Attribute *a = tmpl -> atts.find(XSLA_PRIORITY);
    if (!a)
        prio = defaultPriority(tmpl);
    else
    {
        if (a -> cont.toDouble(prio))
            Err(S, ET_BAD_NUMBER);
    };
    QName q; 
    GP( QName ) mode = NULL;
    if (!!(a = tmpl -> atts.find(XSLA_NAME)))
        E( tmpl -> setLogical(S, q, a -> cont, FALSE) );
    if (q.getLocal() != UNDEF_PHRASE && 
        subtrees.getCurrent() -> getStructure() -> rules().findByName(
	    *this, q))
    {
	Str fullName;
	expandQStr(q, fullName);

	Err1(S, ET_DUPLICATE_RULE_NAME, fullName);
    };
    if (!!(a = tmpl -> atts.find(XSLA_MODE)))
        E( tmpl -> setLogical(S, *(mode = new QName), 
			      a -> cont, FALSE) );
    
    subtrees.getCurrent() -> getStructure() -> rules().insert(
	new RuleItem(tmpl,prio,q,mode.keep()));
    return OK;
}

eFlag Tree::insertAttSet(Sit S, XSLElement *tmpl)
{
    QName q; 
    Attribute *a;
    GP( QName ) sets = NULL;
    if (!!(a = tmpl -> atts.find(XSLA_NAME))) 
	E( tmpl -> setLogical(S, q, a -> cont, FALSE) );
    if (q.getLocal() != UNDEF_PHRASE && 
        attSets().findByName(q))
    {
	Str fullName;
	expandQStr(q, fullName);
	Err1(S, ET_DUPLICATE_ASET_NAME, fullName);
    };    
    attSets().append(new AttSet(q));
    return OK;
}



eFlag Tree::startSubtree(Sit S, const Str& baseURI, XSL_OP dependency)
{
    // look if the URI is on the way to root of the include tree
    if (subtrees.findAmongPredecessors(baseURI))
	Err1(S, E1_CIRCULAR_INCLUSION, baseURI);
    StylesheetStructure *structure;
    if (dependency == XSL_IMPORT)
	structure = createStylesheetStructure(S);
    else
	// find structure of current subtree (always defined since Tree::Tree)
	structure = NZ(subtrees.getCurrent()) -> getStructure();
    subtrees.push(
	new SubtreeInfo(
	    baseURI, 
	    dependency,
	    structure));
    return OK;
}

eFlag Tree::endSubtree(Sit S)
{
    // move current subtree
    subtrees.pop();
    return OK;
}

StylesheetStructure *Tree::createStylesheetStructure(Sit S)
{
    // current subtree is created in Tree::Tree() so must be NZ
    SubtreeInfo *currSubtree = NZ( subtrees.getCurrent() );
    // there is always a structure for an existing subtree
    StylesheetStructure *currStruct  = NZ( currSubtree -> getStructure() );
    // create new structure with precedence one higher
    StylesheetStructure *newStruct = new StylesheetStructure(
	currStruct -> getImportPrecedence() + 1);
    // file newStruct into existing tree if stylesheet structures
    currStruct -> addImportStructure(S, newStruct);
    return newStruct;    
}

eFlag Tree::findBestRule(
    Sit S, 
    XSLElement *&ret, 
    Context *c,
    QName *currMode,
    Bool importsOnly)
{
    return NZ(subtrees[0]) -> getStructure() -> findBestRule(
	S, ret, c, currMode, importsOnly);
}

XSLElement *Tree::findRuleByName(QName &q)
{
    return NZ( subtrees[0] ) -> getStructure() -> findRuleByName(*this, q);
}

eFlag Tree::resolveGlobals(Sit S, Context *c, Processor *proc)
{
    assert(proc);
    QName temp;
    for (int i = 0; i < toplevelVars.number(); i++)
    {
	// resolve the global identified by pointer
	// the name does not matter
	temp.empty();
	E( proc -> resolveGlobal(S, c, temp, toplevelVars[i] -> getElement()) );
    }
    return OK;
}


    
//_TH_ v
/****************************************
T m p L i s t   methods
****************************************/

TmpList::TmpList():PList<Vertex *>()
{
#ifdef _TH_DEBUG
  puts("TmpList constructor");
#endif
  //printf("TmpList constructor >%x<\n",this);
}

TmpList::~TmpList()
{
#ifdef _TH_DEBUG
  puts("TmpList destructor");
#endif
  //  dump();
  freeall(FALSE);
}
void TmpList::append(void *what)
{
  //     printf("TmpList append\n");
  ((Vertex *)what)->ordinal=number();
  PList<Vertex *>::append((Vertex *)what);
}

void TmpList::rm(int n)
{
  //     printf("TmpList rm >%d<\n",n);
  PList<Vertex *>::rm(n);
  for(int i=n;i<number();i++)
    (*this)[i]->ordinal=i;
}

void TmpList::freeall(Bool a)
{
#ifdef _TH_DEBUG
  printf("TmpList freeall(Bool)\n");
#endif
  PList<Vertex *>::freeall(a);
}

void TmpList::dump(int p)
{ 
  Str vname;
  if(p==0) printf("TmpList.dump (%d item(s))\n", nItems);
  else printf("TmpList.dump %d (%d item(s))\n", p, nItems);
  for(int i=0; i<nItems; i++){
    ((*this)[i])->getOwner().expandQStr(toE((*this)[i]) -> getName(),vname);
    printf("%d:>%d< >%x< (%s)\n",i,((*this)[i])->ordinal,((*this)[i]),(char*)vname);
  }
};

int TmpList::findNum(void *p) const
{
  int i;
  for (i = number()-1; (i >= 0) && !((Vertex *)p == (*this)[i]); i--) {};
  return i;
}

void TmpList::rmP(void *p)
{
  rm(((Vertex *)p)->ordinal);
}
//_TH_ ^
