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