]>
Commit | Line | Data |
---|---|---|
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: | |
33 | ||
34 | - handle unknown encodings | |
35 | - process all elements, including CDATA | |
36 | - XML resources should automatically select desired encoding besed on | |
37 | runtime environment (?) (would need BIN and BINZ formats modification, | |
38 | too) | |
39 | ||
40 | */ | |
41 | ||
42 | ||
43 | // converts Expat-produced string in UTF-8 into wxString. | |
44 | inline static wxString CharToString(const char *s, size_t len = wxSTRING_MAXLEN) | |
45 | { | |
46 | #if wxUSE_UNICODE | |
47 | return wxString(s, wxMBConvUTF8, len); | |
48 | #else | |
49 | return wxString(s, len); | |
50 | #endif | |
51 | } | |
52 | ||
53 | bool wxXmlIOHandlerExpat::CanLoad(wxInputStream& stream) | |
54 | { | |
55 | char cheader[7]; | |
56 | cheader[6] = 0; | |
57 | stream.Read(cheader, 6); | |
58 | stream.SeekI(-6, wxFromCurrent); | |
59 | return (strcmp(cheader, "<?xml ") == 0); | |
60 | } | |
61 | ||
62 | ||
63 | struct wxXmlParsingContext | |
64 | { | |
65 | wxXmlNode *root; | |
66 | wxXmlNode *node; | |
67 | wxXmlNode *lastAsText; | |
68 | wxString encoding; | |
69 | wxString version; | |
70 | }; | |
71 | ||
72 | static void StartElementHnd(void *userData, const char *name, const char **atts) | |
73 | { | |
74 | wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData; | |
75 | wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE, CharToString(name)); | |
76 | const char **a = atts; | |
77 | while (*a) | |
78 | { | |
79 | node->AddProperty(CharToString(a[0]), CharToString(a[1])); | |
80 | a += 2; | |
81 | } | |
82 | if (ctx->root == NULL) | |
83 | ctx->root = node; | |
84 | else | |
85 | ctx->node->AddChild(node); | |
86 | ctx->node = node; | |
87 | ctx->lastAsText = NULL; | |
88 | } | |
89 | ||
90 | static void EndElementHnd(void *userData, const char* WXUNUSED(name)) | |
91 | { | |
92 | wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData; | |
93 | ||
94 | ctx->node = ctx->node->GetParent(); | |
95 | ctx->lastAsText = NULL; | |
96 | } | |
97 | ||
98 | static void TextHnd(void *userData, const char *s, int len) | |
99 | { | |
100 | wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData; | |
101 | char *buf = new char[len + 1]; | |
102 | ||
103 | buf[len] = '\0'; | |
104 | memcpy(buf, s, (size_t)len); | |
105 | ||
106 | if (ctx->lastAsText) | |
107 | { | |
108 | ctx->lastAsText->SetContent(ctx->lastAsText->GetContent() + | |
109 | CharToString(buf)); | |
110 | } | |
111 | else | |
112 | { | |
113 | bool whiteOnly = TRUE; | |
114 | for (char *c = buf; *c != '\0'; c++) | |
115 | if (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\r') | |
116 | { | |
117 | whiteOnly = FALSE; | |
118 | break; | |
119 | } | |
120 | if (!whiteOnly) | |
121 | { | |
122 | ctx->lastAsText = new wxXmlNode(wxXML_TEXT_NODE, wxT("text"), | |
123 | CharToString(buf)); | |
124 | ctx->node->AddChild(ctx->lastAsText); | |
125 | } | |
126 | } | |
127 | ||
128 | delete[] buf; | |
129 | } | |
130 | ||
131 | static void CommentHnd(void *userData, const char *data) | |
132 | { | |
133 | wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData; | |
134 | ||
135 | if (ctx->node) | |
136 | { | |
137 | // VS: ctx->node == NULL happens if there is a comment before | |
138 | // the root element (e.g. wxDesigner's output). We ignore such | |
139 | // comments, no big deal... | |
140 | ctx->node->AddChild(new wxXmlNode(wxXML_COMMENT_NODE, | |
141 | wxT("comment"), CharToString(data))); | |
142 | } | |
143 | ctx->lastAsText = NULL; | |
144 | } | |
145 | ||
146 | static void DefaultHnd(void *userData, const char *s, int len) | |
147 | { | |
148 | // XML header: | |
149 | if (len > 6 && memcmp(s, "<?xml ", 6) == 0) | |
150 | { | |
151 | wxXmlParsingContext *ctx = (wxXmlParsingContext*)userData; | |
152 | ||
153 | wxString buf = CharToString(s, (size_t)len); | |
154 | int pos; | |
155 | pos = buf.Find(wxT("encoding=")); | |
156 | if (pos != wxNOT_FOUND) | |
157 | ctx->encoding = buf.Mid(pos + 10).BeforeFirst(buf[(size_t)pos+9]); | |
158 | pos = buf.Find(wxT("version=")); | |
159 | if (pos != wxNOT_FOUND) | |
160 | ctx->version = buf.Mid(pos + 9).BeforeFirst(buf[(size_t)pos+8]); | |
161 | } | |
162 | } | |
163 | ||
164 | bool wxXmlIOHandlerExpat::Load(wxInputStream& stream, wxXmlDocument& doc) | |
165 | { | |
166 | const size_t BUFSIZE = 1024; | |
167 | char buf[BUFSIZE]; | |
168 | wxXmlParsingContext ctx; | |
169 | bool done; | |
170 | XML_Parser parser = XML_ParserCreate(NULL); | |
171 | ||
172 | ctx.root = ctx.node = NULL; | |
173 | XML_SetUserData(parser, (void*)&ctx); | |
174 | XML_SetElementHandler(parser, StartElementHnd, EndElementHnd); | |
175 | XML_SetCharacterDataHandler(parser, TextHnd); | |
176 | XML_SetCommentHandler(parser, CommentHnd); | |
177 | XML_SetDefaultHandler(parser, DefaultHnd); | |
178 | ||
179 | do | |
180 | { | |
181 | size_t len = stream.Read(buf, BUFSIZE).LastRead(); | |
182 | done = (len < BUFSIZE); | |
183 | if (!XML_Parse(parser, buf, len, done)) | |
184 | { | |
185 | wxLogError(_("XML parsing error: '%s' at line %d"), | |
186 | XML_ErrorString(XML_GetErrorCode(parser)), | |
187 | XML_GetCurrentLineNumber(parser)); | |
188 | return FALSE; | |
189 | } | |
190 | } while (!done); | |
191 | ||
192 | doc.SetVersion(ctx.version); | |
193 | doc.SetEncoding(ctx.encoding); | |
194 | doc.SetRoot(ctx.root); | |
195 | ||
196 | XML_ParserFree(parser); | |
197 | return TRUE; | |
198 | } |