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