/* 
 * 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 "datastr.h"
#include "verts.h"
#include <ctype.h>
// #include <malloc.h> - not needed and deprecated anyway

const Str theEmptyString("");

//     implementations for List are in datastr.h (List is a template)


/*****************************************************************
DynBlock

  is a class for "dynamic memory blocks", i.e. blocks that can 
  grow in size.
*****************************************************************/

DynBlock::DynBlock()
{
    first = last = NULL;
    byteCount = 0;
}

inline void DynBlock::remove()
{
    DynBlockItem *d, *d_was;
    if (first)
    {
        for (d = first; d; )
        {
            d_was = d;
            d = d_was -> next;
            delete d_was -> data;
            delete d_was;
        }
    };
    first = last = NULL;
    byteCount = 0;
}

DynBlock::~DynBlock()
{
    remove();
}

inline Bool DynBlock::isEmpty() const
{
    return !byteCount;
}

void DynBlock::nadd(const char *data, int bytes)
{

    DynBlockItem *newitem = new DynBlockItem;
    newitem -> data = new char[bytes];
    memcpy(newitem -> data, data, bytes);
    newitem -> byteCount = bytes;
    newitem -> next = NULL;
    if (last)
        last -> next = newitem;
    else
        first = newitem;
    last = newitem;
    byteCount += bytes;
}

char *DynBlock::getPointer() const
{
    if (!first)
        return NULL;
    if (first -> next)
        compact();
    return (first -> data);
}

// compactToBuffer() joins the parts in a buffer. Returns buf+byteCount.
// if kill_ then the parts will be deallocated

int DynBlock::compactToBuffer_(char* buf, Bool kill_)
{
    int compactedBytes = 0;
    char *p = buf;
    if (first)
    {
        DynBlockItem *d, *d_was;
        for (d = first; d; )
        {
            memcpy(p, d -> data, d -> byteCount);
            p += d -> byteCount;
            compactedBytes += d -> byteCount;
            d = (d_was = d) -> next;
            if (kill_)
            {
                delete[] d_was -> data;
                delete d_was;
            }
        };
        if (kill_)
        {
            first = last = NULL;
            byteCount = 0;
        }
    }
    return compactedBytes;
}


#define thisNoConst (const_cast<DynBlock*>(this))


char *DynBlock::compactToBuffer() const
{
    char *newdata = new char[byteCount+1];
    newdata[thisNoConst -> compactToBuffer_(newdata, FALSE)] = 0;
    return newdata;
}

void DynBlock::compact() const
{
    if (!first || !(first -> next)) return;
    int byteCountWas = byteCount;
    char *newdata = new char[byteCount];
    thisNoConst -> compactToBuffer_(newdata, TRUE);
    thisNoConst -> first = thisNoConst -> last = new DynBlockItem;
    thisNoConst -> first -> data = newdata;
    thisNoConst -> byteCount = first -> byteCount = byteCountWas;
    thisNoConst -> first -> next = NULL;
}



//
//     DynStrBlock
//     a flavour of DynBlock for use in dynamic strings
//
// compactString() allocates a new buffer to hold 'firstPart'
// plus the concat of all the DynStrBlock's parts. Kills the parts
// of the DynStrBlock.
//
char* DynStrBlock::compactString_(const char *firstPart, int firstLen)
{
    int newLength = firstLen + byteCount;
    char *newdata = new char[newLength + 1];
    if (firstLen)
        memcpy(newdata, firstPart, firstLen);
    if (first)
        compactToBuffer_(newdata + firstLen, TRUE);
    newdata[newLength] = 0;
    return newdata;
}




//
//                Str
//                static string
//

