Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/usability/src/libraries/core/Loader.cc @ 11834

Last change on this file since 11834 was 7963, checked in by rgrieder, 14 years ago

Added "replaceLuaTags" function to the Loader. That allows to load files without parsing it as Lua file.
Bottom line:

  • Compiling the level list is about 30 times faster and takes less than half a second now
  • We need to write level files that are XML-valid even if all the Lua stuff is removed. (or: somebody find a better implementation for speeding up that LevelInfo loading)
  • Property svn:eol-style set to native
File size: 15.8 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Fabian 'x3n' Landau
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29#include "Loader.h"
30
31#include <sstream>
32#include <tinyxml/ticpp.h>
33#include <boost/scoped_ptr.hpp>
34
35#include "util/Debug.h"
36#include "util/Exception.h"
37#include "util/StringUtils.h"
38#include "BaseObject.h"
39#include "Iterator.h"
40#include "ObjectList.h"
41#include "LuaState.h"
42#include "Namespace.h"
43#include "Resource.h"
44#include "XMLFile.h"
45
46namespace orxonox
47{
48    std::vector<std::pair<const XMLFile*, ClassTreeMask> > Loader::files_s;
49    ClassTreeMask Loader::currentMask_s;
50
51    bool Loader::open(const XMLFile* file, const ClassTreeMask& mask)
52    {
53        Loader::add(file, mask);
54        return Loader::load(file, mask);
55    }
56
57    void Loader::close()
58    {
59        Loader::unload();
60        Loader::files_s.clear();
61    }
62
63    void Loader::close(const XMLFile* file)
64    {
65        Loader::unload(file);
66        Loader::remove(file);
67    }
68
69    void Loader::add(const XMLFile* file, const ClassTreeMask& mask)
70    {
71        if (!file)
72            return;
73        Loader::files_s.insert(Loader::files_s.end(), std::pair<const XMLFile*, ClassTreeMask>(file, mask));
74    }
75
76    void Loader::remove(const XMLFile* file)
77    {
78        if (!file)
79            return;
80        for (std::vector<std::pair<const XMLFile*, ClassTreeMask> >::iterator it = Loader::files_s.begin(); it != Loader::files_s.end(); ++it)
81        {
82            if (it->first == file)
83            {
84                Loader::files_s.erase(it);
85                break;
86            }
87        }
88    }
89
90    /**
91    @brief
92        Loads all opened files, while conforming to the restrictions given by the input ClassTreeMask.
93    @param mask
94        A ClassTreeMask, which defines which types of classes are loaded and which aren't.
95    @param verbose
96        Whether the loader is verbose (prints its progress in a low output level) or not.
97    @return
98        Returns true if successful.
99    */
100    bool Loader::load(const ClassTreeMask& mask, bool verbose)
101    {
102        bool success = true;
103        for (std::vector<std::pair<const XMLFile*, ClassTreeMask> >::iterator it = Loader::files_s.begin(); it != Loader::files_s.end(); ++it)
104            if (!Loader::load(it->first, it->second * mask, verbose))
105                success = false;
106
107        return success;
108    }
109
110    void Loader::unload(const ClassTreeMask& mask)
111    {
112        for (ObjectList<BaseObject>::iterator it = ObjectList<BaseObject>::begin(); it != ObjectList<BaseObject>::end(); )
113        {
114            if (mask.isIncluded(it->getIdentifier()))
115                (it++)->destroy();
116            else
117                ++it;
118        }
119    }
120
121    /**
122    @brief
123        Reloads all opened files, while conforming to the restrictions given by the input ClassTreeMask.
124    @param mask
125        A ClassTreeMask, which defines which types of classes are reloaded and which aren't.
126    @param verbose
127        Whether the loader is verbose (prints its progress in a low output level) or not.
128    @return
129        Returns true if successful.
130    */
131    bool Loader::reload(const ClassTreeMask& mask, bool verbose)
132    {
133        Loader::unload(mask);
134        return Loader::load(mask, verbose);
135    }
136
137    /**
138    @brief
139        Loads the input file, while conforming to the restrictions given by the input ClassTreeMask.
140    @param file
141        The file to be loaded.
142    @param mask
143        A ClassTreeMask, which defines which types of classes are loaded and which aren't.
144    @param verbose
145        Whether the loader is verbose (prints its progress in a low output level) or not.
146    @return
147        Returns true if successful.
148    */
149    bool Loader::load(const XMLFile* file, const ClassTreeMask& mask, bool verbose, bool bRemoveLuaTags)
150    {
151        if (!file)
152            return false;
153
154        Loader::currentMask_s = file->getMask() * mask;
155
156        std::string xmlInput;
157        if (file->getLuaSupport() && !bRemoveLuaTags)
158        {
159            // Use the LuaState to replace the XML tags (calls our function)
160            scoped_ptr<LuaState> luaState(new LuaState());
161            luaState->setIncludeParser(&Loader::replaceLuaTags);
162            luaState->includeFile(file->getFilename());
163            xmlInput = luaState->getOutput().str();
164        }
165        else
166        {
167            shared_ptr<ResourceInfo> info = Resource::getInfo(file->getFilename());
168            if (info == NULL)
169            {
170                COUT(1) << "Error: Could not find XML file '" << file->getFilename() << "'." << std::endl;
171                return false;
172            }
173            xmlInput = Resource::open(file->getFilename())->getAsString();
174
175            if (bRemoveLuaTags)
176            {
177                // Remove all Lua code.
178                // Note: we only need this to speed up parsing of level files at the
179                // start of the program.
180                // Assumption: the LevelInfo tag does not use Lua scripting
181                xmlInput = removeLuaTags(xmlInput);
182            }
183        }
184
185        try
186        {
187            if(verbose)
188            {
189                COUT(0) << "Start loading " << file->getFilename() << "..." << std::endl;
190                COUT(3) << "Mask: " << Loader::currentMask_s << std::endl;
191            }
192            else
193            {
194                COUT(4) << "Start loading " << file->getFilename() << "..." << std::endl;
195                COUT(4) << "Mask: " << Loader::currentMask_s << std::endl;
196            }
197
198            ticpp::Document xmlfile(file->getFilename());
199            xmlfile.Parse(xmlInput, true);
200
201            ticpp::Element rootElement;
202            rootElement.SetAttribute("name", "root");
203            rootElement.SetAttribute("bAutogenerated", true);
204
205            for (ticpp::Iterator<ticpp::Element> child = xmlfile.FirstChildElement(false); child != child.end(); child++)
206                rootElement.InsertEndChild(*child);
207
208            COUT(4) << "  creating root-namespace..." << std::endl;
209            Namespace* rootNamespace = new Namespace(0);
210            rootNamespace->setLoaderIndentation("    ");
211            rootNamespace->setFile(file);
212            rootNamespace->setNamespace(rootNamespace);
213            rootNamespace->setRoot(true);
214            rootNamespace->XMLPort(rootElement, XMLPort::LoadObject);
215
216            if(verbose)
217                COUT(0) << "Finished loading " << file->getFilename() << '.' << std::endl;
218            else
219                COUT(4) << "Finished loading " << file->getFilename() << '.' << std::endl;
220
221            COUT(4) << "Namespace-tree:" << std::endl << rootNamespace->toString("  ") << std::endl;
222
223            return true;
224        }
225        catch (ticpp::Exception& ex)
226        {
227            COUT(1) << std::endl;
228            COUT(1) << "An XML-error occurred in Loader.cc while loading " << file->getFilename() << ':' << std::endl;
229            COUT(1) << ex.what() << std::endl;
230            COUT(1) << "Loading aborted." << std::endl;
231            return false;
232        }
233        catch (Exception& ex)
234        {
235            COUT(1) << std::endl;
236            COUT(1) << "A loading-error occurred in Loader.cc while loading " << file->getFilename() << ':' << std::endl;
237            COUT(1) << ex.what() << std::endl;
238            COUT(1) << "Loading aborted." << std::endl;
239            return false;
240        }
241        catch (...)
242        {
243            COUT(1) << std::endl;
244            COUT(1) << "An error occurred in Loader.cc while loading " << file->getFilename() << ':' << std::endl;
245            COUT(1) << Exception::handleMessage() << std::endl;
246            COUT(1) << "Loading aborted." << std::endl;
247            return false;
248        }
249    }
250
251    void Loader::unload(const XMLFile* file, const ClassTreeMask& mask)
252    {
253        if (!file)
254            return;
255        for (ObjectList<BaseObject>::iterator it = ObjectList<BaseObject>::begin(); it; )
256        {
257            if ((it->getFile() == file) && mask.isIncluded(it->getIdentifier()))
258                (it++)->destroy();
259            else
260                ++it;
261        }
262    }
263
264    /**
265    @brief
266        Reloads the input file, while conforming to the restrictions given by the input ClassTreeMask.
267    @param file
268        The file to be reloaded.
269    @param mask
270        A ClassTreeMask, which defines which types of classes are reloaded and which aren't.
271    @param verbose
272        Whether the loader is verbose (prints its progress in a low output level) or not.
273    @return
274        Returns true if successful.
275    */
276    bool Loader::reload(const XMLFile* file, const ClassTreeMask& mask, bool verbose)
277    {
278        Loader::unload(file, mask);
279        return Loader::load(file, mask, verbose);
280    }
281
282    bool Loader::getLuaTags(const std::string& text, std::map<size_t, bool>& luaTags)
283    {
284        // fill map with all Lua tags
285        {
286            size_t pos = 0;
287            while ((pos = text.find("<?lua", pos)) != std::string::npos)
288                luaTags[pos++] = true;
289        }
290        {
291            size_t pos = 0;
292            while ((pos = text.find("?>", pos)) != std::string::npos)
293                luaTags[pos++] = false;
294        }
295
296        // erase all tags from the map that are between two quotes
297        {
298            std::map<size_t, bool>::iterator it = luaTags.begin();
299            std::map<size_t, bool>::iterator it2 = it;
300            bool bBetweenQuotes = false;
301            size_t pos = 0;
302            while ((pos = getNextQuote(text, pos)) != std::string::npos)
303            {
304                while ((it != luaTags.end()) && (it->first < pos))
305                {
306                    if (bBetweenQuotes)
307                    {
308                        it2++;
309                        if (it->second && !(it2->second) && it2->first < pos)
310                            it = ++it2;
311                        else
312                            luaTags.erase(it++);
313                    }
314                    else
315                        ++it;
316                }
317                bBetweenQuotes = !bBetweenQuotes;
318                pos++;
319            }
320        }
321
322        // check whether on every opening <?lua tag a closing ?> tag follows
323        {
324            bool expectedValue = true;
325            for (std::map<size_t, bool>::iterator it = luaTags.begin(); it != luaTags.end(); ++it)
326            {
327                if (it->second == expectedValue)
328                    expectedValue = !expectedValue;
329                else
330                {
331                    expectedValue = false;
332                    break;
333                }
334            }
335            if (!expectedValue)
336            {
337                COUT(2) << "Warning: Error in level file" << std::endl;
338                // TODO: error handling
339                return false; 
340            }
341        }
342
343        return true;
344    }
345
346    std::string Loader::replaceLuaTags(const std::string& text)
347    {
348        // create a map with all lua tags
349        std::map<size_t, bool> luaTags;
350        if (!getLuaTags(text, luaTags))
351            return "";
352
353        // Use a stringstream object to speed up the parsing
354        std::ostringstream output;
355
356        // cut the original string into pieces and put them together with print() instead of lua tags
357        {
358            std::map<size_t, bool>::iterator it = luaTags.begin();
359            bool bInPrintFunction = true;
360            size_t start = 0;
361            size_t end = 0;
362
363            do
364            {
365                if (it != luaTags.end())
366                    end = (it++)->first;
367                else
368                    end = std::string::npos;
369
370                unsigned int equalSignCounter = 0;
371
372                if (bInPrintFunction)
373                {
374                    // count ['='[ and ]'='] and replace tags with print([[ and ]])
375                    const std::string& temp = text.substr(start, end - start);
376                    {
377                    size_t pos = 0;
378                    while ((pos = temp.find('[', pos)) != std::string::npos)
379                    {
380                        unsigned int tempCounter = 1;
381                        size_t tempPos = pos++;
382                        while (temp[++tempPos] == '=')
383                        {
384                            tempCounter++;
385                        }
386                        if (temp[tempPos] != '[')
387                        {
388                            tempCounter = 0;
389                        }
390                        else if (tempCounter == 0)
391                        {
392                            tempCounter = 1;
393                        }
394                        if (tempCounter > equalSignCounter)
395                            equalSignCounter = tempCounter;
396                        }
397                    }
398                    {
399                        size_t pos = 0;
400                        while ((pos = temp.find(']', pos)) != std::string::npos)
401                        {
402                            unsigned int tempCounter = 1;
403                            size_t tempPos = pos++;
404                            while (temp[++tempPos] == '=')
405                            {
406                                tempCounter++;
407                            }
408                            if (temp[tempPos] != ']')
409                            {
410                                tempCounter = 0;
411                            }
412                            else if (tempCounter == 0)
413                            {
414                                tempCounter = 1;
415                            }
416                            if (tempCounter > equalSignCounter)
417                                equalSignCounter = tempCounter;
418                        }
419                    }
420                    std::string equalSigns;
421                    for (unsigned int i = 0; i < equalSignCounter; i++)
422                    {
423                        equalSigns += '=';
424                    }
425                    output << "print([" + equalSigns + '[' + temp + ']' + equalSigns +"])";
426                    start = end + 5;
427                }
428                else
429                {
430                    output << text.substr(start, end - start);
431                    start = end + 2;
432                }
433
434                bInPrintFunction = !bInPrintFunction;
435            }
436            while (end != std::string::npos);
437        }
438
439        return output.str();
440    }
441
442    std::string Loader::removeLuaTags(const std::string& text)
443    {
444        // create a map with all lua tags
445        std::map<size_t, bool> luaTags;
446        if (!getLuaTags(text, luaTags))
447            return "";
448
449        // Use a stringstream object to speed up the concatenation
450        std::ostringstream output;
451
452        // cut the original string into pieces and only write the non Lua parts
453        std::map<size_t, bool>::iterator it = luaTags.begin();
454        bool bLuaCode = false;
455        size_t start = 0;
456        size_t end = 0;
457
458        do
459        {
460            if (it != luaTags.end())
461                end = (it++)->first;
462            else
463                end = std::string::npos;
464
465            if (!bLuaCode)
466            {
467                output << text.substr(start, end - start);
468                start = end + 5;
469            }
470            else
471                start = end + 2;
472
473            bLuaCode = !bLuaCode;
474        }
475        while (end != std::string::npos);
476
477        return output.str();
478    }
479}
Note: See TracBrowser for help on using the repository browser.