Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/cpp11_v2/src/libraries/core/Loader.cc @ 11003

Last change on this file since 11003 was 10920, checked in by landauf, 10 years ago

no static functions anymore in ObjectList. you need to instantiate an ObjectList to use it.
this allows for prettier for-loop syntax when iterating over an ObjectList of a specific context: for (T* object : ObjectList<T>(context)) { … }

  • Property svn:eol-style set to native
File size: 16.0 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/filesystem.hpp>
34#include <boost/filesystem/fstream.hpp>
35
36#include "util/Output.h"
37#include "util/Exception.h"
38#include "util/StringUtils.h"
39#include "BaseObject.h"
40#include "LuaState.h"
41#include "Namespace.h"
42#include "Resource.h"
43#include "XMLFile.h"
44#include "object/Iterator.h"
45#include "object/ObjectList.h"
46
47namespace orxonox
48{
49    Loader* Loader::singletonPtr_s = nullptr;
50
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.
58    @param bVerbose
59        Whether the loader is verbose (prints its progress in a low output level) or not.
60    @param bRemoveLuaTags
61        If true lua tags are just ignored and removed. The default is false.
62    @return
63        Returns true if successful.
64    */
65    bool Loader::load(const XMLFile* file, const ClassTreeMask& mask, bool bVerbose, bool bRemoveLuaTags)
66    {
67        if (!file)
68            return false;
69
70        this->currentMask_ = file->getMask() * mask;
71
72        std::string xmlInput;
73
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>>>>());
75        lineTrace->reserve(1000); //arbitrary number
76
77
78        if (file->getLuaSupport() && !bRemoveLuaTags)
79        {
80            // Use the LuaState to replace the XML tags (calls our function)
81            const std::unique_ptr<LuaState> luaState(new LuaState());
82            luaState->setTraceMap(lineTrace);
83            luaState->setIncludeParser(&Loader::replaceLuaTags);
84            luaState->includeFile(file->getFilename());
85            xmlInput = luaState->getOutput().str();
86        }
87        else
88        {
89            std::shared_ptr<ResourceInfo> info = Resource::getInfo(file->getFilename());
90            if (info == nullptr)
91            {
92                orxout(user_error, context::loader) << "Could not find XML file '" << file->getFilename() << "'." << endl;
93                return false;
94            }
95            xmlInput = Resource::open(file->getFilename())->getAsString();
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
103                xmlInput = Loader::removeLuaTags(xmlInput);
104            }
105        }
106
107        try
108        {
109            if(bVerbose)
110            {
111                orxout(user_info) << "Start loading " << file->getFilename() << "..." << endl;
112                orxout(internal_info, context::loader) << "Mask: " << this->currentMask_ << endl;
113            }
114            else
115            {
116                orxout(verbose, context::loader) << "Start loading " << file->getFilename() << "..." << endl;
117                orxout(verbose_more, context::loader) << "Mask: " << this->currentMask_ << endl;
118            }
119
120            ticpp::Document xmlfile(file->getFilename());
121            xmlfile.Parse(xmlInput, true);
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
130            orxout(verbose, context::loader) << "  creating root-namespace..." << endl;
131            Namespace* rootNamespace = new Namespace(Context::getRootContext());
132            rootNamespace->setLoaderIndentation("    ");
133            rootNamespace->setFile(file);
134            rootNamespace->setRoot(true);
135            rootNamespace->XMLPort(rootElement, XMLPort::LoadObject);
136
137            if(bVerbose)
138                orxout(user_info) << "Finished loading " << file->getFilename() << '.' << endl;
139            else
140                orxout(verbose, context::loader) << "Finished loading " << file->getFilename() << '.' << endl;
141
142            orxout(verbose, context::loader) << "Namespace-tree:" << '\n' << rootNamespace->toString("  ") << endl;
143
144            return true;
145        }
146        catch (ticpp::Exception& ex)
147        {
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;
150            OutputLevel ticpplevel = user_error;
151            if (lineTrace->size() > 0)
152            {
153                ticpplevel = internal_error;
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                    {
164                        std::vector<std::pair<std::string, size_t>> linesources = lineTrace->at(line - 1);
165                        std::ostringstream message;
166                        message << "Possible sources of error:" << endl;
167                        for (const std::pair<std::string, size_t>& pair : linesources)
168                        {
169                            message << pair.first << ", Line " << pair.second << endl;
170                        }
171                        orxout(user_error, context::loader) << message.str() << endl;
172                    }
173                }
174            }
175            orxout(ticpplevel, context::loader) << ex.what() << endl;
176            orxout(user_error, context::loader) << "Loading aborted." << endl;
177        }
178        catch (Exception& ex)
179        {
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;
184        }
185        catch (...)
186        {
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;
191        }
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();
200        orxout(internal_error, context::loader) << "The complete xml file has been saved to " << temppath << endl;
201#endif
202        return false;
203    }
204
205    void Loader::unload(const XMLFile* file, const ClassTreeMask& mask)
206    {
207        if (!file)
208            return;
209        for (ObjectList<BaseObject>::iterator it = ObjectList<BaseObject>().begin(); it; )
210        {
211            if ((it->getFile() == file) && mask.isIncluded(it->getIdentifier()))
212                (it++)->destroy();
213            else
214                ++it;
215        }
216    }
217
218    bool Loader::getLuaTags(const std::string& text, std::map<size_t, bool>& luaTags)
219    {
220        // fill map with all Lua tags
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
233        // that means occurrences like "..<?lua.." and "..?>.." would be deleted
234        // however occurrences of lua tags within quotas are retained: ".. <?lua ... ?> .. "
235        {
236            std::map<size_t, bool>::iterator it = luaTags.begin();
237            bool bBetweenQuotes = false;
238            size_t pos = 0;
239            while ((pos = getNextQuote(text, pos)) != std::string::npos)
240            {
241                while ((it != luaTags.end()) && (it->first < pos))
242                {
243                    if (bBetweenQuotes)
244                    {
245                        std::map<size_t, bool>::iterator it2 = it;
246                        it2++;
247                        if (it->second && !(it2->second) && it2->first < pos)
248                            std::advance(it, 2);
249                        else
250                            luaTags.erase(it++);
251                    }
252                    else
253                        ++it;
254                }
255                bBetweenQuotes = !bBetweenQuotes;
256                pos++;
257            }
258        }
259
260        // check whether on every opening <?lua tag a closing ?> tag follows
261        {
262            bool expectedValue = true;
263            for (const auto& mapEntry : luaTags)
264            {
265                if (mapEntry.second == expectedValue)
266                    expectedValue = !expectedValue;
267                else
268                {
269                    expectedValue = false;
270                    break;
271                }
272            }
273            if (!expectedValue)
274            {
275                orxout(internal_error, context::loader) << "Error parsing file: lua tags not matching" << endl;
276                // TODO: error handling
277                return false;
278            }
279        }
280
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
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())
304                    end = (it++)->first;
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 ]])
313                    const std::string& temp = text.substr(start, end - start);
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++;
320                        while (temp[++tempPos] == '=')
321                        {
322                            tempCounter++;
323                        }
324                        if (temp[tempPos] != '[')
325                        {
326                            tempCounter = 0;
327                        }
328                        else if (tempCounter == 0)
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++;
342                            while (temp[++tempPos] == '=')
343                            {
344                                tempCounter++;
345                            }
346                            if (temp[tempPos] != ']')
347                            {
348                                tempCounter = 0;
349                            }
350                            else if (tempCounter == 0)
351                            {
352                                tempCounter = 1;
353                            }
354                            if (tempCounter > equalSignCounter)
355                                equalSignCounter = tempCounter;
356                        }
357                    }
358                    std::string equalSigns;
359                    for (unsigned int i = 0; i < equalSignCounter; i++)
360                    {
361                        equalSigns += '=';
362                    }
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.
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 +"])";
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    }
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
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');
418                start = end + 2;
419            }
420
421            bLuaCode = !bLuaCode;
422        }
423        while (end != std::string::npos);
424
425        return output.str();
426    }
427}
Note: See TracBrowser for help on using the repository browser.