diff options
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 |