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// XML.cpp
33// e.moon 1jul99
34
35#include "XML.h"
36#include "Importer.h"
37
38#include <Autolock.h>
39#include <Debug.h>
40
41#include "array_delete.h"
42#include "set_tools.h"
43
44using namespace std;
45
46__USE_CORTEX_NAMESPACE
47
48// -------------------------------------------------------- //
49// static members
50// -------------------------------------------------------- //
51
52XML::doc_type_map XML::s_docTypeMap;
53BLocker XML::s_docTypeLock("XML::s_docTypeLock");
54
55const BMimeType XML::DocumentType::s_defaultMimeType("text/xml");
56
57// -------------------------------------------------------- //
58// document type operations
59// -------------------------------------------------------- //
60
61// takes responsibility for the given type object
62
63/*static*/
64void XML::AddDocumentType(
65	XML::DocumentType*					type) {
66
67	ASSERT(type);
68	BAutolock _l(s_docTypeLock);
69
70//	s_docTypeMap.insert(
71//		make_pair(type->rootElement, type));
72	s_docTypeMap.insert(
73		pair<const BString, XML::DocumentType*>(type->rootElement, type));
74}
75
76// -------------------------------------------------------- //
77// import/export operations
78// -------------------------------------------------------- //
79
80// identify object in stream
81// returns:
82// - B_OK on success, or
83// - B_BAD_TYPE if no document type matches the root
84//   element of the stream, or
85// - B_IO_ERROR if the document is malformed, or if a
86//   read error occurs.
87
88/*static*/
89status_t XML::Identify(
90	BDataIO*										stream,
91	DocumentType**							outType,
92	list<BString>*							outErrors) {
93
94	ASSERT(stream);
95
96	// prepare the input buffer
97	const uint32 bufferSize = 4096;
98	char* buffer = new char[bufferSize];
99	array_delete<char> _d(buffer);
100
101	// prepare an Importer to figure document type (from first element)
102	Importer i(*outErrors);
103	i.setIdentifyMode();
104
105	while(
106		i.context().state() == ImportContext::PARSING) {
107
108		// read chunk (no 0 terminator)
109		ssize_t readCount = stream->Read(buffer, bufferSize);
110		if(readCount == 0)
111			 // done
112			break;
113		else if(readCount < 0) {
114			// error
115			BString err = "Read error: '";
116			err << strerror(readCount) << "'; ABORTING.";
117			outErrors->push_back(err);
118
119			return B_IO_ERROR;
120		}
121
122		// feed to parser
123		if(!i.parseBuffer(
124			buffer, readCount, !stream)) {
125			break;
126		}
127	}
128
129	// return found type
130	if(i.docType()) {
131		*outType = i.docType();
132		return B_OK;
133	}
134	else return B_BAD_TYPE;
135}
136
137// read the root object from the given stream
138
139/*static*/
140status_t XML::Read(
141	BDataIO*										stream,
142	IPersistent**								outObject,
143	list<BString>*							outErrors) {
144
145	Importer i(*outErrors);
146	status_t err = _DoRead(stream, i, outErrors);
147	if(err == B_OK) {
148		// return completed object
149		ASSERT(i.target());
150		*outObject = i.target();
151	}
152	return err;
153}
154
155/*static*/
156status_t XML::Read(
157	BDataIO*										stream,
158	IPersistent**								outObject,
159	ImportContext*							context) {
160
161	Importer i(context);
162	status_t err = _DoRead(stream, i, &context->errors());
163	if(err == B_OK) {
164		// return completed object
165		ASSERT(i.target());
166		*outObject = i.target();
167	}
168	return err;
169}
170
171// [e.moon 26nov99]
172// populate the provided root object from the given
173// XML stream.  you need to give the expected root
174// (document) element name corresponding to the
175// item you provide.
176// returns:
177// - B_OK on success, or
178// - B_IO_ERROR if the document is malformed, or if a
179//   read error occurs, or
180// - B_ERROR
181
182/*static*/
183status_t XML::Read(
184	BDataIO*										stream,
185	IPersistent*								rootObject,
186	XML::DocumentType*					documentType,
187	list<BString>*							outErrors) {
188
189	Importer i(*outErrors, rootObject, documentType);
190	return _DoRead(stream, i, outErrors);
191}
192
193/*static*/
194status_t XML::Read(
195	BDataIO*										stream,
196	IPersistent*								rootObject,
197	XML::DocumentType*					documentType,
198	ImportContext*							context) {
199
200	Importer i(context, rootObject, documentType);
201	return _DoRead(stream, i, &context->errors());
202}
203
204/*static*/
205status_t XML::_DoRead(
206	BDataIO*										stream,
207	Importer&										i,
208	list<BString>*							outErrors) {
209
210	// prepare the input buffer
211	const uint32 bufferSize = 4096;
212	char* buffer = new char[bufferSize];
213	array_delete<char> _d(buffer);
214
215	while(
216		i.context().state() == ImportContext::PARSING) {
217
218		// read chunk (no 0 terminator)
219		ssize_t readCount = stream->Read(buffer, bufferSize);
220		if(readCount == 0)
221			 // done
222			break;
223		else if(readCount < 0) {
224			// error
225			BString err = "Read error: '";
226			err << strerror(readCount) << "'; ABORTING.";
227			outErrors->push_back(err);
228			return B_IO_ERROR;
229		}
230
231		// feed to parser
232		if(!i.parseBuffer(
233			buffer, readCount, !stream)) {
234			break;
235		}
236	}
237
238	status_t err = B_ERROR;
239	if(i.context().state() == ImportContext::COMPLETE)
240		err = B_OK;
241
242	// clean up
243	return err;
244}
245
246// write the given object to the given stream
247
248/*static*/
249status_t XML::Write(
250	BDataIO*										stream,
251	IPersistent*								object,
252	BString*										outError) {
253
254	ASSERT(object);
255
256	ExportContext context(stream);
257	status_t err = context.writeObject(object);
258	if(err < B_OK)
259		*outError = context.errorText();
260	return err;
261}
262
263// -------------------------------------------------------- //
264// XML::DocumentType
265// -------------------------------------------------------- //
266
267class _NullMapping : public XMLElementMapping {
268public:
269	_NullMapping(
270		const char*									_element) :
271		XMLElementMapping(_element) {}
272
273	IPersistent* create() const { return 0; }
274};
275
276XML::DocumentType::~DocumentType() {
277	// clean up
278	ptr_set_delete(m_mappingSet.begin(), m_mappingSet.end());
279}
280
281XML::DocumentType::DocumentType(
282	const char*									_rootElement,
283	const char*									_mimeType) :
284	rootElement(_rootElement),
285	mimeType(_mimeType ? _mimeType : s_defaultMimeType.Type()) {}
286
287// *** 'factory' interface
288
289// The DocumentType takes ownership of the given mapping
290// object.  If a mapping for the element already exists,
291// the provided object is deleted and the method returns
292// B_NAME_IN_USE.
293status_t XML::DocumentType::addMapping(
294	XMLElementMapping*					mapping) {
295
296	pair<mapping_set::iterator, bool> ret =	m_mappingSet.insert(mapping);
297	if(!ret.second) {
298		delete mapping;
299		return B_NAME_IN_USE;
300	} else
301		return B_OK;
302}
303
304IPersistent* XML::DocumentType::objectFor(
305	const char*									element) {
306
307	_NullMapping m(element);
308	mapping_set::iterator it = m_mappingSet.find(&m);
309
310	return (it != m_mappingSet.end()) ?
311		(*it)->create() : 0;
312}
313
314// END -- XML.cpp --
315