/*
Str::Str()
{
    text_ = NULL;
    byteLength_ = 0;
}

Str::Str(const Str& string)
{
    text_ = NULL;
    operator= (string);
}

Str::Str(char c)
{
    text_ = NULL;
    operator= (c);
}

Str::Str(int num)
{
    text_ = NULL;
    operator= (num);
}

Str::Str(double num)
{
    text_ = NULL;
    operator= (num);
}

Str::Str(const char *chars)
{
    if (!chars) 
        chars="";
    text_ = NULL;
    operator= (chars);
}

Str::Str(const DStr &dstring)
{
    text_ = NULL;
    operator= (dstring);
}

Str& Str::operator=(const Str& string)
{

    remove_();
    int hisByteLen = string.length();
    byteLength_ = hisByteLen;
    text_ = claimMemory(hisByteLen + 1);
    memcpy(operator char*(), string.operator char*(), hisByteLen+1);
    return *this;
}

Str& Str::operator=(const DStr& dynamic)
{

    remove_();
    nset((char*) dynamic, dynamic.length());
    return *this;
}

Str& Str::operator=(const char* chars)
{
    nset(NZ(chars), strlen(chars));
    return *this;
}

Str& Str::operator= (char c)
{

    remove_();
    char *p;
    *(p = claimMemory(2)) = c;
    p[1] = 0;
    byteLength_ = 1;
    text_ = p;
    return *this;
}

Str& Str::operator= (int num)
{
    remove_();
    char buf[20];
    sprintf(buf,"%d",num);
    operator=(buf);
    return *this;
}

Str& Str::operator= (double num)
{
    remove_();
    char buf[20];
    sprintf(buf,"%g",num);
    operator=(buf);
    return *this;
}

void Str::nset(const char* chars, int len)
{

    assert(chars);
    remove_();
    byteLength_ = len;
    text_ = claimMemory(len + 1);
    memcpy(text_, chars, len);
    text_[len] = 0;
}

char Str::operator[] (int index) const
{
    assert(index >= 0 && index <= length());
    pack_();
    return text_[index];
}

Bool Str::isEmpty() const
{
    return !length(); // this is virtual
}

#ifdef SUNSOL // solaris won't expose this in the library if we inline it, more investigation is required.
int Str::length() const
#else
inline int Str::length() const
#endif
{
    return byteLength_;
}

#ifdef SUNSOL // the solaris compilier won't expose this in the library if we inline it, more investigation is required.
Str::operator char*() const
#else
inline Str::operator char*() const
#endif
{
    if (!text_)
        const_cast<Str*>(this) -> empty();
    return text_;
}

void Str::empty()
{
    byteLength_ = 0;
    remove_();
    *(text_ = claimMemory(1)) = 0;
}

int Str::compare(const char* otherChars) const
{
    return (strcmp(operator char*(), otherChars) < 0);
}

int Str::compare(const Str& other) const
{
    return compare((const char*) other);
}

Bool Str::operator== (const Str& other) const
{
    return (Bool) !strcmp(operator char*(), (char*) other);
}

Bool Str::operator== (const char* otherChars) const
{
    return (Bool) !strcmp(operator char*(), otherChars);
}

Bool Str::eqNoCase (const char* other) const
{
    return (strEqNoCase(operator char*(), other) ? TRUE : FALSE);
}

Bool Str::operator< (const char* other)
{
    return compare(other) < 0;
}

Bool Str::operator< (const Str &other)
{
  return operator<((const char*) other);
}

Bool Str::toDouble(double &d) const
{
    char *stopper;
    d = strtod(operator char*(),&stopper);
    return (!!*stopper);
}

Str::~Str()
{
    remove_();
}

DStr& Str::appendSelf(DStr& other)
{
    other.nadd(operator char*(), length());
    remove_();
    return other;
}

DStr Str::operator+ (const Str& other) const
{
    pack_();
    other.pack_();
    DStr temp(*this);
    temp += other;
    return temp;
}

DStr Str::operator+ (const char* otherChars) const
{
    pack_();
    DStr temp(*this);
    temp += otherChars;
    return temp;
}

DStr Str::operator+ (int num) const
{
    pack_();
    DStr temp(*this);
    temp += num;
    return temp;
}


// pack_() is overridden in DStr to put the pieces of the dynamic
// string together
#ifdef SUNSOL // the solaris compilier won't expose this in the library if we inline it, more investigation is required.
void Str::pack_() const
#else
inline void Str::pack_() const
#endif
{
    if (!text_)
        const_cast<Str*>(this) -> empty();
};


// a ridiculous old function (almost dead)
// FIXME: do away with it and namechar() too
//
void Str::speakTerse(DStr &ret)
{
    int i;
    char c;
    pack_();
    for (i = 0; i < length(); i++)
    {
        switch(c = (*this)[i])
        {
        case '\n': 
            ret += "&#10;"; break;
        case '\t': 
            ret += "&#9;"; break;
        default:
            ret += c;
        };
    };
}


// static function performing the same as isalnum() (so far).
// should be extended to handle UTF-8 and XML's requirements
// on valid name characters. FIXME: throw it out.
//
Bool Str::namechar(char c)
{
    return isalnum(c);
}

// another odd function (should be utf8isDigit())
// FIXME: throw it out
//
Bool Str::isdigit_(char c)
{
    return (Bool) isdigit(c);
}




//
//
//                  DStr
//                  dynamic string
//
//


DStr::DStr()
{
	// should be initialized to something non-null.
	returnMemory(text_);
	operator+=("");
};

DStr::DStr(char c)
{
    // was: text = NULL 
    // which leaks memory since Str::Str() is called
    returnMemory(text_);
    operator+=(c);
};
DStr::DStr(const char *chars)
{
    // was: text = NULL 
    // which leaks memory since Str::Str() is called
    returnMemory(text_);
    operator+=(chars);
};

DStr::DStr(const Str& string)
{
    // was: text = NULL 
    // which leaks memory since Str::Str() is called
    returnMemory(text_);
    operator+=(string);
};

DStr::DStr(const DStr& dstring)
{
    // was: text = NULL 
    // which leaks memory since Str::Str() is called
    returnMemory(text_);
    operator+=(dstring);
};

DStr& DStr::operator=(const DStr& other)
{
    remove_();
    operator+=(other);
    return *this;
}


DStr& DStr::nadd(const char *adding, int len)
{
    assert(adding);
    if (text_)
        blocks.nadd(adding, len);
    else
        nset(adding,len);
    return *this;
}


DStr& DStr::operator+= (const char *adding)
{
    if (!text_ || *adding)
        nadd(adding, strlen(adding));
    return *this;
}

DStr& DStr::operator+= (const Str& addingStr)
{
    nadd((char*) addingStr, addingStr.length());
    return *this;
}

DStr& DStr::operator+=(const DStr& other)
{
    if (!other.text_) return *this;
    nadd(other.text_, other.byteLength_);
    DynBlockItem *b;
    for (b = other.blocks.first; b; b = b -> next)
        nadd(b -> data, b -> byteCount);
    return *this;
}

DStr& DStr::operator+= (int addingnum)
{
    Str temp = addingnum;
    return operator +=(temp);
}

DStr& DStr::operator+= (char addingc)
{
    Str temp = addingc;
    return operator +=(temp);
}

DStr& DStr::appendSelf(DStr& other)
{
    other.nadd(text_, byteLength_);
    DynBlockItem *b;
    for (b = blocks.first; b; b = b -> next)
        other.nadd(b -> data, b -> byteCount);
    remove_();
    byteLength_ = 0;
    return other;
}

DStr::~DStr()
{
    remove_();
}

void DStr::remove_()
{
    returnMemory(text_);
    blocks.remove();   
}

inline void DStr::pack_() const
{
    // if not needed leave asap
    if (blocks.isEmpty()) return;
    int blocksLen = blocks.byteCount;
    // concat text_ with the contents of all blocks
    char *oldText_ = const_cast<DStr*>(this) -> text_;
    const_cast<DStr*>(this) -> text_ =
        const_cast<DynStrBlock*>(&blocks) -> 
        compactString_(text_, byteLength_);
    returnMemory(oldText_);
    const_cast<DStr*>(this) -> byteLength_ += blocksLen;
    // the length stays the same
}



#ifdef SUNSOL // the solaris compilier won't expose this in the library if we inline it, more investigation is required.
int DStr::length() const
#else
inline int DStr::length() const
#endif
{
    return byteLength_ + blocks.byteCount; 
}

#ifdef SUNSOL // the solaris compilier won't expose this in the library if we inline it, more investigation is required.
DStr::operator char* () const
#else
inline DStr::operator char* () const
#endif
{
    pack_();
    return text_;
}




//
//
//  string functions
//
//

void escapeChars(DStr& result, const Str& what, 
                 const char* toEscape, const char** substitutes)
{
    char *pStart = what, *p = pStart;
    int chunkLength;
    while (pStart)
    {
        p = strpbrk(pStart, toEscape);
        if (p)
        {
            if (chunkLength = (int)(p - pStart))
                result.nadd(pStart, chunkLength);
            result += substitutes[(int) (NZ(strchr(toEscape, *p)) - toEscape)];
            pStart = ++p;
        }
        else 
        {
            result += pStart;
            pStart = NULL;
        }
    }
}

*/


