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
41using namespace std;
42
43__USE_CORTEX_NAMESPACE
44
45// -------------------------------------------------------- //
46// expat hooks
47// -------------------------------------------------------- //
48
49void _oc_handle_start(
50	void* pUser,
51	const XML_Char* pName,
52	const XML_Char** ppAtts) {
53	((Importer*)pUser)->xmlElementStart(pName, ppAtts);
54}
55
56void _oc_handle_end(
57	void* pUser,
58	const XML_Char* pName) {
59	((Importer*)pUser)->xmlElementEnd(pName);
60}
61
62void _oc_handle_pi(
63	void* pUser,
64	const XML_Char* pTarget,
65	const XML_Char* pData) {
66	((Importer*)pUser)->xmlProcessingInstruction(pTarget, pData);
67}
68
69void _oc_handle_char(
70	void* pUser,
71	const XML_Char* pData,
72	int length) {
73	((Importer*)pUser)->xmlCharacterData(pData, length);
74}
75
76void _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
87Importer::~Importer() {
88	// clean up
89	freeParser();
90
91	delete m_context;
92}
93
94
95Importer::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
107Importer::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
121Importer::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
138Importer::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
161const ImportContext& Importer::context() const {
162	return *m_context;
163}
164
165// matched (or provided) document type
166XML::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)
173IPersistent* 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)
183void Importer::setIdentifyMode() {
184	reset();
185	m_docType = 0;
186	m_identify = true;
187}
188
189void 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
198bool 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
222void 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
250void 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
260void 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
373void 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
441void 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
453void 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
484void 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