xref: /haiku/src/apps/cortex/Persistence/XML.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
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 
44 using namespace std;
45 
46 __USE_CORTEX_NAMESPACE
47 
48 // -------------------------------------------------------- //
49 // static members
50 // -------------------------------------------------------- //
51 
52 XML::doc_type_map XML::s_docTypeMap;
53 BLocker XML::s_docTypeLock("XML::s_docTypeLock");
54 
55 const 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*/
64 void 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*/
89 status_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*/
140 status_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*/
156 status_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*/
183 status_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*/
194 status_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*/
205 status_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*/
249 status_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 
267 class _NullMapping : public XMLElementMapping {
268 public:
269 	_NullMapping(
270 		const char*									_element) :
271 		XMLElementMapping(_element) {}
272 
273 	IPersistent* create() const { return 0; }
274 };
275 
276 XML::DocumentType::~DocumentType() {
277 	// clean up
278 	ptr_set_delete(m_mappingSet.begin(), m_mappingSet.end());
279 }
280 
281 XML::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.
293 status_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 
304 IPersistent* 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