/* 
 * 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.
 */

/* sdom.cpp */

#define SablotAsExport
#include "sdom.h"
#include "base.h"
#include "verts.h"
#include "tree.h"
#include "context.h"
#include "guard.h"

#define getTmpList(v) (toV(v) -> getOwner().tmpList)

char *SDOM_ExceptionMsg[] = 
{
    // SDOM_OK
    (char *) "OK",
    
    // SDOM_INDEX_SIZE_ERR 1
    (char *) "list index out of bounds",
    
    //placeholder
    (char *) "",

    // SDOM_HIERARCHY 3
    (char *) "hierarchy request error",
    
    // SDOM_WRONG_DOCUMENT_ERR 4
    (char *) "node used in a document that did not create it",
    
    //placeholder
    (char *) "", (char *) "",

    // SDOM_NO_MODIFICATION_ALLOWED_ERR 7
    (char *) "modification not allowed",
    
    // SDOM_NOT_FOUND_ERR 8
    (char *) "reference to a non-existent node",
        
    // SDOM_INVALID_NODE_TYPE
    (char *) "invalid node type",
    
    // SDOM_QUERY_PARSE_ERR
    (char *) "query parsing error",
    
    // SDOM_QUERY_EXECUTION_ERR
    (char *) "query execution error",
    
    // SDOM_NOT_OK
    (char *) "general exception"
};

#define SDOM_Err(SITUA, CODE) { SIT( SITUA ).setSDOMExceptionCode( CODE );\
    SIT( SITUA ).message(\
    MT_ERROR, E2_SDOM, CODE, SDOM_ExceptionMsg[CODE]); return CODE; }
    
#define SE(statement) {SDOM_Exception code__ = statement; \
    if (code__) return code__;}
    
#define SIT(PTR) (*(Situation*)PTR)

#define isXMLNS_(qn, resolver) \
    (qn.getPrefix() == resolver -> getOwner().stdPhrase(PHRASE_XMLNS) || \
        (qn.getPrefix() == UNDEF_PHRASE && \
	     qn.getLocal() == resolver -> getOwner().stdPhrase(PHRASE_XMLNS)))

//
//    globals
//

SDOM_NodeCallback *theDisposeCallback = NULL;
#define ownerDoc(NODE) ( &(toV(NODE)->getOwner().getRoot()) )

char* SDOM_newString(const Str& strg)
{
    int len = strg.length();
    char *p = new char[len + 1];
    strcpy(p, (char*)(const char*)strg);
    p[len] = 0;
    return p;
} 

SDOM_Exception SDOM_createElement(SablotSituation s, SDOM_Document d, SDOM_Node *pn, const SDOM_char *tagName)
{
    QName q;
    //try to find the document element
    Element *e = NULL;
    for (int i = 0; toRoot(d) -> contents.number(); i++)
      {
	if (isElement(toRoot(d) -> contents[i]))
	  {
	    e = toE(toRoot(d) -> contents[i]);
	    break;
	  }
      }
    if (!e) e = toRoot(d);

    e -> setLogical(SIT(s), q, tagName, TRUE);
    *pn = new(&(toTree(d) -> getArena())) Element(*toTree(d), q);
    //_TH_ v
    getTmpList(d).append(*pn);
    //_TH_ ^
    return SDOM_OK;
}

SDOM_Exception SDOM_createAttribute(SablotSituation s, SDOM_Document d, SDOM_Node *pn, const SDOM_char *name)
{
    QName q;

    toRoot(d) -> setLogical(SIT(s), q, name, TRUE);
    if (!isXMLNS_(q, toRoot(d)))
        *pn = new(&(toTree(d) -> getArena())) 
	    Attribute(*toTree(d), q, (char*)"", XSLA_NONE);
    else
        *pn = new(&(toTree(d) -> getArena())) 
	    NmSpace(*toTree(d), 
		    q.getPrefix() == UNDEF_PHRASE ? UNDEF_PHRASE : q.getLocal(),
		    UNDEF_PHRASE);
    //_TH_ v
    getTmpList(d).append(*pn);
    //_TH_ ^
    return SDOM_OK;
}

SDOM_Exception SDOM_createTextNode(SablotSituation s, SDOM_Document d, SDOM_Node *pn, const SDOM_char *data)
{
    *pn = new(&(toTree(d) -> getArena())) Text(*toTree(d), (char*) data);
    //_TH_ v
    getTmpList(d).append(*pn);
    //_TH_ ^
    return SDOM_OK;
}

