Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 11662 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
RevLine 
[8765]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
[8848]29/**
30    @file
31    @brief Implementation of the OutputManager singleton.
32*/
33
[8765]34#include "OutputManager.h"
35
[9550]36#include <iostream>
37
[8774]38#include "MemoryWriter.h"
[8780]39#include "ConsoleWriter.h"
[8774]40#include "LogWriter.h"
[8833]41#include "util/Output.h"
[8801]42#include "util/StringUtils.h"
[8765]43
44namespace orxonox
45{
[8848]46    /**
47        @brief Constructor, initializes all values.
48    */
[8765]49    OutputManager::OutputManager()
50    {
[8808]51        this->combinedLevelMask_ = level::none;
[8833]52        this->combinedAdditionalContextsLevelMask_ = level::none;
53        this->combinedAdditionalContextsMask_ = context::none;
54
55        this->subcontextCounter_ = 0;
[9550]56
57        this->isInitialized_ = false;
[11071]58        this->memoryWriterInstance_ = nullptr;
59        this->consoleWriterInstance_ = nullptr;
60        this->logWriterInstance_ = nullptr;
[9550]61
62        // register 'undefined' context in order to give it always the first context-ID
63        this->registerContext("undefined");
[8765]64    }
65
[8848]66    /**
67        @brief Destructor.
68    */
[8765]69    OutputManager::~OutputManager()
70    {
[9550]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_;
[8765]80    }
81
[11071]82    /*static*/ std::shared_ptr<OutputManager>& OutputManager::Testing::getInstancePointer()
[9550]83    {
[11071]84        static std::shared_ptr<OutputManager> instance(new OutputManager());
[9550]85        return instance;
86    }
87
[8848]88    /**
89        @brief Returns the only existing instance of the OutputManager singleton.
90    */
[8765]91    /*static*/ OutputManager& OutputManager::getInstance()
92    {
[9550]93        return *OutputManager::Testing::getInstancePointer();
[8776]94    }
[8774]95
[8848]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    */
[8776]104    /*static*/ OutputManager& OutputManager::getInstanceAndCreateListeners()
105    {
[9550]106        OutputManager& instance = *OutputManager::Testing::getInstancePointer();
[8776]107
[9550]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        }
[8774]114
[8765]115        return instance;
116    }
117
[8848]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    */
[8833]128    void OutputManager::pushMessage(OutputLevel level, const OutputContextContainer& context, const std::string& message)
[8765]129    {
[8772]130        std::vector<std::string> lines;
[8801]131        vectorize(message, '\n', &lines);
[8772]132
[11071]133        for (OutputListener* listener : this->listeners_)
134            listener->unfilteredOutput(level, context, lines);
[8765]135    }
136
[8848]137    /**
138        @brief Adds an output listener to the list of listeners.
139    */
[8765]140    void OutputManager::registerListener(OutputListener* listener)
141    {
[9550]142        listener->registerListener(this);
[8765]143        this->listeners_.push_back(listener);
144        this->updateMasks();
145    }
146
[8848]147    /**
148        @brief Removes an output listener from the list of listeners.
149    */
[8765]150    void OutputManager::unregisterListener(OutputListener* listener)
151    {
[9550]152        listener->unregisterListener(this);
[8765]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
[8848]164    /**
165        @brief Updates all three combined level- and context-masks.
166    */
[8765]167    void OutputManager::updateMasks()
168    {
169        this->updateCombinedLevelMask();
[8833]170        this->updateCombinedAdditionalContextsLevelMask();
171        this->updateCombinedAdditionalContextsMask();
[8765]172    }
173
[8848]174    /**
175        @brief Updates the combined level mask. The masks of all listeners are ORed to form the combined mask.
176    */
[8765]177    void OutputManager::updateCombinedLevelMask()
178    {
[8808]179        int mask = 0;
[11071]180        for (OutputListener* listener : this->listeners_)
181            mask |= listener->getLevelMask();
[8808]182        this->combinedLevelMask_ = static_cast<OutputLevel>(mask);
[8765]183    }
184
[8848]185    /**
186        @brief Updates the combined additional contexts level mask. The masks of all listeners are ORed to form the combined mask.
187    */
[8833]188    void OutputManager::updateCombinedAdditionalContextsLevelMask()
[8765]189    {
[8833]190        int mask = 0;
[11071]191        for (OutputListener* listener : this->listeners_)
192            mask |= listener->getAdditionalContextsLevelMask();
[8833]193        this->combinedAdditionalContextsLevelMask_ = static_cast<OutputLevel>(mask);
[8765]194    }
[8771]195
[8848]196    /**
197        @brief Updates the combined additional contexts mask. The masks of all listeners are ORed to form the combined mask.
198    */
[8833]199    void OutputManager::updateCombinedAdditionalContextsMask()
[8771]200    {
[8833]201        this->combinedAdditionalContextsMask_ = 0;
[11071]202        for (OutputListener* listener : this->listeners_)
203            this->combinedAdditionalContextsMask_ |= listener->getAdditionalContextsMask();
[8833]204    }
205
[8848]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    */
[8833]213    const OutputContextContainer& OutputManager::registerContext(const std::string& name, const std::string& subname)
214    {
[8848]215        // the full name of a context is a combination of name and subname with "::" in between
[8833]216        std::string full_name = name;
217        if (subname != "")
218            full_name += "::" + subname;
219
[8848]220        // check if the context already exists (and return it if it does)
[8833]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
[8848]225        // create a new context container
[8833]226        OutputContextContainer container;
227        container.name = full_name;
228
[8848]229        // check if the mask of the main-context already exists
[8833]230        std::map<std::string, OutputContextMask>::iterator it_mask = this->contextMasks_.find(name);
231        if (it_mask != this->contextMasks_.end())
[8771]232        {
[8848]233            // the mask exists, assign it to the container
[8833]234            container.mask = it_mask->second;
[8771]235        }
236        else
237        {
[8848]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.
[8833]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;
[8771]244        }
[8833]245
[8848]246        // if the context is a sub-context, assign a unique ID.
[8833]247        if (subname == "")
248            container.sub_id = context::no_subcontext;
249        else
250            container.sub_id = ++this->subcontextCounter_; // start with 1
251
[8848]252        // add the new context to the map and return it
[8833]253        return (this->contextContainers_[full_name] = container);
[8771]254    }
255
[8848]256    /**
257        @brief Static function, shortcut to OutputManager::registerContext().
258        The function is declared in OutputDefinitions.h.
259    */
[8833]260    const OutputContextContainer& registerContext(const std::string& name, const std::string& subname)
[8771]261    {
[8833]262        return OutputManager::getInstance().registerContext(name, subname);
[8771]263    }
264
[8848]265    /**
266        @brief Returns a human readable string for each output level.
267    */
[8771]268    const std::string& OutputManager::getLevelName(OutputLevel level) const
269    {
270        switch (level)
271        {
[8848]272            // using static cache variables for speed
[8771]273            case level::none:               { static std::string name = "None"; return name; }
[8805]274            case level::message:            { static std::string name = "Message"; return name; }
[8771]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
[8848]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    */
[8833]295    std::string OutputManager::getDefaultPrefix(OutputLevel level, const OutputContextContainer& context) const
[8771]296    {
[8848]297        // "undefined" context is ignored because it's used implicitly if no explicit context is defined
[9550]298        OutputContextMask undefined_mask = context::undefined().mask;
[8772]299
[8833]300        std::string prefix = this->getLevelName(level) + ": ";
301        if (context.mask != undefined_mask)
302            prefix += "[" + context.name + "] ";
[8799]303
[8774]304        return prefix;
305    }
[8765]306}
Note: See TracBrowser for help on using the repository browser.