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