diff options
author | Mike Buland <eichlan@xagasoft.com> | 2007-05-09 15:04:31 +0000 |
---|---|---|
committer | Mike Buland <eichlan@xagasoft.com> | 2007-05-09 15:04:31 +0000 |
commit | ad92dc50b7cdf7cfe086f21d19442d03a90fd05d (patch) | |
tree | 9ca6f7bde704cb44276a05b6e83f36754e07f732 /src | |
parent | 2e035fee36768e3c765b7f5dc10bf0a3b7d2448b (diff) | |
download | libbu++-ad92dc50b7cdf7cfe086f21d19442d03a90fd05d.tar.gz libbu++-ad92dc50b7cdf7cfe086f21d19442d03a90fd05d.tar.bz2 libbu++-ad92dc50b7cdf7cfe086f21d19442d03a90fd05d.tar.xz libbu++-ad92dc50b7cdf7cfe086f21d19442d03a90fd05d.zip |
Just a few things re-arranged, moved the new taf/xml systems to the inprogress
directory, and moved the old xml system in, so it will require heavy changes.
Diffstat (limited to 'src')
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 | |||
3 | Bu::XmlDocument::XmlDocument() | ||
4 | { | ||
5 | } | ||
6 | |||
7 | Bu::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 | |||
6 | namespace 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 | |||
3 | Bu::XmlNode::XmlNode() | ||
4 | { | ||
5 | } | ||
6 | |||
7 | Bu::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 | |||
6 | namespace 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 | |||
3 | Bu::XmlReader::XmlReader( Bu::Stream &sIn ) : | ||
4 | sIn( sIn ) | ||
5 | { | ||
6 | } | ||
7 | |||
8 | Bu::XmlReader::~XmlReader() | ||
9 | { | ||
10 | } | ||
11 | |||
12 | const 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 | |||
25 | void Bu::XmlReader::burn( int nAmnt ) | ||
26 | { | ||
27 | if( sBuf.getSize() < nAmnt ) | ||
28 | { | ||
29 | lookahead( nAmnt ); | ||
30 | } | ||
31 | |||
32 | //sBuf.remove( nAmnt ); | ||
33 | } | ||
34 | |||
35 | void 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 | |||
46 | Bu::XmlNode *Bu::XmlReader::read() | ||
47 | { | ||
48 | prolog(); | ||
49 | } | ||
50 | |||
51 | void Bu::XmlReader::prolog() | ||
52 | { | ||
53 | XMLDecl(); | ||
54 | Misc(); | ||
55 | } | ||
56 | |||
57 | void Bu::XmlReader::XMLDecl() | ||
58 | { | ||
59 | checkString("<?xml", 5 ); | ||
60 | S(); | ||
61 | VersionInfo(); | ||
62 | EncodingDecl(); | ||
63 | SDDecl(); | ||
64 | Sq(); | ||
65 | checkString("?>", 2 ); | ||
66 | } | ||
67 | |||
68 | void 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 | |||
88 | void 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 | |||
106 | void 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 | |||
122 | void 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 | |||
135 | void 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 | |||
146 | void 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 | |||
163 | void Bu::XmlReader::Eq() | ||
164 | { | ||
165 | Sq(); | ||
166 | checkString("=", 1 ); | ||
167 | Sq(); | ||
168 | } | ||
169 | |||
170 | void 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 | |||
186 | void 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 | |||
202 | Bu::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 | |||
233 | Bu::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 | |||
9 | namespace 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 | |||
3 | Bu::XmlWriter::XmlWriter() | ||
4 | { | ||
5 | } | ||
6 | |||
7 | Bu::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 | |||
6 | namespace 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 | |||
5 | XmlDocument::XmlDocument( XmlNode *pRoot ) | ||
6 | { | ||
7 | this->pRoot = pRoot; | ||
8 | pCurrent = NULL; | ||
9 | bCompleted = (pRoot!=NULL); | ||
10 | } | ||
11 | |||
12 | XmlDocument::~XmlDocument() | ||
13 | { | ||
14 | if( pRoot ) | ||
15 | { | ||
16 | delete pRoot; | ||
17 | } | ||
18 | } | ||
19 | |||
20 | void 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 | |||
38 | void XmlDocument::setName( const char *sName ) | ||
39 | { | ||
40 | pCurrent->setName( sName ); | ||
41 | } | ||
42 | |||
43 | bool XmlDocument::isCompleted() | ||
44 | { | ||
45 | return bCompleted; | ||
46 | } | ||
47 | |||
48 | XmlNode *XmlDocument::getRoot() | ||
49 | { | ||
50 | return pRoot; | ||
51 | } | ||
52 | |||
53 | XmlNode *XmlDocument::detatchRoot() | ||
54 | { | ||
55 | XmlNode *pTemp = pRoot; | ||
56 | pRoot = NULL; | ||
57 | return pTemp; | ||
58 | } | ||
59 | |||
60 | XmlNode *XmlDocument::getCurrent() | ||
61 | { | ||
62 | return pCurrent; | ||
63 | } | ||
64 | |||
65 | void 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 | |||
78 | void XmlDocument::addProperty( const char *sName, const char *sValue ) | ||
79 | { | ||
80 | if( pCurrent ) | ||
81 | { | ||
82 | pCurrent->addProperty( sName, sValue ); | ||
83 | } | ||
84 | } | ||
85 | |||
86 | void 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 | |||
93 | void XmlDocument::addProperty( const char *sName, const char nValue ) | ||
94 | { | ||
95 | char buf[12]; | ||
96 | sprintf( buf, "%hhi", nValue ); | ||
97 | addProperty( sName, buf ); | ||
98 | } | ||
99 | |||
100 | void 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 | |||
107 | void XmlDocument::addProperty( const char *sName, const short nValue ) | ||
108 | { | ||
109 | char buf[12]; | ||
110 | sprintf( buf, "%hi", nValue ); | ||
111 | addProperty( sName, buf ); | ||
112 | } | ||
113 | |||
114 | void XmlDocument::addProperty( const char *sName, const int nValue ) | ||
115 | { | ||
116 | char buf[12]; | ||
117 | sprintf( buf, "%d", nValue ); | ||
118 | addProperty( sName, buf ); | ||
119 | } | ||
120 | |||
121 | void 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 | |||
128 | void XmlDocument::addProperty( const char *sName, const long nValue ) | ||
129 | { | ||
130 | char buf[12]; | ||
131 | sprintf( buf, "%li", nValue ); | ||
132 | addProperty( sName, buf ); | ||
133 | } | ||
134 | |||
135 | void XmlDocument::addProperty( const char *sName, const double dValue ) | ||
136 | { | ||
137 | char buf[40]; | ||
138 | sprintf( buf, "%f", dValue ); | ||
139 | addProperty( sName, buf ); | ||
140 | } | ||
141 | |||
142 | void 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 | */ | ||
14 | class XmlDocument | ||
15 | { | ||
16 | public: | ||
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 | |||
165 | private: | ||
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 | |||
4 | XmlNode::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 | |||
24 | XmlNode::~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 | |||
51 | void 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 | |||
77 | void 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 | |||
104 | const 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 | |||
125 | XmlNode *XmlNode::addChild( const char *sName, const char *sContent ) | ||
126 | { | ||
127 | return addChild( new XmlNode( sName, this, sContent ) ); | ||
128 | } | ||
129 | |||
130 | XmlNode *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 | |||
140 | XmlNode *XmlNode::getParent() | ||
141 | { | ||
142 | return pParent; | ||
143 | } | ||
144 | |||
145 | void 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 | |||
155 | int XmlNode::getNumProperties() | ||
156 | { | ||
157 | return lPropNames.getSize(); | ||
158 | } | ||
159 | |||
160 | const 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 | |||
168 | const 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 | |||
176 | const 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 | |||
184 | void 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 | |||
195 | bool XmlNode::hasChildren() | ||
196 | { | ||
197 | return lChildren.getSize()>0; | ||
198 | } | ||
199 | |||
200 | int XmlNode::getNumChildren() | ||
201 | { | ||
202 | return lChildren.getSize(); | ||
203 | } | ||
204 | |||
205 | XmlNode *XmlNode::getChild( int nIndex ) | ||
206 | { | ||
207 | return (XmlNode *)lChildren[nIndex]; | ||
208 | } | ||
209 | |||
210 | XmlNode *XmlNode::getChild( const char *sName, int nSkip ) | ||
211 | { | ||
212 | return (XmlNode *)hChildren.get( sName, nSkip ); | ||
213 | } | ||
214 | |||
215 | const char *XmlNode::getName() | ||
216 | { | ||
217 | return sName.c_str(); | ||
218 | } | ||
219 | |||
220 | void 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 | |||
230 | XmlNode *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 | |||
315 | void 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 | |||
325 | XmlNode *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 | |||
384 | void 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 | |||
442 | void 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 | */ | ||
18 | class XmlNode | ||
19 | { | ||
20 | public: | ||
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 | |||
222 | private: | ||
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 | |||
6 | XmlReader::XmlReader( bool bStrip ) : | ||
7 | bStrip( bStrip ), | ||
8 | htEntity( new HashFunctionString(), 11 ) | ||
9 | { | ||
10 | } | ||
11 | |||
12 | XmlReader::~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 | |||
22 | void 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 | |||
34 | bool XmlReader::isws( char chr ) | ||
35 | { | ||
36 | return ( chr == ' ' || chr == '\t' || chr == '\n' || chr == '\r' ); | ||
37 | } | ||
38 | |||
39 | bool 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 | |||
56 | bool 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 | |||
72 | void 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 | |||
92 | void 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 | |||
182 | bool 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 | |||
218 | bool 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 | |||
287 | bool 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 | |||
309 | bool 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 | |||
328 | StaticString *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 | |||
375 | bool 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 | |||
472 | bool 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 | */ | ||
26 | class XmlReader : public XmlDocument | ||
27 | { | ||
28 | public: | ||
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 | |||
48 | private: | ||
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 | |||
5 | XmlWriter::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 | |||
18 | XmlWriter::~XmlWriter() | ||
19 | { | ||
20 | } | ||
21 | |||
22 | void XmlWriter::write() | ||
23 | { | ||
24 | write( getRoot(), sIndent.c_str() ); | ||
25 | } | ||
26 | |||
27 | void XmlWriter::write( XmlNode *pRoot, const char *sIndent ) | ||
28 | { | ||
29 | writeNode( pRoot, 0, sIndent ); | ||
30 | } | ||
31 | |||
32 | void XmlWriter::closeNode() | ||
33 | { | ||
34 | XmlDocument::closeNode(); | ||
35 | |||
36 | if( isCompleted() ) | ||
37 | { | ||
38 | write( getRoot(), sIndent.c_str() ); | ||
39 | } | ||
40 | } | ||
41 | |||
42 | void 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 | |||
51 | std::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 | |||
79 | void 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 | |||
91 | void 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 | */ | ||
23 | class XmlWriter : public XmlDocument | ||
24 | { | ||
25 | public: | ||
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 | |||
51 | private: | ||
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 | ||
3 | Bu::XmlDocument::XmlDocument() | 5 | XmlDocument::XmlDocument( XmlNode *pRoot ) |
4 | { | 6 | { |
7 | this->pRoot = pRoot; | ||
8 | pCurrent = NULL; | ||
9 | bCompleted = (pRoot!=NULL); | ||
5 | } | 10 | } |
6 | 11 | ||
7 | Bu::XmlDocument::~XmlDocument() | 12 | XmlDocument::~XmlDocument() |
8 | { | 13 | { |
14 | if( pRoot ) | ||
15 | { | ||
16 | delete pRoot; | ||
17 | } | ||
9 | } | 18 | } |
19 | |||
20 | void 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 | |||
38 | void XmlDocument::setName( const char *sName ) | ||
39 | { | ||
40 | pCurrent->setName( sName ); | ||
41 | } | ||
42 | |||
43 | bool XmlDocument::isCompleted() | ||
44 | { | ||
45 | return bCompleted; | ||
46 | } | ||
47 | |||
48 | XmlNode *XmlDocument::getRoot() | ||
49 | { | ||
50 | return pRoot; | ||
51 | } | ||
52 | |||
53 | XmlNode *XmlDocument::detatchRoot() | ||
54 | { | ||
55 | XmlNode *pTemp = pRoot; | ||
56 | pRoot = NULL; | ||
57 | return pTemp; | ||
58 | } | ||
59 | |||
60 | XmlNode *XmlDocument::getCurrent() | ||
61 | { | ||
62 | return pCurrent; | ||
63 | } | ||
64 | |||
65 | void 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 | |||
78 | void XmlDocument::addProperty( const char *sName, const char *sValue ) | ||
79 | { | ||
80 | if( pCurrent ) | ||
81 | { | ||
82 | pCurrent->addProperty( sName, sValue ); | ||
83 | } | ||
84 | } | ||
85 | |||
86 | void 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 | |||
93 | void XmlDocument::addProperty( const char *sName, const char nValue ) | ||
94 | { | ||
95 | char buf[12]; | ||
96 | sprintf( buf, "%hhi", nValue ); | ||
97 | addProperty( sName, buf ); | ||
98 | } | ||
99 | |||
100 | void 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 | |||
107 | void XmlDocument::addProperty( const char *sName, const short nValue ) | ||
108 | { | ||
109 | char buf[12]; | ||
110 | sprintf( buf, "%hi", nValue ); | ||
111 | addProperty( sName, buf ); | ||
112 | } | ||
113 | |||
114 | void XmlDocument::addProperty( const char *sName, const int nValue ) | ||
115 | { | ||
116 | char buf[12]; | ||
117 | sprintf( buf, "%d", nValue ); | ||
118 | addProperty( sName, buf ); | ||
119 | } | ||
120 | |||
121 | void 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 | |||
128 | void XmlDocument::addProperty( const char *sName, const long nValue ) | ||
129 | { | ||
130 | char buf[12]; | ||
131 | sprintf( buf, "%li", nValue ); | ||
132 | addProperty( sName, buf ); | ||
133 | } | ||
134 | |||
135 | void XmlDocument::addProperty( const char *sName, const double dValue ) | ||
136 | { | ||
137 | char buf[40]; | ||
138 | sprintf( buf, "%f", dValue ); | ||
139 | addProperty( sName, buf ); | ||
140 | } | ||
141 | |||
142 | void 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 | ||
6 | namespace 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 | */ | ||
14 | class XmlDocument | ||
7 | { | 15 | { |
16 | public: | ||
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 | }; | 165 | private: |
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 | ||
3 | Bu::XmlNode::XmlNode() | 4 | XmlNode::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 | ||
7 | Bu::XmlNode::~XmlNode() | 24 | XmlNode::~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 | |||
51 | void 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 | |||
77 | void 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 | |||
104 | const 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 | |||
125 | XmlNode *XmlNode::addChild( const char *sName, const char *sContent ) | ||
126 | { | ||
127 | return addChild( new XmlNode( sName, this, sContent ) ); | ||
128 | } | ||
129 | |||
130 | XmlNode *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 | |||
140 | XmlNode *XmlNode::getParent() | ||
141 | { | ||
142 | return pParent; | ||
143 | } | ||
144 | |||
145 | void 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 | |||
155 | int XmlNode::getNumProperties() | ||
156 | { | ||
157 | return lPropNames.getSize(); | ||
158 | } | ||
159 | |||
160 | const 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 | |||
168 | const 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 | |||
176 | const 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 | |||
184 | void 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 | |||
195 | bool XmlNode::hasChildren() | ||
196 | { | ||
197 | return lChildren.getSize()>0; | ||
198 | } | ||
199 | |||
200 | int XmlNode::getNumChildren() | ||
201 | { | ||
202 | return lChildren.getSize(); | ||
203 | } | ||
204 | |||
205 | XmlNode *XmlNode::getChild( int nIndex ) | ||
206 | { | ||
207 | return (XmlNode *)lChildren[nIndex]; | ||
208 | } | ||
209 | |||
210 | XmlNode *XmlNode::getChild( const char *sName, int nSkip ) | ||
211 | { | ||
212 | return (XmlNode *)hChildren.get( sName, nSkip ); | ||
213 | } | ||
214 | |||
215 | const char *XmlNode::getName() | ||
216 | { | ||
217 | return sName.c_str(); | ||
218 | } | ||
219 | |||
220 | void 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 | |||
230 | XmlNode *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 | |||
315 | void 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 | |||
325 | XmlNode *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 | |||
384 | void 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 | |||
442 | void 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 | ||
6 | namespace 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 | */ | ||
18 | class XmlNode | ||
7 | { | 19 | { |
20 | public: | ||
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 | }; | 222 | private: |
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 | ||
3 | Bu::XmlReader::XmlReader( Bu::Stream &sIn ) : | 6 | XmlReader::XmlReader( bool bStrip ) : |
4 | sIn( sIn ) | 7 | bStrip( bStrip ), |
8 | htEntity( new HashFunctionString(), 11 ) | ||
5 | { | 9 | { |
6 | } | 10 | } |
7 | 11 | ||
8 | Bu::XmlReader::~XmlReader() | 12 | XmlReader::~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 | ||
12 | const char *Bu::XmlReader::lookahead( int nAmnt ) | 22 | void 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 | ||
25 | void 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 ); | 34 | bool XmlReader::isws( char chr ) |
35 | { | ||
36 | return ( chr == ' ' || chr == '\t' || chr == '\n' || chr == '\r' ); | ||
33 | } | 37 | } |
34 | 38 | ||
35 | void Bu::XmlReader::checkString( const char *str, int nLen ) | 39 | bool 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 | ||
46 | Bu::XmlNode *Bu::XmlReader::read() | 56 | bool 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 | ||
51 | void Bu::XmlReader::prolog() | 69 | return true; |
52 | { | ||
53 | XMLDecl(); | ||
54 | Misc(); | ||
55 | } | 70 | } |
56 | 71 | ||
57 | void Bu::XmlReader::XMLDecl() | 72 | void 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 | ||
68 | void Bu::XmlReader::Misc() | 92 | void 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 | ||
88 | void Bu::XmlReader::Comment() | 182 | bool 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 | ||
106 | void Bu::XmlReader::PI() | 218 | bool 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 | ||
122 | void Bu::XmlReader::S() | 287 | bool 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 | ||
135 | void Bu::XmlReader::Sq() | 309 | bool 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 | ||
146 | void Bu::XmlReader::VersionInfo() | 328 | StaticString *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 | ||
163 | void Bu::XmlReader::Eq() | 375 | bool XmlReader::param() |
164 | { | 376 | { |
165 | Sq(); | 377 | FlexBuf fbName; |
166 | checkString("=", 1 ); | 378 | FlexBuf fbValue; |
167 | Sq(); | ||
168 | } | ||
169 | 379 | ||
170 | void 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 | ||
186 | void 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 | ||
202 | Bu::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 | ||
233 | Bu::FString Bu::XmlReader::Name() | 472 | bool 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 | */ | ||
26 | class XmlReader : public XmlDocument | ||
27 | { | ||
28 | public: | ||
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 | |||
48 | private: | ||
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 | ||
9 | namespace 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 | ||
3 | Bu::XmlWriter::XmlWriter() | 5 | XmlWriter::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 | ||
7 | Bu::XmlWriter::~XmlWriter() | 18 | XmlWriter::~XmlWriter() |
8 | { | 19 | { |
9 | } | 20 | } |
21 | |||
22 | void XmlWriter::write() | ||
23 | { | ||
24 | write( getRoot(), sIndent.c_str() ); | ||
25 | } | ||
26 | |||
27 | void XmlWriter::write( XmlNode *pRoot, const char *sIndent ) | ||
28 | { | ||
29 | writeNode( pRoot, 0, sIndent ); | ||
30 | } | ||
31 | |||
32 | void XmlWriter::closeNode() | ||
33 | { | ||
34 | XmlDocument::closeNode(); | ||
35 | |||
36 | if( isCompleted() ) | ||
37 | { | ||
38 | write( getRoot(), sIndent.c_str() ); | ||
39 | } | ||
40 | } | ||
41 | |||
42 | void 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 | |||
51 | std::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 | |||
79 | void 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 | |||
91 | void 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 | ||
6 | namespace 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 | */ | ||
23 | class XmlWriter : public XmlDocument | ||
7 | { | 24 | { |
25 | public: | ||
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 | }; | 51 | private: |
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 |