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] = &block;
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