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