Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/core/Loader.cc @ 11127

Last change on this file since 11127 was 11071, checked in by landauf, 9 years ago

merged branch cpp11_v3 back to trunk

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