xref: /haiku/src/apps/cortex/Persistence/ExportContext.cpp (revision 2f470aec1c92ce6917b8a903e343795dc77af41f)
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