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