SDOM_Exception SDOM_createCDATASection(SablotSituation s, SDOM_Document d, SDOM_Node *pn, const SDOM_char *data)
{
    SE( SDOM_createTextNode(s, d, pn, data) );
    toText(toV(*pn)) -> beCDATA();
    return SDOM_OK;
}

SDOM_Exception SDOM_createComment(
    SablotSituation s, 
    SDOM_Document d, 
    SDOM_Node *pn, 
    const SDOM_char *data)
{
    *pn = new(&(toTree(d) -> getArena())) Comment(*toTree(d), (char*) data);
    //_TH_ v
    getTmpList(d).append(*pn);
    //_TH_ ^
    return SDOM_OK;
}

SDOM_Exception SDOM_createProcessingInstruction(
    SablotSituation s, 
    SDOM_Document d, 
    SDOM_Node *pn, 
    const SDOM_char *target,
    const SDOM_char *data)
{
    *pn = new(&(toTree(d) -> getArena())) ProcInstr(
        *toTree(d), 
        toTree(d) -> unexpand((char*) target), 
	(char*) data);
    //_TH_ v
    getTmpList(d).append(*pn);
    //_TH_ ^
    return SDOM_OK;
}

SDOM_Exception SDOM_disposeNode(SablotSituation s, SDOM_Node n)
{
    Vertex *v = toV(n);
    switch(v -> vt & VT_BASE)
    {
        case VT_ELEMENT:
	        ccdelete(v, toE(v)); break;
	    case VT_ATTRIBUTE:
	        ccdelete(v, toA(v)); break;
	    case VT_NAMESPACE:
	        ccdelete(v, toNS(v)); break;
	    case VT_PI:
	        ccdelete(v, toPI(v)); break;
	    case VT_TEXT: // and CDATA
	        ccdelete(v, toText(v)); break;            	        
	    case VT_XSL:
	        ccdelete(v, toX(v)); break;
	    case VT_COMMENT:
	        ccdelete(v, toComment(v)); break;
		default:
		    assert(!"disposeSDOM_Node");	    
    }
    return SDOM_OK;
}

SDOM_Exception SDOM_getNodeType(SablotSituation s, SDOM_Node n, SDOM_NodeType *pType)
{
    Vertex *v = toV(n);
    switch(basetype(v))
    {
        case VT_ROOT:
	        *pType = SDOM_DOCUMENT_NODE; break;
        case VT_ELEMENT:
	        *pType = SDOM_ELEMENT_NODE; break;
	    case VT_ATTRIBUTE:
	        *pType = SDOM_ATTRIBUTE_NODE; break;
	    case VT_TEXT:
	    {
	        if (toText(v) -> isCDATA())
		        *pType = SDOM_CDATA_SECTION_NODE;
			else
	            *pType = SDOM_TEXT_NODE; 
		}; break;
	    case VT_COMMENT:
	        *pType = SDOM_COMMENT_NODE; break;
		case VT_PI:
		    *pType = SDOM_PROCESSING_INSTRUCTION_NODE; break;
		case VT_NAMESPACE:
		    *pType = SDOM_ATTRIBUTE_NODE;
		default:
		    *pType = SDOM_OTHER_NODE;
	}
	return SDOM_OK;
}

SDOM_Exception SDOM_getNodeName(SablotSituation s, SDOM_Node n, SDOM_char **pName)
{
    Str fullName;
    Vertex *v = toV(n);
    switch(v -> vt & VT_BASE)
    {
    case VT_ELEMENT:
    {
	v -> getOwner().expandQStr(toE(v) -> getName(), fullName);
	*pName = SDOM_newString(fullName);
    };
    break;
    case VT_ATTRIBUTE:
    {
	v -> getOwner().expandQStr(toA(v) -> getName(), fullName);
	*pName = SDOM_newString(fullName);
    };
    break;
    case VT_NAMESPACE:
    {
	v -> getOwner().expandQStr(toNS(v) -> getName(), fullName);
	*pName = SDOM_newString(fullName);
    };
    break;
    case VT_TEXT: // and CDATA
    {
	if (toText(v) -> isCDATA())
	    *pName = SDOM_newString("#cdata-section");
	else
	    *pName = SDOM_newString("#text"); 
    }; break;		
    case VT_COMMENT:
	*pName = SDOM_newString("#comment"); break;
    case VT_ROOT:
	*pName = SDOM_newString("#document"); break;		    
    case VT_PI:
	*pName = SDOM_newString("#processing_instruction"); break;		    
    default:
	*pName = NULL;
    }
    return SDOM_OK;
}

