Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core7/src/libraries/core/Loader.cc @ 10412

Last change on this file since 10412 was 10392, checked in by landauf, 9 years ago

Loader is now a singleton instead of a static class. fixes issue with statically initialized ClassTreeMask (which again requires a BaseObject-Identifier)

  • Property svn:eol-style set to native
File size: 19.3 KB
RevLine 
[1505]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"
[2710]30
[5695]31#include <sstream>
[2710]32#include <tinyxml/ticpp.h>
[5695]33#include <boost/scoped_ptr.hpp>
[10264]34#include <boost/filesystem.hpp>
35#include <boost/filesystem/fstream.hpp>
[2710]36
[8858]37#include "util/Output.h"
[3196]38#include "util/Exception.h"
[5695]39#include "util/StringUtils.h"
[1505]40#include "BaseObject.h"
[5695]41#include "LuaState.h"
[1505]42#include "Namespace.h"
[5695]43#include "Resource.h"
[3196]44#include "XMLFile.h"
[9667]45#include "object/Iterator.h"
46#include "object/ObjectList.h"
[1505]47
48namespace orxonox
49{
[10392]50    Loader* Loader::singletonPtr_s = 0;
[1505]51
[8858]52    bool Loader::open(const XMLFile* file, const ClassTreeMask& mask, bool bVerbose)
[1505]53    {
[10392]54        this->add(file, mask);
55        return this->load(file, mask, bVerbose);
[1505]56    }
57
58    void Loader::close()
59    {
[10392]60        this->unload();
61        this->files_.clear();
[1505]62    }
63
[2087]64    void Loader::close(const XMLFile* file)
[1505]65    {
[10392]66        this->unload(file);
67        this->remove(file);
[1505]68    }
69
[2087]70    void Loader::add(const XMLFile* file, const ClassTreeMask& mask)
[1505]71    {
[2087]72        if (!file)
[1755]73            return;
[10392]74        this->files_.insert(this->files_.end(), std::pair<const XMLFile*, ClassTreeMask>(file, mask));
[1505]75    }
76
[2087]77    void Loader::remove(const XMLFile* file)
[1505]78    {
[2087]79        if (!file)
[1755]80            return;
[10392]81        for (std::vector<std::pair<const XMLFile*, ClassTreeMask> >::iterator it = this->files_.begin(); it != this->files_.end(); ++it)
[1505]82        {
[6417]83            if (it->first == file)
[1505]84            {
[10392]85                this->files_.erase(it);
[1505]86                break;
87            }
88        }
89    }
90
[7648]91    /**
92    @brief
93        Loads all opened files, while conforming to the restrictions given by the input ClassTreeMask.
94    @param mask
95        A ClassTreeMask, which defines which types of classes are loaded and which aren't.
[8858]96    @param bVerbose
[7648]97        Whether the loader is verbose (prints its progress in a low output level) or not.
98    @return
99        Returns true if successful.
100    */
[8858]101    bool Loader::load(const ClassTreeMask& mask, bool bVerbose)
[1505]102    {
103        bool success = true;
[10392]104        for (std::vector<std::pair<const XMLFile*, ClassTreeMask> >::iterator it = this->files_.begin(); it != this->files_.end(); ++it)
105            if (!this->load(it->first, it->second * mask, bVerbose))
[1505]106                success = false;
107
108        return success;
109    }
110
111    void Loader::unload(const ClassTreeMask& mask)
112    {
[1747]113        for (ObjectList<BaseObject>::iterator it = ObjectList<BaseObject>::begin(); it != ObjectList<BaseObject>::end(); )
[1505]114        {
115            if (mask.isIncluded(it->getIdentifier()))
[5929]116                (it++)->destroy();
[1505]117            else
118                ++it;
119        }
120    }
121
[7648]122    /**
123    @brief
124        Reloads all opened files, while conforming to the restrictions given by the input ClassTreeMask.
125    @param mask
126        A ClassTreeMask, which defines which types of classes are reloaded and which aren't.
[8858]127    @param bVerbose
[7648]128        Whether the loader is verbose (prints its progress in a low output level) or not.
129    @return
130        Returns true if successful.
131    */
[8858]132    bool Loader::reload(const ClassTreeMask& mask, bool bVerbose)
[1505]133    {
[10392]134        this->unload(mask);
135        return this->load(mask, bVerbose);
[1505]136    }
137
[7648]138    /**
139    @brief
140        Loads the input file, while conforming to the restrictions given by the input ClassTreeMask.
141    @param file
142        The file to be loaded.
143    @param mask
144        A ClassTreeMask, which defines which types of classes are loaded and which aren't.
[8858]145    @param bVerbose
[7648]146        Whether the loader is verbose (prints its progress in a low output level) or not.
[8108]147    @param bRemoveLuaTags
148        If true lua tags are just ignored and removed. The default is false.
[7648]149    @return
150        Returns true if successful.
151    */
[8858]152    bool Loader::load(const XMLFile* file, const ClassTreeMask& mask, bool bVerbose, bool bRemoveLuaTags)
[1505]153    {
[2087]154        if (!file)
[1755]155            return false;
156
[10392]157        this->currentMask_ = file->getMask() * mask;
[1505]158
[5695]159        std::string xmlInput;
[10264]160
[10265]161        shared_ptr<std::vector<std::vector<std::pair<std::string, size_t> > > > lineTrace(new std::vector<std::vector<std::pair<std::string, size_t> > >());
[10264]162        lineTrace->reserve(1000); //arbitrary number
163
164
[8079]165        if (file->getLuaSupport() && !bRemoveLuaTags)
[5695]166        {
167            // Use the LuaState to replace the XML tags (calls our function)
168            scoped_ptr<LuaState> luaState(new LuaState());
[10264]169            luaState->setTraceMap(lineTrace);
[5695]170            luaState->setIncludeParser(&Loader::replaceLuaTags);
[6417]171            luaState->includeFile(file->getFilename());
[5695]172            xmlInput = luaState->getOutput().str();
173        }
174        else
175        {
[6417]176            shared_ptr<ResourceInfo> info = Resource::getInfo(file->getFilename());
[5695]177            if (info == NULL)
178            {
[8858]179                orxout(user_error, context::loader) << "Could not find XML file '" << file->getFilename() << "'." << endl;
[5695]180                return false;
181            }
[6417]182            xmlInput = Resource::open(file->getFilename())->getAsString();
[8079]183
184            if (bRemoveLuaTags)
185            {
186                // Remove all Lua code.
187                // Note: we only need this to speed up parsing of level files at the
188                // start of the program.
189                // Assumption: the LevelInfo tag does not use Lua scripting
[10392]190                xmlInput = Loader::removeLuaTags(xmlInput);
[8079]191            }
[5695]192        }
[1505]193
194        try
195        {
[8858]196            if(bVerbose)
[7648]197            {
[8858]198                orxout(user_info) << "Start loading " << file->getFilename() << "..." << endl;
[10392]199                orxout(internal_info, context::loader) << "Mask: " << this->currentMask_ << endl;
[7648]200            }
201            else
202            {
[8858]203                orxout(verbose, context::loader) << "Start loading " << file->getFilename() << "..." << endl;
[10392]204                orxout(verbose_more, context::loader) << "Mask: " << this->currentMask_ << endl;
[7648]205            }
[1505]206
[5695]207            ticpp::Document xmlfile(file->getFilename());
208            xmlfile.Parse(xmlInput, true);
[1505]209
210            ticpp::Element rootElement;
211            rootElement.SetAttribute("name", "root");
212            rootElement.SetAttribute("bAutogenerated", true);
213
214            for (ticpp::Iterator<ticpp::Element> child = xmlfile.FirstChildElement(false); child != child.end(); child++)
215                rootElement.InsertEndChild(*child);
216
[8858]217            orxout(verbose, context::loader) << "  creating root-namespace..." << endl;
[9667]218            Namespace* rootNamespace = new Namespace(Context::getRootContext());
[1505]219            rootNamespace->setLoaderIndentation("    ");
[2087]220            rootNamespace->setFile(file);
[1505]221            rootNamespace->setNamespace(rootNamespace);
222            rootNamespace->setRoot(true);
223            rootNamespace->XMLPort(rootElement, XMLPort::LoadObject);
224
[8858]225            if(bVerbose)
226                orxout(user_info) << "Finished loading " << file->getFilename() << '.' << endl;
[7648]227            else
[8858]228                orxout(verbose, context::loader) << "Finished loading " << file->getFilename() << '.' << endl;
[1505]229
[8858]230            orxout(verbose, context::loader) << "Namespace-tree:" << '\n' << rootNamespace->toString("  ") << endl;
[1505]231
232            return true;
233        }
[2171]234        catch (ticpp::Exception& ex)
[1505]235        {
[8858]236            orxout(user_error, context::loader) << endl;
237            orxout(user_error, context::loader) << "An XML-error occurred in Loader.cc while loading " << file->getFilename() << ':' << endl;
[10269]238            OutputLevel ticpplevel = user_error;
[10264]239            if (lineTrace->size() > 0)
240            {
[10269]241                ticpplevel = internal_error;
[10264]242                //Extract the line number from the exception
243                std::string tempstring(ex.what());
244                std::string::size_type pos = tempstring.find("\nLine: ");
245                if (pos != std::string::npos)
246                {
247                    std::istringstream istr(tempstring.substr(pos + 7));
248                    size_t line;
249                    istr >> line;
250                    if (line <= lineTrace->size())
251                    {
[10265]252                        std::vector<std::pair<std::string, size_t> > linesources = lineTrace->at(line - 1);
[10269]253                        std::ostringstream message;
254                        message << "Possible sources of error:" << endl;
[10265]255                        for (std::vector<std::pair<std::string, size_t> >::iterator it = linesources.begin(); it != linesources.end(); ++it)
[10264]256                        {
[10269]257                            message << it->first << ", Line " << it->second << endl;
258                        }
259                        orxout(user_error, context::loader) << message.str() << endl;
[10264]260                    }
261                }
262            }
[10269]263            orxout(ticpplevel, context::loader) << ex.what() << endl;
264            orxout(user_error, context::loader) << "Loading aborted." << endl;
[2171]265        }
266        catch (Exception& ex)
267        {
[8858]268            orxout(user_error, context::loader) << endl;
269            orxout(user_error, context::loader) << "A loading-error occurred in Loader.cc while loading " << file->getFilename() << ':' << endl;
270            orxout(user_error, context::loader) << ex.what() << endl;
271            orxout(user_error, context::loader) << "Loading aborted." << endl;
[2171]272        }
[5747]273        catch (...)
[2171]274        {
[8858]275            orxout(user_error, context::loader) << endl;
276            orxout(user_error, context::loader) << "An error occurred in Loader.cc while loading " << file->getFilename() << ':' << endl;
277            orxout(user_error, context::loader) << Exception::handleMessage() << endl;
278            orxout(user_error, context::loader) << "Loading aborted." << endl;
[1505]279        }
[10264]280        //The Tardis' version of boost is too old...
281#if BOOST_VERSION >= 104600
282        boost::filesystem::path temppath = boost::filesystem::temp_directory_path() / "orxonoxml.xml";
283        //Need binary mode, because xmlInput already has \r\n for windows
284        boost::filesystem::ofstream outfile(temppath, std::ios_base::binary | std::ios_base::out);
285        outfile << xmlInput;
286        outfile.flush();
287        outfile.close();
[10269]288        orxout(internal_error, context::loader) << "The complete xml file has been saved to " << temppath << endl;
[10264]289#endif
290        return false;
[1505]291    }
292
[2087]293    void Loader::unload(const XMLFile* file, const ClassTreeMask& mask)
[1505]294    {
[2087]295        if (!file)
[1755]296            return;
[1747]297        for (ObjectList<BaseObject>::iterator it = ObjectList<BaseObject>::begin(); it; )
[1505]298        {
[2087]299            if ((it->getFile() == file) && mask.isIncluded(it->getIdentifier()))
[5929]300                (it++)->destroy();
[1505]301            else
302                ++it;
303        }
304    }
305
[7648]306    /**
307    @brief
308        Reloads the input file, while conforming to the restrictions given by the input ClassTreeMask.
309    @param file
310        The file to be reloaded.
311    @param mask
312        A ClassTreeMask, which defines which types of classes are reloaded and which aren't.
[8858]313    @param bVerbose
[7648]314        Whether the loader is verbose (prints its progress in a low output level) or not.
315    @return
316        Returns true if successful.
317    */
[8858]318    bool Loader::reload(const XMLFile* file, const ClassTreeMask& mask, bool bVerbose)
[1505]319    {
[10392]320        this->unload(file, mask);
321        return this->load(file, mask, bVerbose);
[1505]322    }
[5695]323
[8079]324    bool Loader::getLuaTags(const std::string& text, std::map<size_t, bool>& luaTags)
[5695]325    {
[8079]326        // fill map with all Lua tags
[5695]327        {
328            size_t pos = 0;
329            while ((pos = text.find("<?lua", pos)) != std::string::npos)
330                luaTags[pos++] = true;
331        }
332        {
333            size_t pos = 0;
334            while ((pos = text.find("?>", pos)) != std::string::npos)
335                luaTags[pos++] = false;
336        }
337
338        // erase all tags from the map that are between two quotes
[10273]339        // that means occurrences like "..<?lua.." and "..?>.." would be deleted
340        // however occurrences of lua tags within quotas are retained: ".. <?lua ... ?> .. "
[5695]341        {
342            std::map<size_t, bool>::iterator it = luaTags.begin();
[10272]343            bool bBetweenQuotes = false;
344            size_t pos = 0;
345            while ((pos = getNextQuote(text, pos)) != std::string::npos)
[5695]346            {
[10272]347                while ((it != luaTags.end()) && (it->first < pos))
[5695]348                {
[10272]349                    if (bBetweenQuotes)
350                    {
[10273]351                        std::map<size_t, bool>::iterator it2 = it;
[10272]352                        it2++;
353                        if (it->second && !(it2->second) && it2->first < pos)
[10273]354                            std::advance(it, 2);
[10272]355                        else
356                            luaTags.erase(it++);
357                    }
358                    else
359                        ++it;
[5695]360                }
[10272]361                bBetweenQuotes = !bBetweenQuotes;
362                pos++;
[5695]363            }
364        }
365
366        // check whether on every opening <?lua tag a closing ?> tag follows
367        {
368            bool expectedValue = true;
369            for (std::map<size_t, bool>::iterator it = luaTags.begin(); it != luaTags.end(); ++it)
370            {
371                if (it->second == expectedValue)
372                    expectedValue = !expectedValue;
373                else
374                {
375                    expectedValue = false;
376                    break;
377                }
378            }
379            if (!expectedValue)
380            {
[10264]381                orxout(internal_error, context::loader) << "Error parsing file: lua tags not matching" << endl;
[8079]382                // TODO: error handling
[8858]383                return false;
[5695]384            }
385        }
386
[8079]387        return true;
388    }
389
390    std::string Loader::replaceLuaTags(const std::string& text)
391    {
392        // create a map with all lua tags
393        std::map<size_t, bool> luaTags;
394        if (!getLuaTags(text, luaTags))
395            return "";
396
[5695]397        // Use a stringstream object to speed up the parsing
398        std::ostringstream output;
399
400        // cut the original string into pieces and put them together with print() instead of lua tags
401        {
402            std::map<size_t, bool>::iterator it = luaTags.begin();
403            bool bInPrintFunction = true;
404            size_t start = 0;
405            size_t end = 0;
406
407            do
408            {
409                if (it != luaTags.end())
[6417]410                    end = (it++)->first;
[5695]411                else
412                    end = std::string::npos;
413
414                unsigned int equalSignCounter = 0;
415
416                if (bInPrintFunction)
417                {
418                    // count ['='[ and ]'='] and replace tags with print([[ and ]])
[6417]419                    const std::string& temp = text.substr(start, end - start);
[5695]420                    {
421                    size_t pos = 0;
422                    while ((pos = temp.find('[', pos)) != std::string::npos)
423                    {
424                        unsigned int tempCounter = 1;
425                        size_t tempPos = pos++;
[6422]426                        while (temp[++tempPos] == '=')
[5695]427                        {
428                            tempCounter++;
429                        }
[6422]430                        if (temp[tempPos] != '[')
[5695]431                        {
432                            tempCounter = 0;
433                        }
[6422]434                        else if (tempCounter == 0)
[5695]435                        {
436                            tempCounter = 1;
437                        }
438                        if (tempCounter > equalSignCounter)
439                            equalSignCounter = tempCounter;
440                        }
441                    }
442                    {
443                        size_t pos = 0;
444                        while ((pos = temp.find(']', pos)) != std::string::npos)
445                        {
446                            unsigned int tempCounter = 1;
447                            size_t tempPos = pos++;
[6422]448                            while (temp[++tempPos] == '=')
[5695]449                            {
450                                tempCounter++;
451                            }
[6422]452                            if (temp[tempPos] != ']')
[5695]453                            {
454                                tempCounter = 0;
455                            }
[6422]456                            else if (tempCounter == 0)
[5695]457                            {
458                                tempCounter = 1;
459                            }
460                            if (tempCounter > equalSignCounter)
461                                equalSignCounter = tempCounter;
462                        }
463                    }
[6417]464                    std::string equalSigns;
[6422]465                    for (unsigned int i = 0; i < equalSignCounter; i++)
[5695]466                    {
[6417]467                        equalSigns += '=';
[5695]468                    }
[10264]469                    //A newline directly after square brackets is ignored. To make sure that the string is printed
470                    //exactly as it is, including newlines at the beginning, insert a space after the brackets.
[10278]471                    bool needsExtraSpace = false;
472                    if (temp.size() > 0 && (temp[0] == '\n' || temp[0] == '\r')) // begins with \n or \r (a line break)
473                        needsExtraSpace = true;
474                    output << "print([" + equalSigns + (needsExtraSpace ? "[ " : "[") + temp + ']' + equalSigns +"])";
[5695]475                    start = end + 5;
476                }
477                else
478                {
479                    output << text.substr(start, end - start);
480                    start = end + 2;
481                }
482
483                bInPrintFunction = !bInPrintFunction;
484            }
485            while (end != std::string::npos);
486        }
487
488        return output.str();
489    }
[8079]490
491    std::string Loader::removeLuaTags(const std::string& text)
492    {
493        // create a map with all lua tags
494        std::map<size_t, bool> luaTags;
495        if (!getLuaTags(text, luaTags))
496            return "";
497
498        // Use a stringstream object to speed up the concatenation
499        std::ostringstream output;
500
501        // cut the original string into pieces and only write the non Lua parts
502        std::map<size_t, bool>::iterator it = luaTags.begin();
503        bool bLuaCode = false;
504        size_t start = 0;
505        size_t end = 0;
506
507        do
508        {
509            if (it != luaTags.end())
510                end = (it++)->first;
511            else
512                end = std::string::npos;
513
514            if (!bLuaCode)
515            {
516                output << text.substr(start, end - start);
517                start = end + 5;
518            }
519            else
[10264]520            {
521                //Preserve the amount of lines, otherwise the linenumber from the xml parse error is useless
522                std::string tempstring = text.substr(start, end - start);
523                output << std::string(std::count(tempstring.begin(), tempstring.end(), '\n'), '\n');
[8079]524                start = end + 2;
[10264]525            }
[8079]526
527            bLuaCode = !bLuaCode;
528        }
529        while (end != std::string::npos);
530
531        return output.str();
532    }
[1505]533}
Note: See TracBrowser for help on using the repository browser.