|
|
|
/* This file is part of the KDE project
|
|
|
|
Copyright (C) 2005 Ariya Hidayat <ariya@kde.org>
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Library General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
* Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
This is a memory-efficient DOM implementation for KOffice. See the API
|
|
|
|
documentation for details.
|
|
|
|
|
|
|
|
IMPORTANT !
|
|
|
|
|
|
|
|
* When you change this stuff, make sure it DOES NOT BREAK the test suite.
|
|
|
|
Build tests/koxmlreadertest.cpp and verify it. Many sleepless nights
|
|
|
|
have been sacrificed for this piece of code, do not let those precious
|
|
|
|
hours wasted!
|
|
|
|
|
|
|
|
* Run testdom.cpp WITH Valgrind's memcheck tool and make sure NO illegal
|
|
|
|
memory read/write and any type of leak occurs. If you are not familiar
|
|
|
|
with Valgrind then RTFM first and come back again later on.
|
|
|
|
|
|
|
|
* The public API shall remain as compatible as TQDom.
|
|
|
|
|
|
|
|
* All TQDom-compatible methods should behave the same. All TQDom-compatible
|
|
|
|
functions should return the same result. In case of doubt, run
|
|
|
|
koxmlreadertest.cpp but uncomment KOXML_USE_TQDOM in koxmlreader.h
|
|
|
|
so that the tests are performed with standard TQDom.
|
|
|
|
|
|
|
|
Some differences compared to TQDom:
|
|
|
|
|
|
|
|
- DOM tree in KoXmlDocument is read-only, you can not modify it. This is
|
|
|
|
sufficient for KOffice since the tree is only accessed when loading
|
|
|
|
a document to the application. For saving the document to XML file,
|
|
|
|
use KoXmlWriter.
|
|
|
|
|
|
|
|
- Comment node (like TQDomComment) is not implemented as comments are
|
|
|
|
simply ignored.
|
|
|
|
|
|
|
|
- DTD, entity and entity reference are not handled. Thus, the associated
|
|
|
|
nodes (like TQDomDocumentType, TQDomEntity, TQDomEntityReference) are also
|
|
|
|
not implemented.
|
|
|
|
|
|
|
|
- Attribute mapping node is not implemented. But of course, functions to
|
|
|
|
query attributes of an element are available.
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "KoXmlReader.h"
|
|
|
|
|
|
|
|
#ifndef KOXML_USE_TQDOM
|
|
|
|
|
|
|
|
#include <tqxml.h>
|
|
|
|
#include <tqdom.h>
|
|
|
|
|
|
|
|
#include <tqmap.h>
|
|
|
|
#include <tqcstring.h>
|
|
|
|
|
|
|
|
// double TQString, used for hashing against namespace and qualified name pair
|
|
|
|
class DTQString
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
DTQString() { }
|
|
|
|
DTQString( const TQString& s1, const TQString& s2 ){ str1 = s1; str2 = s2; }
|
|
|
|
DTQString( const DTQString& b ) { str1 = b.str1; str2 = b.str2; }
|
|
|
|
DTQString& operator=( const DTQString& b ){ str1 = b.str1; str2 = b.str2; return *this; }
|
|
|
|
bool operator==( const DTQString& b ) const { return (str1==b.str1) && (str2==b.str2); }
|
|
|
|
bool operator!=( const DTQString& b ) const { return (str1!=b.str1) || (str2!=b.str2); }
|
|
|
|
bool operator<( const DTQString& b ) const
|
|
|
|
{ return ( str1 < b.str1 ) ? true : ( str1==b.str1 ) ? str2<b.str2 : false; }
|
|
|
|
TQString s1() const { return str1; }
|
|
|
|
TQString s2() const { return str2; }
|
|
|
|
private:
|
|
|
|
TQString str1, str2;
|
|
|
|
};
|
|
|
|
|
|
|
|
// just for completeness, this is the self-test for DTQString above
|
|
|
|
#if 0
|
|
|
|
DTQString b1;
|
|
|
|
DTQString b2;
|
|
|
|
CHECK( b1==b2, true );
|
|
|
|
CHECK( b1!=b2, false );
|
|
|
|
|
|
|
|
b1 = DTQString( "sweet","princess" );
|
|
|
|
b2 = DTQString( "sweet","princess" );
|
|
|
|
CHECK( b1==b2, true );
|
|
|
|
CHECK( b1!=b2, false );
|
|
|
|
|
|
|
|
b1 = DTQString( "sweet","princess" );
|
|
|
|
b2 = DTQString( "bad","prince" );
|
|
|
|
CHECK( b1==b2, false );
|
|
|
|
CHECK( b1!=b2, true );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
class KoXmlStream
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
KoXmlStream(){ saveData = true; data.reserve(1024); pos = 0; }
|
|
|
|
TQString stringData() const { return data; }
|
|
|
|
void setSaveData( bool s ){ saveData = s; }
|
|
|
|
int at() const { return pos; }
|
|
|
|
KoXmlStream& operator<<( const TQString& str )
|
|
|
|
{ if(saveData) data.append(str); pos+=str.length(); return *this; }
|
|
|
|
KoXmlStream& appendEscape( const TQString& str );
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool saveData;
|
|
|
|
TQString data;
|
|
|
|
int pos;
|
|
|
|
};
|
|
|
|
|
|
|
|
KoXmlStream& KoXmlStream::appendEscape( const TQString& str )
|
|
|
|
{
|
|
|
|
unsigned len = str.length();
|
|
|
|
|
|
|
|
if( saveData )
|
|
|
|
{
|
|
|
|
data.reserve( data.length() + len );
|
|
|
|
for( unsigned c=0; c<len; c++ )
|
|
|
|
if( str[c]=='<' ){ data.append( "<"); pos += 4; } else
|
|
|
|
if( str[c]=='>'){ data.append( ">"); pos+= 4; } else
|
|
|
|
if( str[c]=='"'){ data.append( """); pos += 6; } else
|
|
|
|
if( str[c]=='&'){ data.append( "&"); pos += 5; } else
|
|
|
|
{ data.append( str[c] ); pos++; }
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pos += len;
|
|
|
|
for( unsigned c=0; c<len; c++ )
|
|
|
|
if( str[c]=='<' ) pos += 3; else // "<"
|
|
|
|
if( str[c]=='>') pos+= 3; else // ">"
|
|
|
|
if( str[c]=='"') pos += 5; else // """
|
|
|
|
if( str[c]=='&') pos += 4; // "&"
|
|
|
|
}
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
class KoXmlNodeData
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
KoXmlNodeData();
|
|
|
|
virtual ~KoXmlNodeData();
|
|
|
|
|
|
|
|
// generic properties
|
|
|
|
KoXmlNode::NodeType nodeType;
|
|
|
|
TQString tagName;
|
|
|
|
TQString namespaceURI;
|
|
|
|
TQString prefix;
|
|
|
|
TQString localName;
|
|
|
|
|
|
|
|
// reference counting
|
|
|
|
unsigned long count;
|
|
|
|
void ref() { count++; }
|
|
|
|
void unref() { --count; if( !count ) delete this; }
|
|
|
|
|
|
|
|
// type information
|
|
|
|
virtual const char* typeInfo() const { return "Node"; }
|
|
|
|
TQString nodeName() const;
|
|
|
|
|
|
|
|
// for tree and linked-list
|
|
|
|
KoXmlNodeData* parent;
|
|
|
|
KoXmlNodeData* prev;
|
|
|
|
KoXmlNodeData* next;
|
|
|
|
KoXmlNodeData* first;
|
|
|
|
KoXmlNodeData* last;
|
|
|
|
|
|
|
|
TQString text();
|
|
|
|
|
|
|
|
// node manipulation
|
|
|
|
void appendChild( KoXmlNodeData* child );
|
|
|
|
virtual void clear();
|
|
|
|
KoXmlNodeData* ownerDocument();
|
|
|
|
|
|
|
|
// attributes
|
|
|
|
void setAttribute( const TQString& name, const TQString& value );
|
|
|
|
TQString attribute( const TQString& name );
|
|
|
|
bool hasAttribute( const TQString& name );
|
|
|
|
void setAttributeNS( const TQString& nsURI, const TQString& name, const TQString& value );
|
|
|
|
TQString attributeNS( const TQString& nsURI, const TQString& name );
|
|
|
|
bool hasAttributeNS( const TQString& nsURI, const TQString& name );
|
|
|
|
|
|
|
|
// for text and CDATA
|
|
|
|
TQString data() const;
|
|
|
|
void setData( const TQString& data );
|
|
|
|
|
|
|
|
// for document node
|
|
|
|
TQXmlSimpleReader* xmlReader;
|
|
|
|
TQString buffer;
|
|
|
|
bool setContent( TQXmlInputSource* source, TQXmlReader* reader,
|
|
|
|
TQString* errorMsg = 0, int* errorLine = 0, int* errorColumn = 0 );
|
|
|
|
|
|
|
|
// used when doing on-demand (re)parse
|
|
|
|
bool loaded;
|
|
|
|
unsigned startPos, endPos;
|
|
|
|
void loadChildren( int depth=1 );
|
|
|
|
void unloadChildren();
|
|
|
|
bool fastLoading;
|
|
|
|
|
|
|
|
private:
|
|
|
|
TQMap<TQString,TQString> attr;
|
|
|
|
TQMap<DTQString,TQString> attrNS;
|
|
|
|
TQString textData;
|
|
|
|
friend class KoXmlHandler;
|
|
|
|
};
|
|
|
|
|
|
|
|
class KoXmlHandler : public TQXmlDefaultHandler
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
KoXmlHandler( KoXmlNodeData*, bool processNamespace );
|
|
|
|
~KoXmlHandler();
|
|
|
|
|
|
|
|
void setMaxDepth( int d ){ maxDepth = d; }
|
|
|
|
void setInitialOffset( int ofs ){ parseOffset = ofs; }
|
|
|
|
|
|
|
|
// content handler
|
|
|
|
bool startDocument();
|
|
|
|
bool endDocument();
|
|
|
|
|
|
|
|
bool startElement( const TQString& nsURI, const TQString& localName,
|
|
|
|
const TQString& qName, const TQXmlAttributes& atts );
|
|
|
|
bool endElement( const TQString& nsURI, const TQString& localName,
|
|
|
|
const TQString& qName );
|
|
|
|
|
|
|
|
bool characters( const TQString& ch );
|
|
|
|
bool processingInstruction( const TQString& target, const TQString& data );
|
|
|
|
bool skippedEntity( const TQString& name );
|
|
|
|
|
|
|
|
// lexical handler
|
|
|
|
bool startCDATA();
|
|
|
|
bool endCDATA();
|
|
|
|
bool startEntity( const TQString & );
|
|
|
|
bool endEntity( const TQString & );
|
|
|
|
bool startDTD( const TQString& name, const TQString& publicId,
|
|
|
|
const TQString& systemId );
|
|
|
|
bool comment( const TQString& ch );
|
|
|
|
|
|
|
|
// decl handler
|
|
|
|
bool externalEntityDecl( const TQString &name, const TQString &publicId,
|
|
|
|
const TQString &systemId ) ;
|
|
|
|
|
|
|
|
// DTD handler
|
|
|
|
bool notationDecl( const TQString & name, const TQString & publicId,
|
|
|
|
const TQString & systemId );
|
|
|
|
bool unparsedEntityDecl( const TQString &name, const TQString &publicId,
|
|
|
|
const TQString &systemId, const TQString ¬ationName ) ;
|
|
|
|
|
|
|
|
// error handler
|
|
|
|
bool fatalError( const TQXmlParseException& exception );
|
|
|
|
|
|
|
|
TQString errorMsg;
|
|
|
|
int errorLine;
|
|
|
|
int errorColumn;
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool processNamespace;
|
|
|
|
KoXmlNodeData* rootNode;
|
|
|
|
KoXmlNodeData* currentNode;
|
|
|
|
TQString entityName;
|
|
|
|
bool cdata;
|
|
|
|
int parseOffset;
|
|
|
|
KoXmlStream bufferStream;
|
|
|
|
int elementDepth;
|
|
|
|
int maxDepth;
|
|
|
|
};
|
|
|
|
|
|
|
|
// ==================================================================
|
|
|
|
//
|
|
|
|
// KoXmlNodeData
|
|
|
|
//
|
|
|
|
// ==================================================================
|
|
|
|
|
|
|
|
KoXmlNodeData::KoXmlNodeData()
|
|
|
|
{
|
|
|
|
nodeType = KoXmlNode::NullNode;
|
|
|
|
|
|
|
|
tagName = TQString();
|
|
|
|
prefix = TQString();
|
|
|
|
localName = TQString();
|
|
|
|
namespaceURI = TQString();
|
|
|
|
textData = TQString();
|
|
|
|
|
|
|
|
count = 1;
|
|
|
|
parent = 0;
|
|
|
|
prev = next = 0;
|
|
|
|
first = last = 0;
|
|
|
|
|
|
|
|
xmlReader = 0;
|
|
|
|
startPos = endPos = 0;
|
|
|
|
|
|
|
|
fastLoading = false;
|
|
|
|
|
|
|
|
// assume true, it will be set to false by XML parser when this node
|
|
|
|
// apparently has children AND the children are not loaded
|
|
|
|
loaded = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlNodeData::~KoXmlNodeData()
|
|
|
|
{
|
|
|
|
clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KoXmlNodeData::clear()
|
|
|
|
{
|
|
|
|
if( first )
|
|
|
|
for( KoXmlNodeData* node = first; node ; )
|
|
|
|
{
|
|
|
|
KoXmlNodeData* next = node->next;
|
|
|
|
node->unref();
|
|
|
|
node = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
nodeType = KoXmlNode::NullNode;
|
|
|
|
tagName = TQString();
|
|
|
|
prefix = TQString();
|
|
|
|
namespaceURI = TQString();
|
|
|
|
textData = TQString();
|
|
|
|
|
|
|
|
attr.clear();
|
|
|
|
attrNS.clear();
|
|
|
|
|
|
|
|
parent = 0;
|
|
|
|
prev = next = 0;
|
|
|
|
first = last = 0;
|
|
|
|
|
|
|
|
delete xmlReader;
|
|
|
|
xmlReader = 0;
|
|
|
|
buffer = TQString();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString KoXmlNodeData::text()
|
|
|
|
{
|
|
|
|
TQString t( "" );
|
|
|
|
|
|
|
|
loadChildren();
|
|
|
|
|
|
|
|
KoXmlNodeData* node = first;
|
|
|
|
while ( node )
|
|
|
|
{
|
|
|
|
switch( node->nodeType )
|
|
|
|
{
|
|
|
|
case KoXmlNode::ElementNode:
|
|
|
|
t += node->text(); break;
|
|
|
|
case KoXmlNode::TextNode:
|
|
|
|
t += node->data(); break;
|
|
|
|
case KoXmlNode::CDATASectionNode:
|
|
|
|
t += node->data(); break;
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
node = node->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString KoXmlNodeData::nodeName() const
|
|
|
|
{
|
|
|
|
TQString n;
|
|
|
|
|
|
|
|
switch( nodeType )
|
|
|
|
{
|
|
|
|
case KoXmlNode::ElementNode:
|
|
|
|
n = tagName;
|
|
|
|
if (!prefix.isEmpty()) n.prepend(":").prepend(prefix); break;
|
|
|
|
case KoXmlNode::TextNode: return TQString("#text");
|
|
|
|
case KoXmlNode::CDATASectionNode: return TQString("#cdata-section");
|
|
|
|
case KoXmlNode::DocumentNode: return TQString("#document");
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlNodeData* KoXmlNodeData::ownerDocument()
|
|
|
|
{
|
|
|
|
KoXmlNodeData* owner = this;
|
|
|
|
|
|
|
|
while( owner->parent )
|
|
|
|
owner = owner->parent;
|
|
|
|
|
|
|
|
return (owner->nodeType==KoXmlNode::DocumentNode) ? owner : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KoXmlNodeData::appendChild( KoXmlNodeData* node )
|
|
|
|
{
|
|
|
|
node->parent = this;
|
|
|
|
if( !last )
|
|
|
|
first = last = node;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
last->next = node;
|
|
|
|
node->prev = last;
|
|
|
|
node->next = 0;
|
|
|
|
last = node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KoXmlNodeData::setAttribute( const TQString& name, const TQString& value )
|
|
|
|
{
|
|
|
|
attr[ name ] = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString KoXmlNodeData::attribute( const TQString& name )
|
|
|
|
{
|
|
|
|
return attr[ name ];
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlNodeData::hasAttribute( const TQString& name )
|
|
|
|
{
|
|
|
|
return attr.contains( name );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KoXmlNodeData::setAttributeNS( const TQString& nsURI,
|
|
|
|
const TQString& name, const TQString& value )
|
|
|
|
{
|
|
|
|
TQString prefix;
|
|
|
|
TQString localName = name;
|
|
|
|
int i = name.find( ':' );
|
|
|
|
if( i != -1 )
|
|
|
|
{
|
|
|
|
localName = name.mid( i + 1 );
|
|
|
|
prefix = name.left( i );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( prefix.isNull() ) return;
|
|
|
|
|
|
|
|
DTQString key( nsURI, localName );
|
|
|
|
attrNS[ key ] = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString KoXmlNodeData::attributeNS( const TQString& nsURI, const TQString& name )
|
|
|
|
{
|
|
|
|
DTQString key( nsURI, name );
|
|
|
|
return attrNS[ key ];
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlNodeData::hasAttributeNS( const TQString& nsURI, const TQString& name )
|
|
|
|
{
|
|
|
|
DTQString key( nsURI, name );
|
|
|
|
return attrNS.contains( key );
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString KoXmlNodeData::data() const
|
|
|
|
{
|
|
|
|
return textData;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KoXmlNodeData::setData( const TQString& d )
|
|
|
|
{
|
|
|
|
textData = d;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlNodeData::setContent( TQXmlInputSource* source,
|
|
|
|
TQXmlReader* reader, TQString* errorMsg, int* errorLine, int* errorColumn )
|
|
|
|
{
|
|
|
|
if( nodeType != KoXmlNode::DocumentNode )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
clear();
|
|
|
|
nodeType = KoXmlNode::DocumentNode;
|
|
|
|
|
|
|
|
// sanity checks
|
|
|
|
if( !source ) return false;
|
|
|
|
if( !reader ) return false;
|
|
|
|
|
|
|
|
// copy the reader for later on-demand loading
|
|
|
|
// FIXME this is a workaround because no copy is possible with TQXmlReader
|
|
|
|
char* features[] =
|
|
|
|
{
|
|
|
|
"http://xml.org/sax/features/namespaces",
|
|
|
|
"http://xml.org/sax/features/namespace-prefixes",
|
|
|
|
"http://trolltech.com/xml/features/report-whitespace-only-CharData",
|
|
|
|
"http://trolltech.com/xml/features/report-start-end-entity"
|
|
|
|
};
|
|
|
|
xmlReader = new TQXmlSimpleReader;
|
|
|
|
for( int fi=0; fi<4; fi++ )
|
|
|
|
xmlReader->setFeature( features[fi], reader->feature( features[fi] ) );
|
|
|
|
|
|
|
|
bool processNamespace =
|
|
|
|
reader->feature( "http://xml.org/sax/features/namespaces" ) &&
|
|
|
|
!reader->feature( "http://xml.org/sax/features/namespace-prefixes" );
|
|
|
|
|
|
|
|
KoXmlHandler handler( this, processNamespace );
|
|
|
|
reader->setContentHandler( &handler );
|
|
|
|
reader->setErrorHandler( &handler );
|
|
|
|
reader->setLexicalHandler( &handler );
|
|
|
|
reader->setDeclHandler( &handler );
|
|
|
|
reader->setDTDHandler( &handler );
|
|
|
|
|
|
|
|
if( !fastLoading )
|
|
|
|
handler.setMaxDepth( 4 );
|
|
|
|
|
|
|
|
if( !reader->parse( source ) )
|
|
|
|
{
|
|
|
|
// parsing error has occurred
|
|
|
|
if( errorMsg ) *errorMsg = handler.errorMsg;
|
|
|
|
if( errorLine ) *errorLine = handler.errorLine;
|
|
|
|
if( errorColumn ) *errorColumn = handler.errorColumn;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KoXmlNodeData::loadChildren( int depth )
|
|
|
|
{
|
|
|
|
// for more than 1 level, force reloading anyway
|
|
|
|
if( ( depth== 1 ) && loaded ) return;
|
|
|
|
|
|
|
|
KoXmlNodeData* doc = ownerDocument();
|
|
|
|
if( !doc ) return;
|
|
|
|
|
|
|
|
// fast loading? then this on-demand loading makes no sense
|
|
|
|
if( doc->fastLoading ) return;
|
|
|
|
|
|
|
|
unloadChildren();
|
|
|
|
|
|
|
|
bool nsProcess =
|
|
|
|
doc->xmlReader->feature( "http://xml.org/sax/features/namespaces" ) &&
|
|
|
|
!doc->xmlReader->feature( "http://xml.org/sax/features/namespace-prefixes" );
|
|
|
|
|
|
|
|
|
|
|
|
// XML snippet for the children, including this element
|
|
|
|
TQString snippet = doc->buffer.mid( startPos, endPos-startPos+1 );
|
|
|
|
|
|
|
|
// now parse all subnodes
|
|
|
|
KoXmlHandler handler( this, nsProcess );
|
|
|
|
handler.setMaxDepth( depth );
|
|
|
|
handler.setInitialOffset( startPos );
|
|
|
|
doc->xmlReader->setContentHandler( &handler );
|
|
|
|
doc->xmlReader->setErrorHandler( &handler );
|
|
|
|
doc->xmlReader->setLexicalHandler( &handler );
|
|
|
|
doc->xmlReader->setDeclHandler( &handler );
|
|
|
|
doc->xmlReader->setDTDHandler( &handler );
|
|
|
|
|
|
|
|
TQXmlInputSource source;
|
|
|
|
source.setData( snippet );
|
|
|
|
if( !doc->xmlReader->parse( source ) )
|
|
|
|
{
|
|
|
|
// parsing error has occurred, which should not happen
|
|
|
|
// nothing we can do except...
|
|
|
|
loaded = false;
|
|
|
|
tqWarning( "On-demand loading triggers parse error!" );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
loaded = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KoXmlNodeData::unloadChildren()
|
|
|
|
{
|
|
|
|
if( !loaded ) return;
|
|
|
|
|
|
|
|
if( first )
|
|
|
|
for( KoXmlNodeData* node = first; node ; )
|
|
|
|
{
|
|
|
|
KoXmlNodeData* next = node->next;
|
|
|
|
node->unloadChildren();
|
|
|
|
node->unref();
|
|
|
|
node = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
loaded = false;
|
|
|
|
first = last = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ==================================================================
|
|
|
|
//
|
|
|
|
// KoXmlHandler
|
|
|
|
//
|
|
|
|
// ==================================================================
|
|
|
|
|
|
|
|
KoXmlHandler::KoXmlHandler( KoXmlNodeData* n, bool ns ):
|
|
|
|
TQXmlDefaultHandler()
|
|
|
|
{
|
|
|
|
processNamespace = ns;
|
|
|
|
|
|
|
|
rootNode = n;
|
|
|
|
currentNode = n;
|
|
|
|
cdata = false;
|
|
|
|
entityName = TQString();
|
|
|
|
|
|
|
|
errorMsg = TQString();
|
|
|
|
errorLine = 0;
|
|
|
|
errorColumn = 0;
|
|
|
|
|
|
|
|
parseOffset = 0;
|
|
|
|
elementDepth = -1;
|
|
|
|
maxDepth = 999;
|
|
|
|
|
|
|
|
bufferStream.setSaveData( rootNode->nodeType == KoXmlNode::DocumentNode );
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlHandler::~KoXmlHandler()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlHandler::startDocument()
|
|
|
|
{
|
|
|
|
// just for sanity
|
|
|
|
currentNode = rootNode;
|
|
|
|
cdata = false;
|
|
|
|
entityName = TQString();
|
|
|
|
elementDepth = -1;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlHandler::endDocument()
|
|
|
|
{
|
|
|
|
// just for sanity
|
|
|
|
if( rootNode->nodeType == KoXmlNode::DocumentNode )
|
|
|
|
if( currentNode!=rootNode )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if( rootNode->nodeType == KoXmlNode::DocumentNode )
|
|
|
|
rootNode->buffer = bufferStream.stringData();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlHandler::startDTD( const TQString& name, const TQString& publicId,
|
|
|
|
const TQString& systemId )
|
|
|
|
{
|
|
|
|
Q_UNUSED( name );
|
|
|
|
Q_UNUSED( publicId );
|
|
|
|
Q_UNUSED( systemId );
|
|
|
|
|
|
|
|
// we skip DTD
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlHandler::startElement( const TQString& nsURI, const TQString& localName,
|
|
|
|
const TQString& name, const TQXmlAttributes& atts )
|
|
|
|
{
|
|
|
|
Q_UNUSED( localName );
|
|
|
|
|
|
|
|
// sanity check
|
|
|
|
if( !currentNode )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// we are going one level deeper
|
|
|
|
elementDepth++;
|
|
|
|
|
|
|
|
TQString nodePrefix, nodeLocalName, nodeTagName;
|
|
|
|
KoXmlNodeData* element = 0;
|
|
|
|
|
|
|
|
if( processNamespace )
|
|
|
|
{
|
|
|
|
// parse, using namespace
|
|
|
|
nodeTagName = name;
|
|
|
|
nodeLocalName = name;
|
|
|
|
nodePrefix = nsURI.isNull() ? TQString() : TQString("");
|
|
|
|
int i = name.find( ':' );
|
|
|
|
if( i != -1 )
|
|
|
|
{
|
|
|
|
nodeTagName = name.mid( i + 1 );
|
|
|
|
nodeLocalName = nodeTagName;
|
|
|
|
nodePrefix = name.left( i );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( elementDepth <= maxDepth )
|
|
|
|
{
|
|
|
|
// construct a new element
|
|
|
|
element = new KoXmlNodeData;
|
|
|
|
element->nodeType = KoXmlNode::ElementNode;
|
|
|
|
element->parent = currentNode;
|
|
|
|
element->namespaceURI = nsURI;
|
|
|
|
element->prefix = nodePrefix;
|
|
|
|
element->localName = nodeLocalName;
|
|
|
|
element->tagName = nodeTagName;
|
|
|
|
|
|
|
|
// Note: endPos will be later fixed in endElement
|
|
|
|
element->endPos = element->startPos = parseOffset + bufferStream.at();
|
|
|
|
|
|
|
|
// handle the attributes
|
|
|
|
for( int c=0; c<atts.length(); c++ )
|
|
|
|
{
|
|
|
|
TQString prefix;
|
|
|
|
TQString qName; // with prefix
|
|
|
|
TQString name; // without prefix, i.e. local name
|
|
|
|
|
|
|
|
name = qName = atts.qName(c);
|
|
|
|
int i = qName.find( ':' );
|
|
|
|
if( i != -1 ) prefix = qName.left( i );
|
|
|
|
if( i != -1 ) name = qName.mid( i + 1 );
|
|
|
|
element->setAttributeNS( atts.uri(c), qName, atts.value(c) );
|
|
|
|
element->setAttribute( name, atts.value(c) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// save in buffer for later on-demand loading
|
|
|
|
if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
|
|
|
|
{
|
|
|
|
bufferStream << "<";
|
|
|
|
if( !nodePrefix.isEmpty() )
|
|
|
|
bufferStream << nodePrefix << ":";
|
|
|
|
bufferStream << localName;
|
|
|
|
bufferStream << " xmlns";
|
|
|
|
if( !nodePrefix.isEmpty() )
|
|
|
|
bufferStream << ":" << nodePrefix;
|
|
|
|
bufferStream << "=\"";
|
|
|
|
bufferStream.appendEscape( nsURI );
|
|
|
|
bufferStream << "\"";
|
|
|
|
for( int c=0; c<atts.length(); c++ )
|
|
|
|
{
|
|
|
|
TQString prefix;
|
|
|
|
TQString name = atts.qName(c); // qName contains the prefix
|
|
|
|
int i = name.find( ':' );
|
|
|
|
if( i != -1 ) prefix = name.left( i );
|
|
|
|
if( i != -1 ) name = atts.qName(c).mid( i + 1 );
|
|
|
|
if( !atts.uri(c).isEmpty() )
|
|
|
|
bufferStream << " xmlns:" << prefix << "=\"" << atts.uri(c) << "\"";
|
|
|
|
bufferStream << " ";
|
|
|
|
if( !prefix.isEmpty() ) bufferStream << prefix << ":";
|
|
|
|
bufferStream << name << "=\"";
|
|
|
|
bufferStream.appendEscape( atts.value(c) );
|
|
|
|
bufferStream << "\"";
|
|
|
|
}
|
|
|
|
bufferStream << ">";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// parse, without using namespace
|
|
|
|
nodeTagName = name;
|
|
|
|
|
|
|
|
if( elementDepth <= maxDepth )
|
|
|
|
{
|
|
|
|
// construct a new element
|
|
|
|
element = new KoXmlNodeData;
|
|
|
|
element->nodeType = KoXmlNode::ElementNode;
|
|
|
|
element->parent = currentNode;
|
|
|
|
element->namespaceURI = TQString();
|
|
|
|
element->prefix = TQString();
|
|
|
|
element->localName = TQString();
|
|
|
|
element->tagName = nodeTagName;
|
|
|
|
|
|
|
|
if( rootNode->nodeType == KoXmlNode::DocumentNode )
|
|
|
|
element->fastLoading = rootNode->fastLoading;
|
|
|
|
|
|
|
|
// Note: endPos will be later fixed in endElement
|
|
|
|
element->endPos = element->startPos = parseOffset + bufferStream.at();
|
|
|
|
|
|
|
|
// handle the attributes
|
|
|
|
for( int c=0; c<atts.length(); c++ )
|
|
|
|
element->setAttribute( atts.qName(c), atts.value(c) );
|
|
|
|
}
|
|
|
|
|
|
|
|
// save in buffer for later on-demand loading
|
|
|
|
if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
|
|
|
|
{
|
|
|
|
bufferStream << "<";
|
|
|
|
bufferStream << nodeTagName;
|
|
|
|
for( int c=0; c<atts.length(); c++ )
|
|
|
|
{
|
|
|
|
bufferStream << " " << atts.qName(c) << "=\"";
|
|
|
|
bufferStream.appendEscape( atts.value(c) );
|
|
|
|
bufferStream << "\"";
|
|
|
|
}
|
|
|
|
bufferStream << ">";
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we do not parse a complete document, the first element is ignored
|
|
|
|
// this feature is used in on-demand loading
|
|
|
|
// e.g. "<ul><li><b>bold items</b></li></ul>", if root node points to
|
|
|
|
// <ul> tag, then the first <ul> is skipped so that next <li> element
|
|
|
|
// becomes the child of it.
|
|
|
|
if( elementDepth == 0 )
|
|
|
|
if( rootNode->nodeType != KoXmlNode::DocumentNode )
|
|
|
|
{
|
|
|
|
delete element;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( element )
|
|
|
|
{
|
|
|
|
// add as the child and traverse to it
|
|
|
|
currentNode->loaded = true;
|
|
|
|
currentNode->appendChild( element );
|
|
|
|
currentNode = element;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
currentNode->loaded = false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlHandler::endElement( const TQString& nsURI, const TQString& localName,
|
|
|
|
const TQString& qName )
|
|
|
|
{
|
|
|
|
Q_UNUSED( nsURI );
|
|
|
|
Q_UNUSED( localName );
|
|
|
|
|
|
|
|
// sanity check
|
|
|
|
if( !currentNode ) return false;
|
|
|
|
if( !currentNode->parent ) return false;
|
|
|
|
|
|
|
|
// see comments in startElement about first element and on-demand loading
|
|
|
|
if( rootNode->nodeType == KoXmlNode::DocumentNode )
|
|
|
|
if( currentNode == rootNode )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// buffer for on-demand loading
|
|
|
|
if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
|
|
|
|
bufferStream << "</" << qName << ">";
|
|
|
|
|
|
|
|
// fix up the pointer
|
|
|
|
currentNode->endPos = parseOffset + bufferStream.at() - 1;
|
|
|
|
|
|
|
|
// go up one level
|
|
|
|
if( elementDepth <= maxDepth )
|
|
|
|
currentNode = currentNode->parent;
|
|
|
|
|
|
|
|
// we are going up one level
|
|
|
|
elementDepth--;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlHandler::characters( const TQString& str )
|
|
|
|
{
|
|
|
|
// sanity check
|
|
|
|
if( rootNode->nodeType == KoXmlNode::DocumentNode )
|
|
|
|
if( currentNode == rootNode )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// are we inside entity ?
|
|
|
|
if( !entityName.isEmpty() )
|
|
|
|
{
|
|
|
|
// we do not handle entity but need to keep track of it
|
|
|
|
// because we want to skip it alltogether
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( cdata )
|
|
|
|
{
|
|
|
|
// are we inside CDATA section ?
|
|
|
|
if( elementDepth <= maxDepth )
|
|
|
|
{
|
|
|
|
KoXmlNodeData* cdata = new KoXmlNodeData;
|
|
|
|
cdata->nodeType = KoXmlNode::CDATASectionNode;
|
|
|
|
cdata->parent = currentNode;
|
|
|
|
cdata->setData( str );
|
|
|
|
currentNode->appendChild( cdata );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
|
|
|
|
bufferStream << "<![CDATA[" << str << "]]>"; // no escape for CDATA
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// this must be normal text node
|
|
|
|
if( elementDepth <= maxDepth )
|
|
|
|
{
|
|
|
|
KoXmlNodeData* text = new KoXmlNodeData;
|
|
|
|
text->nodeType = KoXmlNode::TextNode;
|
|
|
|
text->parent = currentNode;
|
|
|
|
text->setData( str );
|
|
|
|
currentNode->appendChild( text );
|
|
|
|
}
|
|
|
|
|
|
|
|
// save it for later use
|
|
|
|
if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
|
|
|
|
bufferStream.appendEscape( str );
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlHandler::processingInstruction( const TQString& target,
|
|
|
|
const TQString& data )
|
|
|
|
{
|
|
|
|
// sanity check
|
|
|
|
if( !currentNode )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
KoXmlNodeData* instruction = new KoXmlNodeData;
|
|
|
|
instruction->nodeType = KoXmlNode::ProcessingInstructionNode;
|
|
|
|
instruction->parent = currentNode;
|
|
|
|
instruction->tagName = target;
|
|
|
|
instruction->setData( data );
|
|
|
|
|
|
|
|
currentNode->appendChild( instruction );
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlHandler::skippedEntity( const TQString& name )
|
|
|
|
{
|
|
|
|
Q_UNUSED( name );
|
|
|
|
|
|
|
|
// we skip entity
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlHandler::startCDATA()
|
|
|
|
{
|
|
|
|
cdata = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlHandler::endCDATA()
|
|
|
|
{
|
|
|
|
cdata = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlHandler::startEntity( const TQString& name )
|
|
|
|
{
|
|
|
|
entityName = name;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlHandler::endEntity( const TQString& name )
|
|
|
|
{
|
|
|
|
Q_UNUSED( name );
|
|
|
|
entityName = TQString();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlHandler::comment( const TQString& comment )
|
|
|
|
{
|
|
|
|
Q_UNUSED( comment );
|
|
|
|
|
|
|
|
// we skip comment
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlHandler::unparsedEntityDecl( const TQString &name,
|
|
|
|
const TQString &publicId, const TQString &systemId, const TQString ¬ationName )
|
|
|
|
{
|
|
|
|
Q_UNUSED( name );
|
|
|
|
Q_UNUSED( publicId );
|
|
|
|
Q_UNUSED( systemId );
|
|
|
|
Q_UNUSED( notationName );
|
|
|
|
|
|
|
|
// we skip entity
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlHandler::externalEntityDecl( const TQString &name,
|
|
|
|
const TQString &publicId, const TQString &systemId )
|
|
|
|
{
|
|
|
|
Q_UNUSED( name );
|
|
|
|
Q_UNUSED( publicId );
|
|
|
|
Q_UNUSED( systemId );
|
|
|
|
|
|
|
|
// we skip entity
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlHandler::notationDecl( const TQString & name,
|
|
|
|
const TQString & publicId, const TQString & systemId )
|
|
|
|
{
|
|
|
|
Q_UNUSED( name );
|
|
|
|
Q_UNUSED( publicId );
|
|
|
|
Q_UNUSED( systemId );
|
|
|
|
|
|
|
|
// we skip notation node
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlHandler::fatalError( const TQXmlParseException& exception )
|
|
|
|
{
|
|
|
|
errorMsg = exception.message();
|
|
|
|
errorLine = exception.lineNumber();
|
|
|
|
errorColumn = exception.columnNumber();
|
|
|
|
return TQXmlDefaultHandler::fatalError( exception );
|
|
|
|
}
|
|
|
|
|
|
|
|
// ==================================================================
|
|
|
|
//
|
|
|
|
// KoXmlNode
|
|
|
|
//
|
|
|
|
// ==================================================================
|
|
|
|
|
|
|
|
// Creates a null node
|
|
|
|
KoXmlNode::KoXmlNode()
|
|
|
|
{
|
|
|
|
d = new KoXmlNodeData;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Destroys this node
|
|
|
|
KoXmlNode::~KoXmlNode()
|
|
|
|
{
|
|
|
|
if( d ) d->unref();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates a copy of another node
|
|
|
|
KoXmlNode::KoXmlNode( const KoXmlNode& node )
|
|
|
|
{
|
|
|
|
d = node.d;
|
|
|
|
d->ref();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates a node for specific implementation
|
|
|
|
KoXmlNode::KoXmlNode( KoXmlNodeData* data )
|
|
|
|
{
|
|
|
|
d = data;
|
|
|
|
data->ref();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates a shallow copy of another node
|
|
|
|
KoXmlNode& KoXmlNode::operator=( const KoXmlNode& node )
|
|
|
|
{
|
|
|
|
d->unref();
|
|
|
|
d = node.d;
|
|
|
|
d->ref();
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note: two null nodes are always equal
|
|
|
|
bool KoXmlNode::operator==( const KoXmlNode& node ) const
|
|
|
|
{
|
|
|
|
if( isNull() && node.isNull() ) return true;
|
|
|
|
return( d==node.d );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note: two null nodes are always equal
|
|
|
|
bool KoXmlNode::operator!=( const KoXmlNode& node ) const
|
|
|
|
{
|
|
|
|
if( isNull() && !node.isNull() ) return true;
|
|
|
|
if( !isNull() && node.isNull() ) return true;
|
|
|
|
if( isNull() && node.isNull() ) return false;
|
|
|
|
return( d!=node.d );
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlNode::NodeType KoXmlNode::nodeType() const
|
|
|
|
{
|
|
|
|
return d->nodeType;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlNode::isElement() const
|
|
|
|
{
|
|
|
|
return d->nodeType == ElementNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlNode::isText() const
|
|
|
|
{
|
|
|
|
return (d->nodeType == TextNode) || isCDATASection();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlNode::isCDATASection() const
|
|
|
|
{
|
|
|
|
return d->nodeType == CDATASectionNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlNode::isDocument() const
|
|
|
|
{
|
|
|
|
return d->nodeType == DocumentNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlNode::isNull() const
|
|
|
|
{
|
|
|
|
return d->nodeType == NullNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KoXmlNode::clear()
|
|
|
|
{
|
|
|
|
d->unref();
|
|
|
|
d = new KoXmlNodeData;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString KoXmlNode::nodeName() const
|
|
|
|
{
|
|
|
|
return d->nodeName();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString KoXmlNode::prefix() const
|
|
|
|
{
|
|
|
|
return isElement() ? d->prefix : TQString();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString KoXmlNode::namespaceURI() const
|
|
|
|
{
|
|
|
|
return isElement() ? d->namespaceURI : TQString();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString KoXmlNode::localName() const
|
|
|
|
{
|
|
|
|
return isElement() ? d->localName : TQString();
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlDocument KoXmlNode::ownerDocument() const
|
|
|
|
{
|
|
|
|
KoXmlNodeData* node = d;
|
|
|
|
while( node->parent ) node = node->parent;
|
|
|
|
|
|
|
|
if( node->nodeType != DocumentNode ) return KoXmlDocument();
|
|
|
|
return KoXmlDocument( node );
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlNode KoXmlNode::parentNode() const
|
|
|
|
{
|
|
|
|
return d->parent ? KoXmlNode( d->parent ) : KoXmlNode();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlNode::hasChildNodes() const
|
|
|
|
{
|
|
|
|
d->loadChildren();
|
|
|
|
return d->first!=0 ;
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlNode KoXmlNode::firstChild() const
|
|
|
|
{
|
|
|
|
if( !d->fastLoading )
|
|
|
|
d->loadChildren();
|
|
|
|
return d->first ? KoXmlNode( d->first ) : KoXmlNode();
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlNode KoXmlNode::lastChild() const
|
|
|
|
{
|
|
|
|
if( !d->fastLoading )
|
|
|
|
d->loadChildren();
|
|
|
|
return d->last ? KoXmlNode( d->last ) : KoXmlNode();
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlNode KoXmlNode::nextSibling() const
|
|
|
|
{
|
|
|
|
return d->next ? KoXmlNode( d->next ) : KoXmlNode();
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlNode KoXmlNode::previousSibling() const
|
|
|
|
{
|
|
|
|
return d->prev ? KoXmlNode( d->prev ) : KoXmlNode();
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlNode KoXmlNode::namedItem( const TQString& name ) const
|
|
|
|
{
|
|
|
|
if( !d->fastLoading )
|
|
|
|
d->loadChildren();
|
|
|
|
|
|
|
|
KoXmlNodeData* node = d->first;
|
|
|
|
while ( node )
|
|
|
|
{
|
|
|
|
if( node->nodeName() == name )
|
|
|
|
return KoXmlNode( node );
|
|
|
|
node = node->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
// not found
|
|
|
|
return KoXmlNode();
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlNode KoXmlNode::namedItemNS( const TQString& nsURI, const TQString& name ) const
|
|
|
|
{
|
|
|
|
if( !d->fastLoading )
|
|
|
|
d->loadChildren();
|
|
|
|
|
|
|
|
KoXmlNodeData* node = d->first;
|
|
|
|
while ( node )
|
|
|
|
{
|
|
|
|
if( !node->prefix.isNull() )
|
|
|
|
if( node->namespaceURI == nsURI )
|
|
|
|
if( node->localName == name )
|
|
|
|
return KoXmlNode( node );
|
|
|
|
node = node->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
// not found
|
|
|
|
return KoXmlNode();
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlElement KoXmlNode::toElement()
|
|
|
|
{
|
|
|
|
return isElement() ? KoXmlElement( d ) : KoXmlElement();
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlText KoXmlNode::toText()
|
|
|
|
{
|
|
|
|
return isText() ? KoXmlText( d ) : KoXmlText();
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlCDATASection KoXmlNode::toCDATASection()
|
|
|
|
{
|
|
|
|
return isCDATASection() ? KoXmlCDATASection( (KoXmlNodeData*)d ) :
|
|
|
|
KoXmlCDATASection();
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlDocument KoXmlNode::toDocument()
|
|
|
|
{
|
|
|
|
return isDocument() ? KoXmlDocument( d ) : KoXmlDocument();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KoXmlNode::load( int depth )
|
|
|
|
{
|
|
|
|
d->loadChildren( depth );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KoXmlNode::unload()
|
|
|
|
{
|
|
|
|
d->unloadChildren();
|
|
|
|
}
|
|
|
|
|
|
|
|
// ==================================================================
|
|
|
|
//
|
|
|
|
// KoXmlElement
|
|
|
|
//
|
|
|
|
// ==================================================================
|
|
|
|
|
|
|
|
// Creates an empty element
|
|
|
|
KoXmlElement::KoXmlElement(): KoXmlNode()
|
|
|
|
{
|
|
|
|
d->unref();
|
|
|
|
d = new KoXmlNodeData;
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlElement::~KoXmlElement()
|
|
|
|
{
|
|
|
|
d->unref();
|
|
|
|
d = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates a shallow copy of another element
|
|
|
|
KoXmlElement::KoXmlElement( const KoXmlElement& element ): KoXmlNode()
|
|
|
|
{
|
|
|
|
d->unref();
|
|
|
|
d = element.d;
|
|
|
|
d->ref();
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlElement::KoXmlElement( KoXmlNodeData* data ): KoXmlNode()
|
|
|
|
{
|
|
|
|
d->unref();
|
|
|
|
d = data;
|
|
|
|
d->ref();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copies another element
|
|
|
|
KoXmlElement& KoXmlElement::operator=( const KoXmlElement& element )
|
|
|
|
{
|
|
|
|
KoXmlNode::operator=( element );
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlElement::operator== ( const KoXmlElement& element ) const
|
|
|
|
{
|
|
|
|
if( isNull() || element.isNull() ) return false;
|
|
|
|
return (d==element.d);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlElement::operator!= ( const KoXmlElement& element ) const
|
|
|
|
{
|
|
|
|
if( isNull() && element.isNull() ) return false;
|
|
|
|
if( isNull() || element.isNull() ) return true;
|
|
|
|
return (d!=element.d);
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString KoXmlElement::tagName() const
|
|
|
|
{
|
|
|
|
return isElement() ? ((KoXmlNodeData*)d)->tagName: TQString();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString KoXmlElement::text() const
|
|
|
|
{
|
|
|
|
return d->text();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlElement::isElement() const
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString KoXmlElement::attribute( const TQString& name ) const
|
|
|
|
{
|
|
|
|
return attribute( name, TQString() );
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString KoXmlElement::attribute( const TQString& name,
|
|
|
|
const TQString& defaultValue ) const
|
|
|
|
{
|
|
|
|
if( !isElement() )
|
|
|
|
return defaultValue;
|
|
|
|
|
|
|
|
if( !hasAttribute( name ) )
|
|
|
|
return defaultValue;
|
|
|
|
|
|
|
|
return ((KoXmlNodeData*)d)->attribute( name );
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString KoXmlElement::attributeNS( const TQString& namespaceURI,
|
|
|
|
const TQString& localName, const TQString& defaultValue ) const
|
|
|
|
{
|
|
|
|
if( !isElement() )
|
|
|
|
return defaultValue;
|
|
|
|
|
|
|
|
if( !hasAttributeNS( namespaceURI,localName ) )
|
|
|
|
return defaultValue;
|
|
|
|
|
|
|
|
return ((KoXmlNodeData*)d)->attributeNS( namespaceURI,localName );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlElement::hasAttribute( const TQString& name ) const
|
|
|
|
{
|
|
|
|
return isElement() ? ((KoXmlNodeData*)d)->hasAttribute( name ) : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlElement::hasAttributeNS( const TQString& namespaceURI,
|
|
|
|
const TQString& localName ) const
|
|
|
|
{
|
|
|
|
return isElement() ? ((KoXmlNodeData*)d)->hasAttributeNS(
|
|
|
|
namespaceURI, localName ) : false;;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ==================================================================
|
|
|
|
//
|
|
|
|
// KoXmlText
|
|
|
|
//
|
|
|
|
// ==================================================================
|
|
|
|
|
|
|
|
KoXmlText::KoXmlText(): KoXmlNode()
|
|
|
|
{
|
|
|
|
d->unref();
|
|
|
|
d = new KoXmlNodeData;
|
|
|
|
d->nodeType = TextNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlText::~KoXmlText()
|
|
|
|
{
|
|
|
|
if( d ) d->unref();
|
|
|
|
d = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlText::KoXmlText( const KoXmlText& text ): KoXmlNode()
|
|
|
|
{
|
|
|
|
d->unref();
|
|
|
|
d = (KoXmlNodeData*) text.d;
|
|
|
|
d->ref();
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlText::KoXmlText( KoXmlNodeData* data ): KoXmlNode()
|
|
|
|
{
|
|
|
|
d->unref();
|
|
|
|
d = data;
|
|
|
|
d->ref();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlText::isText() const
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString KoXmlText::data() const
|
|
|
|
{
|
|
|
|
return ((KoXmlNodeData*)d)->data();
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlText& KoXmlText::operator=( const KoXmlText& element )
|
|
|
|
{
|
|
|
|
KoXmlNode::operator=( element );
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ==================================================================
|
|
|
|
//
|
|
|
|
// KoXmlCDATASection
|
|
|
|
//
|
|
|
|
// ==================================================================
|
|
|
|
|
|
|
|
KoXmlCDATASection::KoXmlCDATASection(): KoXmlText()
|
|
|
|
{
|
|
|
|
d->unref();
|
|
|
|
d = new KoXmlNodeData;
|
|
|
|
d->nodeType = KoXmlNode::CDATASectionNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlCDATASection::~KoXmlCDATASection()
|
|
|
|
{
|
|
|
|
d->unref();
|
|
|
|
d = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlCDATASection::KoXmlCDATASection( const KoXmlCDATASection& cdata ):
|
|
|
|
KoXmlText()
|
|
|
|
{
|
|
|
|
d->unref();
|
|
|
|
d = (KoXmlNodeData*) cdata.d;
|
|
|
|
d->ref();
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlCDATASection::KoXmlCDATASection( KoXmlNodeData* cdata ):
|
|
|
|
KoXmlText()
|
|
|
|
{
|
|
|
|
d->unref();
|
|
|
|
d = cdata;
|
|
|
|
d->ref();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlCDATASection::isCDATASection() const
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlCDATASection& KoXmlCDATASection::operator=( const KoXmlCDATASection& cdata )
|
|
|
|
{
|
|
|
|
KoXmlNode::operator=( cdata );
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ==================================================================
|
|
|
|
//
|
|
|
|
// KoXmlDocument
|
|
|
|
//
|
|
|
|
// ==================================================================
|
|
|
|
|
|
|
|
KoXmlDocument::KoXmlDocument(): KoXmlNode()
|
|
|
|
{
|
|
|
|
d->unref();
|
|
|
|
d = new KoXmlNodeData;
|
|
|
|
d->nodeType = KoXmlNode::DocumentNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlDocument::~KoXmlDocument()
|
|
|
|
{
|
|
|
|
d->unref();
|
|
|
|
d = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlDocument::KoXmlDocument( KoXmlNodeData* data ): KoXmlNode()
|
|
|
|
{
|
|
|
|
d->unref();
|
|
|
|
d = data;
|
|
|
|
d->ref();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates a copy of another document
|
|
|
|
KoXmlDocument::KoXmlDocument( const KoXmlDocument& doc ): KoXmlNode()
|
|
|
|
{
|
|
|
|
d->unref();
|
|
|
|
d = doc.d;
|
|
|
|
d->ref();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates a shallow copy of another document
|
|
|
|
KoXmlDocument& KoXmlDocument::operator=( const KoXmlDocument& doc )
|
|
|
|
{
|
|
|
|
KoXmlNode::operator=( doc );
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Checks if this document and doc are equals
|
|
|
|
bool KoXmlDocument::operator==( const KoXmlDocument& doc ) const
|
|
|
|
{
|
|
|
|
return( d==doc.d );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Checks if this document and doc are not equals
|
|
|
|
bool KoXmlDocument::operator!=( const KoXmlDocument& doc ) const
|
|
|
|
{
|
|
|
|
return( d!=doc.d );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlDocument::isDocument() const
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
KoXmlElement KoXmlDocument::documentElement() const
|
|
|
|
{
|
|
|
|
for( KoXmlNodeData* node=d->first; node; )
|
|
|
|
if( node->nodeType==KoXmlNode::ElementNode )
|
|
|
|
return KoXmlElement( node );
|
|
|
|
else node = node->next;
|
|
|
|
|
|
|
|
return KoXmlElement();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KoXmlDocument::setFastLoading( bool f )
|
|
|
|
{
|
|
|
|
d->fastLoading = f;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlDocument::fastLoading() const
|
|
|
|
{
|
|
|
|
return d->fastLoading;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlDocument::setContent( TQXmlInputSource *source, TQXmlReader *reader,
|
|
|
|
TQString* errorMsg, int* errorLine, int* errorColumn )
|
|
|
|
{
|
|
|
|
if( d->nodeType != KoXmlNode::DocumentNode )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return d->setContent( source, reader, errorMsg, errorLine, errorColumn );
|
|
|
|
}
|
|
|
|
|
|
|
|
// no namespace processing
|
|
|
|
bool KoXmlDocument::setContent( TQIODevice* device, TQString* errorMsg,
|
|
|
|
int* errorLine, int* errorColumn )
|
|
|
|
{
|
|
|
|
return setContent( device, false, errorMsg, errorLine, errorColumn );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KoXmlDocument::setContent( TQIODevice* device, bool namespaceProcessing,
|
|
|
|
TQString* errorMsg, int* errorLine, int* errorColumn )
|
|
|
|
{
|
|
|
|
if( d->nodeType != KoXmlNode::DocumentNode )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
TQXmlSimpleReader reader;
|
|
|
|
reader.setFeature( "http://xml.org/sax/features/namespaces", namespaceProcessing );
|
|
|
|
reader.setFeature( "http://xml.org/sax/features/namespace-prefixes", !namespaceProcessing );
|
|
|
|
reader.setFeature( "http://trolltech.com/xml/features/report-whitespace-only-CharData", false );
|
|
|
|
|
|
|
|
// FIXME this hack is apparently private
|
|
|
|
//reader.setUndefEntityInAttrHack(true);
|
|
|
|
|
|
|
|
TQXmlInputSource source( device );
|
|
|
|
return d->setContent( &source, &reader, errorMsg, errorLine, errorColumn );
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
KoXmlElement KoXml::namedItemNS( const KoXmlNode& node, const char* nsURI,
|
|
|
|
const char* localName )
|
|
|
|
{
|
|
|
|
#ifdef KOXML_USE_TQDOM
|
|
|
|
// David's solution for namedItemNS, only for TQDom stuff
|
|
|
|
KoXmlNode n = node.firstChild();
|
|
|
|
for ( ; !n.isNull(); n = n.nextSibling() ) {
|
|
|
|
if ( n.isElement() && n.localName() == localName &&
|
|
|
|
n.namespaceURI() == nsURI )
|
|
|
|
return n.toElement();
|
|
|
|
}
|
|
|
|
return KoXmlElement();
|
|
|
|
#else
|
|
|
|
return node.namedItemNS( nsURI, localName).toElement();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void KoXml::load( KoXmlNode& node, int depth )
|
|
|
|
{
|
|
|
|
#ifdef KOXML_USE_TQDOM
|
|
|
|
// do nothing, TQDom has no on-demand loading
|
|
|
|
Q_UNUSED( node );
|
|
|
|
Q_UNUSED( depth );
|
|
|
|
#else
|
|
|
|
node.load( depth );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void KoXml::unload( KoXmlNode& node )
|
|
|
|
{
|
|
|
|
#ifdef KOXML_USE_TQDOM
|
|
|
|
// do nothing, TQDom has no on-demand unloading
|
|
|
|
Q_UNUSED( node );
|
|
|
|
#else
|
|
|
|
node.unload();
|
|
|
|
#endif
|
|
|
|
}
|