SDOM_Exception SDOM_getNodeNSUri(SablotSituation s, SDOM_Node n, SDOM_char **pName)
{
    Str value;
    Vertex *v = toV(n);
    switch(v -> vt & VT_BASE)
    {
    case VT_ELEMENT:
    {
	value = v -> getOwner().expand(toE(v) -> getName().getUri());
	*pName = SDOM_newString(value);
    };
    break;
    case VT_ATTRIBUTE:
    {
	value = v -> getOwner().expand(toA(v) -> getName().getUri());
	*pName = SDOM_newString(value);
    };
    break;
    default:
	*pName = NULL;
    }
    return SDOM_OK;
}

SDOM_Exception SDOM_getNodePrefix(SablotSituation s, SDOM_Node n, SDOM_char **pName)
{
    Str value;
    Vertex *v = toV(n);
    switch(v -> vt & VT_BASE)
    {
    case VT_ELEMENT:
    {
	value = v -> getOwner().expand(toE(v) -> getName().getPrefix());
	*pName = SDOM_newString(value);
    };
    break;
    case VT_ATTRIBUTE:
    {
	value = v -> getOwner().expand(toA(v) -> getName().getPrefix());
	*pName = SDOM_newString(value);
    };
    break;
    default:
	*pName = NULL;
    }
    return SDOM_OK;
}

SDOM_Exception SDOM_getNodeLocalName(SablotSituation s, SDOM_Node n, SDOM_char **pName)
{
    Str value;
    Vertex *v = toV(n);
    switch(v -> vt & VT_BASE)
    {
    case VT_ELEMENT:
    {
	value = v -> getOwner().expand(toE(v) -> getName().getLocal());
	*pName = SDOM_newString(value);
    };
    break;
    case VT_ATTRIBUTE:
    {
	value = v -> getOwner().expand(toA(v) -> getName().getLocal());
	*pName = SDOM_newString(value);
    };
    break;
    default:
	*pName = NULL;
    }
    return SDOM_OK;
}

SDOM_Exception SDOM_setNodeName(SablotSituation s, SDOM_Node n, const SDOM_char *name)
{
    Vertex *v = toV(n);
    QName q;
    if (isRoot(v))
        SDOM_Err(s, SDOM_NO_MODIFICATION_ALLOWED_ERR);

    switch (v -> vt & VT_BASE) 
    {
    case VT_ELEMENT:
	toE(v) -> setLogical(SIT(s), q, name, TRUE);
	break;
    default:
    {
	if (v -> parent)
	    toE(v -> parent) -> setLogical(SIT(s), q, name, TRUE);
	else
	    v -> getOwner().getRoot().setLogical(SIT(s), q, name, TRUE);
    }
    }

    switch(v -> vt & VT_BASE)
    {
    case VT_ELEMENT:
	toE(v) -> name = q;
	break;
    case VT_ATTRIBUTE:
	toA(v) -> name = q;
	break;
    case VT_NAMESPACE:
    {
	toNS(v) -> name = q;
	toNS(v) -> prefix = q.getLocal();
    }; break;
    case VT_PI:
	toPI(v) -> name = q;
	break;
    default:
	SDOM_Err(s, SDOM_NO_MODIFICATION_ALLOWED_ERR);
    }
    return SDOM_OK;
}

SDOM_Exception SDOM_getNodeValue(SablotSituation s, SDOM_Node n, SDOM_char **pValue)
{
    Vertex *v = toV(n);
    switch(v -> vt & VT_BASE)
    {
	    case VT_ATTRIBUTE:
	        *pValue = SDOM_newString(toA(v) -> cont);
		    break;
	    case VT_NAMESPACE:
	        *pValue = SDOM_newString(v -> getOwner().expand(toNS(v) -> uri));
		    break;
	    case VT_TEXT: // and CDATA section
	        *pValue = SDOM_newString(toText(v) -> cont);
		    break;
	    case VT_COMMENT:
	        *pValue = SDOM_newString(toComment(v) -> cont);
		    break;
		case VT_PI:
	        *pValue = SDOM_newString(toPI(v) -> cont);
		    break;
		default:
		    // element and document (root) have void value
		    *pValue = NULL;
	}
	return SDOM_OK;
}

