Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

ConsoleWriter now accepts and writes to any std::ostream, not just std::cout. Makes it easier to test.

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