xref: /haiku/src/apps/cortex/Persistence/Importer.cpp (revision 19ae20e67e91fc09cc9fc5c0e60e21e24e7a53eb)
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