SDOM_Exception SDOM_setNodeValue(SablotSituation s, SDOM_Node n, const SDOM_char *value)
{
    Vertex *v = toV(n);
    switch(v -> vt & VT_BASE)
    {
	    case VT_ATTRIBUTE:
	        toA(v) -> cont = value;
		    break;
	    case VT_NAMESPACE:
	        toNS(v) -> uri = v -> getOwner().unexpand(value);
		    break;
	    case VT_TEXT: // and CDATA section
	        toText(v) -> cont = value;
		    break;
	    case VT_COMMENT:
	        toComment(v) -> cont = value;
		    break;
		case VT_PI:
	        toPI(v) -> cont = value;
		    break;
		default:
		    // element and document (root) have void value
		    SDOM_Err(s, SDOM_NO_MODIFICATION_ALLOWED_ERR);
	}
	return SDOM_OK;
}

SDOM_Exception SDOM_getParentNode(SablotSituation s, SDOM_Node n, SDOM_Node *pParent)
{
    Vertex *v = toV(n);
    if (isRoot(v) || isAttr(v))
        *pParent = NULL;
	else
	    *pParent = v -> parent;
	return SDOM_OK;
}

SDOM_Exception SDOM_getFirstChild(SablotSituation s, SDOM_Node n, SDOM_Node *pFirstChild)
{
    Vertex *v = toV(n);
    if (!isElement(v) && !isRoot(v))
        *pFirstChild = NULL;
	else
	{
	    int childCount = toE(v) -> contents.number();
	    if (childCount)
	        *pFirstChild = (SDOM_Node)(toE(v) -> contents[0]);
		else
		    *pFirstChild = NULL;
	}
	return SDOM_OK;
}

SDOM_Exception SDOM_getLastChild(SablotSituation s, SDOM_Node n, SDOM_Node *pLastChild)
{
    Vertex *v = toV(n);
    if (!isElement(v) && !isRoot(v))
        *pLastChild = NULL;
	else
	{
	    int childCount = toE(v) -> contents.number();
	    if (childCount)
	        *pLastChild = (SDOM_Node)(toE(v) -> contents.last());
		else
		    *pLastChild = NULL;
	}
	return SDOM_OK;
}

SDOM_Exception SDOM_getPreviousSibling(SablotSituation s, SDOM_Node n, SDOM_Node *pPreviousSibling)
{
    switch(toV(n) -> vt & VT_BASE)
    {
        case VT_ATTRIBUTE:
	    case VT_NAMESPACE:
        case VT_ROOT:	            
	        *pPreviousSibling = NULL;
		    break;
        default:
            *pPreviousSibling = toV(n) -> getPreviousSibling();
	}
    return SDOM_OK;
}

SDOM_Exception SDOM_getNextSibling(SablotSituation s, SDOM_Node n, SDOM_Node *pNextSibling)
{
    switch(toV(n) -> vt & VT_BASE)
    {
        case VT_ATTRIBUTE:
    	case VT_NAMESPACE:    
        case VT_ROOT:	            
	        *pNextSibling = NULL;
		    break;
        default:
            *pNextSibling = toV(n) -> getNextSibling();
	}
    return SDOM_OK;
}

SDOM_Exception SDOM_getOwnerDocument(SablotSituation s, SDOM_Node n, SDOM_Document *pOwnerDocument)
{
    if (isRoot(toV(n)))
        *pOwnerDocument = NULL;
	else
        *pOwnerDocument = ownerDoc(n);
    return SDOM_OK;
}

Bool hasElementChild(RootNode *r)
{
    for (int i = 0; i < r -> contents.number(); i++)
        if (isElement(r -> contents[i]))
	        return TRUE;
	return FALSE;
}

// is first ancestor of second?
Bool isAncestor(Vertex *first, Vertex *second)
{
    for (Vertex *p = second; p; p = p -> parent)
        if (p == first) return TRUE;
	return FALSE;
}