Str::Str( const char * chars )
{
	if( !chars ) {
		text_[0] = '\0';
		lCurSize = 1;
		lCurMaxSize = StrInitialSize;
		ptr_ = NULL;
	}
	else {
		int x = strlen( chars );
		lCurSize = (unsigned long)x;
		if( lCurSize < StrInitialSize ) {
			memcpy( text_, chars, lCurSize + 1 );
			ptr_ = NULL;
			lCurMaxSize = StrInitialSize;
		}
		else {
			ptr_ = (char *)malloc( lCurMaxSize );
			memcpy( ptr_, chars, lCurSize + 1 );
			lCurMaxSize = StrIncrementSize(lCurSize + 1);
		}
	}
}

Str::Str( const Str& String )
{
	if( ( lCurSize = String.lCurSize ) < StrInitialSize ) {
		memcpy( text_, String.text_, lCurSize + 1 );
		lCurMaxSize = StrInitialSize;
		ptr_ = NULL;
	}
	else {
		ptr_ = (char *)malloc( lCurMaxSize );
		memcpy( ptr_, String.ptr_, lCurSize + 1 );
		lCurMaxSize = String.lCurMaxSize;
	}
}

Str& Str::operator += ( char c )
{
	unsigned long lTotalSize = lCurSize + 1;

	if( lCurSize < StrInitialSize ) {
		// current value stored in text_
		if( lTotalSize < StrInitialSize ) {
			// still fit into text_
			text_[lCurSize] = c;
			text_[lTotalSize] = '\0';
		}
		else {
			if( lTotalSize >= lCurMaxSize ) {
				// won't fit into current ptr_ or text_
				lCurMaxSize = StrIncrementSize(lTotalSize + 1);
				ptr_ = (char *)realloc( ptr_, lCurMaxSize );
			}
			ptr_[lCurSize] = c;
			ptr_[lTotalSize] = '\0';
		}
	}
	else {
		// current value stored in ptr_
		if( lTotalSize < lCurMaxSize ) {
			// still fit into ptr_
			ptr_[lCurSize] = c;
			ptr_[lTotalSize] = '\0';
		}
		else {
			// need to allocate a new buffer
			lCurMaxSize = StrIncrementSize(lTotalSize + 1);
			ptr_ = (char *)realloc( ptr_, lCurMaxSize );
			ptr_[lCurSize] = c;
			ptr_[lTotalSize] = '\0';
		}
	}
	lCurSize++;
	return *this;
}

