1 /*
2 * Copyright (c) 1999-2000, Eric Moon.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions, and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions, and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31
32 // Importer.cpp
33 // e.moon 28jun99
34
35 #include "Importer.h"
36 #include <stdexcept>
37
38 #include <Autolock.h>
39 #include <Debug.h>
40
41 using namespace std;
42
43 __USE_CORTEX_NAMESPACE
44
45 // -------------------------------------------------------- //
46 // expat hooks
47 // -------------------------------------------------------- //
48
_oc_handle_start(void * pUser,const XML_Char * pName,const XML_Char ** ppAtts)49 void _oc_handle_start(
50 void* pUser,
51 const XML_Char* pName,
52 const XML_Char** ppAtts) {
53 ((Importer*)pUser)->xmlElementStart(pName, ppAtts);
54 }
55
_oc_handle_end(void * pUser,const XML_Char * pName)56 void _oc_handle_end(
57 void* pUser,
58 const XML_Char* pName) {
59 ((Importer*)pUser)->xmlElementEnd(pName);
60 }
61
_oc_handle_pi(void * pUser,const XML_Char * pTarget,const XML_Char * pData)62 void _oc_handle_pi(
63 void* pUser,
64 const XML_Char* pTarget,
65 const XML_Char* pData) {
66 ((Importer*)pUser)->xmlProcessingInstruction(pTarget, pData);
67 }
68
_oc_handle_char(void * pUser,const XML_Char * pData,int length)69 void _oc_handle_char(
70 void* pUser,
71 const XML_Char* pData,
72 int length) {
73 ((Importer*)pUser)->xmlCharacterData(pData, length);
74 }
75
_oc_handle_default(void * pUser,const XML_Char * pData,int length)76 void _oc_handle_default(
77 void* pUser,
78 const XML_Char* pData,
79 int length) {
80 ((Importer*)pUser)->xmlDefaultData(pData, length);
81 }
82
83 // -------------------------------------------------------- //
84 // ctor/dtor
85 // -------------------------------------------------------- //
86
~Importer()87 Importer::~Importer() {
88 // clean up
89 freeParser();
90
91 delete m_context;
92 }
93
94
Importer(list<BString> & errors)95 Importer::Importer(
96 list<BString>& errors) :
97
98 m_parser(0),
99 m_docType(0),
100 m_identify(false),
101 m_context(new ImportContext(errors)),
102 m_rootObject(0) {
103
104 initParser();
105 }
106
Importer(ImportContext * context)107 Importer::Importer(
108 ImportContext* context) :
109
110 m_parser(0),
111 m_docType(0),
112 m_identify(false),
113 m_context(context),
114 m_rootObject(0) {
115
116 ASSERT(m_context);
117
118 initParser();
119 }
120
Importer(list<BString> & errors,IPersistent * rootObject,XML::DocumentType * docType)121 Importer::Importer(
122 list<BString>& errors,
123 IPersistent* rootObject,
124 XML::DocumentType* docType) :
125
126 m_parser(0),
127 m_docType(docType),
128 m_identify(false),
129 m_context(new ImportContext(errors)),
130 m_rootObject(rootObject) {
131
132 ASSERT(rootObject);
133 ASSERT(docType);
134
135 initParser();
136 }
137
Importer(ImportContext * context,IPersistent * rootObject,XML::DocumentType * docType)138 Importer::Importer(
139 ImportContext* context,
140 IPersistent* rootObject,
141 XML::DocumentType* docType) :
142
143 m_parser(0),
144 m_docType(docType),
145 m_identify(false),
146 m_context(context),
147 m_rootObject(rootObject) {
148
149 ASSERT(m_context);
150 ASSERT(rootObject);
151 ASSERT(docType);
152
153 initParser();
154 }
155
156 // -------------------------------------------------------- //
157 // accessors
158 // -------------------------------------------------------- //
159
160 // the import context
context() const161 const ImportContext& Importer::context() const {
162 return *m_context;
163 }
164
165 // matched (or provided) document type
docType() const166 XML::DocumentType* Importer::docType() const {
167 return m_docType;
168 }
169
170 // completed object (available if
171 // context().state() == ImportContext::COMPLETE, or
172 // if a root object was provided to the ctor)
target() const173 IPersistent* Importer::target() const {
174 return m_rootObject;
175 }
176
177 // -------------------------------------------------------- //
178 // operations
179 // -------------------------------------------------------- //
180
181 // put the importer into 'identify mode'
182 // (disengaged once the first element is encountered)
setIdentifyMode()183 void Importer::setIdentifyMode() {
184 reset();
185 m_docType = 0;
186 m_identify = true;
187 }
188
reset()189 void Importer::reset() {
190 // doesn't forget document type from identify cycle!
191
192 m_identify = false;
193 m_context->reset();
194 m_rootObject = 0;
195 }
196
197 // handle a buffer; return false if an error occurs
parseBuffer(const char * pBuffer,uint32 length,bool last)198 bool Importer::parseBuffer(
199 const char* pBuffer,
200 uint32 length,
201 bool last) {
202
203 ASSERT(m_parser);
204
205 int err = XML_Parse(m_parser, pBuffer, length, last);
206
207 if(!err) {
208 BString str = "Parse Error: ";
209 str << XML_ErrorString(XML_GetErrorCode(m_parser));
210 m_context->reportError(str.String());
211 return false;
212
213 } else
214 return true;
215 }
216
217 // -------------------------------------------------------- //
218 // internal operations
219 // -------------------------------------------------------- //
220
221 // create & initialize parser
initParser()222 void Importer::initParser() {
223 ASSERT(!m_parser);
224 m_parser = XML_ParserCreate(0);
225 m_context->m_pParser = m_parser;
226
227 XML_SetElementHandler(
228 m_parser,
229 &_oc_handle_start,
230 &_oc_handle_end);
231
232 XML_SetProcessingInstructionHandler(
233 m_parser,
234 &_oc_handle_pi);
235
236 XML_SetCharacterDataHandler(
237 m_parser,
238 &_oc_handle_char);
239
240 XML_SetDefaultHandlerExpand(
241 m_parser,
242 &_oc_handle_default);
243
244 XML_SetUserData(
245 m_parser,
246 (void*)this);
247 }
248
249 // clean up the parser
freeParser()250 void Importer::freeParser() {
251 ASSERT(m_parser);
252 XML_ParserFree(m_parser);
253 m_parser = 0;
254 }
255
256 // -------------------------------------------------------- //
257 // XML parser event hooks
258 // -------------------------------------------------------- //
259
xmlElementStart(const char * pName,const char ** ppAttributes)260 void Importer::xmlElementStart(
261 const char* pName,
262 const char** ppAttributes) {
263
264 if(m_context->m_state != ImportContext::PARSING)
265 return;
266
267 IPersistent* target = 0;
268
269 if(!m_context->m_elementStack.size()) {
270 // this is the first element; identify or verify document type
271
272 if(m_rootObject) {
273 // test against expected document type
274 ASSERT(m_docType);
275 if(m_docType->rootElement != pName) {
276 BString err("Unexpected document element (should be <");
277 err << m_docType->rootElement << "/>";
278 m_context->reportError(err.String());
279 return;
280 }
281
282 // target the provided root object
283 target = m_rootObject;
284 }
285 else {
286 // look up doc type
287 BAutolock _l(XML::s_docTypeLock);
288 XML::doc_type_map::iterator it = XML::s_docTypeMap.find(
289 BString(pName));
290
291 if(it != XML::s_docTypeMap.end())
292 m_docType = (*it).second;
293 else {
294 // whoops, don't know how to handle this element:
295 BString err("No document type registered for element '");
296 err << pName << "'.";
297 m_context->reportError(err.String());
298 return;
299 }
300
301 if(m_identify) {
302 // end of identify cycle
303 m_context->m_state = ImportContext::COMPLETE;
304 return;
305 }
306 }
307 }
308 // at this point, there'd better be a valid document type
309 ASSERT(m_docType);
310
311 // push element name onto the stack
312 m_context->m_elementStack.push_back(pName);
313
314 // try to create an object for this element if necessary
315 if(!target)
316 target = m_docType->objectFor(pName);
317
318 if(target) {
319 // call 'begin import' hook
320 m_context->m_objectStack.push_back(
321 make_pair(m_context->element(), target));
322 target->xmlImportBegin(*m_context);
323
324 // error? bail
325 if(m_context->state() != ImportContext::PARSING)
326 return;
327
328 // walk attributes
329 while(*ppAttributes) {
330 target->xmlImportAttribute(
331 ppAttributes[0],
332 ppAttributes[1],
333 *m_context);
334
335 // error? bail
336 if(m_context->state() != ImportContext::PARSING)
337 return;
338
339 ppAttributes += 2;
340 }
341 } else {
342 // no object directly maps to this element; hand to
343 // the current focus object
344
345 ASSERT(m_context->m_objectStack.size());
346 IPersistent* curObject = m_context->m_objectStack.back().second;
347 ASSERT(curObject);
348
349 curObject->xmlImportChildBegin(
350 pName,
351 *m_context);
352
353 // error? bail
354 if(m_context->state() != ImportContext::PARSING)
355 return;
356
357 // walk attributes
358 while(*ppAttributes) {
359 curObject->xmlImportChildAttribute(
360 ppAttributes[0],
361 ppAttributes[1],
362 *m_context);
363
364 // error? bail
365 if(m_context->state() != ImportContext::PARSING)
366 return;
367
368 ppAttributes += 2;
369 }
370 }
371 }
372
xmlElementEnd(const char * pName)373 void Importer::xmlElementEnd(
374 const char* pName) {
375
376 if(m_context->m_state != ImportContext::PARSING)
377 return;
378 ASSERT(m_docType);
379
380 // PRINT(("Importer::xmlElementEnd(): %s\n", pName));
381
382 // compare name to element on top of stack
383 if(!m_context->m_elementStack.size() ||
384 m_context->m_elementStack.back() != pName) {
385 m_context->reportError("Mismatched end tag.");
386 return;
387 }
388
389 // see if it matches the topmost object
390 IPersistent* pObject = 0;
391 if(!m_context->m_objectStack.size()) {
392 m_context->reportError("No object being constructed.");
393 return;
394 }
395 if(m_context->m_objectStack.back().first == m_context->element()) {
396 // matched; pop it
397 pObject = m_context->m_objectStack.back().second;
398 m_context->m_objectStack.pop_back();
399 }
400
401 if(pObject) {
402 // notify object that import is complete
403 pObject->xmlImportComplete(
404 *m_context);
405
406 // error? bail
407 if(m_context->state() != ImportContext::PARSING)
408 return;
409
410 if(m_context->m_objectStack.size()) {
411 // hand the newly-constructed child to its parent
412 m_context->m_objectStack.back().second->xmlImportChild(
413 pObject,
414 *m_context);
415 } else {
416 // done
417 ASSERT(m_context->m_elementStack.size() == 1);
418 m_context->m_state = ImportContext::COMPLETE;
419 if(m_rootObject) {
420 ASSERT(m_rootObject == pObject);
421 } else
422 m_rootObject = pObject;
423 }
424 }
425 else {
426 // notify current topmost object
427 ASSERT(m_context->m_objectStack.size());
428 IPersistent* curObject = m_context->m_objectStack.back().second;
429 ASSERT(curObject);
430
431 curObject->xmlImportChildComplete(
432 pName,
433 *m_context);
434 }
435
436 // remove entry from element stack
437 m_context->m_elementStack.pop_back();
438 ASSERT(m_context->m_objectStack.size() <= m_context->m_elementStack.size());
439 }
440
xmlProcessingInstruction(const char * pTarget,const char * pData)441 void Importer::xmlProcessingInstruction(
442 const char* pTarget,
443 const char* pData) {
444
445 if(m_context->m_state != ImportContext::PARSING)
446 return;
447 // PRINT(("Importer::xmlProcessingInstruction(): %s, %s\n",
448 // pTarget, pData));
449
450 }
451
452 // not 0-terminated
xmlCharacterData(const char * pData,int32 length)453 void Importer::xmlCharacterData(
454 const char* pData,
455 int32 length) {
456
457 if(m_context->m_state != ImportContext::PARSING)
458 return;
459
460 // see if the current element matches the topmost object
461 IPersistent* pObject = 0;
462 if(!m_context->m_objectStack.size()) {
463 m_context->reportError("No object being constructed.");
464 return;
465 }
466
467 pObject = m_context->m_objectStack.back().second;
468 if(m_context->m_objectStack.back().first == m_context->element()) {
469
470 pObject->xmlImportContent(
471 pData,
472 length,
473 *m_context);
474 }
475 else {
476 pObject->xmlImportChildContent(
477 pData,
478 length,
479 *m_context);
480 }
481 }
482
483 // not 0-terminated
xmlDefaultData(const char * pData,int32 length)484 void Importer::xmlDefaultData(
485 const char* pData,
486 int32 length) {
487
488 if(m_context->m_state != ImportContext::PARSING)
489 return;
490 // PRINT(("Importer::xmlDefaultData()\n"));
491 }
492
493 // END -- Importer.cpp --
494