SDOM_Exception SDOM_insertBefore(SablotSituation s, SDOM_Node n, SDOM_Node newChild, SDOM_Node refChild)
{
    Vertex *v = toV(n);
    
    // check if v is an element (or root)
    if (!isElement(v))
        SDOM_Err(s, SDOM_HIERARCHY);
	
	// check the type of newChild
	if (!newChild)
	    SDOM_Err(s, SDOM_NOT_FOUND_ERR)
	else
	    switch(basetype(newChild))
    	{
    	    case VT_ATTRIBUTE:
    	    case VT_NAMESPACE:
    	    case VT_ROOT:
    	        SDOM_Err(s, SDOM_HIERARCHY); 	    
    	}
	    
	// check if newChild is from the same doc
	if ((isRoot(v) && ownerDoc(newChild) != v) ||
	    (!isRoot(v) && ownerDoc(v) != ownerDoc(newChild)))
	    SDOM_Err(s, SDOM_WRONG_DOCUMENT_ERR);
	
	// check type of the reference child
	if (refChild)
	    switch(toV(refChild) -> vt & VT_BASE)
    	{
    	    case VT_ATTRIBUTE:
    	    case VT_NAMESPACE:
    	    case VT_ROOT:
    	        SDOM_Err(s, SDOM_HIERARCHY); 	    
    	}
	
	// check if newChild is not an ancestor of n
	if (isAncestor(toV(newChild), toV(n)))
	    SDOM_Err(s, SDOM_HIERARCHY);
	    
	// check if not attempting to have more doc elements
	if (isRoot(v) && isElement(newChild) && hasElementChild(toRoot(v)))
	    SDOM_Err(s, SDOM_HIERARCHY);
	    
	// see if newChild needs to be removed from tree
	Vertex *parent;
	if (NULL != (parent = toV(newChild) -> parent))
	    SE( SDOM_removeChild(s, parent, newChild) );
	    
	// v ??? 
	int ndx = toE(v) -> contents.getIndex(toV(newChild));
	if (ndx != -1)
	    toE(v) -> contents.rm(ndx);
	// ^ ???
	//_TH_ v
	getTmpList(n).rmP(newChild);
	//_TH_ ^
	if (refChild)
	{
    	ndx = toE(v) -> contents.getIndex(toV(refChild));
	    if (ndx == -1)
	        SDOM_Err(s, SDOM_NOT_FOUND_ERR);
	    toE(v) -> contents.insertBefore(toV(newChild), ndx);
	}
	else
	    toE(v) -> contents.append(toV(newChild));
	    // toE(v) -> contents.appendAndSetOrdinal(toV(newChild));
	toV(newChild) -> setParent(v);
	return SDOM_OK;
}

SDOM_Exception SDOM_removeChild(SablotSituation s, SDOM_Node n, SDOM_Node oldChild)
{
    Vertex *v = toV(n);
    if (!isElement(v))
        SDOM_Err(s, SDOM_INVALID_NODE_TYPE);
	switch(toV(oldChild) -> vt & VT_BASE)
    	{
    	    case VT_ATTRIBUTE:
    	    case VT_NAMESPACE:
    	    case VT_ROOT:
    	        SDOM_Err(s, SDOM_INVALID_NODE_TYPE); 	    
    	}
	if (toV(oldChild) -> parent != toV(n))
	    SDOM_Err(s, SDOM_NOT_FOUND_ERR);
	toE(v) -> removeChild(toV(oldChild));
	//_TH_ v
	getTmpList(n).append(oldChild);
	//_TH_ ^
    return SDOM_OK;
}

SDOM_Exception SDOM_replaceChild(SablotSituation s, SDOM_Node n, SDOM_Node newChild, SDOM_Node oldChild)
{
    SE( SDOM_insertBefore(s, n, newChild, oldChild) );
    SE( SDOM_removeChild(s, n, oldChild) );
    return SDOM_OK;
}

SDOM_Exception SDOM_appendChild(SablotSituation s, SDOM_Node n, SDOM_Node newChild)
{
    return SDOM_insertBefore(s, n, newChild, NULL);
}

SDOM_Exception cloneVertex(SablotSituation, Tree *, Vertex *, int, Vertex **);

SDOM_Exception cloneVertexList(SablotSituation s, Tree *t, VertexList *vlForeign, int deep, Element *tParent)
{
    Vertex *newVertex;
    for (int i = 0; i < vlForeign -> number(); i++)
    {
        SE( cloneVertex(s, t, (*vlForeign)[i], deep, &newVertex) );
	    // the following also handles atts and namespaces correctly
	    tParent -> newChild(SIT(s), newVertex);    	    
    }   
    return SDOM_OK;
}

