aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/inprogress/tafdocument.cpp (renamed from src/tafdocument.cpp)0
-rw-r--r--src/inprogress/tafdocument.h (renamed from src/tafdocument.h)0
-rw-r--r--src/inprogress/tafnode.cpp (renamed from src/tafnode.cpp)0
-rw-r--r--src/inprogress/tafnode.h (renamed from src/tafnode.h)0
-rw-r--r--src/inprogress/tafreader.cpp (renamed from src/tafreader.cpp)0
-rw-r--r--src/inprogress/tafreader.h (renamed from src/tafreader.h)0
-rw-r--r--src/inprogress/tafwriter.cpp (renamed from src/tafwriter.cpp)0
-rw-r--r--src/inprogress/tafwriter.h (renamed from src/tafwriter.h)0
-rw-r--r--src/inprogress/xmldocument.cpp9
-rw-r--r--src/inprogress/xmldocument.h22
-rw-r--r--src/inprogress/xmlnode.cpp9
-rw-r--r--src/inprogress/xmlnode.h22
-rw-r--r--src/inprogress/xmlreader.cpp267
-rw-r--r--src/inprogress/xmlreader.h121
-rw-r--r--src/inprogress/xmlwriter.cpp9
-rw-r--r--src/inprogress/xmlwriter.h22
-rw-r--r--src/old/xmldocument.cpp149
-rw-r--r--src/old/xmldocument.h171
-rw-r--r--src/old/xmlnode.cpp445
-rw-r--r--src/old/xmlnode.h236
-rw-r--r--src/old/xmlreader.cpp602
-rw-r--r--src/old/xmlreader.h141
-rw-r--r--src/old/xmlwriter.cpp173
-rw-r--r--src/old/xmlwriter.h96
-rw-r--r--src/xmldocument.cpp146
-rw-r--r--src/xmldocument.h175
-rw-r--r--src/xmlnode.cpp440
-rw-r--r--src/xmlnode.h240
-rw-r--r--src/xmlreader.cpp665
-rw-r--r--src/xmlreader.h252
-rw-r--r--src/xmlwriter.cpp168
-rw-r--r--src/xmlwriter.h100
32 files changed, 2340 insertions, 2340 deletions
diff --git a/src/tafdocument.cpp b/src/inprogress/tafdocument.cpp
index bd44dd5..bd44dd5 100644
--- a/src/tafdocument.cpp
+++ b/src/inprogress/tafdocument.cpp
diff --git a/src/tafdocument.h b/src/inprogress/tafdocument.h
index 171f829..171f829 100644
--- a/src/tafdocument.h
+++ b/src/inprogress/tafdocument.h
diff --git a/src/tafnode.cpp b/src/inprogress/tafnode.cpp
index c9756ec..c9756ec 100644
--- a/src/tafnode.cpp
+++ b/src/inprogress/tafnode.cpp
diff --git a/src/tafnode.h b/src/inprogress/tafnode.h
index 34f5289..34f5289 100644
--- a/src/tafnode.h
+++ b/src/inprogress/tafnode.h
diff --git a/src/tafreader.cpp b/src/inprogress/tafreader.cpp
index f94fe44..f94fe44 100644
--- a/src/tafreader.cpp
+++ b/src/inprogress/tafreader.cpp
diff --git a/src/tafreader.h b/src/inprogress/tafreader.h
index 2dbb9ea..2dbb9ea 100644
--- a/src/tafreader.h
+++ b/src/inprogress/tafreader.h
diff --git a/src/tafwriter.cpp b/src/inprogress/tafwriter.cpp
index 3e6c025..3e6c025 100644
--- a/src/tafwriter.cpp
+++ b/src/inprogress/tafwriter.cpp
diff --git a/src/tafwriter.h b/src/inprogress/tafwriter.h
index 7057d62..7057d62 100644
--- a/src/tafwriter.h
+++ b/src/inprogress/tafwriter.h
diff --git a/src/inprogress/xmldocument.cpp b/src/inprogress/xmldocument.cpp
new file mode 100644
index 0000000..cb21826
--- /dev/null
+++ b/src/inprogress/xmldocument.cpp
@@ -0,0 +1,9 @@
1#include "xmldocument.h"
2
3Bu::XmlDocument::XmlDocument()
4{
5}
6
7Bu::XmlDocument::~XmlDocument()
8{
9}
diff --git a/src/inprogress/xmldocument.h b/src/inprogress/xmldocument.h
new file mode 100644
index 0000000..e16e3ea
--- /dev/null
+++ b/src/inprogress/xmldocument.h
@@ -0,0 +1,22 @@
1#ifndef XML_DOCUMENT_H
2#define XML_DOCUMENT_H
3
4#include <stdint.h>
5
6namespace Bu
7{
8 /**
9 *
10 */
11 class XmlDocument
12 {
13 public:
14 XmlDocument();
15 virtual ~XmlDocument();
16
17 private:
18
19 };
20}
21
22#endif
diff --git a/src/inprogress/xmlnode.cpp b/src/inprogress/xmlnode.cpp
new file mode 100644
index 0000000..58ef5c5
--- /dev/null
+++ b/src/inprogress/xmlnode.cpp
@@ -0,0 +1,9 @@
1#include "xmlnode.h"
2
3Bu::XmlNode::XmlNode()
4{
5}
6
7Bu::XmlNode::~XmlNode()
8{
9}
diff --git a/src/inprogress/xmlnode.h b/src/inprogress/xmlnode.h
new file mode 100644
index 0000000..cd9961a
--- /dev/null
+++ b/src/inprogress/xmlnode.h
@@ -0,0 +1,22 @@
1#ifndef XML_NODE_H
2#define XML_NODE_H
3
4#include <stdint.h>
5
6namespace Bu
7{
8 /**
9 *
10 */
11 class XmlNode
12 {
13 public:
14 XmlNode();
15 virtual ~XmlNode();
16
17 private:
18
19 };
20}
21
22#endif
diff --git a/src/inprogress/xmlreader.cpp b/src/inprogress/xmlreader.cpp
new file mode 100644
index 0000000..bd241cf
--- /dev/null
+++ b/src/inprogress/xmlreader.cpp
@@ -0,0 +1,267 @@
1#include "xmlreader.h"
2
3Bu::XmlReader::XmlReader( Bu::Stream &sIn ) :
4 sIn( sIn )
5{
6}
7
8Bu::XmlReader::~XmlReader()
9{
10}
11
12const char *Bu::XmlReader::lookahead( int nAmnt )
13{
14 if( sBuf.getSize() >= nAmnt )
15 return sBuf.getStr();
16
17 int nNew = nAmnt - sBuf.getSize();
18 char *buf = new char[nNew];
19 sIn.read( buf, nNew );
20 sBuf.append( buf );
21
22 return sBuf.getStr();
23}
24
25void Bu::XmlReader::burn( int nAmnt )
26{
27 if( sBuf.getSize() < nAmnt )
28 {
29 lookahead( nAmnt );
30 }
31
32 //sBuf.remove( nAmnt );
33}
34
35void Bu::XmlReader::checkString( const char *str, int nLen )
36{
37 if( !strncmp( str, lookahead( nLen ), nLen ) )
38 {
39 burn( nLen );
40 return;
41 }
42
43 throw Bu::ExceptionBase("Expected string '%s'", str );
44}
45
46Bu::XmlNode *Bu::XmlReader::read()
47{
48 prolog();
49}
50
51void Bu::XmlReader::prolog()
52{
53 XMLDecl();
54 Misc();
55}
56
57void Bu::XmlReader::XMLDecl()
58{
59 checkString("<?xml", 5 );
60 S();
61 VersionInfo();
62 EncodingDecl();
63 SDDecl();
64 Sq();
65 checkString("?>", 2 );
66}
67
68void Bu::XmlReader::Misc()
69{
70 for(;;)
71 {
72 S();
73 if( !strncmp("<!--", lookahead( 4 ), 4 ) )
74 {
75 Comment();
76 }
77 else if( !strncmp("<?", lookahead( 2 ), 2 ) )
78 {
79 PI();
80 }
81 else
82 {
83 return;
84 }
85 }
86}
87
88void Bu::XmlReader::Comment()
89{
90 checkString("<!--", 4 );
91 for(;;)
92 {
93 unsigned char c = *lookahead(1);
94 if( c == '-' )
95 {
96 if( lookahead(2)[1] == '-' )
97 {
98 checkString("-->", 3 );
99 return;
100 }
101 }
102 burn( 1 );
103 }
104}
105
106void Bu::XmlReader::PI()
107{
108 checkString("<?", 2 );
109 FString sName = Name();
110 printf("PI: %s\n---\n", sName.getStr() );
111 S();
112 for(int j = 0;; j++ )
113 {
114 if( !strncmp( "?>", lookahead(j+2)+j, 2 ) )
115 {
116 burn( j+2 );
117 return;
118 }
119 }
120}
121
122void Bu::XmlReader::S()
123{
124 for( int j = 0;; j++ )
125 {
126 char c = *lookahead( 1 );
127 if( c == 0x20 || c == 0x9 || c == 0xD || c == 0xA )
128 continue;
129 if( j == 0 )
130 throw ExceptionBase("Expected whitespace.");
131 return;
132 }
133}
134
135void Bu::XmlReader::Sq()
136{
137 for(;;)
138 {
139 char c = *lookahead( 1 );
140 if( c == 0x20 || c == 0x9 || c == 0xD || c == 0xA )
141 continue;
142 return;
143 }
144}
145
146void Bu::XmlReader::VersionInfo()
147{
148 try
149 {
150 S();
151 checkString("version", 7 );
152 }
153 catch( ExceptionBase &e )
154 {
155 return;
156 }
157 Eq();
158 Bu::FString ver = AttValue();
159 if( ver != "1.1" )
160 throw ExceptionBase("Currently we only support xml version 1.1\n");
161}
162
163void Bu::XmlReader::Eq()
164{
165 Sq();
166 checkString("=", 1 );
167 Sq();
168}
169
170void Bu::XmlReader::EncodingDecl()
171{
172 S();
173 try
174 {
175 checkString("encoding", 8 );
176 }
177 catch( ExceptionBase &e )
178 {
179 return;
180 }
181
182 Eq();
183 AttValue();
184}
185
186void Bu::XmlReader::SDDecl()
187{
188 S();
189 try
190 {
191 checkString("standalone", 10 );
192 }
193 catch( ExceptionBase &e )
194 {
195 return;
196 }
197
198 Eq();
199 AttValue();
200}
201
202Bu::FString Bu::XmlReader::AttValue()
203{
204 char q = *lookahead(1);
205 if( q == '\"' )
206 {
207 for( int j = 2;; j++ )
208 {
209 if( lookahead(j)[j-1] == '\"' )
210 {
211 Bu::FString ret( lookahead(j)+1, j-2 );
212 burn( j );
213 return ret;
214 }
215 }
216 }
217 else if( q == '\'' )
218 {
219 for( int j = 2;; j++ )
220 {
221 if( lookahead(j)[j-1] == '\'' )
222 {
223 Bu::FString ret( lookahead(j)+1, j-2 );
224 burn( j );
225 return ret;
226 }
227 }
228 }
229
230 throw ExceptionBase("Excpected either \' or \".\n");
231}
232
233Bu::FString Bu::XmlReader::Name()
234{
235 unsigned char c = *lookahead( 1 );
236 if( c != ':' && c != '_' &&
237 (c < 'A' || c > 'Z') &&
238 (c < 'a' || c > 'z') &&
239 (c < 0xC0 || c > 0xD6 ) &&
240 (c < 0xD8 || c > 0xF6 ) &&
241 (c < 0xF8))
242 {
243 throw ExceptionBase("Invalid entity name starting character.");
244 }
245
246 for( int j = 1;; j++ )
247 {
248 unsigned char c = lookahead(j+1)[j];
249 if( isS( c ) )
250 {
251 FString ret( lookahead(j+1), j+1 );
252 burn( j+1 );
253 return ret;
254 }
255 if( c != ':' && c != '_' && c != '-' && c != '.' && c != 0xB7 &&
256 (c < 'A' || c > 'Z') &&
257 (c < 'a' || c > 'z') &&
258 (c < '0' || c > '9') &&
259 (c < 0xC0 || c > 0xD6 ) &&
260 (c < 0xD8 || c > 0xF6 ) &&
261 (c < 0xF8))
262 {
263 throw ExceptionBase("Invalid character in name.");
264 }
265 }
266}
267
diff --git a/src/inprogress/xmlreader.h b/src/inprogress/xmlreader.h
new file mode 100644
index 0000000..708a386
--- /dev/null
+++ b/src/inprogress/xmlreader.h
@@ -0,0 +1,121 @@
1#ifndef XML_READER_H
2#define XML_READER_H
3
4#include <stdint.h>
5#include "bu/stream.h"
6#include "bu/fstring.h"
7#include "bu/xmlnode.h"
8
9namespace Bu
10{
11 /**
12 * An Xml 1.1 reader. I've decided to write this, this time, based on the
13 * official W3C reccomendation, now included with the source code. I've
14 * named the productions in the parser states the same as in that document,
15 * which may make them easier to find, etc, although possibly slightly less
16 * optimized than writing my own reduced grammer.
17 *
18 * Below I will list differences between my parser and the official standard
19 * as I come up with them.
20 * - Encoding and Standalone headings are ignored for the moment. (4.3.3,
21 * 2.9)
22 * - The standalone heading attribute can have any standard whitespace
23 * before it (the specs say only spaces, no newlines). (2.9)
24 * - Since standalone is ignored, it is currently allowed to have any
25 * value (should be restricted to "yes" or "no"). (2.9)
26 * - Currently only UTF-8 / ascii are parsed.
27 * - [optional] The content of comments is thrown away. (2.5)
28 * - The content of processing instruction blocks is parsed properly, but
29 * thrown away. (2.6)
30 */
31 class XmlReader
32 {
33 public:
34 XmlReader( Bu::Stream &sIn );
35 virtual ~XmlReader();
36
37 XmlNode *read();
38
39 private:
40 Bu::Stream &sIn;
41 Bu::FString sBuf;
42
43 private: // Helpers
44 const char *lookahead( int nAmnt );
45 void burn( int nAmnt );
46 void checkString( const char *str, int nLen );
47
48 private: // States
49 /**
50 * The headers, etc.
51 */
52 void prolog();
53
54 /**
55 * The xml decleration (version, encoding, etc).
56 */
57 void XMLDecl();
58
59 /**
60 * Misc things, Includes Comments and PIData (Processing Instructions).
61 */
62 void Misc();
63
64 /**
65 * Comments
66 */
67 void Comment();
68
69 /**
70 * Processing Instructions
71 */
72 void PI();
73
74 /**
75 * Whitespace eater.
76 */
77 void S();
78
79 /**
80 * Optional whitespace eater.
81 */
82 void Sq();
83
84 /**
85 * XML Version spec
86 */
87 void VersionInfo();
88
89 /**
90 * Your basic equals sign with surrounding whitespace.
91 */
92 void Eq();
93
94 /**
95 * Read in an attribute value.
96 */
97 FString AttValue();
98
99 /**
100 * Read in the name of something.
101 */
102 FString Name();
103
104 /**
105 * Encoding decleration in the header
106 */
107 void EncodingDecl();
108
109 /**
110 * Standalone decleration in the header
111 */
112 void SDDecl();
113
114 bool isS( unsigned char c )
115 {
116 return ( c == 0x20 || c == 0x9 || c == 0xD || c == 0xA );
117 }
118 };
119}
120
121#endif
diff --git a/src/inprogress/xmlwriter.cpp b/src/inprogress/xmlwriter.cpp
new file mode 100644
index 0000000..23a5175
--- /dev/null
+++ b/src/inprogress/xmlwriter.cpp
@@ -0,0 +1,9 @@
1#include "xmlwriter.h"
2
3Bu::XmlWriter::XmlWriter()
4{
5}
6
7Bu::XmlWriter::~XmlWriter()
8{
9}
diff --git a/src/inprogress/xmlwriter.h b/src/inprogress/xmlwriter.h
new file mode 100644
index 0000000..796d6fb
--- /dev/null
+++ b/src/inprogress/xmlwriter.h
@@ -0,0 +1,22 @@
1#ifndef XML_WRITER_H
2#define XML_WRITER_H
3
4#include <stdint.h>
5
6namespace Bu
7{
8 /**
9 *
10 */
11 class XmlWriter
12 {
13 public:
14 XmlWriter();
15 virtual ~XmlWriter();
16
17 private:
18
19 };
20}
21
22#endif
diff --git a/src/old/xmldocument.cpp b/src/old/xmldocument.cpp
deleted file mode 100644
index d7867d5..0000000
--- a/src/old/xmldocument.cpp
+++ /dev/null
@@ -1,149 +0,0 @@
1#include <stdio.h>
2#include <stdlib.h>
3#include "xmlwriter.h"
4
5XmlDocument::XmlDocument( XmlNode *pRoot )
6{
7 this->pRoot = pRoot;
8 pCurrent = NULL;
9 bCompleted = (pRoot!=NULL);
10}
11
12XmlDocument::~XmlDocument()
13{
14 if( pRoot )
15 {
16 delete pRoot;
17 }
18}
19
20void XmlDocument::addNode( const char *sName, const char *sContent, bool bClose )
21{
22 if( pRoot == NULL )
23 {
24 // This is the first node, so ignore position and just insert it.
25 pCurrent = pRoot = new XmlNode( sName, NULL, sContent );
26 }
27 else
28 {
29 pCurrent = pCurrent->addChild( sName, sContent );
30 }
31
32 if( bClose )
33 {
34 closeNode();
35 }
36}
37
38void XmlDocument::setName( const char *sName )
39{
40 pCurrent->setName( sName );
41}
42
43bool XmlDocument::isCompleted()
44{
45 return bCompleted;
46}
47
48XmlNode *XmlDocument::getRoot()
49{
50 return pRoot;
51}
52
53XmlNode *XmlDocument::detatchRoot()
54{
55 XmlNode *pTemp = pRoot;
56 pRoot = NULL;
57 return pTemp;
58}
59
60XmlNode *XmlDocument::getCurrent()
61{
62 return pCurrent;
63}
64
65void XmlDocument::closeNode()
66{
67 if( pCurrent != NULL )
68 {
69 pCurrent = pCurrent->getParent();
70
71 if( pCurrent == NULL )
72 {
73 bCompleted = true;
74 }
75 }
76}
77
78void XmlDocument::addProperty( const char *sName, const char *sValue )
79{
80 if( pCurrent )
81 {
82 pCurrent->addProperty( sName, sValue );
83 }
84}
85
86void XmlDocument::addProperty( const char *sName, const unsigned char nValue )
87{
88 char buf[12];
89 sprintf( buf, "%hhi", nValue );
90 addProperty( sName, buf );
91}
92
93void XmlDocument::addProperty( const char *sName, const char nValue )
94{
95 char buf[12];
96 sprintf( buf, "%hhi", nValue );
97 addProperty( sName, buf );
98}
99
100void XmlDocument::addProperty( const char *sName, const unsigned short nValue )
101{
102 char buf[12];
103 sprintf( buf, "%hi", nValue );
104 addProperty( sName, buf );
105}
106
107void XmlDocument::addProperty( const char *sName, const short nValue )
108{
109 char buf[12];
110 sprintf( buf, "%hi", nValue );
111 addProperty( sName, buf );
112}
113
114void XmlDocument::addProperty( const char *sName, const int nValue )
115{
116 char buf[12];
117 sprintf( buf, "%d", nValue );
118 addProperty( sName, buf );
119}
120
121void XmlDocument::addProperty( const char *sName, const unsigned long nValue )
122{
123 char buf[12];
124 sprintf( buf, "%li", nValue );
125 addProperty( sName, buf );
126}
127
128void XmlDocument::addProperty( const char *sName, const long nValue )
129{
130 char buf[12];
131 sprintf( buf, "%li", nValue );
132 addProperty( sName, buf );
133}
134
135void XmlDocument::addProperty( const char *sName, const double dValue )
136{
137 char buf[40];
138 sprintf( buf, "%f", dValue );
139 addProperty( sName, buf );
140}
141
142void XmlDocument::setContent( const char *sContent )
143{
144 if( pCurrent )
145 {
146 pCurrent->setContent( sContent );
147 }
148}
149
diff --git a/src/old/xmldocument.h b/src/old/xmldocument.h
deleted file mode 100644
index 6671c41..0000000
--- a/src/old/xmldocument.h
+++ /dev/null
@@ -1,171 +0,0 @@
1#ifndef XMLDOCUMENT
2#define XMLDOCUMENT
3
4#include "xmlnode.h"
5
6/**
7 * Keeps track of an easily managed set of XmlNode information. Allows simple
8 * operations for logical writing to and reading from XML structures. Using
9 * already formed structures is simply done through the XmlNode structures,
10 * and the getRoot function here. Creation is performed through a simple set
11 * of operations that creates the data in a stream type format.
12 *@author Mike Buland
13 */
14class XmlDocument
15{
16public:
17 /**
18 * Construct either a blank XmlDocuemnt or construct a document around an
19 * existing XmlNode. Be careful, once an XmlNode is passed into a document
20 * the document takes over ownership and will delete it when the XmlDocument
21 * is deleted.
22 *@param pRoot The XmlNode to use as the root of this document, or NULL if
23 * you want to start a new document.
24 */
25 XmlDocument( XmlNode *pRoot=NULL );
26
27 /**
28 * Destroy all contained nodes.
29 */
30 virtual ~XmlDocument();
31
32 /**
33 * Add a new node to the document. The new node is appended to the end of
34 * the current context, i.e. XmlNode, and the new node, provided it isn't
35 * close as part of this operation, will become the current context.
36 *@param sName The name of the new node to add.
37 *@param sContent A content string to be placed inside of the new node.
38 *@param bClose Set this to true to close the node immediately after adding
39 * the node and setting the content and name. If this is set to true the
40 * node is appended, but the context node doesn't change.
41 */
42 void addNode( const char *sName=NULL, const char *sContent=NULL, bool bClose=false );
43
44 /**
45 * Set the name of the current node context.
46 *@param sName The new name of the node.
47 */
48 void setName( const char *sName );
49
50 /**
51 * Close the current node context. This will move the current context to
52 * the parent node of the former current node. If the current node was the
53 * root then the "completed" flag is set and no more operations are allowed.
54 */
55 void closeNode();
56
57 /**
58 * Change the content of the current node at the current position between
59 * nodes.
60 *@param sContent The new content of the current node.
61 */
62 void setContent( const char *sContent );
63
64 /**
65 * Add a named property to the current context node.
66 *@param sName The name of the property to add.
67 *@param sValue The string value of the property.
68 */
69 void addProperty( const char *sName, const char *sValue );
70
71 /**
72 * Add a named property to the current context node, converting the
73 * numerical parameter to text using standrd printf style conversion.
74 *@param sName The name of the property to add.
75 *@param nValue The numerical value to add.
76 */
77 void addProperty( const char *sName, const unsigned char nValue );
78
79 /**
80 * Add a named property to the current context node, converting the
81 * numerical parameter to text using standrd printf style conversion.
82 *@param sName The name of the property to add.
83 *@param nValue The numerical value to add.
84 */
85 void addProperty( const char *sName, const char nValue );
86
87 /**
88 * Add a named property to the current context node, converting the
89 * numerical parameter to text using standrd printf style conversion.
90 *@param sName The name of the property to add.
91 *@param nValue The numerical value to add.
92 */
93 void addProperty( const char *sName, const unsigned short nValue );
94
95 /**
96 * Add a named property to the current context node, converting the
97 * numerical parameter to text using standrd printf style conversion.
98 *@param sName The name of the property to add.
99 *@param nValue The numerical value to add.
100 */
101 void addProperty( const char *sName, const short nValue );
102
103 /**
104 * Add a named property to the current context node, converting the
105 * numerical parameter to text using standrd printf style conversion.
106 *@param sName The name of the property to add.
107 *@param nValue The numerical value to add.
108 */
109 void addProperty( const char *sName, const unsigned long nValue );
110
111 /**
112 * Add a named property to the current context node, converting the
113 * numerical parameter to text using standrd printf style conversion.
114 *@param sName The name of the property to add.
115 *@param nValue The numerical value to add.
116 */
117 void addProperty( const char *sName, const long nValue );
118
119 /**
120 * Add a named property to the current context node, converting the
121 * numerical parameter to text using standrd printf style conversion.
122 *@param sName The name of the property to add.
123 *@param nValue The numerical value to add.
124 */
125 void addProperty( const char *sName, const int nValue );
126
127 /**
128 * Add a named property to the current context node, converting the
129 * numerical parameter to text using standrd printf style conversion.
130 *@param sName The name of the property to add.
131 *@param dValue The numerical value to add.
132 */
133 void addProperty( const char *sName, const double dValue );
134
135 /**
136 * The XmlDocuemnt is considered completed if the root node has been closed.
137 * Once an XmlDocument has been completed, you can no longer perform
138 * operations on it.
139 *@return True if completed, false if still in progress.
140 */
141 bool isCompleted();
142
143 /**
144 * Get a pointer to the root object of this XmlDocument.
145 *@returns A pointer to an internally owned XmlNode. Do not delete this
146 * XmlNode.
147 */
148 XmlNode *getRoot();
149
150 /**
151 * Get a pointer to the root object of this XmlDocument, and remove the
152 * ownership from this object.
153 *@returns A pointer to an internally owned XmlNode. Do not delete this
154 * XmlNode.
155 */
156 XmlNode *detatchRoot();
157
158 /**
159 * Get the current context node, which could be the same as the root node.
160 *@returns A pointer to an internally owned XmlNode. Do not delete this
161 * XmlNode.
162 */
163 XmlNode *getCurrent();
164
165private:
166 XmlNode *pRoot; /**< The root node. */
167 XmlNode *pCurrent; /**< The current node. */
168 bool bCompleted; /**< Is it completed? */
169};
170
171#endif
diff --git a/src/old/xmlnode.cpp b/src/old/xmlnode.cpp
deleted file mode 100644
index b1ed9a9..0000000
--- a/src/old/xmlnode.cpp
+++ /dev/null
@@ -1,445 +0,0 @@
1#include "xmlnode.h"
2#include "hashfunctionstring.h"
3
4XmlNode::XmlNode( const char *sName, XmlNode *pParent, const char *sContent ) :
5 hProperties( new HashFunctionString(), 53, false ),
6 hChildren( new HashFunctionString(), 53, true )
7{
8 this->pParent = pParent;
9 if( sName != NULL )
10 {
11 setName( sName );
12 }
13 if( sContent != NULL )
14 {
15 this->sPreContent = new std::string( sContent );
16 }
17 else
18 {
19 this->sPreContent = NULL;
20 }
21 nCurContent = 0;
22}
23
24XmlNode::~XmlNode()
25{
26 for( int j = 0; j < lChildren.getSize(); j++ )
27 {
28 delete (XmlNode *)lChildren[j];
29 }
30 for( int j = 0; j < lPropNames.getSize(); j++ )
31 {
32 delete (std::string *)lPropNames[j];
33 }
34 for( int j = 0; j < lPropValues.getSize(); j++ )
35 {
36 delete (std::string *)lPropValues[j];
37 }
38 for( int j = 0; j < lPostContent.getSize(); j++ )
39 {
40 if( lPostContent[j] != NULL )
41 {
42 delete (std::string *)lPostContent[j];
43 }
44 }
45 if( sPreContent )
46 {
47 delete sPreContent;
48 }
49}
50
51void XmlNode::setName( const char *sName )
52{
53 if( pParent )
54 {
55 if( this->sName.size() == 0 )
56 {
57 // We're not in the hash yet, so add us
58 this->sName = sName;
59 pParent->hChildren.insert( this->sName.c_str(), this );
60 }
61 else
62 {
63 // Slightly more tricky, delete us, then add us...
64 pParent->hChildren.del( this->sName.c_str() );
65 this->sName = sName;
66 pParent->hChildren.insert( this->sName.c_str(), this );
67 }
68 }
69 else
70 {
71 // If we have no parent, then just set the name string, we don't need
72 // to worry about hashing.
73 this->sName = sName;
74 }
75}
76
77void XmlNode::setContent( const char *sContent, int nIndex )
78{
79 if( nIndex == -1 )
80 {
81 nIndex = nCurContent;
82 }
83 if( nIndex == 0 )
84 {
85 if( this->sPreContent )
86 {
87 delete this->sPreContent;
88 }
89
90 this->sPreContent = new std::string( sContent );
91 }
92 else
93 {
94 nIndex--;
95 if( lPostContent[nIndex] )
96 {
97 delete (std::string *)lPostContent[nIndex];
98 }
99
100 lPostContent.setAt( nIndex, new std::string( sContent ) );
101 }
102}
103
104const char *XmlNode::getContent( int nIndex )
105{
106 if( nIndex == 0 )
107 {
108 if( sPreContent )
109 {
110 return sPreContent->c_str();
111 }
112 }
113 else
114 {
115 nIndex--;
116 if( lPostContent[nIndex] )
117 {
118 return ((std::string *)lPostContent[nIndex])->c_str();
119 }
120 }
121
122 return NULL;
123}
124
125XmlNode *XmlNode::addChild( const char *sName, const char *sContent )
126{
127 return addChild( new XmlNode( sName, this, sContent ) );
128}
129
130XmlNode *XmlNode::addChild( XmlNode *pNode )
131{
132 lChildren.append( pNode );
133 lPostContent.append( NULL );
134 nCurContent++;
135 pNode->pParent = this;
136
137 return pNode;
138}
139
140XmlNode *XmlNode::getParent()
141{
142 return pParent;
143}
144
145void XmlNode::addProperty( const char *sName, const char *sValue )
146{
147 std::string *pName = new std::string( sName );
148 std::string *pValue = new std::string( sValue );
149
150 hProperties.insert( pName->c_str(), pValue->c_str() );
151 lPropNames.append( pName );
152 lPropValues.append( pValue );
153}
154
155int XmlNode::getNumProperties()
156{
157 return lPropNames.getSize();
158}
159
160const char *XmlNode::getPropertyName( int nIndex )
161{
162 std::string *tmp = ((std::string *)lPropNames[nIndex]);
163 if( tmp == NULL )
164 return NULL;
165 return tmp->c_str();
166}
167
168const char *XmlNode::getProperty( int nIndex )
169{
170 std::string *tmp = ((std::string *)lPropValues[nIndex]);
171 if( tmp == NULL )
172 return NULL;
173 return tmp->c_str();
174}
175
176const char *XmlNode::getProperty( const char *sName )
177{
178 const char *tmp = (const char *)hProperties[sName];
179 if( tmp == NULL )
180 return NULL;
181 return tmp;
182}
183
184void XmlNode::deleteProperty( int nIndex )
185{
186 hProperties.del( ((std::string *)lPropNames[nIndex])->c_str() );
187
188 delete (std::string *)lPropNames[nIndex];
189 delete (std::string *)lPropValues[nIndex];
190
191 lPropNames.deleteAt( nIndex );
192 lPropValues.deleteAt( nIndex );
193}
194
195bool XmlNode::hasChildren()
196{
197 return lChildren.getSize()>0;
198}
199
200int XmlNode::getNumChildren()
201{
202 return lChildren.getSize();
203}
204
205XmlNode *XmlNode::getChild( int nIndex )
206{
207 return (XmlNode *)lChildren[nIndex];
208}
209
210XmlNode *XmlNode::getChild( const char *sName, int nSkip )
211{
212 return (XmlNode *)hChildren.get( sName, nSkip );
213}
214
215const char *XmlNode::getName()
216{
217 return sName.c_str();
218}
219
220void XmlNode::deleteNode( int nIndex, const char *sReplacementText )
221{
222 XmlNode *xRet = detatchNode( nIndex, sReplacementText );
223
224 if( xRet != NULL )
225 {
226 delete xRet;
227 }
228}
229
230XmlNode *XmlNode::detatchNode( int nIndex, const char *sReplacementText )
231{
232 if( nIndex < 0 || nIndex >= lChildren.getSize() )
233 return NULL;
234
235 // The real trick when deleteing a node isn't actually deleting it, it's
236 // reforming the content around the node that's now missing...hmmm...
237
238 if( nIndex == 0 )
239 {
240 // If the index is zero we have to deal with the pre-content
241 if( sReplacementText )
242 {
243 if( sPreContent == NULL )
244 {
245 sPreContent = new std::string( sReplacementText );
246 }
247 else
248 {
249 *sPreContent += sReplacementText;
250 }
251 }
252 if( lPostContent.getSize() > 0 )
253 {
254 if( lPostContent[0] != NULL )
255 {
256 if( sPreContent == NULL )
257 {
258 sPreContent = new std::string(
259 ((std::string *)lPostContent[0])->c_str()
260 );
261 }
262 else
263 {
264 *sPreContent +=
265 ((std::string *)lPostContent[0])->c_str();
266 }
267 }
268 delete (std::string *)lPostContent[0];
269 lPostContent.deleteAt( 0 );
270 }
271 }
272 else
273 {
274 int nCont = nIndex-1;
275 // If it's above zero we deal with the post-content only
276 if( sReplacementText )
277 {
278 if( lPostContent[nCont] == NULL )
279 {
280 lPostContent.setAt( nCont, new std::string( sReplacementText ) );
281 }
282 else
283 {
284 *((std::string *)lPostContent[nCont]) += sReplacementText;
285 }
286 }
287 if( lPostContent.getSize() > nIndex )
288 {
289 if( lPostContent[nIndex] != NULL )
290 {
291 if( lPostContent[nCont] == NULL )
292 {
293 lPostContent.setAt( nCont, new std::string(
294 ((std::string *)lPostContent[nIndex])->c_str()
295 ) );
296 }
297 else
298 {
299 *((std::string *)lPostContent[nCont]) +=
300 ((std::string *)lPostContent[nIndex])->c_str();
301 }
302 }
303 delete (std::string *)lPostContent[nIndex];
304 lPostContent.deleteAt( nIndex );
305 }
306 }
307
308 XmlNode *xRet = (XmlNode *)lChildren[nIndex];
309 hChildren.del( ((XmlNode *)lChildren[nIndex])->getName() );
310 lChildren.deleteAt( nIndex );
311
312 return xRet;
313}
314
315void XmlNode::replaceNode( int nIndex, XmlNode *pNewNode )
316{
317 if( nIndex < 0 || nIndex >= lChildren.getSize() )
318 return; //TODO: throw an exception
319
320 delete (XmlNode *)lChildren[nIndex];
321 lChildren.setAt( nIndex, pNewNode );
322 pNewNode->pParent = this;
323}
324
325XmlNode *XmlNode::getCopy()
326{
327 XmlNode *pNew = new XmlNode();
328
329 pNew->sName = sName;
330 if( sPreContent )
331 {
332 pNew->sPreContent = new std::string( sPreContent->c_str() );
333 }
334 else
335 {
336 pNew->sPreContent = NULL;
337 }
338 pNew->nCurContent = 0;
339
340 int nSize = lPostContent.getSize();
341 pNew->lPostContent.setSize( nSize );
342 for( int j = 0; j < nSize; j++ )
343 {
344 if( lPostContent[j] )
345 {
346 pNew->lPostContent.setAt(
347 j, new std::string(
348 ((std::string *)lPostContent[j])->c_str()
349 )
350 );
351 }
352 else
353 {
354 pNew->lPostContent.setAt( j, NULL );
355 }
356 }
357
358 nSize = lChildren.getSize();
359 pNew->lChildren.setSize( nSize );
360 for( int j = 0; j < nSize; j++ )
361 {
362 XmlNode *pChild = ((XmlNode *)lChildren[j])->getCopy();
363 pNew->lChildren.setAt( j, pChild );
364 pChild->pParent = pNew;
365 pNew->hChildren.insert( pChild->getName(), pChild );
366 }
367
368 nSize = lPropNames.getSize();
369 pNew->lPropNames.setSize( nSize );
370 pNew->lPropValues.setSize( nSize );
371 for( int j = 0; j < nSize; j++ )
372 {
373 std::string *pProp = new std::string( ((std::string *)lPropNames[j])->c_str() );
374 std::string *pVal = new std::string( ((std::string *)lPropValues[j])->c_str() );
375 pNew->lPropNames.setAt( j, pProp );
376 pNew->lPropValues.setAt( j, pVal );
377 pNew->hProperties.insert( pProp->c_str(), pVal->c_str() );
378 pNew->nCurContent++;
379 }
380
381 return pNew;
382}
383
384void XmlNode::deleteNodeKeepChildren( int nIndex )
385{
386 // This is a tricky one...we need to do some patching to keep things all
387 // even...
388 XmlNode *xRet = (XmlNode *)lChildren[nIndex];
389
390 if( xRet == NULL )
391 {
392 return;
393 }
394 else
395 {
396 if( getContent( nIndex ) )
397 {
398 std::string sBuf( getContent( nIndex ) );
399 sBuf += xRet->getContent( 0 );
400 setContent( sBuf.c_str(), nIndex );
401 }
402 else
403 {
404 setContent( xRet->getContent( 0 ), nIndex );
405 }
406
407 int nSize = xRet->lChildren.getSize();
408 for( int j = 0; j < nSize; j++ )
409 {
410 XmlNode *pCopy = ((XmlNode *)xRet->lChildren[j])->getCopy();
411 pCopy->pParent = this;
412 lChildren.insertBefore( pCopy, nIndex+j );
413
414 if( xRet->lPostContent[j] )
415 {
416 lPostContent.insertBefore(
417 new std::string( ((std::string *)xRet->lPostContent[j])->c_str() ),
418 nIndex+j
419 );
420 }
421 else
422 {
423 lPostContent.insertBefore( NULL, nIndex+j );
424 }
425 }
426
427 if( getContent( nIndex+nSize ) )
428 {
429 //SString sBuf( getContent( nIndex+nSize ) );
430 //sBuf.catfrom( xRet->getContent( nSize ) );
431 //setContent( sBuf, nIndex+nSize );
432 }
433 else
434 {
435 setContent( xRet->getContent( nSize ), nIndex+nSize );
436 }
437
438 deleteNode( nIndex+nSize );
439 }
440}
441
442void XmlNode::replaceNodeWithChildren( int nIndex, XmlNode *pNewNode )
443{
444}
445
diff --git a/src/old/xmlnode.h b/src/old/xmlnode.h
deleted file mode 100644
index 7525306..0000000
--- a/src/old/xmlnode.h
+++ /dev/null
@@ -1,236 +0,0 @@
1#ifndef XMLNODE
2#define XMLNODE
3
4#include <iostream>
5#include "linkedlist.h"
6#include "hashtable.h"
7
8/**
9 * Maintains all data pertient to an XML node, including sub-nodes and content.
10 * All child nodes can be accessed through index and through name via a hash
11 * table. This makes it very easy to gain simple and fast access to all of
12 * your data. For most applications, the memory footprint is also rather
13 * small. While XmlNode objects can be used directly to create XML structures
14 * it is highly reccomended that all operations be performed through the
15 * XmlDocument class.
16 *@author Mike Buland
17 */
18class XmlNode
19{
20public:
21 /**
22 * Construct a new XmlNode.
23 *@param sName The name of the node.
24 *@param pParent The parent node.
25 *@param sContent The initial content string.
26 */
27 XmlNode(
28 const char *sName=NULL,
29 XmlNode *pParent = NULL,
30 const char *sContent=NULL
31 );
32
33 /**
34 * Delete the node and cleanup all memory.
35 */
36 virtual ~XmlNode();
37
38 /**
39 * Change the name of the node.
40 *@param sName The new name of the node.
41 */
42 void setName( const char *sName );
43
44 /**
45 * Construct a new node and add it as a child to this node, also return a
46 * pointer to the newly constructed node.
47 *@param sName The name of the new node.
48 *@param sContent The initial content of the new node.
49 *@returns A pointer to the newly created child node.
50 */
51 XmlNode *addChild( const char *sName, const char *sContent=NULL );
52
53 /**
54 * Add an already created XmlNode as a child to this node. The new child
55 * XmlNode's parent will be changed appropriately and the parent XmlNode
56 * will take ownership of the child.
57 *@param pChild The child XmlNode to add to this XmlNode.
58 *@returns A pointer to the child node that was just added.
59 */
60 XmlNode *addChild( XmlNode *pChild );
61
62 /**
63 * Add a new property to the XmlNode. Properties are name/value pairs.
64 *@param sName The name of the property. Specifying a name that's already
65 * in use will overwrite that property.
66 *@param sValue The textual value of the property.
67 */
68 void addProperty( const char *sName, const char *sValue );
69
70 /**
71 * Get a pointer to the parent node, if any.
72 *@returns A pointer to the node's parent, or NULL if there isn't one.
73 */
74 XmlNode *getParent();
75
76 /**
77 * Tells you if this node has children.
78 *@returns True if this node has at least one child, false otherwise.
79 */
80 bool hasChildren();
81
82 /**
83 * Tells you how many children this node has.
84 *@returns The number of children this node has.
85 */
86 int getNumChildren();
87
88 /**
89 * Get a child node at a specific index.
90 *@param nIndex The zero-based index of the child to retreive.
91 *@returns A pointer to the child, or NULL if you requested an invalid
92 * index.
93 */
94 XmlNode *getChild( int nIndex );
95
96 /**
97 * Get a child with the specified name, and possibly skip value. For an
98 * explination of skip values see the HashTable.
99 *@param sName The name of the child to find.
100 *@param nSkip The number of nodes with that name to skip.
101 *@returns A pointer to the child, or NULL if no child with that name was
102 * found.
103 */
104 XmlNode *getChild( const char *sName, int nSkip=0 );
105
106 /**
107 * Get a pointer to the name of this node. Do not change this, use setName
108 * instead.
109 *@returns A pointer to the name of this node.
110 */
111 const char *getName();
112
113 /**
114 * Set the content of this node, optionally at a specific index. Using the
115 * default of -1 will set the content after the last added node.
116 *@param sContent The content string to use.
117 *@param nIndex The index of the content.
118 */
119 void setContent( const char *sContent, int nIndex=-1 );
120
121 /**
122 * Get the content string at a given index, or zero for initial content.
123 *@param nIndex The index of the content.
124 *@returns A pointer to the content at that location.
125 */
126 const char *getContent( int nIndex = 0 );
127
128 /**
129 * Get the number of properties in this node.
130 *@returns The number of properties in this node.
131 */
132 int getNumProperties();
133
134 /**
135 * Get a property's name by index.
136 *@param nIndex The index of the property to examine.
137 *@returns A pointer to the name of the property specified, or NULL if none
138 * found.
139 */
140 const char *getPropertyName( int nIndex );
141
142 /**
143 * Get a proprty's value by index.
144 *@param nIndex The index of the property to examine.
145 *@returns A pointer to the value of the property specified, or NULL if none
146 * found.
147 */
148 const char *getProperty( int nIndex );
149
150 /**
151 * Get a propery's value by name.
152 *@param sName The name of the property to examine.
153 *@returns A pointer to the value of the property specified, or NULL if none
154 * found.
155 */
156 const char *getProperty( const char *sName );
157
158 /**
159 * Delete a property by index.
160 *@param nIndex The index of the property to delete.
161 *@returns True if the property was found and deleted, false if it wasn't
162 * found.
163 */
164 void deleteProperty( int nIndex );
165
166 /**
167 * Delete a child node, possibly replacing it with some text. This actually
168 * fixes all content strings around the newly deleted child node.
169 *@param nIndex The index of the node to delete.
170 *@param sReplacementText The optional text to replace the node with.
171 *@returns True of the node was found, and deleted, false if it wasn't
172 * found.
173 */
174 void deleteNode( int nIndex, const char *sReplacementText = NULL );
175
176 /**
177 * Delete a given node, but move all of it's children and content up to
178 * replace the deleted node. All of the content of the child node is
179 * spliced seamlessly into place with the parent node's content.
180 *@param nIndex The node to delete.
181 *@returns True if the node was found and deleted, false if it wasn't.
182 */
183 void deleteNodeKeepChildren( int nIndex );
184
185 /**
186 * Detatch a given child node from this node. This effectively works just
187 * like a deleteNode, except that instead of deleting the node it is removed
188 * and returned, and all ownership is given up.
189 *@param nIndex The index of the node to detatch.
190 *@param sReplacementText The optional text to replace the detatched node
191 * with.
192 *@returns A pointer to the newly detatched node, which then passes
193 * ownership to the caller.
194 */
195 XmlNode *detatchNode( int nIndex, const char *sReplacementText = NULL );
196
197 /**
198 * Replace a given node with a different node that is not currently owned by
199 * this XmlNode or any ancestor.
200 *@param nIndex The index of the node to replace.
201 *@param pNewNode The new node to replace the old node with.
202 *@returns True if the node was found and replaced, false if it wasn't.
203 */
204 void replaceNode( int nIndex, XmlNode *pNewNode );
205
206 /**
207 * Replace a given node with the children and content of a given node.
208 *@param nIndex The index of the node to replace.
209 *@param pNewNode The node that contains the children and content that will
210 * replace the node specified by nIndex.
211 *@returns True if the node was found and replaced, false if it wasn't.
212 */
213 void replaceNodeWithChildren( int nIndex, XmlNode *pNewNode );
214
215 /**
216 * Get a copy of this node and all children. getCopy is recursive, so
217 * beware copying large trees of xml.
218 *@returns A newly created copy of this node and all of it's children.
219 */
220 XmlNode *getCopy();
221
222private:
223 std::string sName; /**< The name of the node. */
224 std::string *sPreContent; /**< The content that goes before any node. */
225 LinkedList lChildren; /**< The children. */
226 LinkedList lPostContent; /**< The content that comes after children. */
227 HashTable hProperties; /**< Property hashtable. */
228 HashTable hChildren; /**< Children hashtable. */
229 LinkedList lPropNames; /**< List of property names. */
230 LinkedList lPropValues; /**< List of property values. */
231 XmlNode *pParent; /**< A pointer to the parent of this node. */
232 int nCurContent; /**< The current content we're on, for using the -1 on
233 setContent. */
234};
235
236#endif
diff --git a/src/old/xmlreader.cpp b/src/old/xmlreader.cpp
deleted file mode 100644
index 18df69c..0000000
--- a/src/old/xmlreader.cpp
+++ /dev/null
@@ -1,602 +0,0 @@
1#include "xmlreader.h"
2#include "exceptions.h"
3#include <string.h>
4#include "hashfunctionstring.h"
5
6XmlReader::XmlReader( bool bStrip ) :
7 bStrip( bStrip ),
8 htEntity( new HashFunctionString(), 11 )
9{
10}
11
12XmlReader::~XmlReader()
13{
14 void *i = htEntity.getFirstItemPos();
15 while( (i = htEntity.getNextItemPos( i ) ) )
16 {
17 free( (char *)(htEntity.getItemID( i )) );
18 delete (StaticString *)htEntity.getItemData( i );
19 }
20}
21
22void XmlReader::addEntity( const char *name, const char *value )
23{
24 if( htEntity[name] ) return;
25
26 char *sName = strdup( name );
27 StaticString *sValue = new StaticString( value );
28
29 htEntity.insert( sName, sValue );
30}
31
32#define gcall( x ) if( x == false ) return false;
33
34bool XmlReader::isws( char chr )
35{
36 return ( chr == ' ' || chr == '\t' || chr == '\n' || chr == '\r' );
37}
38
39bool XmlReader::ws()
40{
41 while( true )
42 {
43 char chr = getChar();
44 if( isws( chr ) )
45 {
46 usedChar();
47 }
48 else
49 {
50 return true;
51 }
52 }
53 return true;
54}
55
56bool XmlReader::buildDoc()
57{
58 // take care of initial whitespace
59 gcall( ws() );
60 textDecl();
61 entity();
62 addEntity("gt", ">");
63 addEntity("lt", "<");
64 addEntity("amp", "&");
65 addEntity("apos", "\'");
66 addEntity("quot", "\"");
67 gcall( node() );
68
69 return true;
70}
71
72void XmlReader::textDecl()
73{
74 if( getChar() == '<' && getChar( 1 ) == '?' )
75 {
76 usedChar( 2 );
77 for(;;)
78 {
79 if( getChar() == '?' )
80 {
81 if( getChar( 1 ) == '>' )
82 {
83 usedChar( 2 );
84 return;
85 }
86 }
87 usedChar();
88 }
89 }
90}
91
92void XmlReader::entity()
93{
94 for(;;)
95 {
96 ws();
97
98 if( getChar() == '<' && getChar( 1 ) == '!' )
99 {
100 usedChar( 2 );
101 ws();
102 std::string buf;
103 for(;;)
104 {
105 char chr = getChar();
106 usedChar();
107 if( isws( chr ) ) break;
108 buf += chr;
109 }
110
111 if( strcmp( buf.c_str(), "ENTITY") == 0 )
112 {
113 ws();
114 std::string name;
115 for(;;)
116 {
117 char chr = getChar();
118 usedChar();
119 if( isws( chr ) ) break;
120 name += chr;
121 }
122 ws();
123 char quot = getChar();
124 usedChar();
125 if( quot != '\'' && quot != '\"' )
126 {
127 throw XmlException(
128 "Only quoted entity values are supported."
129 );
130 }
131 std::string value;
132 for(;;)
133 {
134 char chr = getChar();
135 usedChar();
136 if( chr == '&' )
137 {
138 StaticString *tmp = getEscape();
139 if( tmp == NULL ) throw XmlException("Entity thing");
140 value += tmp->getString();
141 delete tmp;
142 }
143 else if( chr == quot )
144 {
145 break;
146 }
147 else
148 {
149 value += chr;
150 }
151 }
152 ws();
153 if( getChar() == '>' )
154 {
155 usedChar();
156
157 addEntity( name.c_str(), value.c_str() );
158 }
159 else
160 {
161 throw XmlException(
162 "Malformed ENTITY: unexpected '%c' found.",
163 getChar()
164 );
165 }
166 }
167 else
168 {
169 throw XmlException(
170 "Unsupported header symbol: %s",
171 buf.c_str()
172 );
173 }
174 }
175 else
176 {
177 return;
178 }
179 }
180}
181
182bool XmlReader::node()
183{
184 gcall( startNode() )
185
186 // At this point, we are closing the startNode
187 char chr = getChar();
188 if( chr == '>' )
189 {
190 usedChar();
191
192 // Now we process the guts of the node.
193 gcall( content() );
194 }
195 else if( chr == '/' )
196 {
197 // This is the tricky one, one more validation, then we close the node.
198 usedChar();
199 if( getChar() == '>' )
200 {
201 closeNode();
202 usedChar();
203 }
204 else
205 {
206 throw XmlException("Close node in singleNode malformed!");
207 }
208 }
209 else
210 {
211 throw XmlException("Close node expected, but not found.");
212 return false;
213 }
214
215 return true;
216}
217
218bool XmlReader::startNode()
219{
220 if( getChar() == '<' )
221 {
222 usedChar();
223
224 if( getChar() == '/' )
225 {
226 // Heh, it's actually a close node, go figure
227 FlexBuf fbName;
228 usedChar();
229 gcall( ws() );
230
231 while( true )
232 {
233 char chr = getChar();
234 if( isws( chr ) || chr == '>' )
235 {
236 // Here we actually compare the name we got to the name
237 // we already set, they have to match exactly.
238 if( !strcasecmp( getCurrent()->getName(), fbName.getData() ) )
239 {
240 closeNode();
241 break;
242 }
243 else
244 {
245 throw XmlException("Got a mismatched node close tag.");
246 }
247 }
248 else
249 {
250 fbName.appendData( chr );
251 usedChar();
252 }
253 }
254
255 gcall( ws() );
256 if( getChar() == '>' )
257 {
258 // Everything is cool.
259 usedChar();
260 }
261 else
262 {
263 throw XmlException("Got extra junk data instead of node close tag.");
264 }
265 }
266 else
267 {
268 // We're good, format is consistant
269 addNode();
270
271 // Skip extra whitespace
272 gcall( ws() );
273 gcall( name() );
274 gcall( ws() );
275 gcall( paramlist() );
276 gcall( ws() );
277 }
278 }
279 else
280 {
281 throw XmlException("Expected to find node opening char, '<'.");
282 }
283
284 return true;
285}
286
287bool XmlReader::name()
288{
289 FlexBuf fbName;
290
291 while( true )
292 {
293 char chr = getChar();
294 if( isws( chr ) || chr == '>' || chr == '/' )
295 {
296 setName( fbName.getData() );
297 return true;
298 }
299 else
300 {
301 fbName.appendData( chr );
302 usedChar();
303 }
304 }
305
306 return true;
307}
308
309bool XmlReader::paramlist()
310{
311 while( true )
312 {
313 char chr = getChar();
314 if( chr == '/' || chr == '>' )
315 {
316 return true;
317 }
318 else
319 {
320 gcall( param() );
321 gcall( ws() );
322 }
323 }
324
325 return true;
326}
327
328StaticString *XmlReader::getEscape()
329{
330 if( getChar( 1 ) == '#' )
331 {
332 // If the entity starts with a # it's a character escape code
333 int base = 10;
334 usedChar( 2 );
335 if( getChar() == 'x' )
336 {
337 base = 16;
338 usedChar();
339 }
340 char buf[4];
341 int j = 0;
342 for( j = 0; getChar() != ';'; j++ )
343 {
344 buf[j] = getChar();
345 usedChar();
346 }
347 usedChar();
348 buf[j] = '\0';
349 buf[0] = (char)strtol( buf, (char **)NULL, base );
350 buf[1] = '\0';
351
352 return new StaticString( buf );
353 }
354 else
355 {
356 // ...otherwise replace with the appropriate string...
357 std::string buf;
358 usedChar();
359 for(;;)
360 {
361 char cbuf = getChar();
362 usedChar();
363 if( cbuf == ';' ) break;
364 buf += cbuf;
365 }
366
367 StaticString *tmp = (StaticString *)htEntity[buf.c_str()];
368 if( tmp == NULL ) return NULL;
369
370 StaticString *ret = new StaticString( *tmp );
371 return ret;
372 }
373}
374
375bool XmlReader::param()
376{
377 FlexBuf fbName;
378 FlexBuf fbValue;
379
380 while( true )
381 {
382 char chr = getChar();
383 if( isws( chr ) || chr == '=' )
384 {
385 break;
386 }
387 else
388 {
389 fbName.appendData( chr );
390 usedChar();
391 }
392 }
393
394 gcall( ws() );
395
396 if( getChar() == '=' )
397 {
398 usedChar();
399
400 gcall( ws() );
401
402 char chr = getChar();
403 if( chr == '"' )
404 {
405 // Better quoted rhs
406 usedChar();
407
408 while( true )
409 {
410 chr = getChar();
411 if( chr == '"' )
412 {
413 usedChar();
414 addProperty( fbName.getData(), fbValue.getData() );
415 return true;
416 }
417 else
418 {
419 if( chr == '&' )
420 {
421 StaticString *tmp = getEscape();
422 if( tmp == NULL ) return false;
423 fbValue.appendData( tmp->getString() );
424 delete tmp;
425 }
426 else
427 {
428 fbValue.appendData( chr );
429 usedChar();
430 }
431 }
432 }
433 }
434 else
435 {
436 // Simple one-word rhs
437 while( true )
438 {
439 chr = getChar();
440 if( isws( chr ) || chr == '/' || chr == '>' )
441 {
442 addProperty( fbName.getData(), fbValue.getData() );
443 return true;
444 }
445 else
446 {
447 if( chr == '&' )
448 {
449 StaticString *tmp = getEscape();
450 if( tmp == NULL ) return false;
451 fbValue.appendData( tmp->getString() );
452 delete tmp;
453 }
454 else
455 {
456 fbValue.appendData( chr );
457 usedChar();
458 }
459 }
460 }
461 }
462 }
463 else
464 {
465 throw XmlException("Expected an equals to seperate the params.");
466 return false;
467 }
468
469 return true;
470}
471
472bool XmlReader::content()
473{
474 FlexBuf fbContent;
475
476 if( bStrip ) gcall( ws() );
477
478 while( true )
479 {
480 char chr = getChar();
481 if( chr == '<' )
482 {
483 if( getChar(1) == '/' )
484 {
485 if( fbContent.getLength() > 0 )
486 {
487 if( bStrip )
488 {
489 int j;
490 for( j = fbContent.getLength()-1; isws(fbContent.getData()[j]); j-- );
491 ((char *)fbContent.getData())[j+1] = '\0';
492 }
493 setContent( fbContent.getData() );
494 }
495 usedChar( 2 );
496 gcall( ws() );
497 FlexBuf fbName;
498 while( true )
499 {
500 chr = getChar();
501 if( isws( chr ) || chr == '>' )
502 {
503 if( !strcasecmp( getCurrent()->getName(), fbName.getData() ) )
504 {
505 closeNode();
506 break;
507 }
508 else
509 {
510 throw XmlException("Mismatched close tag found: <%s> to <%s>.", getCurrent()->getName(), fbName.getData() );
511 }
512 }
513 else
514 {
515 fbName.appendData( chr );
516 usedChar();
517 }
518 }
519 gcall( ws() );
520 if( getChar() == '>' )
521 {
522 usedChar();
523 return true;
524 }
525 else
526 {
527 throw XmlException("Malformed close tag.");
528 }
529 }
530 else if( getChar(1) == '!' )
531 {
532 // We know it's a comment, let's see if it's proper
533 if( getChar(2) != '-' ||
534 getChar(3) != '-' )
535 {
536 // Not a valid XML comment
537 throw XmlException("Malformed comment start tag found.");
538 }
539
540 usedChar( 4 );
541
542 // Now burn text until we find the close tag
543 for(;;)
544 {
545 if( getChar() == '-' )
546 {
547 if( getChar( 1 ) == '-' )
548 {
549 // The next one has to be a '>' now
550 if( getChar( 2 ) != '>' )
551 {
552 throw XmlException("Malformed comment close tag found. You cannot have a '--' that isn't followed by a '>' in a comment.");
553 }
554 usedChar( 3 );
555 break;
556 }
557 else
558 {
559 // Found a dash followed by a non dash, that's ok...
560 usedChar( 2 );
561 }
562 }
563 else
564 {
565 // Burn comment chars
566 usedChar();
567 }
568 }
569 }
570 else
571 {
572 if( fbContent.getLength() > 0 )
573 {
574 if( bStrip )
575 {
576 int j;
577 for( j = fbContent.getLength()-1; isws(fbContent.getData()[j]); j-- );
578 ((char *)fbContent.getData())[j+1] = '\0';
579 }
580 setContent( fbContent.getData() );
581 fbContent.clearData();
582 }
583 gcall( node() );
584 }
585
586 if( bStrip ) gcall( ws() );
587 }
588 else if( chr == '&' )
589 {
590 StaticString *tmp = getEscape();
591 if( tmp == NULL ) return false;
592 fbContent.appendData( tmp->getString() );
593 delete tmp;
594 }
595 else
596 {
597 fbContent.appendData( chr );
598 usedChar();
599 }
600 }
601}
602
diff --git a/src/old/xmlreader.h b/src/old/xmlreader.h
deleted file mode 100644
index c8f7202..0000000
--- a/src/old/xmlreader.h
+++ /dev/null
@@ -1,141 +0,0 @@
1#ifndef XMLREADER
2#define XMLREADER
3
4#include <stdio.h>
5#include "xmldocument.h"
6#include "flexbuf.h"
7#include "hashtable.h"
8#include "staticstring.h"
9
10/**
11 * Takes care of reading in xml formatted data from a file. This could/should
12 * be made more arbitrary in the future so that we can read the data from any
13 * source. This is actually made quite simple already since all data read in
14 * is handled by one single helper function and then palced into a FlexBuf for
15 * easy access by the other functions. The FlexBuf also allows for block
16 * reading from disk, which improves speed by a noticable amount.
17 * <br>
18 * There are also some extra features implemented that allow you to break the
19 * standard XML reader specs and eliminate leading and trailing whitespace in
20 * all read content. This is useful in situations where you allow additional
21 * whitespace in the files to make them easily human readable. The resturned
22 * content will be NULL in sitautions where all content between nodes was
23 * stripped.
24 *@author Mike Buland
25 */
26class XmlReader : public XmlDocument
27{
28public:
29 /**
30 * Create a standard XmlReader. The optional parameter bStrip allows you to
31 * create a reader that will strip out all leading and trailing whitespace
32 * in content, a-la html.
33 *@param bStrip Strip out leading and trailing whitespace?
34 */
35 XmlReader( bool bStrip=false );
36
37 /**
38 * Destroy this XmlReader.
39 */
40 virtual ~XmlReader();
41
42 /**
43 * Build a document based on some kind of input. This is called
44 * automatically by the constructor.
45 */
46 bool buildDoc();
47
48private:
49 /**
50 * This is called by the low level automoton in order to get the next
51 * character. This function should return a character at the current
52 * position plus nIndex, but does not increment the current character.
53 *@param nIndex The index of the character from the current stream position.
54 *@returns A single character at the requested position, or 0 for end of
55 * stream.
56 */
57 virtual char getChar( int nIndex = 0 ) = 0;
58
59 /**
60 * Called to increment the current stream position by a single character.
61 */
62 virtual void usedChar( int nAmnt = 1) = 0;
63
64 /**
65 * Automoton function: is whitespace.
66 *@param chr A character
67 *@returns True if chr is whitespace, false otherwise.
68 */
69 bool isws( char chr );
70
71 /**
72 * Automoton function: ws. Skips sections of whitespace.
73 *@returns True if everything was ok, False for end of stream.
74 */
75 bool ws();
76
77 /**
78 * Automoton function: node. Processes an XmlNode
79 *@returns True if everything was ok, False for end of stream.
80 */
81 bool node();
82
83 /**
84 * Automoton function: startNode. Processes the begining of a node.
85 *@returns True if everything was ok, False for end of stream.
86 */
87 bool startNode();
88
89 /**
90 * Automoton function: name. Processes the name of a node.
91 *@returns True if everything was ok, False for end of stream.
92 */
93 bool name();
94
95 /**
96 * Automoton function: textDecl. Processes the xml text decleration, if
97 * there is one.
98 */
99 void textDecl();
100
101 /**
102 * Automoton function: entity. Processes an entity from the header.
103 */
104 void entity();
105
106 /**
107 * Adds an entity to the list, if it doesn't already exist.
108 *@param name The name of the entity
109 *@param value The value of the entity
110 */
111 void addEntity( const char *name, const char *value );
112
113 StaticString *getEscape();
114
115 /**
116 * Automoton function: paramlist. Processes a list of node params.
117 *@returns True if everything was ok, False for end of stream.
118 */
119 bool paramlist();
120
121 /**
122 * Automoton function: param. Processes a single parameter.
123 *@returns True if everything was ok, False for end of stream.
124 */
125 bool param();
126
127 /**
128 * Automoton function: content. Processes node content.
129 *@returns True if everything was ok, False for end of stream.
130 */
131 bool content();
132
133 FlexBuf fbContent; /**< buffer for the current node's content. */
134 FlexBuf fbParamName; /**< buffer for the current param's name. */
135 FlexBuf fbParamValue; /**< buffer for the current param's value. */
136 bool bStrip; /**< Are we stripping whitespace? */
137
138 HashTable htEntity; /**< Entity type definitions. */
139};
140
141#endif
diff --git a/src/old/xmlwriter.cpp b/src/old/xmlwriter.cpp
deleted file mode 100644
index 56880b6..0000000
--- a/src/old/xmlwriter.cpp
+++ /dev/null
@@ -1,173 +0,0 @@
1#include <stdio.h>
2#include <stdlib.h>
3#include "xmlwriter.h"
4
5XmlWriter::XmlWriter( const char *sIndent, XmlNode *pRoot ) :
6 XmlDocument( pRoot )
7{
8 if( sIndent == NULL )
9 {
10 this->sIndent = "";
11 }
12 else
13 {
14 this->sIndent = sIndent;
15 }
16}
17
18XmlWriter::~XmlWriter()
19{
20}
21
22void XmlWriter::write()
23{
24 write( getRoot(), sIndent.c_str() );
25}
26
27void XmlWriter::write( XmlNode *pRoot, const char *sIndent )
28{
29 writeNode( pRoot, 0, sIndent );
30}
31
32void XmlWriter::closeNode()
33{
34 XmlDocument::closeNode();
35
36 if( isCompleted() )
37 {
38 write( getRoot(), sIndent.c_str() );
39 }
40}
41
42void XmlWriter::writeIndent( int nIndent, const char *sIndent )
43{
44 if( sIndent == NULL ) return;
45 for( int j = 0; j < nIndent; j++ )
46 {
47 writeString( sIndent );
48 }
49}
50
51std::string XmlWriter::escape( std::string sIn )
52{
53 std::string sOut;
54
55 std::string::const_iterator i;
56 for( i = sIn.begin(); i != sIn.end(); i++ )
57 {
58 if( ((*i >= ' ' && *i <= '9') ||
59 (*i >= 'a' && *i <= 'z') ||
60 (*i >= 'A' && *i <= 'Z') ) &&
61 (*i != '\"' && *i != '\'' && *i != '&')
62 )
63 {
64 sOut += *i;
65 }
66 else
67 {
68 sOut += "&#";
69 char buf[4];
70 sprintf( buf, "%u", (unsigned char)*i );
71 sOut += buf;
72 sOut += ';';
73 }
74 }
75
76 return sOut;
77}
78
79void XmlWriter::writeNodeProps( XmlNode *pNode, int nIndent, const char *sIndent )
80{
81 for( int j = 0; j < pNode->getNumProperties(); j++ )
82 {
83 writeString(" ");
84 writeString( pNode->getPropertyName( j ) );
85 writeString("=\"");
86 writeString( escape( pNode->getProperty( j ) ).c_str() );
87 writeString("\"");
88 }
89}
90
91void XmlWriter::writeNode( XmlNode *pNode, int nIndent, const char *sIndent )
92{
93 if( pNode->hasChildren() )
94 {
95 writeIndent( nIndent, sIndent );
96 writeString("<");
97 writeString( pNode->getName() );
98 writeNodeProps( pNode, nIndent, sIndent );
99 if( sIndent )
100 writeString(">\n");
101 else
102 writeString(">");
103
104 if( pNode->getContent( 0 ) )
105 {
106 writeIndent( nIndent+1, sIndent );
107 if( sIndent )
108 {
109 writeString( pNode->getContent( 0 ) );
110 writeString("\n");
111 }
112 else
113 writeString( pNode->getContent( 0 ) );
114 }
115
116 int nNumChildren = pNode->getNumChildren();
117 for( int j = 0; j < nNumChildren; j++ )
118 {
119 writeNode( pNode->getChild( j ), nIndent+1, sIndent );
120 if( pNode->getContent( j+1 ) )
121 {
122 writeIndent( nIndent+1, sIndent );
123 if( sIndent )
124 {
125 writeString( pNode->getContent( j+1 ) );
126 writeString("\n");
127 }
128 else
129 writeString( pNode->getContent( j+1 ) );
130 }
131 }
132
133 writeIndent( nIndent, sIndent );
134 if( sIndent )
135 {
136 writeString("</");
137 writeString( pNode->getName() );
138 writeString(">\n");
139 }
140 else
141 {
142 writeString("</");
143 writeString( pNode->getName() );
144 writeString(">");
145 }
146 }
147 else if( pNode->getContent() )
148 {
149 writeIndent( nIndent, sIndent );
150 writeString("<");
151 writeString( pNode->getName() );
152 writeNodeProps( pNode, nIndent, sIndent );
153 writeString(">");
154 writeString( pNode->getContent() );
155 writeString("</");
156 writeString( pNode->getName() );
157 writeString(">");
158 if( sIndent )
159 writeString("\n");
160 }
161 else
162 {
163 writeIndent( nIndent, sIndent );
164 writeString("<");
165 writeString( pNode->getName() );
166 writeNodeProps( pNode, nIndent, sIndent );
167 if( sIndent )
168 writeString("/>\n");
169 else
170 writeString("/>");
171 }
172}
173
diff --git a/src/old/xmlwriter.h b/src/old/xmlwriter.h
deleted file mode 100644
index c48e810..0000000
--- a/src/old/xmlwriter.h
+++ /dev/null
@@ -1,96 +0,0 @@
1#ifndef XMLWRITER
2#define XMLWRITER
3
4#include "xmlnode.h"
5#include "xmldocument.h"
6
7/**
8 * Implements xml writing in the XML standard format. Also allows you to
9 * break that format and auto-indent your exported xml data for ease of
10 * reading. The auto-indenting will only be applied to sections that
11 * have no content of their own already. This means that except for
12 * whitespace all of your data will be preserved perfectly.
13 * You can create an XmlWriter object around a file, or access the static
14 * write function directly and just hand it a filename and a root XmlNode.
15 * When using an XmlWriter object the interface is identicle to that of
16 * the XmlDocument class, so reference that class for API info. However
17 * when the initial (or root) node is closed, and the document is finished
18 * the file will be created and written to automatically. The user can
19 * check to see if this is actually true by calling the isFinished
20 * function in the XmlDocument class.
21 *@author Mike Buland
22 */
23class XmlWriter : public XmlDocument
24{
25public:
26 /**
27 * Construct a standard XmlWriter.
28 *@param sIndent Set this to something other than NULL to include it as an
29 * indent before each node in the output that doesn't already have content.
30 * If you are using the whitespace stripping option in the XmlReader and set
31 * this to a tab or some spaces it will never effect the content of your
32 * file.
33 */
34 XmlWriter( const char *sIndent=NULL, XmlNode *pRoot=NULL );
35
36 /**
37 * Destroy the writer.
38 */
39 virtual ~XmlWriter();
40
41 /**
42 * This override of the parent class closeNode function calls the parent
43 * class, but also triggers a write operation when the final node is closed.
44 * This means that by checking the isCompleted() function the user may also
45 * check to see if their file has been written or not.
46 */
47 void closeNode();
48
49 void write();
50
51private:
52 std::string sIndent; /**< The indent string */
53
54 std::string escape( std::string sIn );
55
56 /**
57 * Write the file.
58 *@param pNode The root node
59 *@param sIndent The indent text.
60 */
61 void write( XmlNode *pNode, const char *sIndent=NULL );
62
63 /**
64 * Write a node in the file, including children.
65 *@param pNode The node to write.
66 *@param nIndent The indent level (the number of times to include sIndent)
67 *@param sIndent The indent text.
68 */
69 void writeNode( XmlNode *pNode, int nIndent, const char *sIndent );
70
71 /**
72 * Write the properties of a node.
73 *@param pNode The node who's properties to write.
74 *@param nIndent The indent level of the containing node
75 *@param sIndent The indent text.
76 */
77 void writeNodeProps( XmlNode *pNode, int nIndent, const char *sIndent );
78
79 /**
80 * Called to write the actual indent.
81 *@param nIndent The indent level.
82 *@param sIndent The indent text.
83 */
84 void writeIndent( int nIndent, const char *sIndent );
85
86 /**
87 * This is the function that must be overridden in order to use this class.
88 * It must write the null-terminated string sString, minus the mull,
89 * verbatum to it's output device. Adding extra characters for any reason
90 * will break the XML formatting.
91 *@param sString The string data to write to the output.
92 */
93 virtual void writeString( const char *sString ) = 0;
94};
95
96#endif
diff --git a/src/xmldocument.cpp b/src/xmldocument.cpp
index cb21826..d7867d5 100644
--- a/src/xmldocument.cpp
+++ b/src/xmldocument.cpp
@@ -1,9 +1,149 @@
1#include "xmldocument.h" 1#include <stdio.h>
2#include <stdlib.h>
3#include "xmlwriter.h"
2 4
3Bu::XmlDocument::XmlDocument() 5XmlDocument::XmlDocument( XmlNode *pRoot )
4{ 6{
7 this->pRoot = pRoot;
8 pCurrent = NULL;
9 bCompleted = (pRoot!=NULL);
5} 10}
6 11
7Bu::XmlDocument::~XmlDocument() 12XmlDocument::~XmlDocument()
8{ 13{
14 if( pRoot )
15 {
16 delete pRoot;
17 }
9} 18}
19
20void XmlDocument::addNode( const char *sName, const char *sContent, bool bClose )
21{
22 if( pRoot == NULL )
23 {
24 // This is the first node, so ignore position and just insert it.
25 pCurrent = pRoot = new XmlNode( sName, NULL, sContent );
26 }
27 else
28 {
29 pCurrent = pCurrent->addChild( sName, sContent );
30 }
31
32 if( bClose )
33 {
34 closeNode();
35 }
36}
37
38void XmlDocument::setName( const char *sName )
39{
40 pCurrent->setName( sName );
41}
42
43bool XmlDocument::isCompleted()
44{
45 return bCompleted;
46}
47
48XmlNode *XmlDocument::getRoot()
49{
50 return pRoot;
51}
52
53XmlNode *XmlDocument::detatchRoot()
54{
55 XmlNode *pTemp = pRoot;
56 pRoot = NULL;
57 return pTemp;
58}
59
60XmlNode *XmlDocument::getCurrent()
61{
62 return pCurrent;
63}
64
65void XmlDocument::closeNode()
66{
67 if( pCurrent != NULL )
68 {
69 pCurrent = pCurrent->getParent();
70
71 if( pCurrent == NULL )
72 {
73 bCompleted = true;
74 }
75 }
76}
77
78void XmlDocument::addProperty( const char *sName, const char *sValue )
79{
80 if( pCurrent )
81 {
82 pCurrent->addProperty( sName, sValue );
83 }
84}
85
86void XmlDocument::addProperty( const char *sName, const unsigned char nValue )
87{
88 char buf[12];
89 sprintf( buf, "%hhi", nValue );
90 addProperty( sName, buf );
91}
92
93void XmlDocument::addProperty( const char *sName, const char nValue )
94{
95 char buf[12];
96 sprintf( buf, "%hhi", nValue );
97 addProperty( sName, buf );
98}
99
100void XmlDocument::addProperty( const char *sName, const unsigned short nValue )
101{
102 char buf[12];
103 sprintf( buf, "%hi", nValue );
104 addProperty( sName, buf );
105}
106
107void XmlDocument::addProperty( const char *sName, const short nValue )
108{
109 char buf[12];
110 sprintf( buf, "%hi", nValue );
111 addProperty( sName, buf );
112}
113
114void XmlDocument::addProperty( const char *sName, const int nValue )
115{
116 char buf[12];
117 sprintf( buf, "%d", nValue );
118 addProperty( sName, buf );
119}
120
121void XmlDocument::addProperty( const char *sName, const unsigned long nValue )
122{
123 char buf[12];
124 sprintf( buf, "%li", nValue );
125 addProperty( sName, buf );
126}
127
128void XmlDocument::addProperty( const char *sName, const long nValue )
129{
130 char buf[12];
131 sprintf( buf, "%li", nValue );
132 addProperty( sName, buf );
133}
134
135void XmlDocument::addProperty( const char *sName, const double dValue )
136{
137 char buf[40];
138 sprintf( buf, "%f", dValue );
139 addProperty( sName, buf );
140}
141
142void XmlDocument::setContent( const char *sContent )
143{
144 if( pCurrent )
145 {
146 pCurrent->setContent( sContent );
147 }
148}
149
diff --git a/src/xmldocument.h b/src/xmldocument.h
index e16e3ea..6671c41 100644
--- a/src/xmldocument.h
+++ b/src/xmldocument.h
@@ -1,22 +1,171 @@
1#ifndef XML_DOCUMENT_H 1#ifndef XMLDOCUMENT
2#define XML_DOCUMENT_H 2#define XMLDOCUMENT
3 3
4#include <stdint.h> 4#include "xmlnode.h"
5 5
6namespace Bu 6/**
7 * Keeps track of an easily managed set of XmlNode information. Allows simple
8 * operations for logical writing to and reading from XML structures. Using
9 * already formed structures is simply done through the XmlNode structures,
10 * and the getRoot function here. Creation is performed through a simple set
11 * of operations that creates the data in a stream type format.
12 *@author Mike Buland
13 */
14class XmlDocument
7{ 15{
16public:
8 /** 17 /**
9 * 18 * Construct either a blank XmlDocuemnt or construct a document around an
19 * existing XmlNode. Be careful, once an XmlNode is passed into a document
20 * the document takes over ownership and will delete it when the XmlDocument
21 * is deleted.
22 *@param pRoot The XmlNode to use as the root of this document, or NULL if
23 * you want to start a new document.
10 */ 24 */
11 class XmlDocument 25 XmlDocument( XmlNode *pRoot=NULL );
12 {
13 public:
14 XmlDocument();
15 virtual ~XmlDocument();
16 26
17 private: 27 /**
28 * Destroy all contained nodes.
29 */
30 virtual ~XmlDocument();
31
32 /**
33 * Add a new node to the document. The new node is appended to the end of
34 * the current context, i.e. XmlNode, and the new node, provided it isn't
35 * close as part of this operation, will become the current context.
36 *@param sName The name of the new node to add.
37 *@param sContent A content string to be placed inside of the new node.
38 *@param bClose Set this to true to close the node immediately after adding
39 * the node and setting the content and name. If this is set to true the
40 * node is appended, but the context node doesn't change.
41 */
42 void addNode( const char *sName=NULL, const char *sContent=NULL, bool bClose=false );
43
44 /**
45 * Set the name of the current node context.
46 *@param sName The new name of the node.
47 */
48 void setName( const char *sName );
49
50 /**
51 * Close the current node context. This will move the current context to
52 * the parent node of the former current node. If the current node was the
53 * root then the "completed" flag is set and no more operations are allowed.
54 */
55 void closeNode();
56
57 /**
58 * Change the content of the current node at the current position between
59 * nodes.
60 *@param sContent The new content of the current node.
61 */
62 void setContent( const char *sContent );
63
64 /**
65 * Add a named property to the current context node.
66 *@param sName The name of the property to add.
67 *@param sValue The string value of the property.
68 */
69 void addProperty( const char *sName, const char *sValue );
70
71 /**
72 * Add a named property to the current context node, converting the
73 * numerical parameter to text using standrd printf style conversion.
74 *@param sName The name of the property to add.
75 *@param nValue The numerical value to add.
76 */
77 void addProperty( const char *sName, const unsigned char nValue );
78
79 /**
80 * Add a named property to the current context node, converting the
81 * numerical parameter to text using standrd printf style conversion.
82 *@param sName The name of the property to add.
83 *@param nValue The numerical value to add.
84 */
85 void addProperty( const char *sName, const char nValue );
86
87 /**
88 * Add a named property to the current context node, converting the
89 * numerical parameter to text using standrd printf style conversion.
90 *@param sName The name of the property to add.
91 *@param nValue The numerical value to add.
92 */
93 void addProperty( const char *sName, const unsigned short nValue );
94
95 /**
96 * Add a named property to the current context node, converting the
97 * numerical parameter to text using standrd printf style conversion.
98 *@param sName The name of the property to add.
99 *@param nValue The numerical value to add.
100 */
101 void addProperty( const char *sName, const short nValue );
102
103 /**
104 * Add a named property to the current context node, converting the
105 * numerical parameter to text using standrd printf style conversion.
106 *@param sName The name of the property to add.
107 *@param nValue The numerical value to add.
108 */
109 void addProperty( const char *sName, const unsigned long nValue );
110
111 /**
112 * Add a named property to the current context node, converting the
113 * numerical parameter to text using standrd printf style conversion.
114 *@param sName The name of the property to add.
115 *@param nValue The numerical value to add.
116 */
117 void addProperty( const char *sName, const long nValue );
118
119 /**
120 * Add a named property to the current context node, converting the
121 * numerical parameter to text using standrd printf style conversion.
122 *@param sName The name of the property to add.
123 *@param nValue The numerical value to add.
124 */
125 void addProperty( const char *sName, const int nValue );
126
127 /**
128 * Add a named property to the current context node, converting the
129 * numerical parameter to text using standrd printf style conversion.
130 *@param sName The name of the property to add.
131 *@param dValue The numerical value to add.
132 */
133 void addProperty( const char *sName, const double dValue );
134
135 /**
136 * The XmlDocuemnt is considered completed if the root node has been closed.
137 * Once an XmlDocument has been completed, you can no longer perform
138 * operations on it.
139 *@return True if completed, false if still in progress.
140 */
141 bool isCompleted();
142
143 /**
144 * Get a pointer to the root object of this XmlDocument.
145 *@returns A pointer to an internally owned XmlNode. Do not delete this
146 * XmlNode.
147 */
148 XmlNode *getRoot();
149
150 /**
151 * Get a pointer to the root object of this XmlDocument, and remove the
152 * ownership from this object.
153 *@returns A pointer to an internally owned XmlNode. Do not delete this
154 * XmlNode.
155 */
156 XmlNode *detatchRoot();
157
158 /**
159 * Get the current context node, which could be the same as the root node.
160 *@returns A pointer to an internally owned XmlNode. Do not delete this
161 * XmlNode.
162 */
163 XmlNode *getCurrent();
18 164
19 }; 165private:
20} 166 XmlNode *pRoot; /**< The root node. */
167 XmlNode *pCurrent; /**< The current node. */
168 bool bCompleted; /**< Is it completed? */
169};
21 170
22#endif 171#endif
diff --git a/src/xmlnode.cpp b/src/xmlnode.cpp
index 58ef5c5..b1ed9a9 100644
--- a/src/xmlnode.cpp
+++ b/src/xmlnode.cpp
@@ -1,9 +1,445 @@
1#include "xmlnode.h" 1#include "xmlnode.h"
2#include "hashfunctionstring.h"
2 3
3Bu::XmlNode::XmlNode() 4XmlNode::XmlNode( const char *sName, XmlNode *pParent, const char *sContent ) :
5 hProperties( new HashFunctionString(), 53, false ),
6 hChildren( new HashFunctionString(), 53, true )
4{ 7{
8 this->pParent = pParent;
9 if( sName != NULL )
10 {
11 setName( sName );
12 }
13 if( sContent != NULL )
14 {
15 this->sPreContent = new std::string( sContent );
16 }
17 else
18 {
19 this->sPreContent = NULL;
20 }
21 nCurContent = 0;
5} 22}
6 23
7Bu::XmlNode::~XmlNode() 24XmlNode::~XmlNode()
8{ 25{
26 for( int j = 0; j < lChildren.getSize(); j++ )
27 {
28 delete (XmlNode *)lChildren[j];
29 }
30 for( int j = 0; j < lPropNames.getSize(); j++ )
31 {
32 delete (std::string *)lPropNames[j];
33 }
34 for( int j = 0; j < lPropValues.getSize(); j++ )
35 {
36 delete (std::string *)lPropValues[j];
37 }
38 for( int j = 0; j < lPostContent.getSize(); j++ )
39 {
40 if( lPostContent[j] != NULL )
41 {
42 delete (std::string *)lPostContent[j];
43 }
44 }
45 if( sPreContent )
46 {
47 delete sPreContent;
48 }
9} 49}
50
51void XmlNode::setName( const char *sName )
52{
53 if( pParent )
54 {
55 if( this->sName.size() == 0 )
56 {
57 // We're not in the hash yet, so add us
58 this->sName = sName;
59 pParent->hChildren.insert( this->sName.c_str(), this );
60 }
61 else
62 {
63 // Slightly more tricky, delete us, then add us...
64 pParent->hChildren.del( this->sName.c_str() );
65 this->sName = sName;
66 pParent->hChildren.insert( this->sName.c_str(), this );
67 }
68 }
69 else
70 {
71 // If we have no parent, then just set the name string, we don't need
72 // to worry about hashing.
73 this->sName = sName;
74 }
75}
76
77void XmlNode::setContent( const char *sContent, int nIndex )
78{
79 if( nIndex == -1 )
80 {
81 nIndex = nCurContent;
82 }
83 if( nIndex == 0 )
84 {
85 if( this->sPreContent )
86 {
87 delete this->sPreContent;
88 }
89
90 this->sPreContent = new std::string( sContent );
91 }
92 else
93 {
94 nIndex--;
95 if( lPostContent[nIndex] )
96 {
97 delete (std::string *)lPostContent[nIndex];
98 }
99
100 lPostContent.setAt( nIndex, new std::string( sContent ) );
101 }
102}
103
104const char *XmlNode::getContent( int nIndex )
105{
106 if( nIndex == 0 )
107 {
108 if( sPreContent )
109 {
110 return sPreContent->c_str();
111 }
112 }
113 else
114 {
115 nIndex--;
116 if( lPostContent[nIndex] )
117 {
118 return ((std::string *)lPostContent[nIndex])->c_str();
119 }
120 }
121
122 return NULL;
123}
124
125XmlNode *XmlNode::addChild( const char *sName, const char *sContent )
126{
127 return addChild( new XmlNode( sName, this, sContent ) );
128}
129
130XmlNode *XmlNode::addChild( XmlNode *pNode )
131{
132 lChildren.append( pNode );
133 lPostContent.append( NULL );
134 nCurContent++;
135 pNode->pParent = this;
136
137 return pNode;
138}
139
140XmlNode *XmlNode::getParent()
141{
142 return pParent;
143}
144
145void XmlNode::addProperty( const char *sName, const char *sValue )
146{
147 std::string *pName = new std::string( sName );
148 std::string *pValue = new std::string( sValue );
149
150 hProperties.insert( pName->c_str(), pValue->c_str() );
151 lPropNames.append( pName );
152 lPropValues.append( pValue );
153}
154
155int XmlNode::getNumProperties()
156{
157 return lPropNames.getSize();
158}
159
160const char *XmlNode::getPropertyName( int nIndex )
161{
162 std::string *tmp = ((std::string *)lPropNames[nIndex]);
163 if( tmp == NULL )
164 return NULL;
165 return tmp->c_str();
166}
167
168const char *XmlNode::getProperty( int nIndex )
169{
170 std::string *tmp = ((std::string *)lPropValues[nIndex]);
171 if( tmp == NULL )
172 return NULL;
173 return tmp->c_str();
174}
175
176const char *XmlNode::getProperty( const char *sName )
177{
178 const char *tmp = (const char *)hProperties[sName];
179 if( tmp == NULL )
180 return NULL;
181 return tmp;
182}
183
184void XmlNode::deleteProperty( int nIndex )
185{
186 hProperties.del( ((std::string *)lPropNames[nIndex])->c_str() );
187
188 delete (std::string *)lPropNames[nIndex];
189 delete (std::string *)lPropValues[nIndex];
190
191 lPropNames.deleteAt( nIndex );
192 lPropValues.deleteAt( nIndex );
193}
194
195bool XmlNode::hasChildren()
196{
197 return lChildren.getSize()>0;
198}
199
200int XmlNode::getNumChildren()
201{
202 return lChildren.getSize();
203}
204
205XmlNode *XmlNode::getChild( int nIndex )
206{
207 return (XmlNode *)lChildren[nIndex];
208}
209
210XmlNode *XmlNode::getChild( const char *sName, int nSkip )
211{
212 return (XmlNode *)hChildren.get( sName, nSkip );
213}
214
215const char *XmlNode::getName()
216{
217 return sName.c_str();
218}
219
220void XmlNode::deleteNode( int nIndex, const char *sReplacementText )
221{
222 XmlNode *xRet = detatchNode( nIndex, sReplacementText );
223
224 if( xRet != NULL )
225 {
226 delete xRet;
227 }
228}
229
230XmlNode *XmlNode::detatchNode( int nIndex, const char *sReplacementText )
231{
232 if( nIndex < 0 || nIndex >= lChildren.getSize() )
233 return NULL;
234
235 // The real trick when deleteing a node isn't actually deleting it, it's
236 // reforming the content around the node that's now missing...hmmm...
237
238 if( nIndex == 0 )
239 {
240 // If the index is zero we have to deal with the pre-content
241 if( sReplacementText )
242 {
243 if( sPreContent == NULL )
244 {
245 sPreContent = new std::string( sReplacementText );
246 }
247 else
248 {
249 *sPreContent += sReplacementText;
250 }
251 }
252 if( lPostContent.getSize() > 0 )
253 {
254 if( lPostContent[0] != NULL )
255 {
256 if( sPreContent == NULL )
257 {
258 sPreContent = new std::string(
259 ((std::string *)lPostContent[0])->c_str()
260 );
261 }
262 else
263 {
264 *sPreContent +=
265 ((std::string *)lPostContent[0])->c_str();
266 }
267 }
268 delete (std::string *)lPostContent[0];
269 lPostContent.deleteAt( 0 );
270 }
271 }
272 else
273 {
274 int nCont = nIndex-1;
275 // If it's above zero we deal with the post-content only
276 if( sReplacementText )
277 {
278 if( lPostContent[nCont] == NULL )
279 {
280 lPostContent.setAt( nCont, new std::string( sReplacementText ) );
281 }
282 else
283 {
284 *((std::string *)lPostContent[nCont]) += sReplacementText;
285 }
286 }
287 if( lPostContent.getSize() > nIndex )
288 {
289 if( lPostContent[nIndex] != NULL )
290 {
291 if( lPostContent[nCont] == NULL )
292 {
293 lPostContent.setAt( nCont, new std::string(
294 ((std::string *)lPostContent[nIndex])->c_str()
295 ) );
296 }
297 else
298 {
299 *((std::string *)lPostContent[nCont]) +=
300 ((std::string *)lPostContent[nIndex])->c_str();
301 }
302 }
303 delete (std::string *)lPostContent[nIndex];
304 lPostContent.deleteAt( nIndex );
305 }
306 }
307
308 XmlNode *xRet = (XmlNode *)lChildren[nIndex];
309 hChildren.del( ((XmlNode *)lChildren[nIndex])->getName() );
310 lChildren.deleteAt( nIndex );
311
312 return xRet;
313}
314
315void XmlNode::replaceNode( int nIndex, XmlNode *pNewNode )
316{
317 if( nIndex < 0 || nIndex >= lChildren.getSize() )
318 return; //TODO: throw an exception
319
320 delete (XmlNode *)lChildren[nIndex];
321 lChildren.setAt( nIndex, pNewNode );
322 pNewNode->pParent = this;
323}
324
325XmlNode *XmlNode::getCopy()
326{
327 XmlNode *pNew = new XmlNode();
328
329 pNew->sName = sName;
330 if( sPreContent )
331 {
332 pNew->sPreContent = new std::string( sPreContent->c_str() );
333 }
334 else
335 {
336 pNew->sPreContent = NULL;
337 }
338 pNew->nCurContent = 0;
339
340 int nSize = lPostContent.getSize();
341 pNew->lPostContent.setSize( nSize );
342 for( int j = 0; j < nSize; j++ )
343 {
344 if( lPostContent[j] )
345 {
346 pNew->lPostContent.setAt(
347 j, new std::string(
348 ((std::string *)lPostContent[j])->c_str()
349 )
350 );
351 }
352 else
353 {
354 pNew->lPostContent.setAt( j, NULL );
355 }
356 }
357
358 nSize = lChildren.getSize();
359 pNew->lChildren.setSize( nSize );
360 for( int j = 0; j < nSize; j++ )
361 {
362 XmlNode *pChild = ((XmlNode *)lChildren[j])->getCopy();
363 pNew->lChildren.setAt( j, pChild );
364 pChild->pParent = pNew;
365 pNew->hChildren.insert( pChild->getName(), pChild );
366 }
367
368 nSize = lPropNames.getSize();
369 pNew->lPropNames.setSize( nSize );
370 pNew->lPropValues.setSize( nSize );
371 for( int j = 0; j < nSize; j++ )
372 {
373 std::string *pProp = new std::string( ((std::string *)lPropNames[j])->c_str() );
374 std::string *pVal = new std::string( ((std::string *)lPropValues[j])->c_str() );
375 pNew->lPropNames.setAt( j, pProp );
376 pNew->lPropValues.setAt( j, pVal );
377 pNew->hProperties.insert( pProp->c_str(), pVal->c_str() );
378 pNew->nCurContent++;
379 }
380
381 return pNew;
382}
383
384void XmlNode::deleteNodeKeepChildren( int nIndex )
385{
386 // This is a tricky one...we need to do some patching to keep things all
387 // even...
388 XmlNode *xRet = (XmlNode *)lChildren[nIndex];
389
390 if( xRet == NULL )
391 {
392 return;
393 }
394 else
395 {
396 if( getContent( nIndex ) )
397 {
398 std::string sBuf( getContent( nIndex ) );
399 sBuf += xRet->getContent( 0 );
400 setContent( sBuf.c_str(), nIndex );
401 }
402 else
403 {
404 setContent( xRet->getContent( 0 ), nIndex );
405 }
406
407 int nSize = xRet->lChildren.getSize();
408 for( int j = 0; j < nSize; j++ )
409 {
410 XmlNode *pCopy = ((XmlNode *)xRet->lChildren[j])->getCopy();
411 pCopy->pParent = this;
412 lChildren.insertBefore( pCopy, nIndex+j );
413
414 if( xRet->lPostContent[j] )
415 {
416 lPostContent.insertBefore(
417 new std::string( ((std::string *)xRet->lPostContent[j])->c_str() ),
418 nIndex+j
419 );
420 }
421 else
422 {
423 lPostContent.insertBefore( NULL, nIndex+j );
424 }
425 }
426
427 if( getContent( nIndex+nSize ) )
428 {
429 //SString sBuf( getContent( nIndex+nSize ) );
430 //sBuf.catfrom( xRet->getContent( nSize ) );
431 //setContent( sBuf, nIndex+nSize );
432 }
433 else
434 {
435 setContent( xRet->getContent( nSize ), nIndex+nSize );
436 }
437
438 deleteNode( nIndex+nSize );
439 }
440}
441
442void XmlNode::replaceNodeWithChildren( int nIndex, XmlNode *pNewNode )
443{
444}
445
diff --git a/src/xmlnode.h b/src/xmlnode.h
index cd9961a..7525306 100644
--- a/src/xmlnode.h
+++ b/src/xmlnode.h
@@ -1,22 +1,236 @@
1#ifndef XML_NODE_H 1#ifndef XMLNODE
2#define XML_NODE_H 2#define XMLNODE
3 3
4#include <stdint.h> 4#include <iostream>
5#include "linkedlist.h"
6#include "hashtable.h"
5 7
6namespace Bu 8/**
9 * Maintains all data pertient to an XML node, including sub-nodes and content.
10 * All child nodes can be accessed through index and through name via a hash
11 * table. This makes it very easy to gain simple and fast access to all of
12 * your data. For most applications, the memory footprint is also rather
13 * small. While XmlNode objects can be used directly to create XML structures
14 * it is highly reccomended that all operations be performed through the
15 * XmlDocument class.
16 *@author Mike Buland
17 */
18class XmlNode
7{ 19{
20public:
8 /** 21 /**
9 * 22 * Construct a new XmlNode.
23 *@param sName The name of the node.
24 *@param pParent The parent node.
25 *@param sContent The initial content string.
10 */ 26 */
11 class XmlNode 27 XmlNode(
12 { 28 const char *sName=NULL,
13 public: 29 XmlNode *pParent = NULL,
14 XmlNode(); 30 const char *sContent=NULL
15 virtual ~XmlNode(); 31 );
32
33 /**
34 * Delete the node and cleanup all memory.
35 */
36 virtual ~XmlNode();
37
38 /**
39 * Change the name of the node.
40 *@param sName The new name of the node.
41 */
42 void setName( const char *sName );
43
44 /**
45 * Construct a new node and add it as a child to this node, also return a
46 * pointer to the newly constructed node.
47 *@param sName The name of the new node.
48 *@param sContent The initial content of the new node.
49 *@returns A pointer to the newly created child node.
50 */
51 XmlNode *addChild( const char *sName, const char *sContent=NULL );
52
53 /**
54 * Add an already created XmlNode as a child to this node. The new child
55 * XmlNode's parent will be changed appropriately and the parent XmlNode
56 * will take ownership of the child.
57 *@param pChild The child XmlNode to add to this XmlNode.
58 *@returns A pointer to the child node that was just added.
59 */
60 XmlNode *addChild( XmlNode *pChild );
61
62 /**
63 * Add a new property to the XmlNode. Properties are name/value pairs.
64 *@param sName The name of the property. Specifying a name that's already
65 * in use will overwrite that property.
66 *@param sValue The textual value of the property.
67 */
68 void addProperty( const char *sName, const char *sValue );
69
70 /**
71 * Get a pointer to the parent node, if any.
72 *@returns A pointer to the node's parent, or NULL if there isn't one.
73 */
74 XmlNode *getParent();
75
76 /**
77 * Tells you if this node has children.
78 *@returns True if this node has at least one child, false otherwise.
79 */
80 bool hasChildren();
81
82 /**
83 * Tells you how many children this node has.
84 *@returns The number of children this node has.
85 */
86 int getNumChildren();
87
88 /**
89 * Get a child node at a specific index.
90 *@param nIndex The zero-based index of the child to retreive.
91 *@returns A pointer to the child, or NULL if you requested an invalid
92 * index.
93 */
94 XmlNode *getChild( int nIndex );
95
96 /**
97 * Get a child with the specified name, and possibly skip value. For an
98 * explination of skip values see the HashTable.
99 *@param sName The name of the child to find.
100 *@param nSkip The number of nodes with that name to skip.
101 *@returns A pointer to the child, or NULL if no child with that name was
102 * found.
103 */
104 XmlNode *getChild( const char *sName, int nSkip=0 );
105
106 /**
107 * Get a pointer to the name of this node. Do not change this, use setName
108 * instead.
109 *@returns A pointer to the name of this node.
110 */
111 const char *getName();
112
113 /**
114 * Set the content of this node, optionally at a specific index. Using the
115 * default of -1 will set the content after the last added node.
116 *@param sContent The content string to use.
117 *@param nIndex The index of the content.
118 */
119 void setContent( const char *sContent, int nIndex=-1 );
16 120
17 private: 121 /**
122 * Get the content string at a given index, or zero for initial content.
123 *@param nIndex The index of the content.
124 *@returns A pointer to the content at that location.
125 */
126 const char *getContent( int nIndex = 0 );
127
128 /**
129 * Get the number of properties in this node.
130 *@returns The number of properties in this node.
131 */
132 int getNumProperties();
133
134 /**
135 * Get a property's name by index.
136 *@param nIndex The index of the property to examine.
137 *@returns A pointer to the name of the property specified, or NULL if none
138 * found.
139 */
140 const char *getPropertyName( int nIndex );
141
142 /**
143 * Get a proprty's value by index.
144 *@param nIndex The index of the property to examine.
145 *@returns A pointer to the value of the property specified, or NULL if none
146 * found.
147 */
148 const char *getProperty( int nIndex );
149
150 /**
151 * Get a propery's value by name.
152 *@param sName The name of the property to examine.
153 *@returns A pointer to the value of the property specified, or NULL if none
154 * found.
155 */
156 const char *getProperty( const char *sName );
157
158 /**
159 * Delete a property by index.
160 *@param nIndex The index of the property to delete.
161 *@returns True if the property was found and deleted, false if it wasn't
162 * found.
163 */
164 void deleteProperty( int nIndex );
165
166 /**
167 * Delete a child node, possibly replacing it with some text. This actually
168 * fixes all content strings around the newly deleted child node.
169 *@param nIndex The index of the node to delete.
170 *@param sReplacementText The optional text to replace the node with.
171 *@returns True of the node was found, and deleted, false if it wasn't
172 * found.
173 */
174 void deleteNode( int nIndex, const char *sReplacementText = NULL );
175
176 /**
177 * Delete a given node, but move all of it's children and content up to
178 * replace the deleted node. All of the content of the child node is
179 * spliced seamlessly into place with the parent node's content.
180 *@param nIndex The node to delete.
181 *@returns True if the node was found and deleted, false if it wasn't.
182 */
183 void deleteNodeKeepChildren( int nIndex );
184
185 /**
186 * Detatch a given child node from this node. This effectively works just
187 * like a deleteNode, except that instead of deleting the node it is removed
188 * and returned, and all ownership is given up.
189 *@param nIndex The index of the node to detatch.
190 *@param sReplacementText The optional text to replace the detatched node
191 * with.
192 *@returns A pointer to the newly detatched node, which then passes
193 * ownership to the caller.
194 */
195 XmlNode *detatchNode( int nIndex, const char *sReplacementText = NULL );
196
197 /**
198 * Replace a given node with a different node that is not currently owned by
199 * this XmlNode or any ancestor.
200 *@param nIndex The index of the node to replace.
201 *@param pNewNode The new node to replace the old node with.
202 *@returns True if the node was found and replaced, false if it wasn't.
203 */
204 void replaceNode( int nIndex, XmlNode *pNewNode );
205
206 /**
207 * Replace a given node with the children and content of a given node.
208 *@param nIndex The index of the node to replace.
209 *@param pNewNode The node that contains the children and content that will
210 * replace the node specified by nIndex.
211 *@returns True if the node was found and replaced, false if it wasn't.
212 */
213 void replaceNodeWithChildren( int nIndex, XmlNode *pNewNode );
214
215 /**
216 * Get a copy of this node and all children. getCopy is recursive, so
217 * beware copying large trees of xml.
218 *@returns A newly created copy of this node and all of it's children.
219 */
220 XmlNode *getCopy();
18 221
19 }; 222private:
20} 223 std::string sName; /**< The name of the node. */
224 std::string *sPreContent; /**< The content that goes before any node. */
225 LinkedList lChildren; /**< The children. */
226 LinkedList lPostContent; /**< The content that comes after children. */
227 HashTable hProperties; /**< Property hashtable. */
228 HashTable hChildren; /**< Children hashtable. */
229 LinkedList lPropNames; /**< List of property names. */
230 LinkedList lPropValues; /**< List of property values. */
231 XmlNode *pParent; /**< A pointer to the parent of this node. */
232 int nCurContent; /**< The current content we're on, for using the -1 on
233 setContent. */
234};
21 235
22#endif 236#endif
diff --git a/src/xmlreader.cpp b/src/xmlreader.cpp
index bd241cf..18df69c 100644
--- a/src/xmlreader.cpp
+++ b/src/xmlreader.cpp
@@ -1,82 +1,176 @@
1#include "xmlreader.h" 1#include "xmlreader.h"
2#include "exceptions.h"
3#include <string.h>
4#include "hashfunctionstring.h"
2 5
3Bu::XmlReader::XmlReader( Bu::Stream &sIn ) : 6XmlReader::XmlReader( bool bStrip ) :
4 sIn( sIn ) 7 bStrip( bStrip ),
8 htEntity( new HashFunctionString(), 11 )
5{ 9{
6} 10}
7 11
8Bu::XmlReader::~XmlReader() 12XmlReader::~XmlReader()
9{ 13{
14 void *i = htEntity.getFirstItemPos();
15 while( (i = htEntity.getNextItemPos( i ) ) )
16 {
17 free( (char *)(htEntity.getItemID( i )) );
18 delete (StaticString *)htEntity.getItemData( i );
19 }
10} 20}
11 21
12const char *Bu::XmlReader::lookahead( int nAmnt ) 22void XmlReader::addEntity( const char *name, const char *value )
13{ 23{
14 if( sBuf.getSize() >= nAmnt ) 24 if( htEntity[name] ) return;
15 return sBuf.getStr();
16 25
17 int nNew = nAmnt - sBuf.getSize(); 26 char *sName = strdup( name );
18 char *buf = new char[nNew]; 27 StaticString *sValue = new StaticString( value );
19 sIn.read( buf, nNew );
20 sBuf.append( buf );
21 28
22 return sBuf.getStr(); 29 htEntity.insert( sName, sValue );
23} 30}
24 31
25void Bu::XmlReader::burn( int nAmnt ) 32#define gcall( x ) if( x == false ) return false;
26{
27 if( sBuf.getSize() < nAmnt )
28 {
29 lookahead( nAmnt );
30 }
31 33
32 //sBuf.remove( nAmnt ); 34bool XmlReader::isws( char chr )
35{
36 return ( chr == ' ' || chr == '\t' || chr == '\n' || chr == '\r' );
33} 37}
34 38
35void Bu::XmlReader::checkString( const char *str, int nLen ) 39bool XmlReader::ws()
36{ 40{
37 if( !strncmp( str, lookahead( nLen ), nLen ) ) 41 while( true )
38 { 42 {
39 burn( nLen ); 43 char chr = getChar();
40 return; 44 if( isws( chr ) )
45 {
46 usedChar();
47 }
48 else
49 {
50 return true;
51 }
41 } 52 }
42 53 return true;
43 throw Bu::ExceptionBase("Expected string '%s'", str );
44} 54}
45 55
46Bu::XmlNode *Bu::XmlReader::read() 56bool XmlReader::buildDoc()
47{ 57{
48 prolog(); 58 // take care of initial whitespace
49} 59 gcall( ws() );
60 textDecl();
61 entity();
62 addEntity("gt", ">");
63 addEntity("lt", "<");
64 addEntity("amp", "&");
65 addEntity("apos", "\'");
66 addEntity("quot", "\"");
67 gcall( node() );
50 68
51void Bu::XmlReader::prolog() 69 return true;
52{
53 XMLDecl();
54 Misc();
55} 70}
56 71
57void Bu::XmlReader::XMLDecl() 72void XmlReader::textDecl()
58{ 73{
59 checkString("<?xml", 5 ); 74 if( getChar() == '<' && getChar( 1 ) == '?' )
60 S(); 75 {
61 VersionInfo(); 76 usedChar( 2 );
62 EncodingDecl(); 77 for(;;)
63 SDDecl(); 78 {
64 Sq(); 79 if( getChar() == '?' )
65 checkString("?>", 2 ); 80 {
81 if( getChar( 1 ) == '>' )
82 {
83 usedChar( 2 );
84 return;
85 }
86 }
87 usedChar();
88 }
89 }
66} 90}
67 91
68void Bu::XmlReader::Misc() 92void XmlReader::entity()
69{ 93{
70 for(;;) 94 for(;;)
71 { 95 {
72 S(); 96 ws();
73 if( !strncmp("<!--", lookahead( 4 ), 4 ) ) 97
74 { 98 if( getChar() == '<' && getChar( 1 ) == '!' )
75 Comment();
76 }
77 else if( !strncmp("<?", lookahead( 2 ), 2 ) )
78 { 99 {
79 PI(); 100 usedChar( 2 );
101 ws();
102 std::string buf;
103 for(;;)
104 {
105 char chr = getChar();
106 usedChar();
107 if( isws( chr ) ) break;
108 buf += chr;
109 }
110
111 if( strcmp( buf.c_str(), "ENTITY") == 0 )
112 {
113 ws();
114 std::string name;
115 for(;;)
116 {
117 char chr = getChar();
118 usedChar();
119 if( isws( chr ) ) break;
120 name += chr;
121 }
122 ws();
123 char quot = getChar();
124 usedChar();
125 if( quot != '\'' && quot != '\"' )
126 {
127 throw XmlException(
128 "Only quoted entity values are supported."
129 );
130 }
131 std::string value;
132 for(;;)
133 {
134 char chr = getChar();
135 usedChar();
136 if( chr == '&' )
137 {
138 StaticString *tmp = getEscape();
139 if( tmp == NULL ) throw XmlException("Entity thing");
140 value += tmp->getString();
141 delete tmp;
142 }
143 else if( chr == quot )
144 {
145 break;
146 }
147 else
148 {
149 value += chr;
150 }
151 }
152 ws();
153 if( getChar() == '>' )
154 {
155 usedChar();
156
157 addEntity( name.c_str(), value.c_str() );
158 }
159 else
160 {
161 throw XmlException(
162 "Malformed ENTITY: unexpected '%c' found.",
163 getChar()
164 );
165 }
166 }
167 else
168 {
169 throw XmlException(
170 "Unsupported header symbol: %s",
171 buf.c_str()
172 );
173 }
80 } 174 }
81 else 175 else
82 { 176 {
@@ -85,182 +179,423 @@ void Bu::XmlReader::Misc()
85 } 179 }
86} 180}
87 181
88void Bu::XmlReader::Comment() 182bool XmlReader::node()
89{ 183{
90 checkString("<!--", 4 ); 184 gcall( startNode() )
91 for(;;) 185
186 // At this point, we are closing the startNode
187 char chr = getChar();
188 if( chr == '>' )
189 {
190 usedChar();
191
192 // Now we process the guts of the node.
193 gcall( content() );
194 }
195 else if( chr == '/' )
92 { 196 {
93 unsigned char c = *lookahead(1); 197 // This is the tricky one, one more validation, then we close the node.
94 if( c == '-' ) 198 usedChar();
199 if( getChar() == '>' )
95 { 200 {
96 if( lookahead(2)[1] == '-' ) 201 closeNode();
97 { 202 usedChar();
98 checkString("-->", 3 ); 203 }
99 return; 204 else
100 } 205 {
206 throw XmlException("Close node in singleNode malformed!");
101 } 207 }
102 burn( 1 );
103 } 208 }
209 else
210 {
211 throw XmlException("Close node expected, but not found.");
212 return false;
213 }
214
215 return true;
104} 216}
105 217
106void Bu::XmlReader::PI() 218bool XmlReader::startNode()
107{ 219{
108 checkString("<?", 2 ); 220 if( getChar() == '<' )
109 FString sName = Name();
110 printf("PI: %s\n---\n", sName.getStr() );
111 S();
112 for(int j = 0;; j++ )
113 { 221 {
114 if( !strncmp( "?>", lookahead(j+2)+j, 2 ) ) 222 usedChar();
223
224 if( getChar() == '/' )
115 { 225 {
116 burn( j+2 ); 226 // Heh, it's actually a close node, go figure
117 return; 227 FlexBuf fbName;
228 usedChar();
229 gcall( ws() );
230
231 while( true )
232 {
233 char chr = getChar();
234 if( isws( chr ) || chr == '>' )
235 {
236 // Here we actually compare the name we got to the name
237 // we already set, they have to match exactly.
238 if( !strcasecmp( getCurrent()->getName(), fbName.getData() ) )
239 {
240 closeNode();
241 break;
242 }
243 else
244 {
245 throw XmlException("Got a mismatched node close tag.");
246 }
247 }
248 else
249 {
250 fbName.appendData( chr );
251 usedChar();
252 }
253 }
254
255 gcall( ws() );
256 if( getChar() == '>' )
257 {
258 // Everything is cool.
259 usedChar();
260 }
261 else
262 {
263 throw XmlException("Got extra junk data instead of node close tag.");
264 }
118 } 265 }
266 else
267 {
268 // We're good, format is consistant
269 addNode();
270
271 // Skip extra whitespace
272 gcall( ws() );
273 gcall( name() );
274 gcall( ws() );
275 gcall( paramlist() );
276 gcall( ws() );
277 }
278 }
279 else
280 {
281 throw XmlException("Expected to find node opening char, '<'.");
119 } 282 }
283
284 return true;
120} 285}
121 286
122void Bu::XmlReader::S() 287bool XmlReader::name()
123{ 288{
124 for( int j = 0;; j++ ) 289 FlexBuf fbName;
290
291 while( true )
125 { 292 {
126 char c = *lookahead( 1 ); 293 char chr = getChar();
127 if( c == 0x20 || c == 0x9 || c == 0xD || c == 0xA ) 294 if( isws( chr ) || chr == '>' || chr == '/' )
128 continue; 295 {
129 if( j == 0 ) 296 setName( fbName.getData() );
130 throw ExceptionBase("Expected whitespace."); 297 return true;
131 return; 298 }
299 else
300 {
301 fbName.appendData( chr );
302 usedChar();
303 }
132 } 304 }
305
306 return true;
133} 307}
134 308
135void Bu::XmlReader::Sq() 309bool XmlReader::paramlist()
136{ 310{
137 for(;;) 311 while( true )
138 { 312 {
139 char c = *lookahead( 1 ); 313 char chr = getChar();
140 if( c == 0x20 || c == 0x9 || c == 0xD || c == 0xA ) 314 if( chr == '/' || chr == '>' )
141 continue; 315 {
142 return; 316 return true;
317 }
318 else
319 {
320 gcall( param() );
321 gcall( ws() );
322 }
143 } 323 }
324
325 return true;
144} 326}
145 327
146void Bu::XmlReader::VersionInfo() 328StaticString *XmlReader::getEscape()
147{ 329{
148 try 330 if( getChar( 1 ) == '#' )
149 { 331 {
150 S(); 332 // If the entity starts with a # it's a character escape code
151 checkString("version", 7 ); 333 int base = 10;
334 usedChar( 2 );
335 if( getChar() == 'x' )
336 {
337 base = 16;
338 usedChar();
339 }
340 char buf[4];
341 int j = 0;
342 for( j = 0; getChar() != ';'; j++ )
343 {
344 buf[j] = getChar();
345 usedChar();
346 }
347 usedChar();
348 buf[j] = '\0';
349 buf[0] = (char)strtol( buf, (char **)NULL, base );
350 buf[1] = '\0';
351
352 return new StaticString( buf );
152 } 353 }
153 catch( ExceptionBase &e ) 354 else
154 { 355 {
155 return; 356 // ...otherwise replace with the appropriate string...
357 std::string buf;
358 usedChar();
359 for(;;)
360 {
361 char cbuf = getChar();
362 usedChar();
363 if( cbuf == ';' ) break;
364 buf += cbuf;
365 }
366
367 StaticString *tmp = (StaticString *)htEntity[buf.c_str()];
368 if( tmp == NULL ) return NULL;
369
370 StaticString *ret = new StaticString( *tmp );
371 return ret;
156 } 372 }
157 Eq();
158 Bu::FString ver = AttValue();
159 if( ver != "1.1" )
160 throw ExceptionBase("Currently we only support xml version 1.1\n");
161} 373}
162 374
163void Bu::XmlReader::Eq() 375bool XmlReader::param()
164{ 376{
165 Sq(); 377 FlexBuf fbName;
166 checkString("=", 1 ); 378 FlexBuf fbValue;
167 Sq();
168}
169 379
170void Bu::XmlReader::EncodingDecl() 380 while( true )
171{
172 S();
173 try
174 {
175 checkString("encoding", 8 );
176 }
177 catch( ExceptionBase &e )
178 { 381 {
179 return; 382 char chr = getChar();
383 if( isws( chr ) || chr == '=' )
384 {
385 break;
386 }
387 else
388 {
389 fbName.appendData( chr );
390 usedChar();
391 }
180 } 392 }
181 393
182 Eq(); 394 gcall( ws() );
183 AttValue();
184}
185 395
186void Bu::XmlReader::SDDecl() 396 if( getChar() == '=' )
187{
188 S();
189 try
190 {
191 checkString("standalone", 10 );
192 }
193 catch( ExceptionBase &e )
194 { 397 {
195 return; 398 usedChar();
196 }
197 399
198 Eq(); 400 gcall( ws() );
199 AttValue();
200}
201 401
202Bu::FString Bu::XmlReader::AttValue() 402 char chr = getChar();
203{ 403 if( chr == '"' )
204 char q = *lookahead(1);
205 if( q == '\"' )
206 {
207 for( int j = 2;; j++ )
208 { 404 {
209 if( lookahead(j)[j-1] == '\"' ) 405 // Better quoted rhs
406 usedChar();
407
408 while( true )
210 { 409 {
211 Bu::FString ret( lookahead(j)+1, j-2 ); 410 chr = getChar();
212 burn( j ); 411 if( chr == '"' )
213 return ret; 412 {
413 usedChar();
414 addProperty( fbName.getData(), fbValue.getData() );
415 return true;
416 }
417 else
418 {
419 if( chr == '&' )
420 {
421 StaticString *tmp = getEscape();
422 if( tmp == NULL ) return false;
423 fbValue.appendData( tmp->getString() );
424 delete tmp;
425 }
426 else
427 {
428 fbValue.appendData( chr );
429 usedChar();
430 }
431 }
214 } 432 }
215 } 433 }
216 } 434 else
217 else if( q == '\'' )
218 {
219 for( int j = 2;; j++ )
220 { 435 {
221 if( lookahead(j)[j-1] == '\'' ) 436 // Simple one-word rhs
437 while( true )
222 { 438 {
223 Bu::FString ret( lookahead(j)+1, j-2 ); 439 chr = getChar();
224 burn( j ); 440 if( isws( chr ) || chr == '/' || chr == '>' )
225 return ret; 441 {
442 addProperty( fbName.getData(), fbValue.getData() );
443 return true;
444 }
445 else
446 {
447 if( chr == '&' )
448 {
449 StaticString *tmp = getEscape();
450 if( tmp == NULL ) return false;
451 fbValue.appendData( tmp->getString() );
452 delete tmp;
453 }
454 else
455 {
456 fbValue.appendData( chr );
457 usedChar();
458 }
459 }
226 } 460 }
227 } 461 }
228 } 462 }
463 else
464 {
465 throw XmlException("Expected an equals to seperate the params.");
466 return false;
467 }
229 468
230 throw ExceptionBase("Excpected either \' or \".\n"); 469 return true;
231} 470}
232 471
233Bu::FString Bu::XmlReader::Name() 472bool XmlReader::content()
234{ 473{
235 unsigned char c = *lookahead( 1 ); 474 FlexBuf fbContent;
236 if( c != ':' && c != '_' &&
237 (c < 'A' || c > 'Z') &&
238 (c < 'a' || c > 'z') &&
239 (c < 0xC0 || c > 0xD6 ) &&
240 (c < 0xD8 || c > 0xF6 ) &&
241 (c < 0xF8))
242 {
243 throw ExceptionBase("Invalid entity name starting character.");
244 }
245 475
246 for( int j = 1;; j++ ) 476 if( bStrip ) gcall( ws() );
477
478 while( true )
247 { 479 {
248 unsigned char c = lookahead(j+1)[j]; 480 char chr = getChar();
249 if( isS( c ) ) 481 if( chr == '<' )
482 {
483 if( getChar(1) == '/' )
484 {
485 if( fbContent.getLength() > 0 )
486 {
487 if( bStrip )
488 {
489 int j;
490 for( j = fbContent.getLength()-1; isws(fbContent.getData()[j]); j-- );
491 ((char *)fbContent.getData())[j+1] = '\0';
492 }
493 setContent( fbContent.getData() );
494 }
495 usedChar( 2 );
496 gcall( ws() );
497 FlexBuf fbName;
498 while( true )
499 {
500 chr = getChar();
501 if( isws( chr ) || chr == '>' )
502 {
503 if( !strcasecmp( getCurrent()->getName(), fbName.getData() ) )
504 {
505 closeNode();
506 break;
507 }
508 else
509 {
510 throw XmlException("Mismatched close tag found: <%s> to <%s>.", getCurrent()->getName(), fbName.getData() );
511 }
512 }
513 else
514 {
515 fbName.appendData( chr );
516 usedChar();
517 }
518 }
519 gcall( ws() );
520 if( getChar() == '>' )
521 {
522 usedChar();
523 return true;
524 }
525 else
526 {
527 throw XmlException("Malformed close tag.");
528 }
529 }
530 else if( getChar(1) == '!' )
531 {
532 // We know it's a comment, let's see if it's proper
533 if( getChar(2) != '-' ||
534 getChar(3) != '-' )
535 {
536 // Not a valid XML comment
537 throw XmlException("Malformed comment start tag found.");
538 }
539
540 usedChar( 4 );
541
542 // Now burn text until we find the close tag
543 for(;;)
544 {
545 if( getChar() == '-' )
546 {
547 if( getChar( 1 ) == '-' )
548 {
549 // The next one has to be a '>' now
550 if( getChar( 2 ) != '>' )
551 {
552 throw XmlException("Malformed comment close tag found. You cannot have a '--' that isn't followed by a '>' in a comment.");
553 }
554 usedChar( 3 );
555 break;
556 }
557 else
558 {
559 // Found a dash followed by a non dash, that's ok...
560 usedChar( 2 );
561 }
562 }
563 else
564 {
565 // Burn comment chars
566 usedChar();
567 }
568 }
569 }
570 else
571 {
572 if( fbContent.getLength() > 0 )
573 {
574 if( bStrip )
575 {
576 int j;
577 for( j = fbContent.getLength()-1; isws(fbContent.getData()[j]); j-- );
578 ((char *)fbContent.getData())[j+1] = '\0';
579 }
580 setContent( fbContent.getData() );
581 fbContent.clearData();
582 }
583 gcall( node() );
584 }
585
586 if( bStrip ) gcall( ws() );
587 }
588 else if( chr == '&' )
250 { 589 {
251 FString ret( lookahead(j+1), j+1 ); 590 StaticString *tmp = getEscape();
252 burn( j+1 ); 591 if( tmp == NULL ) return false;
253 return ret; 592 fbContent.appendData( tmp->getString() );
593 delete tmp;
254 } 594 }
255 if( c != ':' && c != '_' && c != '-' && c != '.' && c != 0xB7 && 595 else
256 (c < 'A' || c > 'Z') &&
257 (c < 'a' || c > 'z') &&
258 (c < '0' || c > '9') &&
259 (c < 0xC0 || c > 0xD6 ) &&
260 (c < 0xD8 || c > 0xF6 ) &&
261 (c < 0xF8))
262 { 596 {
263 throw ExceptionBase("Invalid character in name."); 597 fbContent.appendData( chr );
598 usedChar();
264 } 599 }
265 } 600 }
266} 601}
diff --git a/src/xmlreader.h b/src/xmlreader.h
index 708a386..c8f7202 100644
--- a/src/xmlreader.h
+++ b/src/xmlreader.h
@@ -1,121 +1,141 @@
1#ifndef XML_READER_H 1#ifndef XMLREADER
2#define XML_READER_H 2#define XMLREADER
3
4#include <stdio.h>
5#include "xmldocument.h"
6#include "flexbuf.h"
7#include "hashtable.h"
8#include "staticstring.h"
9
10/**
11 * Takes care of reading in xml formatted data from a file. This could/should
12 * be made more arbitrary in the future so that we can read the data from any
13 * source. This is actually made quite simple already since all data read in
14 * is handled by one single helper function and then palced into a FlexBuf for
15 * easy access by the other functions. The FlexBuf also allows for block
16 * reading from disk, which improves speed by a noticable amount.
17 * <br>
18 * There are also some extra features implemented that allow you to break the
19 * standard XML reader specs and eliminate leading and trailing whitespace in
20 * all read content. This is useful in situations where you allow additional
21 * whitespace in the files to make them easily human readable. The resturned
22 * content will be NULL in sitautions where all content between nodes was
23 * stripped.
24 *@author Mike Buland
25 */
26class XmlReader : public XmlDocument
27{
28public:
29 /**
30 * Create a standard XmlReader. The optional parameter bStrip allows you to
31 * create a reader that will strip out all leading and trailing whitespace
32 * in content, a-la html.
33 *@param bStrip Strip out leading and trailing whitespace?
34 */
35 XmlReader( bool bStrip=false );
3 36
4#include <stdint.h> 37 /**
5#include "bu/stream.h" 38 * Destroy this XmlReader.
6#include "bu/fstring.h" 39 */
7#include "bu/xmlnode.h" 40 virtual ~XmlReader();
41
42 /**
43 * Build a document based on some kind of input. This is called
44 * automatically by the constructor.
45 */
46 bool buildDoc();
47
48private:
49 /**
50 * This is called by the low level automoton in order to get the next
51 * character. This function should return a character at the current
52 * position plus nIndex, but does not increment the current character.
53 *@param nIndex The index of the character from the current stream position.
54 *@returns A single character at the requested position, or 0 for end of
55 * stream.
56 */
57 virtual char getChar( int nIndex = 0 ) = 0;
58
59 /**
60 * Called to increment the current stream position by a single character.
61 */
62 virtual void usedChar( int nAmnt = 1) = 0;
63
64 /**
65 * Automoton function: is whitespace.
66 *@param chr A character
67 *@returns True if chr is whitespace, false otherwise.
68 */
69 bool isws( char chr );
70
71 /**
72 * Automoton function: ws. Skips sections of whitespace.
73 *@returns True if everything was ok, False for end of stream.
74 */
75 bool ws();
76
77 /**
78 * Automoton function: node. Processes an XmlNode
79 *@returns True if everything was ok, False for end of stream.
80 */
81 bool node();
8 82
9namespace Bu
10{
11 /** 83 /**
12 * An Xml 1.1 reader. I've decided to write this, this time, based on the 84 * Automoton function: startNode. Processes the begining of a node.
13 * official W3C reccomendation, now included with the source code. I've 85 *@returns True if everything was ok, False for end of stream.
14 * named the productions in the parser states the same as in that document, 86 */
15 * which may make them easier to find, etc, although possibly slightly less 87 bool startNode();
16 * optimized than writing my own reduced grammer. 88
17 * 89 /**
18 * Below I will list differences between my parser and the official standard 90 * Automoton function: name. Processes the name of a node.
19 * as I come up with them. 91 *@returns True if everything was ok, False for end of stream.
20 * - Encoding and Standalone headings are ignored for the moment. (4.3.3, 92 */
21 * 2.9) 93 bool name();
22 * - The standalone heading attribute can have any standard whitespace 94
23 * before it (the specs say only spaces, no newlines). (2.9) 95 /**
24 * - Since standalone is ignored, it is currently allowed to have any 96 * Automoton function: textDecl. Processes the xml text decleration, if
25 * value (should be restricted to "yes" or "no"). (2.9) 97 * there is one.
26 * - Currently only UTF-8 / ascii are parsed. 98 */
27 * - [optional] The content of comments is thrown away. (2.5) 99 void textDecl();
28 * - The content of processing instruction blocks is parsed properly, but 100
29 * thrown away. (2.6) 101 /**
30 */ 102 * Automoton function: entity. Processes an entity from the header.
31 class XmlReader 103 */
32 { 104 void entity();
33 public: 105
34 XmlReader( Bu::Stream &sIn ); 106 /**
35 virtual ~XmlReader(); 107 * Adds an entity to the list, if it doesn't already exist.
36 108 *@param name The name of the entity
37 XmlNode *read(); 109 *@param value The value of the entity
38 110 */
39 private: 111 void addEntity( const char *name, const char *value );
40 Bu::Stream &sIn; 112
41 Bu::FString sBuf; 113 StaticString *getEscape();
42 114
43 private: // Helpers 115 /**
44 const char *lookahead( int nAmnt ); 116 * Automoton function: paramlist. Processes a list of node params.
45 void burn( int nAmnt ); 117 *@returns True if everything was ok, False for end of stream.
46 void checkString( const char *str, int nLen ); 118 */
47 119 bool paramlist();
48 private: // States 120
49 /** 121 /**
50 * The headers, etc. 122 * Automoton function: param. Processes a single parameter.
51 */ 123 *@returns True if everything was ok, False for end of stream.
52 void prolog(); 124 */
53 125 bool param();
54 /** 126
55 * The xml decleration (version, encoding, etc). 127 /**
56 */ 128 * Automoton function: content. Processes node content.
57 void XMLDecl(); 129 *@returns True if everything was ok, False for end of stream.
58 130 */
59 /** 131 bool content();
60 * Misc things, Includes Comments and PIData (Processing Instructions). 132
61 */ 133 FlexBuf fbContent; /**< buffer for the current node's content. */
62 void Misc(); 134 FlexBuf fbParamName; /**< buffer for the current param's name. */
63 135 FlexBuf fbParamValue; /**< buffer for the current param's value. */
64 /** 136 bool bStrip; /**< Are we stripping whitespace? */
65 * Comments 137
66 */ 138 HashTable htEntity; /**< Entity type definitions. */
67 void Comment(); 139};
68
69 /**
70 * Processing Instructions
71 */
72 void PI();
73
74 /**
75 * Whitespace eater.
76 */
77 void S();
78
79 /**
80 * Optional whitespace eater.
81 */
82 void Sq();
83
84 /**
85 * XML Version spec
86 */
87 void VersionInfo();
88
89 /**
90 * Your basic equals sign with surrounding whitespace.
91 */
92 void Eq();
93
94 /**
95 * Read in an attribute value.
96 */
97 FString AttValue();
98
99 /**
100 * Read in the name of something.
101 */
102 FString Name();
103
104 /**
105 * Encoding decleration in the header
106 */
107 void EncodingDecl();
108
109 /**
110 * Standalone decleration in the header
111 */
112 void SDDecl();
113
114 bool isS( unsigned char c )
115 {
116 return ( c == 0x20 || c == 0x9 || c == 0xD || c == 0xA );
117 }
118 };
119}
120 140
121#endif 141#endif
diff --git a/src/xmlwriter.cpp b/src/xmlwriter.cpp
index 23a5175..56880b6 100644
--- a/src/xmlwriter.cpp
+++ b/src/xmlwriter.cpp
@@ -1,9 +1,173 @@
1#include <stdio.h>
2#include <stdlib.h>
1#include "xmlwriter.h" 3#include "xmlwriter.h"
2 4
3Bu::XmlWriter::XmlWriter() 5XmlWriter::XmlWriter( const char *sIndent, XmlNode *pRoot ) :
6 XmlDocument( pRoot )
4{ 7{
8 if( sIndent == NULL )
9 {
10 this->sIndent = "";
11 }
12 else
13 {
14 this->sIndent = sIndent;
15 }
5} 16}
6 17
7Bu::XmlWriter::~XmlWriter() 18XmlWriter::~XmlWriter()
8{ 19{
9} 20}
21
22void XmlWriter::write()
23{
24 write( getRoot(), sIndent.c_str() );
25}
26
27void XmlWriter::write( XmlNode *pRoot, const char *sIndent )
28{
29 writeNode( pRoot, 0, sIndent );
30}
31
32void XmlWriter::closeNode()
33{
34 XmlDocument::closeNode();
35
36 if( isCompleted() )
37 {
38 write( getRoot(), sIndent.c_str() );
39 }
40}
41
42void XmlWriter::writeIndent( int nIndent, const char *sIndent )
43{
44 if( sIndent == NULL ) return;
45 for( int j = 0; j < nIndent; j++ )
46 {
47 writeString( sIndent );
48 }
49}
50
51std::string XmlWriter::escape( std::string sIn )
52{
53 std::string sOut;
54
55 std::string::const_iterator i;
56 for( i = sIn.begin(); i != sIn.end(); i++ )
57 {
58 if( ((*i >= ' ' && *i <= '9') ||
59 (*i >= 'a' && *i <= 'z') ||
60 (*i >= 'A' && *i <= 'Z') ) &&
61 (*i != '\"' && *i != '\'' && *i != '&')
62 )
63 {
64 sOut += *i;
65 }
66 else
67 {
68 sOut += "&#";
69 char buf[4];
70 sprintf( buf, "%u", (unsigned char)*i );
71 sOut += buf;
72 sOut += ';';
73 }
74 }
75
76 return sOut;
77}
78
79void XmlWriter::writeNodeProps( XmlNode *pNode, int nIndent, const char *sIndent )
80{
81 for( int j = 0; j < pNode->getNumProperties(); j++ )
82 {
83 writeString(" ");
84 writeString( pNode->getPropertyName( j ) );
85 writeString("=\"");
86 writeString( escape( pNode->getProperty( j ) ).c_str() );
87 writeString("\"");
88 }
89}
90
91void XmlWriter::writeNode( XmlNode *pNode, int nIndent, const char *sIndent )
92{
93 if( pNode->hasChildren() )
94 {
95 writeIndent( nIndent, sIndent );
96 writeString("<");
97 writeString( pNode->getName() );
98 writeNodeProps( pNode, nIndent, sIndent );
99 if( sIndent )
100 writeString(">\n");
101 else
102 writeString(">");
103
104 if( pNode->getContent( 0 ) )
105 {
106 writeIndent( nIndent+1, sIndent );
107 if( sIndent )
108 {
109 writeString( pNode->getContent( 0 ) );
110 writeString("\n");
111 }
112 else
113 writeString( pNode->getContent( 0 ) );
114 }
115
116 int nNumChildren = pNode->getNumChildren();
117 for( int j = 0; j < nNumChildren; j++ )
118 {
119 writeNode( pNode->getChild( j ), nIndent+1, sIndent );
120 if( pNode->getContent( j+1 ) )
121 {
122 writeIndent( nIndent+1, sIndent );
123 if( sIndent )
124 {
125 writeString( pNode->getContent( j+1 ) );
126 writeString("\n");
127 }
128 else
129 writeString( pNode->getContent( j+1 ) );
130 }
131 }
132
133 writeIndent( nIndent, sIndent );
134 if( sIndent )
135 {
136 writeString("</");
137 writeString( pNode->getName() );
138 writeString(">\n");
139 }
140 else
141 {
142 writeString("</");
143 writeString( pNode->getName() );
144 writeString(">");
145 }
146 }
147 else if( pNode->getContent() )
148 {
149 writeIndent( nIndent, sIndent );
150 writeString("<");
151 writeString( pNode->getName() );
152 writeNodeProps( pNode, nIndent, sIndent );
153 writeString(">");
154 writeString( pNode->getContent() );
155 writeString("</");
156 writeString( pNode->getName() );
157 writeString(">");
158 if( sIndent )
159 writeString("\n");
160 }
161 else
162 {
163 writeIndent( nIndent, sIndent );
164 writeString("<");
165 writeString( pNode->getName() );
166 writeNodeProps( pNode, nIndent, sIndent );
167 if( sIndent )
168 writeString("/>\n");
169 else
170 writeString("/>");
171 }
172}
173
diff --git a/src/xmlwriter.h b/src/xmlwriter.h
index 796d6fb..c48e810 100644
--- a/src/xmlwriter.h
+++ b/src/xmlwriter.h
@@ -1,22 +1,96 @@
1#ifndef XML_WRITER_H 1#ifndef XMLWRITER
2#define XML_WRITER_H 2#define XMLWRITER
3 3
4#include <stdint.h> 4#include "xmlnode.h"
5#include "xmldocument.h"
5 6
6namespace Bu 7/**
8 * Implements xml writing in the XML standard format. Also allows you to
9 * break that format and auto-indent your exported xml data for ease of
10 * reading. The auto-indenting will only be applied to sections that
11 * have no content of their own already. This means that except for
12 * whitespace all of your data will be preserved perfectly.
13 * You can create an XmlWriter object around a file, or access the static
14 * write function directly and just hand it a filename and a root XmlNode.
15 * When using an XmlWriter object the interface is identicle to that of
16 * the XmlDocument class, so reference that class for API info. However
17 * when the initial (or root) node is closed, and the document is finished
18 * the file will be created and written to automatically. The user can
19 * check to see if this is actually true by calling the isFinished
20 * function in the XmlDocument class.
21 *@author Mike Buland
22 */
23class XmlWriter : public XmlDocument
7{ 24{
25public:
8 /** 26 /**
9 * 27 * Construct a standard XmlWriter.
28 *@param sIndent Set this to something other than NULL to include it as an
29 * indent before each node in the output that doesn't already have content.
30 * If you are using the whitespace stripping option in the XmlReader and set
31 * this to a tab or some spaces it will never effect the content of your
32 * file.
10 */ 33 */
11 class XmlWriter 34 XmlWriter( const char *sIndent=NULL, XmlNode *pRoot=NULL );
12 {
13 public:
14 XmlWriter();
15 virtual ~XmlWriter();
16 35
17 private: 36 /**
37 * Destroy the writer.
38 */
39 virtual ~XmlWriter();
40
41 /**
42 * This override of the parent class closeNode function calls the parent
43 * class, but also triggers a write operation when the final node is closed.
44 * This means that by checking the isCompleted() function the user may also
45 * check to see if their file has been written or not.
46 */
47 void closeNode();
48
49 void write();
18 50
19 }; 51private:
20} 52 std::string sIndent; /**< The indent string */
53
54 std::string escape( std::string sIn );
55
56 /**
57 * Write the file.
58 *@param pNode The root node
59 *@param sIndent The indent text.
60 */
61 void write( XmlNode *pNode, const char *sIndent=NULL );
62
63 /**
64 * Write a node in the file, including children.
65 *@param pNode The node to write.
66 *@param nIndent The indent level (the number of times to include sIndent)
67 *@param sIndent The indent text.
68 */
69 void writeNode( XmlNode *pNode, int nIndent, const char *sIndent );
70
71 /**
72 * Write the properties of a node.
73 *@param pNode The node who's properties to write.
74 *@param nIndent The indent level of the containing node
75 *@param sIndent The indent text.
76 */
77 void writeNodeProps( XmlNode *pNode, int nIndent, const char *sIndent );
78
79 /**
80 * Called to write the actual indent.
81 *@param nIndent The indent level.
82 *@param sIndent The indent text.
83 */
84 void writeIndent( int nIndent, const char *sIndent );
85
86 /**
87 * This is the function that must be overridden in order to use this class.
88 * It must write the null-terminated string sString, minus the mull,
89 * verbatum to it's output device. Adding extra characters for any reason
90 * will break the XML formatting.
91 *@param sString The string data to write to the output.
92 */
93 virtual void writeString( const char *sString ) = 0;
94};
21 95
22#endif 96#endif