Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 10846 was 10577, checked in by landauf, 10 years ago

no need to store the namespace again, this is already done in the namespace's constructor

  • 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.