Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 10771 was 10624, checked in by landauf, 9 years ago

merged branch core7 back to trunk

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