Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 9531 was 9530, checked in by landauf, 12 years ago

small refactoring in output system

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