]> git.saurik.com Git - wxWidgets.git/blob - src/xrc/xmlexpat.cpp
extracted 4 copies of identical code into a function
[wxWidgets.git] / src / xrc / xmlexpat.cpp
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 based 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, wxConvUTF8, 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 }