SDOM_Exception cloneVertex(SablotSituation s, Tree *t, Vertex *foreign, int deep, Vertex **clone)
{
    Tree *tForeign = &(foreign -> getOwner());
    QName q;
    EQName expanded;

	if (basetype(foreign) == VT_ROOT) 
        SDOM_Err(s, SDOM_INVALID_NODE_TYPE);

	// get the correctly unexpanded name	
	if (basetype(foreign) == VT_ELEMENT || 
	    basetype(foreign) == VT_ATTRIBUTE ||
	    basetype(foreign) == VT_PI ||
	    basetype(foreign) == VT_NAMESPACE)
	{	
        switch(basetype(foreign))
        {
            case VT_ELEMENT:
    		    tForeign -> expandQ(toE(foreign) -> getName(), expanded);
    		    break;
    	    case VT_ATTRIBUTE:
    		    tForeign -> expandQ(toA(foreign) -> getName(), expanded);
    		    break;
    	    case VT_NAMESPACE:
    		    tForeign -> expandQ(toNS(foreign) -> getName(), expanded);
    		    break;
    		case VT_PI:
    		    tForeign -> expandQ(toPI(foreign) -> getName(), expanded);
    		    break;
    	};
	    q.setLocal(t -> unexpand(expanded.getLocal()));
	    q.setPrefix(t -> unexpand(expanded.getPrefix()));
	    q.setUri(t -> unexpand(expanded.getUri()));
    }

    // create the actual copy
    switch(basetype(foreign))
    {
        case VT_ELEMENT:
            *clone = new(&(t -> getArena())) 
	            Element(*t, q);
	        break;
	    case VT_ATTRIBUTE:
            *clone = new(&(t -> getArena())) 
	            Attribute(*t, q, toA(foreign) -> cont, XSLA_NONE);
		    break;
		case VT_NAMESPACE:
            *clone = new(&(t -> getArena())) NmSpace(*t, 
	            t -> unexpand(tForeign -> expand(toNS(foreign) -> prefix)),
		        t -> unexpand(tForeign -> expand(toNS(foreign) -> uri)));
		    break;
		case VT_PI:
            *clone = new(&(t -> getArena())) ProcInstr(*t, q.getLocal(), toPI(foreign) -> cont);
		    break;
	    case VT_COMMENT:
            *clone = new(&(t -> getArena())) Comment(*t, toComment(foreign) -> cont);
	            break;
	    case VT_TEXT:
	        {
	            *clone = new(&(t -> getArena())) Text(*t, toText(foreign) -> cont);	        		
    	        if (toText(foreign) -> isCDATA())		
                    toText(*clone) -> beCDATA();
			}; break;
    }
    
    if (isElement(foreign))
    {
        // must clone atts and namespaces in any case
	    assert(isElement(*clone));
        cloneVertexList(s, t, &(toE(foreign) -> atts), deep, toE(*clone));
        cloneVertexList(s, t, &(toE(foreign) -> namespaces), deep, toE(*clone));
	    
	    // if deep then recurse
	    if (deep)
            cloneVertexList(s, t, &(toE(foreign) -> contents), deep, toE(*clone));        
	}
    return SDOM_OK;
}

SDOM_Exception SDOM_cloneForeignNode(SablotSituation s, SDOM_Document d, SDOM_Node n, int deep, SDOM_Node *clone)
{
  SDOM_Exception se=cloneVertex(s, toTree(d), toV(n), deep, (Vertex**) clone);
    //_TH_ v
  getTmpList(d).append(*clone);
    //_TH_ ^
  return se;
}

SDOM_Exception SDOM_cloneNode(SablotSituation s, SDOM_Node n, int deep, SDOM_Node *clone)
{
    return SDOM_cloneForeignNode(s, ownerDoc(n), n, deep, clone);
}

SDOM_Exception SDOM_getAttribute(SablotSituation s, SDOM_Node n, const SDOM_char *name, SDOM_char **pValue)
{
    QName q;
    if (!isElement(toV(n)))
        SDOM_Err(s, SDOM_INVALID_NODE_TYPE);
    Element *e = toE(toV(n));

    e -> setLogical(SIT(s), q, name, FALSE);
    Vertex *v;
    Bool isXMLNS = isXMLNS_(q, e);
    if (!isXMLNS)
        v = e -> atts.find(q);
	else
	    v = e -> namespaces.find(
	        q.getLocal() != UNDEF_PHRASE ? q.getLocal() : UNDEF_PHRASE);
    if (!v)
        *pValue = SDOM_newString("");
	else
	{
	    if (!isXMLNS)
            *pValue = SDOM_newString(toA(v) -> cont);
    	else
            *pValue = SDOM_newString(e -> getOwner().expand(toNS(v) -> uri));
	}
    return SDOM_OK;	    
}

