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 void ExportContext::writeAttr( 183 const char* key, 184 int8 value) {_WRITE_ATTR_BODY(value)} 185 186 void ExportContext::writeAttr( 187 const char* key, 188 uint8 value) {_WRITE_ATTR_BODY(uint32(value))} 189 190 void ExportContext::writeAttr( 191 const char* key, 192 int16 value) {_WRITE_ATTR_BODY(value)} 193 194 void ExportContext::writeAttr( 195 const char* key, 196 uint16 value) {_WRITE_ATTR_BODY(uint32(value))} 197 198 void ExportContext::writeAttr( 199 const char* key, 200 int32 value) {_WRITE_ATTR_BODY(value)} 201 202 void ExportContext::writeAttr( 203 const char* key, 204 uint32 value) {_WRITE_ATTR_BODY(value)} 205 206 void ExportContext::writeAttr( 207 const char* key, 208 int64 value) {_WRITE_ATTR_BODY(value)} 209 210 void ExportContext::writeAttr( 211 const char* key, 212 uint64 value) {_WRITE_ATTR_BODY(value)} 213 214 void ExportContext::writeAttr( 215 const char* key, 216 const char* value) {_WRITE_ATTR_BODY(value)} 217 218 void ExportContext::writeAttr( 219 const char* key, 220 const BString& value) {_WRITE_ATTR_BODY(value)} 221 222 void ExportContext::writeAttr( 223 const char* key, 224 float value) {_WRITE_ATTR_BODY(value)} 225 226 // writes a child object. 227 // should only be called from IPersistent::xmlExportContent(). 228 // returns B_OK on success, or B_ERROR if an error occurred. 229 status_t ExportContext::writeObject( 230 IPersistent* object) { 231 232 // * SETUP 233 ASSERT(object); 234 if(m_state == ABORT) 235 return B_ERROR; 236 state_t origState = m_state; 237 238 // write entry to object stack 239 m_objectStack.push_back(object_entry()); 240 object_entry& entry = m_objectStack.back(); 241 entry.object = object; 242 243 // * START TAG 244 int elements = m_elementStack.size(); 245 m_state = WRITE_BEGIN; 246 object->xmlExportBegin(*this); 247 248 if(m_state == ABORT) 249 return B_ERROR; 250 251 if(!entry.element) 252 reportError("writeObject(): no start tag for object.\n"); 253 else if(m_elementStack.size() - elements > 1) 254 reportError("writeObject(): object wrote more than one start tag.\n"); 255 256 if(m_state == ABORT) 257 return B_ERROR; 258 259 // * ATTRIBUTES 260 m_state = WRITE_ATTRIBUTES; 261 object->xmlExportAttributes(*this); 262 263 if(m_state == ABORT) 264 return B_ERROR; 265 266 // * CONTENT 267 m_state = WRITE_CONTENT; 268 object->xmlExportContent(*this); 269 270 if(m_state == ABORT) 271 return B_ERROR; 272 273 // * END 274 m_state = WRITE_END; 275 object->xmlExportEnd(*this); 276 277 m_state = origState; 278 279 // pop object entry 280 m_objectStack.pop_back(); 281 282 return (m_state == ABORT) ? B_ERROR : B_OK; 283 } 284 285 // writes an arbitrary string to the stream (calls reportError() 286 // on failure.) 287 288 status_t ExportContext::writeString( 289 const BString& string) { 290 291 return writeString(string.String(), string.Length()); 292 } 293 294 status_t ExportContext::writeString( 295 const char* data, 296 ssize_t length) { 297 298 ssize_t written = stream->Write(data, length); 299 if(written < 0) { 300 BString err = "Write error: '"; 301 err << strerror(written) << "'.\n"; 302 reportError(err.String()); 303 return written; 304 } 305 else if(written < length) { 306 BString err = "Write incomplete: '"; 307 err << written << " of " << length << " bytes written.\n"; 308 reportError(err.String()); 309 return B_IO_ERROR; 310 } 311 return B_OK; 312 } 313 314 315 // -------------------------------------------------------- // 316 // *** indentation helpers 317 // -------------------------------------------------------- // 318 319 const char* ExportContext::indentString() const { 320 return m_indentString.String(); 321 } 322 323 uint16 ExportContext::indentLevel() const { 324 return m_indentLevel; 325 } 326 327 void ExportContext::indentLess() { 328 m_indentLevel = (m_indentLevel > m_indentIncrement) ? 329 m_indentLevel - m_indentIncrement : 0; 330 m_indentString.SetTo(' ', m_indentLevel); 331 } 332 333 void ExportContext::indentMore() { 334 m_indentLevel += m_indentIncrement; 335 m_indentString.SetTo(' ', m_indentLevel); 336 } 337 338 // -------------------------------------------------------- // 339 // *** error-reporting operations 340 // -------------------------------------------------------- // 341 342 class dump_element { public: 343 BString& _s; 344 345 dump_element(BString& s) : _s(s) {} 346 void operator()(const ExportContext::element_entry& entry) { 347 _s << " " << entry.name << '\n'; 348 } 349 }; 350 351 // register a fatal error; halts the write process 352 // as soon as possible. 353 void ExportContext::reportError( 354 const char* text) { 355 356 m_error << "FATAL ERROR: "; 357 m_error << text << "\n"; 358 if(m_elementStack.size()) { 359 _dumpElementStack(m_error); 360 } 361 362 m_state = ABORT; 363 } 364 365 void ExportContext::_dumpElementStack( 366 BString& out) { 367 out << "Element stack:\n"; 368 for_each(m_elementStack.begin(), m_elementStack.end(), dump_element(out)); 369 } 370 371 // END -- ExportContext.cpp -- 372 373