void Str::nset( const char * chars, int len )
{
	if( !chars || !len ) {
		text_[0] = '\0';
		lCurSize = 1;
		// Keep the ptr and lCurMaxSize unchange, need for memory cleanup.
	}
	else {
		lCurSize = (unsigned long)len;
		if( lCurSize < StrInitialSize ) {
			memcpy( text_, chars, lCurSize );
			text_[lCurSize] = '\0';
			// Keep the ptr and lCurMaxSize unchange, need for memory cleanup.
		}
		else {
			if( lCurSize >= lCurMaxSize ) {
				// Current buffer too small
				lCurMaxSize = StrIncrementSize(lCurSize + 1);
				ptr_ = (char *)realloc( ptr_, lCurMaxSize );
			}
			memcpy( ptr_, chars, lCurSize );
			ptr_[lCurSize] = '\0';
		}
	}
}

Str& Str::nadd( const char * chars, int len )
{
	unsigned long lTotalSize = lCurSize + len;

	if( lCurSize < StrInitialSize ) {
		// current value stored in text_
		if( lTotalSize < StrInitialSize ) {
			// still fit into text_
			memcpy( text_ + lCurSize, chars, len );
			text_[lTotalSize] = '\0';
		}
		else {
			if( lTotalSize >= lCurMaxSize ) {
				// won't fit into current ptr_ or text_
				lCurMaxSize = StrIncrementSize(lTotalSize + 1);
				ptr_ = (char *)realloc( ptr_, lCurMaxSize );
			}
			memcpy( ptr_, text_, lCurSize );
			memcpy( ptr_ + lCurSize, chars, len );
			ptr_[lTotalSize] = '\0';
		}
	}
	else {
		// current value stored in ptr_
		if( lTotalSize < lCurMaxSize ) {
			// still fit into ptr_
			memcpy( ptr_ + lCurSize, chars, len );
			ptr_[lTotalSize] = '\0';
		}
		else {
			// need to allocate a new buffer
			lCurMaxSize = StrIncrementSize(lTotalSize + 1);
			ptr_ = (char *)realloc( ptr_, lCurMaxSize );
			memcpy( ptr_ + lCurSize, chars, len );
			ptr_[lTotalSize] = '\0';
		}
	}
	lCurSize = lTotalSize;
	return *this;
}

