Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/util/output/OutputManager.cc @ 12080

Last change on this file since 12080 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: 11.6 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/**
30    @file
31    @brief Implementation of the OutputManager singleton.
32*/
33
34#include "OutputManager.h"
35
36#include <iostream>
37
38#include "MemoryWriter.h"
39#include "ConsoleWriter.h"
40#include "LogWriter.h"
41#include "util/Output.h"
42#include "util/StringUtils.h"
43
44namespace orxonox
45{
46    /**
47        @brief Constructor, initializes all values.
48    */
49    OutputManager::OutputManager()
50    {
51        this->combinedLevelMask_ = level::none;
52        this->combinedAdditionalContextsLevelMask_ = level::none;
53        this->combinedAdditionalContextsMask_ = context::none;
54
55        this->subcontextCounter_ = 0;
56
57        this->isInitialized_ = false;
58        this->memoryWriterInstance_ = nullptr;
59        this->consoleWriterInstance_ = nullptr;
60        this->logWriterInstance_ = nullptr;
61
62        // register 'undefined' context in order to give it always the first context-ID
63        this->registerContext("undefined");
64    }
65
66    /**
67        @brief Destructor.
68    */
69    OutputManager::~OutputManager()
70    {
71        while (!this->listeners_.empty())
72            this->unregisterListener(this->listeners_[0]);
73
74        if (this->memoryWriterInstance_)
75            delete this->memoryWriterInstance_;
76        if (this->consoleWriterInstance_)
77            delete this->consoleWriterInstance_;
78        if (this->logWriterInstance_)
79            delete this->logWriterInstance_;
80    }
81
82    /*static*/ std::shared_ptr<OutputManager>& OutputManager::Testing::getInstancePointer()
83    {
84        static std::shared_ptr<OutputManager> instance(new OutputManager());
85        return instance;
86    }
87
88    /**
89        @brief Returns the only existing instance of the OutputManager singleton.
90    */
91    /*static*/ OutputManager& OutputManager::getInstance()
92    {
93        return *OutputManager::Testing::getInstancePointer();
94    }
95
96    /**
97        @brief Returns the only existing instance of the OutputManager singleton
98        and ensures that the most important output listeners exist.
99
100        You should use this function if you send output to OutputManager and want
101        to be sure that the most important output listeners exist. Don't use it
102        elsewhere inside the output system to avoid circular calls.
103    */
104    /*static*/ OutputManager& OutputManager::getInstanceAndCreateListeners()
105    {
106        OutputManager& instance = *OutputManager::Testing::getInstancePointer();
107
108        if (!instance.isInitialized_) {
109            instance.isInitialized_ = true;
110            instance.memoryWriterInstance_ = new MemoryWriter();
111            instance.consoleWriterInstance_ = new ConsoleWriter(std::cout);
112            instance.logWriterInstance_ = new LogWriter();
113        }
114
115        return instance;
116    }
117
118    /**
119        @brief Sends an output message to all output listeners.
120        @param level The level of the message
121        @param context The context of the message
122        @param message The output message (may contain '\\n')
123
124        This function splits the message into lines (if it contains '\\n') and
125        sends it to the output listeners. They may ignore the message if it
126        doesn't match their level- and context-masks.
127    */
128    void OutputManager::pushMessage(OutputLevel level, const OutputContextContainer& context, const std::string& message)
129    {
130        std::vector<std::string> lines;
131        vectorize(message, '\n', &lines);
132
133        for (OutputListener* listener : this->listeners_)
134            listener->unfilteredOutput(level, context, lines);
135    }
136
137    /**
138        @brief Adds an output listener to the list of listeners.
139    */
140    void OutputManager::registerListener(OutputListener* listener)
141    {
142        listener->registerListener(this);
143        this->listeners_.push_back(listener);
144        this->updateMasks();
145    }
146
147    /**
148        @brief Removes an output listener from the list of listeners.
149    */
150    void OutputManager::unregisterListener(OutputListener* listener)
151    {
152        listener->unregisterListener(this);
153        for (std::vector<OutputListener*>::iterator it = this->listeners_.begin(); it != this->listeners_.end(); ++it)
154        {
155            if (*it == listener)
156            {
157                this->listeners_.erase(it);
158                break;
159            }
160        }
161        this->updateMasks();
162    }
163
164    /**
165        @brief Updates all three combined level- and context-masks.
166    */
167    void OutputManager::updateMasks()
168    {
169        this->updateCombinedLevelMask();
170        this->updateCombinedAdditionalContextsLevelMask();
171        this->updateCombinedAdditionalContextsMask();
172    }
173
174    /**
175        @brief Updates the combined level mask. The masks of all listeners are ORed to form the combined mask.
176    */
177    void OutputManager::updateCombinedLevelMask()
178    {
179        int mask = 0;
180        for (OutputListener* listener : this->listeners_)
181            mask |= listener->getLevelMask();
182        this->combinedLevelMask_ = static_cast<OutputLevel>(mask);
183    }
184
185    /**
186        @brief Updates the combined additional contexts level mask. The masks of all listeners are ORed to form the combined mask.
187    */
188    void OutputManager::updateCombinedAdditionalContextsLevelMask()
189    {
190        int mask = 0;
191        for (OutputListener* listener : this->listeners_)
192            mask |= listener->getAdditionalContextsLevelMask();
193        this->combinedAdditionalContextsLevelMask_ = static_cast<OutputLevel>(mask);
194    }
195
196    /**
197        @brief Updates the combined additional contexts mask. The masks of all listeners are ORed to form the combined mask.
198    */
199    void OutputManager::updateCombinedAdditionalContextsMask()
200    {
201        this->combinedAdditionalContextsMask_ = 0;
202        for (OutputListener* listener : this->listeners_)
203            this->combinedAdditionalContextsMask_ |= listener->getAdditionalContextsMask();
204    }
205
206    /**
207        @brief Registers a context (or sub-context) and returns the container which identifies the context.
208        @param name The name of the context
209        @param subname The name of the sub-context (or "" if it is not a sub-context)
210
211        If the context doesn't exist, it gets created. Otherwise the existing instance is returned.
212    */
213    const OutputContextContainer& OutputManager::registerContext(const std::string& name, const std::string& subname)
214    {
215        // the full name of a context is a combination of name and subname with "::" in between
216        std::string full_name = name;
217        if (subname != "")
218            full_name += "::" + subname;
219
220        // check if the context already exists (and return it if it does)
221        std::map<std::string, OutputContextContainer>::iterator it_container = this->contextContainers_.find(full_name);
222        if (it_container != this->contextContainers_.end())
223            return it_container->second;
224
225        // create a new context container
226        OutputContextContainer container;
227        container.name = full_name;
228
229        // check if the mask of the main-context already exists
230        std::map<std::string, OutputContextMask>::iterator it_mask = this->contextMasks_.find(name);
231        if (it_mask != this->contextMasks_.end())
232        {
233            // the mask exists, assign it to the container
234            container.mask = it_mask->second;
235        }
236        else
237        {
238            // the mask doesn't exist, create it. It's a binary mask. The n-th main-context is defined by the n-th bit in the mask.
239            container.mask = static_cast<OutputContextMask>(0x1) << this->contextMasks_.size();
240            this->contextMasks_[name] = container.mask;
241
242            if (container.mask == 0)
243                orxout(internal_warning) << "More than " << sizeof(OutputContextMask) * 8 << " output contexts defined. Context '" << name << "' might not get filtered correctly" << endl;
244        }
245
246        // if the context is a sub-context, assign a unique ID.
247        if (subname == "")
248            container.sub_id = context::no_subcontext;
249        else
250            container.sub_id = ++this->subcontextCounter_; // start with 1
251
252        // add the new context to the map and return it
253        return (this->contextContainers_[full_name] = container);
254    }
255
256    /**
257        @brief Static function, shortcut to OutputManager::registerContext().
258        The function is declared in OutputDefinitions.h.
259    */
260    const OutputContextContainer& registerContext(const std::string& name, const std::string& subname)
261    {
262        return OutputManager::getInstance().registerContext(name, subname);
263    }
264
265    /**
266        @brief Returns a human readable string for each output level.
267    */
268    const std::string& OutputManager::getLevelName(OutputLevel level) const
269    {
270        switch (level)
271        {
272            // using static cache variables for speed
273            case level::none:               { static std::string name = "None"; return name; }
274            case level::message:            { static std::string name = "Message"; return name; }
275            case level::debug_output:       { static std::string name = "Debug"; return name; }
276            case level::user_error:         { static std::string name = "Error"; return name; }
277            case level::user_warning:       { static std::string name = "Warning"; return name; }
278            case level::user_status:        { static std::string name = "Status"; return name; }
279            case level::user_info:          { static std::string name = "Info"; return name; }
280            case level::internal_error:     { static std::string name = "Error (internal)"; return name; }
281            case level::internal_warning:   { static std::string name = "Warning (internal)"; return name; }
282            case level::internal_status:    { static std::string name = "Status (internal)"; return name; }
283            case level::internal_info:      { static std::string name = "Info (internal)"; return name; }
284            case level::verbose:            { static std::string name = "Verbose"; return name; }
285            case level::verbose_more:       { static std::string name = "Verbose (more)"; return name; }
286            case level::verbose_ultra:      { static std::string name = "Verbose (ultra)"; return name; }
287            default:                        { static std::string name = ""; return name; }
288        }
289    }
290
291    /**
292        @brief Returns a string containing the name of the level and the context (if any) which
293        can be prepended to an output message if it is written to the console or the log file.
294    */
295    std::string OutputManager::getDefaultPrefix(OutputLevel level, const OutputContextContainer& context) const
296    {
297        // "undefined" context is ignored because it's used implicitly if no explicit context is defined
298        OutputContextMask undefined_mask = context::undefined().mask;
299
300        std::string prefix = this->getLevelName(level) + ": ";
301        if (context.mask != undefined_mask)
302            prefix += "[" + context.name + "] ";
303
304        return prefix;
305    }
306}
Note: See TracBrowser for help on using the repository browser.