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 // ExportContext.cpp 33 // e.moon 30jun99 34 35 #include "ExportContext.h" 36 #include "IPersistent.h" 37 38 #include <DataIO.h> 39 40 #include <algorithm> 41 #include <cstdio> 42 43 __USE_CORTEX_NAMESPACE 44 45 46 // -------------------------------------------------------- // 47 // ctor/dtor 48 // -------------------------------------------------------- // 49 50 ExportContext::~ExportContext() {} 51 52 ExportContext::ExportContext() : 53 54 stream(0), 55 m_indentLevel(0), 56 m_indentIncrement(4), 57 m_attrColumn(30), 58 m_state(INIT) {} 59 60 ExportContext::ExportContext( 61 BDataIO* _stream) : 62 63 stream(_stream), 64 m_indentLevel(0), 65 m_indentIncrement(2), 66 m_attrColumn(30), 67 m_state(INIT) { 68 69 ASSERT(_stream); 70 } 71 72 73 74 // -------------------------------------------------------- // 75 // *** XML formatting helpers 76 // -------------------------------------------------------- // 77 78 // writes a start tag. should only be called from 79 // IPersistent::xmlExportBegin(). 80 void ExportContext::beginElement( 81 const char* name) { 82 83 ASSERT(name); 84 85 if(!m_objectStack.size()) { 86 reportError("beginElement(): no object being written.\n"); 87 return; 88 } 89 if(m_state != WRITE_BEGIN && m_state != WRITE_CONTENT) { 90 reportError("beginElement(): not allowed.\n"); 91 return; 92 } 93 94 // push tag onto element stack, and link to entry for the current object 95 m_elementStack.push_back(element_entry()); 96 m_elementStack.back().name = name; 97 m_objectStack.back().element = m_elementStack.back().name.String(); 98 99 // write tag 100 BString out; 101 out << "\n" << indentString() << '<' << name; 102 writeString(out); 103 indentMore(); 104 } 105 106 // writes an end tag corresponding to the current element. 107 // should only be called from IPersistent::xmlExportEnd() or 108 // xmlExportContent(). 109 110 void ExportContext::endElement() { 111 112 if(!m_objectStack.size()) { 113 reportError("endElement(): no object being written.\n"); 114 return; 115 } 116 ASSERT(m_elementStack.size()); 117 element_entry& entry = m_elementStack.back(); 118 119 if(m_state != WRITE_END && m_state != WRITE_CONTENT) { 120 reportError("endElement(): not allowed.\n"); 121 return; 122 } 123 124 indentLess(); 125 126 BString out; 127 128 // write closing tag 129 if(!entry.hasContent) 130 out << "/>"; 131 else 132 out << "\n" << indentString() << "</" << entry.name.String() << ">"; 133 134 writeString(out); 135 136 // pop element off stack 137 m_elementStack.pop_back(); 138 } 139 140 // indicates that content follows (writes the end of the 141 // current element's start tag.) 142 void ExportContext::beginContent() { 143 144 if(!m_objectStack.size()) { 145 reportError("beginContent(): no object being written.\n"); 146 return; 147 } 148 ASSERT(m_elementStack.size()); 149 element_entry& entry = m_elementStack.back(); 150 151 if(m_state != WRITE_CONTENT) { 152 reportError("beginContent(): not allowed.\n"); 153 return; 154 } 155 156 BString out = ">"; 157 writeString(out); 158 159 entry.hasContent = true; 160 } 161 162 #define _WRITE_ATTR_BODY(VAL_SPEC) \ 163 if(!m_objectStack.size()) {\ 164 reportError("writeAttr(): no object being written.\n");\ 165 return;\ 166 }\ 167 ASSERT(m_elementStack.size());\ 168 if(m_state != WRITE_ATTRIBUTES &&\ 169 m_state != WRITE_CONTENT) {\ 170 reportError("writeAttr(): not allowed (state mismatch).\n");\ 171 return;\ 172 }\ 173 \ 174 m_elementStack.back().hasAttributes = true;\ 175 \ 176 BString out;\ 177 out << "\n" << indentString() << key;\ 178 _pad_with_spaces(out, key, *this, m_attrColumn) << " = '" << VAL_SPEC << '\'';\ 179 \ 180 writeString(out); 181 182 #if B_BEOS_VERSION > B_BEOS_VERSION_4_5 183 void ExportContext::writeAttr( 184 const char* key, 185 int8 value) {_WRITE_ATTR_BODY(value)} 186 187 void ExportContext::writeAttr( 188 const char* key, 189 uint8 value) {_WRITE_ATTR_BODY(uint32(value))} 190 191 void ExportContext::writeAttr( 192 const char* key, 193 int16 value) {_WRITE_ATTR_BODY(value)} 194 195 void ExportContext::writeAttr( 196 const char* key, 197 uint16 value) {_WRITE_ATTR_BODY(uint32(value))} 198 #endif 199 200 void ExportContext::writeAttr( 201 const char* key, 202 int32 value) {_WRITE_ATTR_BODY(value)} 203 204 void ExportContext::writeAttr( 205 const char* key, 206 uint32 value) {_WRITE_ATTR_BODY(value)} 207 208 void ExportContext::writeAttr( 209 const char* key, 210 int64 value) {_WRITE_ATTR_BODY(value)} 211 212 void ExportContext::writeAttr( 213 const char* key, 214 uint64 value) {_WRITE_ATTR_BODY(value)} 215 216 void ExportContext::writeAttr( 217 const char* key, 218 const char* value) {_WRITE_ATTR_BODY(value)} 219 220 void ExportContext::writeAttr( 221 const char* key, 222 const BString& value) {_WRITE_ATTR_BODY(value)} 223 224 void ExportContext::writeAttr( 225 const char* key, 226 float value) {_WRITE_ATTR_BODY(value)} 227 228 // writes a child object. 229 // should only be called from IPersistent::xmlExportContent(). 230 // returns B_OK on success, or B_ERROR if an error occurred. 231 status_t ExportContext::writeObject( 232 IPersistent* object) { 233 234 // * SETUP 235 ASSERT(object); 236 if(m_state == ABORT) 237 return B_ERROR; 238 state_t origState = m_state; 239 240 // write entry to object stack 241 m_objectStack.push_back(object_entry()); 242 object_entry& entry = m_objectStack.back(); 243 entry.object = object; 244 245 // * START TAG 246 int elements = m_elementStack.size(); 247 m_state = WRITE_BEGIN; 248 object->xmlExportBegin(*this); 249 250 if(m_state == ABORT) 251 return B_ERROR; 252 253 if(!entry.element) 254 reportError("writeObject(): no start tag for object.\n"); 255 else if(m_elementStack.size() - elements > 1) 256 reportError("writeObject(): object wrote more than one start tag.\n"); 257 258 if(m_state == ABORT) 259 return B_ERROR; 260 261 // * ATTRIBUTES 262 m_state = WRITE_ATTRIBUTES; 263 object->xmlExportAttributes(*this); 264 265 if(m_state == ABORT) 266 return B_ERROR; 267 268 // * CONTENT 269 m_state = WRITE_CONTENT; 270 object->xmlExportContent(*this); 271 272 if(m_state == ABORT) 273 return B_ERROR; 274 275 // * END 276 m_state = WRITE_END; 277 object->xmlExportEnd(*this); 278 279 m_state = origState; 280 281 // pop object entry 282 m_objectStack.pop_back(); 283 284 return (m_state == ABORT) ? B_ERROR : B_OK; 285 } 286 287 // writes an arbitrary string to the stream (calls reportError() 288 // on failure.) 289 290 status_t ExportContext::writeString( 291 const BString& string) { 292 293 return writeString(string.String(), string.Length()); 294 } 295 296 status_t ExportContext::writeString( 297 const char* data, 298 ssize_t length) { 299 300 ssize_t written = stream->Write(data, length); 301 if(written < 0) { 302 BString err = "Write error: '"; 303 err << strerror(written) << "'.\n"; 304 reportError(err.String()); 305 return written; 306 } 307 else if(written < length) { 308 BString err = "Write incomplete: '"; 309 err << written << " of " << length << " bytes written.\n"; 310 reportError(err.String()); 311 return B_IO_ERROR; 312 } 313 return B_OK; 314 } 315 316 317 // -------------------------------------------------------- // 318 // *** indentation helpers 319 // -------------------------------------------------------- // 320 321 const char* ExportContext::indentString() const { 322 return m_indentString.String(); 323 } 324 325 uint16 ExportContext::indentLevel() const { 326 return m_indentLevel; 327 } 328 329 void ExportContext::indentLess() { 330 m_indentLevel = (m_indentLevel > m_indentIncrement) ? 331 m_indentLevel - m_indentIncrement : 0; 332 m_indentString.SetTo(' ', m_indentLevel); 333 } 334 335 void ExportContext::indentMore() { 336 m_indentLevel += m_indentIncrement; 337 m_indentString.SetTo(' ', m_indentLevel); 338 } 339 340 // -------------------------------------------------------- // 341 // *** error-reporting operations 342 // -------------------------------------------------------- // 343 344 class dump_element { public: 345 BString& _s; 346 347 dump_element(BString& s) : _s(s) {} 348 void operator()(const ExportContext::element_entry& entry) { 349 _s << " " << entry.name << '\n'; 350 } 351 }; 352 353 // register a fatal error; halts the write process 354 // as soon as possible. 355 void ExportContext::reportError( 356 const char* text) { 357 358 m_error << "FATAL ERROR: "; 359 m_error << text << "\n"; 360 if(m_elementStack.size()) { 361 _dumpElementStack(m_error); 362 } 363 364 m_state = ABORT; 365 } 366 367 void ExportContext::_dumpElementStack( 368 BString& out) { 369 out << "Element stack:\n"; 370 for_each(m_elementStack.begin(), m_elementStack.end(), dump_element(out)); 371 } 372 373 // END -- ExportContext.cpp -- 374 375