void Str::speakTerse( Str& ret )
{
	char * strptr = lCurSize < StrInitialSize ? text_ : ptr_;

	for( unsigned long i = 0; i < lCurSize; i++ ) {
		switch( strptr[i] ) {
		case '\n':
			ret += "&#10;"; break;
		case '\t':
			ret += "&#9;"; break;
		default:
			ret += strptr[i];
		};
	}

}

void escapeChars( Str& result, const Str& what, const char * toEscape, const char ** substitutes )
{
	char * pStart = what, * p = pStart;
	int chunkLength = 0;

	while( pStart ) {
		p = strpbrk(pStart, toEscape);
		if( p ) {
			if( chunkLength = (int)(p - pStart) )
				result.nadd( pStart, chunkLength );
			result += substitutes[(int)( NZ(strchr( toEscape, *p ) ) - toEscape )];
			pStart = ++p;
		}
		else {
			result += pStart;
			pStart = NULL;
		}
	}
}


//
//
//                  QName
//                  holds a qname as in the XML spec
//
//

QName::QName( Processor *proc_ )
:
proc( proc_ )
{
    local = UNDEF_PHRASE;
    uri = UNDEF_PHRASE;
    prefix = UNDEF_PHRASE;
}

QName::QName(const QName &other)
{
    local = other.local;
    uri = other.uri;
    prefix = other.prefix;

    proc = other.proc;
}

Bool QName::isEmpty() const
{
    return (uri == UNDEF_PHRASE && local == UNDEF_PHRASE);
};

void QName::empty()
{
    prefix = UNDEF_PHRASE;
    local = UNDEF_PHRASE;
    uri = UNDEF_PHRASE;
}

void QName::speak(DStr &s, SpeakMode mode) const
{
    if (prefix != UNDEF_PHRASE)
    {
        s += proc -> dict().getKey(prefix);
        s += ":";
    };
    s += proc -> dict().getKey(local);
}

Bool QName::prefixIsNS(const Str &uri2) const
{
    Phrase uri2Id;
    E( proc -> dict().insert(uri2, uri2Id) );
    return (uri == uri2Id);
} 

