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
~ExportContext()50 ExportContext::~ExportContext() {}
51
ExportContext()52 ExportContext::ExportContext() :
53
54 stream(0),
55 m_indentLevel(0),
56 m_indentIncrement(4),
57 m_attrColumn(30),
58 m_state(INIT) {}
59
ExportContext(BDataIO * _stream)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().
beginElement(const char * name)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
endElement()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.)
beginContent()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
writeAttr(const char * key,int8 value)182 void ExportContext::writeAttr(
183 const char* key,
184 int8 value) {_WRITE_ATTR_BODY(value)}
185
writeAttr(const char * key,uint8 value)186 void ExportContext::writeAttr(
187 const char* key,
188 uint8 value) {_WRITE_ATTR_BODY(uint32(value))}
189
writeAttr(const char * key,int16 value)190 void ExportContext::writeAttr(
191 const char* key,
192 int16 value) {_WRITE_ATTR_BODY(value)}
193
writeAttr(const char * key,uint16 value)194 void ExportContext::writeAttr(
195 const char* key,
196 uint16 value) {_WRITE_ATTR_BODY(uint32(value))}
197
writeAttr(const char * key,int32 value)198 void ExportContext::writeAttr(
199 const char* key,
200 int32 value) {_WRITE_ATTR_BODY(value)}
201
writeAttr(const char * key,uint32 value)202 void ExportContext::writeAttr(
203 const char* key,
204 uint32 value) {_WRITE_ATTR_BODY(value)}
205
writeAttr(const char * key,int64 value)206 void ExportContext::writeAttr(
207 const char* key,
208 int64 value) {_WRITE_ATTR_BODY(value)}
209
writeAttr(const char * key,uint64 value)210 void ExportContext::writeAttr(
211 const char* key,
212 uint64 value) {_WRITE_ATTR_BODY(value)}
213
writeAttr(const char * key,const char * value)214 void ExportContext::writeAttr(
215 const char* key,
216 const char* value) {_WRITE_ATTR_BODY(value)}
217
writeAttr(const char * key,const BString & value)218 void ExportContext::writeAttr(
219 const char* key,
220 const BString& value) {_WRITE_ATTR_BODY(value)}
221
writeAttr(const char * key,float value)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.
writeObject(IPersistent * object)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
writeString(const BString & string)288 status_t ExportContext::writeString(
289 const BString& string) {
290
291 return writeString(string.String(), string.Length());
292 }
293
writeString(const char * data,ssize_t length)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
indentString() const319 const char* ExportContext::indentString() const {
320 return m_indentString.String();
321 }
322
indentLevel() const323 uint16 ExportContext::indentLevel() const {
324 return m_indentLevel;
325 }
326
indentLess()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
indentMore()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
dump_element(BString & s)345 dump_element(BString& s) : _s(s) {}
operator ()(const ExportContext::element_entry & entry)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.
reportError(const char * text)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
_dumpElementStack(BString & out)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