Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 10352 was 10278, checked in by landauf, 9 years ago

fixed issue with inline-lua-code. only add an extra space if there's actually a line-break at the beginning of the text-content. otherwise code like this: mesh="ast<?lua print(1) ?>.mesh" becomes mesh="ast1 .mesh" instead of mesh="ast1.mesh"

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