SDOM_Exception SDOM_getAttributeNode(SablotSituation s, SDOM_Node n, const SDOM_char *name, SDOM_Node *attr)
{
    QName q;
    if (!isElement(toV(n)))
        SDOM_Err(s, SDOM_INVALID_NODE_TYPE);
    Element *e = toE(toV(n));

    e -> setLogical(SIT(s), q, name, FALSE);
    Vertex *v;
    Bool isXMLNS = isXMLNS_(q, e);
    if (!isXMLNS)
        v = e -> atts.find(q);
	else
	    v = e -> namespaces.find(
	        q.getLocal() != UNDEF_PHRASE ? q.getLocal() : UNDEF_PHRASE);
    if (!v)
        *attr = 0;
    else
      {
	*attr = (SDOM_Node)v;
      }
    return SDOM_OK;	    
}

SDOM_Exception SDOM_setAttribute(SablotSituation s, SDOM_Node n, const SDOM_char*attName, const SDOM_char *attValue)
{
    QName q;
    if (!isElement(toV(n)))
        SDOM_Err(s, SDOM_INVALID_NODE_TYPE);
    Element *e = toE(toV(n));

    e -> setLogical(SIT(s), q, attName, FALSE);
    Vertex *v;
    Bool isXMLNS = isXMLNS_(q, e);
    if (!isXMLNS)
        v = e -> atts.find(q);
	else
	    v = e -> namespaces.find(
	        q.getLocal() != UNDEF_PHRASE ? q.getLocal() : UNDEF_PHRASE);
    if (!v)
    {
   	    SE( SDOM_createAttribute(s, ownerDoc(e), (SDOM_Node*) &v, attName));
	//_TH_ v
        getTmpList(n).rmP(v);
	//_TH_ ^
        if (!isXMLNS)
 	        e -> atts.append(toA(v));
	    else
	        e -> namespaces.append(toNS(v));
	    toV(v) -> setParent(e);
	}
	SE( SDOM_setNodeValue(s, v, attValue) );
    return SDOM_OK;	    
}


SDOM_Exception SDOM_removeAttribute(SablotSituation s, SDOM_Node n, const SDOM_char *name)
{
    QName q;
    if (!isElement(toV(n)))
        SDOM_Err(s, SDOM_INVALID_NODE_TYPE);
    Element *e = toE(toV(n));

    e -> setLogical(SIT(s), q, name, FALSE);
    if (!isXMLNS_(q, e))
    {
        int attNdx = e -> atts.findNdx(q);
        if (attNdx != -1)
        {
	    //_TH_ v
	    Vertex *tmp = e -> atts[attNdx];
	    //_TH_ ^
            e -> atts[attNdx] -> parent = NULL;
            e -> atts.rm(attNdx);
	    //_TH_ v
	    getTmpList(n).append(tmp);
	    //_TH_ ^
    	}
	}
	else
	{
        int attNdx = e -> namespaces.findNdx(
	        q.getLocal() != UNDEF_PHRASE ? q.getLocal() : UNDEF_PHRASE);
        if (attNdx != -1)
        {
            e -> namespaces[attNdx] -> parent = NULL;
            e -> namespaces.rm(attNdx);
    	}	
	}
    return SDOM_OK;	    
}


SDOM_Exception SDOM_getAttributeElement(SablotSituation s, SDOM_Node attr, SDOM_Node *owner)
{
  Vertex *v = toV(attr);
  if ( isAttr(v) )
    {
      *owner = v -> parent;
      return SDOM_OK;
    }
  else
    return SDOM_HIERARCHY;
}

SDOM_Exception SDOM_getAttributeList(SablotSituation s, SDOM_Node n, SDOM_NodeList *pAttrList)
{
    *pAttrList = new CList;
    if (isElement(toV(n)))
    {
        int i;
        NSList& namespaces = toE(toV(n)) -> namespaces;
	    for (i = 0; i < namespaces.number(); i++)
	        ((CList*)(*pAttrList)) -> append(namespaces[i]);
        AttList& atts = toE(toV(n)) -> atts;
	    for (i = 0; i < atts.number(); i++)
	        ((CList*)(*pAttrList)) -> append(atts[i]);
    }    
    return SDOM_OK;
}