eFlag QName::setLogical(const Str &s, const NSList *resolver, Bool defaultToo)
{
    char *p = (char *)(Str &) s,
        *q = strchr(p,':'),
        *localPart;
    if (q)
    {
        *q = 0;
        setPrefix(p);
        *q = ':';
        localPart = q + 1;
    }
    else
    {
        prefix = UNDEF_PHRASE;
        localPart = p;
    };
    E( NZ(resolver) -> resolve(uri = prefix, defaultToo) );
    if (strchr(localPart,':'))
        Err1(proc -> situation, E1_EXTRA_COLON, (char *)(Str &)s)
    else
        setLocal(localPart);
    return OK;
}

void QName::setPrefix(const Str& pref)
{
    proc -> dict().insert(pref, prefix);
}


void QName::setLocal(const Str& loc)
{
    proc -> dict().insert(loc, local);
}

void QName::setUri(const Str& u)
{
    proc -> dict().insert(u, uri);
}


void QName::findPrefix(const NSList& resolver)
{
    if (uri == UNDEF_PHRASE)
        prefix = UNDEF_PHRASE;
    else
        resolver.unresolve(prefix = uri);
}


Bool QName::operator== (const QName &other) const
{
    return local == other.local && uri == other.uri;
}

Str QName::getname() const
{
    DStr s;
    speak(s,SM_NAME);
    return s;
}

const Str& QName::getLocal() const
{
    return proc -> dict().getKey(local);
}

const Str& QName::getUri() const
{
    return proc -> dict().getKey(uri);
}

const Str& QName::getPrefix() const
{
    return proc -> dict().getKey(prefix);
}

const Bool QName::hasPrefix() const
{
   return (prefix != UNDEF_PHRASE);
}

//
//
//                  StrStrList
//
//



int StrStrList::findNum(const Str &key_) const
{
    int i, count = number();
    for (i = 0; (i < count) && !(key_ == ((*this)[i] -> key)); i++) {};
    return (i < count) ? i : -1;
}

Str* StrStrList::find(const Str &_key) const
{
    int ndx = findNum(_key);
    return (ndx != -1) ? &((*this)[ndx] -> value) : NULL;
}

void StrStrList::appendConstruct(const Str &key, const Str& value)
{
    StrStr* newItem = new StrStr;
    newItem -> key  = key;
    newItem -> value = value;
    append(newItem);
}

//
//
//                  NamespaceStack
//
//

int NamespaceStack::findNum(const Str &prefix_) const
{
    int i, count = number();
    for (i = count-1; (i >= 0) && !(prefix_ == ((*this)[i] -> prefix)); i--) {};
    return i;
}

Str* NamespaceStack::getUri(const Str &_prefix) const
{
    int ndx = findNum(_prefix);
    return (ndx != -1) ? &((*this)[ndx] -> uri) : NULL;
}

Bool NamespaceStack::isHidden(const Str &_prefix) const
{
    int ndx = findNum(_prefix);
    return (ndx != -1) ? ((*this)[ndx] -> hide) : true;
}

void NamespaceStack::appendConstruct(const Str &prefix, const Str& uri, Bool hide /* = false*/)
{
    NamespaceStackObj* newItem = new NamespaceStackObj;
    newItem -> prefix  = prefix;
    newItem -> uri = uri;
    newItem -> hide = hide;
    append(newItem);
}

//
//
//                  P2List
//
//




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

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

void P2List::appendConstruct(Phrase key, Phrase value)
{
    PhrasePhrase* newItem = new PhrasePhrase;
    newItem -> key = key;
    newItem -> value = value;
    append(newItem);
}




//
//
//                  QNameList
//
//


const QName* QNameList::find(const QName &what) const
{
    int i, count = number();
    for (i = 0; (i < count) && !(what == (*operator[](i))); i++) {};
    return (i < count) ? operator[](i) : NULL;
}



//
//
//                  QNameStrList
//
//


const Str* QNameStrList::find(const QName &what) const
{
    int i, count = number();
    for (i = 0; i < count && !(what == operator[](i) -> key); i++) {};
    return (i < count) ? &(operator[](i) -> value) : NULL;
}

void QNameStrList::appendConstruct(const QName &key, const Str& value)
{
    QNameStr* newItem = new QNameStr(proc);
    newItem -> key  = key;
    newItem -> value = value;
    append(newItem);
}

