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