root/OpenSceneGraph/trunk/src/osgDB/XmlParser.cpp @ 10943

Revision 10943, 12.1 kB (checked in by robert, 5 years ago)

From Jean-Sebastien Guay, "OK, so here are new changes.

1. The node type will be set to ATOM on read of <tag prop="..." ... /> type tags.
2. GROUP and NODE are now written using the same code (and not just duplicated code). Also NODE will not be written as an ATOM if it has no children or contents, so you need to set the type to ATOM if you want the <tag ... /> style.
3. You had put the write of "/>" for ATOM after the "return true", so it had no effect... Moved to before the return.
4. ATOM did not write its properties correctly, fixed.
5. As an added bonus, I made the write() method indent the output so it's more readable. It brings a small public interface change but the indent argument has a default value so client code doesn't need to change (if there even is any).
6. Another added bonus, I've simplified the write() method a bit by factoring out the write for children and properties into protected methods."

Line 
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2009 Robert Osfield
2 *
3 * This library is open source and may be redistributed and/or modified under
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version.  The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * OpenSceneGraph Public License for more details.
12*/
13
14#include <osgDB/XmlParser>
15#include <osgDB/FileUtils>
16
17#include <osg/Notify>
18
19using namespace osgDB;
20
21XmlNode* osgDB::readXmlFile(const std::string& filename,const Options* options)
22{
23    std::string foundFile = osgDB::findDataFile(filename, options);
24    if (!foundFile.empty())
25    {
26        XmlNode::Input input;
27        input.open(foundFile);
28        input.readAllDataIntoBuffer();
29
30        if (!input)
31        {
32            osg::notify(osg::NOTICE)<<"Could not open XML file: "<<filename<<std::endl;
33            return 0;
34        }
35
36        osg::ref_ptr<XmlNode> root = new XmlNode;
37        root->read(input);
38
39        return root.release();
40    }
41    else
42    {
43        osg::notify(osg::NOTICE)<<"Could not find XML file: "<<filename<<std::endl;
44        return 0;
45    }
46}
47
48std::string osgDB::trimEnclosingSpaces(const std::string& str)
49{
50    if (str.empty()) return str;
51
52    std::string::size_type start = str.find_first_not_of(' ');
53    if (start==std::string::npos) return std::string();
54
55    std::string::size_type end = str.find_last_not_of(' ');
56    if (end==std::string::npos) return std::string();
57
58    return std::string(str, start, (end-start)+1);
59}
60
61
62XmlNode* osgDB::readXmlStream(std::istream& fin)
63{
64    XmlNode::Input input;
65    input.attach(fin);
66    input.readAllDataIntoBuffer();
67
68    if (!input)
69    {
70        osg::notify(osg::NOTICE)<<"Could not attach to XML stream."<<std::endl;
71        return 0;
72    }
73
74    osg::ref_ptr<XmlNode> root = new XmlNode;
75    root->read(input);
76
77    return root.release();
78}
79
80
81XmlNode::Input::Input():
82    _currentPos(0)
83{
84    setUpControlMappings();
85}
86
87XmlNode::Input::Input(const Input&):
88    _currentPos(0)
89{
90    setUpControlMappings();
91}
92
93XmlNode::Input::~Input()
94{
95}
96
97void XmlNode::Input::setUpControlMappings()
98{
99    addControlToCharacter("&amp;",'&');
100    addControlToCharacter("&lt;",'<');
101    addControlToCharacter("&gt;",'>');
102    addControlToCharacter("&quot;",'"');
103    addControlToCharacter("&apos;",'\'');
104}
105
106void XmlNode::Input::addControlToCharacter(const std::string& control, int c)
107{
108    _controlToCharacterMap[control] = c;
109    _characterToControlMap[c] = control;
110}
111
112void XmlNode::Input::open(const std::string& filename)
113{
114    _fin.open(filename.c_str());
115}
116
117void XmlNode::Input::attach(std::istream& fin)
118{
119    std::ios &fios = _fin;
120    fios.rdbuf(fin.rdbuf());
121}
122
123void XmlNode::Input::readAllDataIntoBuffer()
124{
125    while(_fin)
126    {
127        int c = _fin.get();
128        if (c>=0 && c<=255)
129        {
130            _buffer.push_back(c);
131        }
132    }
133}
134
135void XmlNode::Input::skipWhiteSpace()
136{
137    while(_currentPos<_buffer.size() && (_buffer[_currentPos]==' ' || _buffer[_currentPos]=='\t'))
138    {
139        // osg::notify(osg::NOTICE)<<"_currentPos="<<_currentPos<<"_buffer.size()="<<_buffer.size()<<" v="<<_buffer[_currentPos]<<std::endl;
140        ++_currentPos;
141    }
142    //osg::notify(osg::NOTICE)<<"done"<<std::endl;
143}
144
145XmlNode::XmlNode()
146{
147    type = UNASSIGNED;
148}
149
150bool XmlNode::read(Input& input)
151{
152    if (type == UNASSIGNED) type = ROOT;
153
154    while(input)
155    {
156        //input.skipWhiteSpace();
157        if (input.match("<!--"))
158        {
159            XmlNode* commentNode = new XmlNode;
160            commentNode->type = XmlNode::COMMENT;
161            children.push_back(commentNode);
162
163            input += 4;
164            XmlNode::Input::size_type end = input.find("-->");
165            commentNode->contents = input.substr(0, end);
166            if (end!=std::string::npos)
167            {
168                osg::notify(osg::INFO)<<"Valid Comment record ["<<commentNode->contents<<"]"<<std::endl;
169                input += (end+3);
170            }
171            else
172            {
173                osg::notify(osg::NOTICE)<<"Error: Unclosed Comment record ["<<commentNode->contents<<"]"<<std::endl;
174                input += end;
175            }
176        }
177        else if (input.match("</"))
178        {
179            input += 2;
180            XmlNode::Input::size_type end = input.find(">");
181            std::string comment = input.substr(0, end);
182            if (end!=std::string::npos)
183            {
184                osg::notify(osg::INFO)<<"Valid end tag ["<<comment<<"]"<<std::endl;
185                input += (end+1);
186            }
187            else
188            {
189                osg::notify(osg::NOTICE)<<"Error: Unclosed end tag ["<<comment<<"]"<<std::endl;
190                input += end;
191            }
192
193            if (comment==name) osg::notify(osg::INFO)<<"end tag is matched correctly"<<std::endl;
194            else osg::notify(osg::NOTICE)<<"Error: end tag is not matched correctly"<<std::endl;
195
196            return true;
197        }
198        else if (input.match("<?"))
199        {
200            XmlNode* commentNode = new XmlNode;
201            commentNode->type = XmlNode::INFORMATION;
202            children.push_back(commentNode);
203
204            input += 2;
205            XmlNode::Input::size_type end = input.find("?>");
206            commentNode->contents = input.substr(0, end);
207            if (end!=std::string::npos)
208            {
209                osg::notify(osg::INFO)<<"Valid infomation record ["<<commentNode->contents<<"]"<<std::endl;
210                input += (end+2);
211            }
212            else
213            {
214                osg::notify(osg::NOTICE)<<"Error: Unclosed infomation record ["<<commentNode->contents<<"]"<<std::endl;
215                input += end;
216            }
217        }
218        else if (input.match("<"))
219        {
220            XmlNode* childNode = new XmlNode;
221            childNode->type = XmlNode::NODE;
222            children.push_back(childNode);
223
224            input += 1;
225
226            input.skipWhiteSpace();
227
228            int c = 0;
229            while ((c=input[0])>=0 && c!=' ' && c!='>' && c!='/')
230            {
231                childNode->name.push_back(c);
232                ++input;
233            }
234
235            while ((c=input[0])>=0 && c!='>' && c!='/')
236            {
237                Input::size_type prev_pos = input.currentPosition();
238
239                input.skipWhiteSpace();
240                std::string option;
241                std::string value;
242                while((c=input[0])>=0 && c!='>' && c!='/' && c!='"' && c!='\'' && c!='=' && c!=' ')
243                {
244                    option.push_back(c);
245                    ++input;
246                }
247                input.skipWhiteSpace();
248                if (input[0]=='=')
249                {
250                    ++input;
251
252                    input.skipWhiteSpace();
253
254                    if (input[0]=='"')
255                    {
256                        ++input;
257                        while((c=input[0])>=0 && c!='"')
258                        {
259                            value.push_back(c);
260                            ++input;
261                        }
262                        ++input;
263                    }
264                    else if (input[0]=='\'')
265                    {
266                        ++input;
267                        while((c=input[0])>=0 && c!='\'')
268                        {
269                            value.push_back(c);
270                            ++input;
271                        }
272                        ++input;
273                    }
274                    else
275                    {
276                        ++input;
277                        while((c=input[0])>=0 && c!=' ' && c!='"' && c!='\'' && c!='>')
278                        {
279                            value.push_back(c);
280                            ++input;
281                        }
282                    }
283                }
284
285                if (prev_pos == input.currentPosition())
286                {
287                    osg::notify(osg::NOTICE)<<"Error, parser iterator note advanced, position: "<<input.substr(0,50)<<std::endl;
288                    ++input;
289                }
290
291                if (!option.empty())
292                {
293                    osg::notify(osg::INFO)<<"Assigning option "<<option<<" with value "<<value<<std::endl;
294                    childNode->properties[option] = value;
295                }
296            }
297
298            if ((c=input[0])>=0 && (c=='>' || c=='/'))
299            {
300                ++input;
301
302                osg::notify(osg::INFO)<<"Valid tag ["<<childNode->name<<"]"<<std::endl;
303
304                if (c=='/')
305                {
306                    if ((c=input[0])>=0 && c=='>')
307                    {
308                        ++input;
309                        osg::notify(osg::INFO)<<"tag is closed correctly"<<std::endl;
310                        childNode->type = ATOM;
311                    }
312                    else 
313                        osg::notify(osg::NOTICE)<<"Error: tag is not closed correctly"<<std::endl;
314                }
315                else
316                {
317                    bool result = childNode->read(input);
318                    if (!result) return false;
319                }
320
321                if (type==NODE && !children.empty()) type = GROUP;
322            }
323            else
324            {
325                osg::notify(osg::NOTICE)<<"Unclosed tag ["<<childNode->name<<"]"<<std::endl;
326                return false;
327            }
328
329        }
330        else
331        {
332            int c = input[0];
333
334            if (c=='&')
335            {
336                std::string value;
337                while(input && (c=input.get())!=';') { value.push_back(c); }
338                value.push_back(c);
339
340                if (input._controlToCharacterMap.count(value)!=0)
341                {
342                    c = input._controlToCharacterMap[value];
343                    osg::notify(osg::INFO)<<"Read control character "<<value<<" converted to "<<char(c)<<std::endl;
344                    contents.push_back(c);
345                }
346                else
347                {
348                    osg::notify(osg::NOTICE)<<"Warning: read control character "<<value<<", but have no mapping to convert it to."<<std::endl;
349                }
350            }
351            else
352            {
353                contents.push_back( c );
354                ++input;
355            }
356
357        }
358    }
359
360    if (type==NODE && !children.empty()) type = GROUP;
361    return false;
362}
363
364bool XmlNode::write(std::ostream& fout, const std::string& indent) const
365{
366    switch(type)
367    {
368        case(UNASSIGNED):
369            return false;
370        case(ATOM):
371        {
372            fout<<indent<<"<"<<name;
373            writeProperties(fout);
374            fout<<" />"<<std::endl;
375            return true;
376        }
377        case(ROOT):
378        {
379            writeChildren(fout, indent);
380            return true;
381        }
382        case(NODE):
383        case(GROUP):
384        {
385            fout<<indent<<"<"<<name;
386            writeProperties(fout);
387            fout<<">"<<std::endl;
388
389            writeChildren(fout, indent + "  ");
390
391            fout<<indent<<"</"<<name<<">"<<std::endl;
392            return true;
393        }
394        case(COMMENT):
395        {
396            fout<<indent<<"<!--"<<contents<<"-->"<<std::endl;
397            return true;
398        }
399        case(INFORMATION):
400        {
401            fout<<indent<<"<?"<<contents<<"?>"<<std::endl;
402            return true;
403        }
404    }
405    return false;
406}
407
408bool XmlNode::writeString(std::ostream& fout, const std::string& str) const
409{
410    fout<<str;
411    return true;
412}
413
414bool XmlNode::writeChildren(std::ostream& fout, const std::string& indent) const
415{
416    for(Children::const_iterator citr = children.begin();
417        citr != children.end();
418        ++citr)
419    {
420        if (!(*citr)->write(fout, indent))
421            return false;
422    }
423
424    return true;
425}
426
427bool XmlNode::writeProperties(std::ostream& fout) const
428{
429    for(Properties::const_iterator oitr = properties.begin();
430        oitr != properties.end();
431        ++oitr)
432    {
433        fout<<" "<<oitr->first<<"=\"";
434        if (!writeString(fout,oitr->second))
435            return false;
436        fout<<"\"";
437    }
438
439    return true;
440}
Note: See TracBrowser for help on using the browser.