Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

merged testing branch back to trunk. unbelievable it took me 13 months to finish this chore…

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