1 /*
2 * @(#)file/XML.cpp 3.00 10 Aug 2001
3 *
4 * Copyright (c) 2001 Pete Goodliffe (pete@cthree.org)
5 *
6 * This file is part of TSE3 - the Trax Sequencer Engine version 3.00.
7 *
8 * This library is modifiable/redistributable under the terms of the GNU
9 * General Public License.
10 *
11 * You should have received a copy of the GNU General Public License along
12 * with this program; see the file COPYING. If not, write to the Free Software
13 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
14 *
15 */
16
17 #include "tse3/file/XML.h"
18
19 #include "tse3/Progress.h"
20
21 #include <stack>
22
23 // Loading Songs
24 #include <memory>
25 #include "tse3/Song.h"
26
27 /******************************************************************************
28 * XmlLoadInfo
29 *****************************************************************************/
30
XmlLoadInfo()31 TSE3::File::XmlLoadInfo::XmlLoadInfo()
32 : PPQN(TSE3::Clock::PPQN), song(0),
33 // major(-1), minor(-1), gcc 3.0 doesn't like this XXX
34 unknownChunks(false), unknownData(false),
35 noChunks(0), progress(0)
36 {
37 major = -1;
38 minor = -1;
39 }
40
41
42 /******************************************************************************
43 * XmlFileWriter
44 *****************************************************************************/
45
46 class TSE3::File::XmlFileWriterImpl
47 {
48 public:
49 std::stack<std::string> elements;
50 };
51
XmlFileWriter(std::ostream & o)52 TSE3::File::XmlFileWriter::XmlFileWriter(std::ostream &o)
53 : out(o), indentLevel(0), pimpl(new TSE3::File::XmlFileWriterImpl)
54 {
55 //out << "<?xml version=\"1.0\"?>\n";
56 //TSE3MDL isn't real XML (yet) so we don't write this
57 }
58
59
~XmlFileWriter()60 TSE3::File::XmlFileWriter::~XmlFileWriter()
61 {
62 delete pimpl;
63 }
64
65
indent(std::ostream & out)66 void TSE3::File::XmlFileWriter::indent(std::ostream &out)
67 {
68 for (int n = 0; n < indentLevel; n++) out << " ";
69 }
70
71
openElement(const std::string & name)72 void TSE3::File::XmlFileWriter::openElement(const std::string &name)
73 {
74 indent(out);
75 out << "<" << name << ">\n";
76 pimpl->elements.push(name);
77 indentLevel++;
78 }
79
80
closeElement()81 void TSE3::File::XmlFileWriter::closeElement()
82 {
83 indentLevel--;
84 indent(out);
85 out << "</" << pimpl->elements.top() << ">\n";
86 pimpl->elements.pop();
87 }
88
89
element(const std::string & name,const std::string & value)90 void TSE3::File::XmlFileWriter::element(const std::string &name,
91 const std::string &value)
92 {
93 indent(out);
94 out << "<" << name << " value=\"" << value << "\"/>\n";
95 }
96
97
element(const std::string & name,const char * value)98 void TSE3::File::XmlFileWriter::element(const std::string &name,
99 const char *value)
100 {
101 indent(out);
102 out << "<" << name << " value=\"" << value << "\"/>\n";
103 }
104
105
element(const std::string & name,int value)106 void TSE3::File::XmlFileWriter::element(const std::string &name, int value)
107 {
108 indent(out);
109 out << "<" << name << " value=\"" << value << "\"/>\n";
110 }
111
112
element(const std::string & name,size_t value)113 void TSE3::File::XmlFileWriter::element(const std::string &name, size_t value)
114 {
115 indent(out);
116 out << "<" << name << " value=\"" << value << "\"/>\n";
117 }
118
119
element(const std::string & name,bool value)120 void TSE3::File::XmlFileWriter::element(const std::string &name, bool value)
121 {
122 indent(out);
123 out << "<" << name << " value=\"" << (value ? "true" : "false") << "\"/>\n";
124 }
125
126
comment(const std::string & comment)127 void TSE3::File::XmlFileWriter::comment(const std::string &comment)
128 {
129 indent(out);
130 out << "<!-- " << comment << " -->\n";
131 }
132
133
134
135 /******************************************************************************
136 * XmlFileReader
137 *****************************************************************************/
138
XmlFileReader(std::istream & in)139 TSE3::File::XmlFileReader::XmlFileReader(std::istream &in) : in(in)
140 {
141 }
142
143
~XmlFileReader()144 TSE3::File::XmlFileReader::~XmlFileReader()
145 {
146 }
147
148
149 namespace
150 {
151 class SimpleNumberParser : public TSE3::File::XmlElementParser
152 {
153 public:
SimpleNumberParser(int & number)154 SimpleNumberParser(int &number)
155 : number(number) {}
156 /**
157 * @reimplemented
158 */
parse(const std::string & data)159 void parse(const std::string &data)
160 {
161 int i;
162 std::istringstream si(data);
163 si >> i;
164 number = i;
165 }
166 private:
167 int &number;
168 };
169 }
170
171
load()172 TSE3::Song *TSE3::File::XmlFileReader::load()
173 {
174 XmlBlockParser parser;
175 XmlLoadInfo info;
176 XmlBlockParser tse3parser;
177 SimpleNumberParser versionMajor(info.major);
178 SimpleNumberParser versionMinor(info.minor);
179 SimpleNumberParser ppqn(info.PPQN);
180 std::auto_ptr<TSE3::Song> song(new TSE3::Song(0));
181
182 info.song = song.get();
183 info.progress = 0;//progress;
184
185 parser.add("TSE3", tse3parser);
186
187 tse3parser.add("Version-Major", versionMajor);
188 tse3parser.add("Version-Minor", versionMinor);
189 tse3parser.add("PPQN", ppqn);
190
191 parser.parse(in, "", info);
192 return song.release();
193 }
194
195
196 /******************************************************************************
197 * XmlBlockParser
198 *****************************************************************************/
199
XmlBlockParser()200 TSE3::File::XmlBlockParser::XmlBlockParser()
201 : catchAll(0)
202 {
203 }
204
205
add(const std::string & name,XmlBlockParser & block)206 void TSE3::File::XmlBlockParser::add(const std::string &name, XmlBlockParser &block)
207 {
208 blocks[name] = █
209 }
210
211
add(const std::string & name,XmlElementParser & item)212 void TSE3::File::XmlBlockParser::add(const std::string &name, XmlElementParser &item)
213 {
214 elements[name] = &item;
215 }
216
217
add(XmlElementParser & item)218 void TSE3::File::XmlBlockParser::add(XmlElementParser &item)
219 {
220 catchAll = &item;
221 }
222
223
parse(std::istream & in,const std::string & tag,XmlLoadInfo & info)224 void TSE3::File::XmlBlockParser::parse(std::istream &in, const std::string &tag,
225 XmlLoadInfo &info)
226 {
227 // Check that the chunk opens correctly
228 std::cout << "XBP: start of tag given as \""<<tag<<"\"\n";
229
230
231 if (info.progress)
232 {
233 info.progress->progress(in.tellg());
234 }
235
236 bool more = true;
237 std::string line;
238 while (more && getline(ws(in), line))
239 {
240 std::cout << "XBP: line[ " << line << " ]";
241 if (line == "</" + tag + ">")
242 {
243 std::cout << " is matching end tag\n";
244 more = false;
245 }
246 else if (!line.size()
247 || line.find("<!--") != std::string::npos
248 || line[0] != '<')
249 {
250 std::cout << " is comment, skipping...\n";
251 // skip the comment
252 }
253 else if (line[0] != '<')
254 {
255 std::cout << " is some content, skipping...\n";
256 }
257 else if (line.find("/>") == std::string::npos)
258 {
259 std::string tag = line.substr(1, std::string::npos);
260 tag = tag.substr(0, tag.find(">"));
261 tag = tag.substr(0, tag.find(" "));
262 std::cout << " is open element \""<<tag<<"\" ";
263
264 // search for it in the blocks
265 if (blocks.find(tag) != blocks.end())
266 {
267 std::cout << "with match\n";
268 blocks[tag]->parse(in, tag, info);
269 }
270 else
271 {
272 std::cout << "without match, skipping contents\n";
273 skipBlock(in);
274 info.unknownChunks = true;
275 }
276 }
277 else
278 {
279 // search for it in the elements
280 line = line.substr(1, std::string::npos);
281 const std::string name = line.substr(0, line.find(" "));
282 std::string data;
283 if (line.find("value=\""))
284 {
285 data = line.substr(line.find("value=\"")+7);
286 data = data.substr(0, data.find("\""));
287 }
288 std::cout << " is single element \""<<name<<"\" with data \""<<data<<"\" ";
289 if (elements.find(name) != elements.end())
290 {
291 std::cout << "with match\n";
292 elements[name]->parse(data);
293 }
294 else if (catchAll)
295 {
296 std::cout << "without match, calling catch all\n";
297 catchAll->parse(line);
298 }
299 else
300 {
301 std::cout << "without match\n";
302 info.unknownData = true;
303 }
304 }
305 }
306 std::cout << "XBP: end\n";
307 }
308
309
skipBlock(std::istream & in)310 void TSE3::File::XmlBlockParser::skipBlock(std::istream &in)
311 {
312 std::string open;
313 getline(ws(in), open);
314 int depth = 1;
315 std::string line;
316 do
317 {
318 getline(ws(in), line);
319 if (line.find("</") == 0)
320 {
321 --depth;
322 }
323 else if (line[0] == '<'
324 && line[1] != '!'
325 && line[1] != '?'
326 && (line.find("/>") == std::string::npos))
327 {
328 ++depth;
329 }
330 }
331 while (!in.eof() && depth);
332 }
333
334
335 /******************************************************************************
336 * XmlElementParser class
337 *****************************************************************************/
338
~XmlElementParser()339 TSE3::File::XmlElementParser::~XmlElementParser()
340 {
341 }
342