Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/testing/src/libraries/util/output/OutputManager.cc @ 9540

Last change on this file since 9540 was 9540, checked in by landauf, 11 years ago

some refactoring in OutputManager - for unittests it is sometimes necessary to create a new instance of OutputManager (or a mock), hence we should avoid storing dead references

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