]>
Commit | Line | Data |
---|---|---|
78d14f80 VS |
1 | ///////////////////////////////////////////////////////////////////////////// |
2 | // Name: xmlexpat.cpp | |
3 | // Purpose: wxXmlDocument - XML reader via Expat | |
4 | // Author: Vaclav Slavik | |
5 | // Created: 2001/04/30 | |
6 | // RCS-ID: $Id$ | |
7 | // Copyright: (c) 2001 Vaclav Slavik | |
8 | // Licence: wxWindows licence | |
9 | ///////////////////////////////////////////////////////////////////////////// | |
10 | ||
11 | #ifdef __GNUG__ | |
12 | // nothing - already in xml.cpp | |
13 | #endif | |
14 | ||
15 | // For compilers that support precompilation, includes "wx.h". | |
16 | #include "wx/wxprec.h" | |
17 | ||
18 | #ifdef __BORLANDC__ | |
19 | #pragma hdrstop | |
20 | #endif | |
21 | ||
22 | #include "wx/wfstream.h" | |
23 | #include "wx/intl.h" | |
24 | #include "wx/log.h" | |
25 | #include "wx/strconv.h" | |
26 | #include "wx/xrc/xmlio.h" | |
27 | ||
28 | #include "xmlparse.h" | |
29 | ||
30 | /* | |
31 | ||
32 | FIXME: | |
78d14f80 | 33 | - process all elements, including CDATA |
78d14f80 VS |
34 | |
35 | */ | |
36 | ||
37 | ||
38 | // converts Expat-produced string in UTF-8 into wxString. | |
480505bc VS |
39 | inline static wxString CharToString(wxMBConv *conv, |
40 | const char *s, size_t len = wxSTRING_MAXLEN) | |
78d14f80 VS |
41 | { |
42 | #if wxUSE_UNICODE | |
480505bc | 43 | (void)conv; |
00393283 | 44 | return wxString(s, wxConvUTF8, len); |
78d14f80 | 45 | #else |
480505bc VS |
46 | if ( conv ) |
47 | { | |
48 | size_t nLen = (len != wxSTRING_MAXLEN) ? len : | |
49 | nLen = wxConvUTF8.MB2WC((wchar_t*) NULL, s, 0); | |
50 | ||
51 | wchar_t *buf = new wchar_t[nLen+1]; | |
52 | wxConvUTF8.MB2WC(buf, s, nLen); | |
53 | buf[nLen] = 0; | |
54 | return wxString(buf, *conv, len); | |
55 | delete[] buf; | |
56 | } | |
57 | else | |
58 | return wxString(s, len); | |
78d14f80 VS |
59 | #endif |
60 | } | |
61 | ||
62 | bool wxXmlIOHandlerExpat::CanLoad(wxInputStream& stream) | |
63 | { | |
64 | char cheader[7]; | |
65 | cheader[6] = 0; | |
66 | stream.Read(cheader, 6); | |
67 | stream.SeekI(-6, wxFromCurrent); | |
68 | return (strcmp(cheader, "<?xml ") == 0); | |
69 | } | |
70 | ||
71 | ||
72 | struct wxXmlParsingContext | |
73 | { | |
480505bc VS |
74 | wxMBConv *conv; |
75 | ||
78d14f80 VS |
76 | wxXmlNode *root; |
77 | wxXmlNode *node; | |
78 | wxXmlNode *lastAsText; | |
480505bc VS |
79 | wxString encoding; |
80 | wxString version; | |
78d14f80 VS |
81 | }; |
82 | ||
83 | static void StartElementHnd(void *userData, const char *name, const char **atts) | |
84 | { | |
85 | wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData; | |
480505bc | 86 | wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE, CharToString(ctx->conv, name)); |
78d14f80 VS |
87 | const char **a = atts; |
88 | while (*a) | |
89 | { | |
480505bc | 90 | node->AddProperty(CharToString(ctx->conv, a[0]), CharToString(ctx->conv, a[1])); |
78d14f80 VS |
91 | a += 2; |
92 | } | |
93 | if (ctx->root == NULL) | |
94 | ctx->root = node; | |
95 | else | |
96 | ctx->node->AddChild(node); | |
97 | ctx->node = node; | |
98 | ctx->lastAsText = NULL; | |
99 | } | |
100 | ||
101 | static void EndElementHnd(void *userData, const char* WXUNUSED(name)) | |
102 | { | |
103 | wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData; | |
104 | ||
105 | ctx->node = ctx->node->GetParent(); | |
106 | ctx->lastAsText = NULL; | |
107 | } | |
108 | ||
109 | static void TextHnd(void *userData, const char *s, int len) | |
110 | { | |
111 | wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData; | |
112 | char *buf = new char[len + 1]; | |
113 | ||
114 | buf[len] = '\0'; | |
115 | memcpy(buf, s, (size_t)len); | |
116 | ||
117 | if (ctx->lastAsText) | |
118 | { | |
119 | ctx->lastAsText->SetContent(ctx->lastAsText->GetContent() + | |
480505bc | 120 | CharToString(ctx->conv, buf)); |
78d14f80 VS |
121 | } |
122 | else | |
123 | { | |
124 | bool whiteOnly = TRUE; | |
125 | for (char *c = buf; *c != '\0'; c++) | |
126 | if (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\r') | |
127 | { | |
128 | whiteOnly = FALSE; | |
129 | break; | |
130 | } | |
131 | if (!whiteOnly) | |
132 | { | |
133 | ctx->lastAsText = new wxXmlNode(wxXML_TEXT_NODE, wxT("text"), | |
480505bc | 134 | CharToString(ctx->conv, buf)); |
78d14f80 VS |
135 | ctx->node->AddChild(ctx->lastAsText); |
136 | } | |
137 | } | |
138 | ||
139 | delete[] buf; | |
140 | } | |
141 | ||
142 | static void CommentHnd(void *userData, const char *data) | |
143 | { | |
144 | wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData; | |
145 | ||
146 | if (ctx->node) | |
147 | { | |
148 | // VS: ctx->node == NULL happens if there is a comment before | |
149 | // the root element (e.g. wxDesigner's output). We ignore such | |
150 | // comments, no big deal... | |
151 | ctx->node->AddChild(new wxXmlNode(wxXML_COMMENT_NODE, | |
480505bc | 152 | wxT("comment"), CharToString(ctx->conv, data))); |
78d14f80 VS |
153 | } |
154 | ctx->lastAsText = NULL; | |
155 | } | |
156 | ||
157 | static void DefaultHnd(void *userData, const char *s, int len) | |
158 | { | |
159 | // XML header: | |
160 | if (len > 6 && memcmp(s, "<?xml ", 6) == 0) | |
161 | { | |
162 | wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData; | |
163 | ||
480505bc | 164 | wxString buf = CharToString(ctx->conv, s, (size_t)len); |
78d14f80 VS |
165 | int pos; |
166 | pos = buf.Find(wxT("encoding=")); | |
167 | if (pos != wxNOT_FOUND) | |
168 | ctx->encoding = buf.Mid(pos + 10).BeforeFirst(buf[(size_t)pos+9]); | |
169 | pos = buf.Find(wxT("version=")); | |
170 | if (pos != wxNOT_FOUND) | |
171 | ctx->version = buf.Mid(pos + 9).BeforeFirst(buf[(size_t)pos+8]); | |
172 | } | |
173 | } | |
174 | ||
480505bc VS |
175 | static int UnknownEncodingHnd(void * WXUNUSED(encodingHandlerData), |
176 | const XML_Char *name, XML_Encoding *info) | |
177 | { | |
178 | // We must build conversion table for expat. The easiest way to do so | |
179 | // is to let wxCSConv convert as string containing all characters to | |
180 | // wide character representation: | |
181 | wxCSConv conv(name); | |
182 | char mbBuf[255]; | |
183 | wchar_t wcBuf[255]; | |
184 | size_t i; | |
185 | ||
186 | for (i = 0; i < 255; i++) | |
187 | mbBuf[i] = i+1; | |
188 | mbBuf[255] = 0; | |
189 | conv.MB2WC(wcBuf, mbBuf, 255); | |
190 | wcBuf[255] = 0; | |
191 | ||
192 | info->map[0] = 0; | |
193 | for (i = 0; i < 255; i++) | |
194 | info->map[i+1] = (int)wcBuf[i]; | |
195 | ||
196 | info->data = NULL; | |
197 | info->convert = NULL; | |
198 | info->release = NULL; | |
199 | ||
200 | return 1; | |
201 | } | |
202 | ||
203 | bool wxXmlIOHandlerExpat::Load(wxInputStream& stream, wxXmlDocument& doc, | |
204 | const wxString& encoding) | |
78d14f80 VS |
205 | { |
206 | const size_t BUFSIZE = 1024; | |
207 | char buf[BUFSIZE]; | |
208 | wxXmlParsingContext ctx; | |
209 | bool done; | |
210 | XML_Parser parser = XML_ParserCreate(NULL); | |
211 | ||
212 | ctx.root = ctx.node = NULL; | |
480505bc VS |
213 | ctx.encoding = wxT("UTF-8"); // default in absence of encoding="" |
214 | ctx.conv = NULL; | |
215 | #if !wxUSE_UNICODE | |
216 | if ( encoding != wxT("UTF-8") && encoding != wxT("utf-8") ) | |
217 | ctx.conv = new wxCSConv(encoding); | |
218 | #endif | |
219 | ||
78d14f80 VS |
220 | XML_SetUserData(parser, (void*)&ctx); |
221 | XML_SetElementHandler(parser, StartElementHnd, EndElementHnd); | |
222 | XML_SetCharacterDataHandler(parser, TextHnd); | |
223 | XML_SetCommentHandler(parser, CommentHnd); | |
224 | XML_SetDefaultHandler(parser, DefaultHnd); | |
480505bc | 225 | XML_SetUnknownEncodingHandler(parser, UnknownEncodingHnd, NULL); |
78d14f80 VS |
226 | |
227 | do | |
228 | { | |
229 | size_t len = stream.Read(buf, BUFSIZE).LastRead(); | |
230 | done = (len < BUFSIZE); | |
231 | if (!XML_Parse(parser, buf, len, done)) | |
232 | { | |
233 | wxLogError(_("XML parsing error: '%s' at line %d"), | |
234 | XML_ErrorString(XML_GetErrorCode(parser)), | |
235 | XML_GetCurrentLineNumber(parser)); | |
236 | return FALSE; | |
237 | } | |
238 | } while (!done); | |
239 | ||
240 | doc.SetVersion(ctx.version); | |
480505bc | 241 | doc.SetFileEncoding(ctx.encoding); |
78d14f80 VS |
242 | doc.SetRoot(ctx.root); |
243 | ||
244 | XML_ParserFree(parser); | |
480505bc VS |
245 | #if !wxUSE_UNICODE |
246 | if ( ctx.conv ) | |
247 | delete ctx.conv; | |
248 | #endif | |
249 | ||
78d14f80 VS |
250 | return TRUE; |
251 | } |