//
//
//

SDOM_Exception SDOM_docToString(SablotSituation s, SDOM_Document d, SDOM_char **pSerialized)
{
    // using explicit temp because of BeOS
    char *serializedTemp = NULL;
    toTree(d) -> serialize(SIT(s), serializedTemp);
    *pSerialized = serializedTemp;
    return SDOM_OK;
}

//_PH_
SDOM_Exception SDOM_nodeToString(SablotSituation s, SDOM_Document d, 
				 SDOM_Node n,
				 SDOM_char **pSerialized)
{
    char *serializedTemp = NULL;
    toTree(d) -> serializeNode(SIT(s), toE(n), serializedTemp);
    *pSerialized = serializedTemp;
    return SDOM_OK;
}

//
//
//

SDOM_Exception SDOM_getNodeListLength(SablotSituation s, SDOM_NodeList l, int *pLength)
{
    *pLength = ((CList*)l) -> number();
    return SDOM_OK;
}

SDOM_Exception SDOM_getNodeListItem(SablotSituation s, SDOM_NodeList l, int index, SDOM_Node *pItem)
{
    if ((index < 0) || (index >= ((CList*)l) -> number()))
        SDOM_Err(s, SDOM_INDEX_SIZE_ERR);
    *pItem = ((CList*)l) -> operator[](index);
    return SDOM_OK;
}

SDOM_Exception SDOM_disposeNodeList(SablotSituation s, SDOM_NodeList l)
{
    if (!((CList*)l) -> decRefCount())
        delete (CList*)l;
	return SDOM_OK;
}


//
//
//

SDOM_Exception SDOM_xql(SablotSituation s, const SDOM_char *query, SDOM_Node currentNode, SDOM_NodeList *pResult)
{
    RootNode &root = toV(currentNode) -> getOwner().getRoot();
    
    // find the document element
    int i;
    for (i = 0; i < root.contents.number(); i++)
        if (isElement(root.contents[i])) break;
	
    Element *docElement = (i < root.contents.number()) ?
        toE(root.contents[i]) : &root;
    Expression queryEx(*docElement);
    *pResult = NULL;
    
    // parse the query string as a non-pattern
    if (queryEx.parse(SIT(s), (char*)query, FALSE, TRUE))
        SDOM_Err(s, SDOM_QUERY_PARSE_ERR);
		
	// create an initial context
    GP( Context ) dummy = new Context(NULL); //_cn_ no current node
    GP( Context ) c;
    c.assign(dummy);
    (*c).set(toV(currentNode));
	
	// create the result context
	if (queryEx.createContext(SIT(s), c))
        SDOM_Err(s, SDOM_QUERY_EXECUTION_ERR);
	
	// preserve the CList
	(*c).getArrayForDOM() -> incRefCount();
    c.unkeep();
	*pResult = (SDOM_NodeList) (*c).getArrayForDOM();
    return SDOM_OK;
}

//
//    exception retrieval
//

int SDOM_getExceptionCode(SablotSituation s)
{
    return SIT(s).getSDOMExceptionCode();
}

char* SDOM_getExceptionMessage(SablotSituation s)
{
    return SDOM_newString(
        SDOM_ExceptionMsg[SDOM_getExceptionCode(s)]);
}

void SDOM_getExceptionDetails(
    SablotSituation s,
    int *code,
    char **message,
    char **documentURI,
    int *fileLine)
{
    Str message_, documentURI_;
    MsgCode code_;
    int fileLine_;
    SIT(s).getSDOMExceptionExtra(code_, message_, documentURI_, fileLine_);
    *code = code_;
    *fileLine = fileLine_;
    *documentURI = SDOM_newString(documentURI_);
    *message = SDOM_newString(message_);
}



//
//    internal
//

void SDOM_setNodeInstanceData(SDOM_Node n, void *data)
{
    toV(n) -> setInstanceData(data);
}

void* SDOM_getNodeInstanceData(SDOM_Node n)
{
    return toV(n) -> getInstanceData();
}

void SDOM_setDisposeCallback(SDOM_NodeCallback *f)
{
    theDisposeCallback = f;
}

SDOM_NodeCallback *SDOM_getDisposeCallback()
{
    return theDisposeCallback;
}



//_TH_ v
void SDOM_tmpListDump(SDOM_Document doc, int p)
{
  getTmpList(doc).dump(p);
}
//_TH_ ^

