1 /*=========================================================================
2 
3   Library:   CTK
4 
5   Copyright (c) Kitware Inc.
6 
7   Licensed under the Apache License, Version 2.0 (the "License");
8   you may not use this file except in compliance with the License.
9   You may obtain a copy of the License at
10 
11       http://www.apache.org/licenses/LICENSE-2.0.txt
12 
13   Unless required by applicable law or agreed to in writing, software
14   distributed under the License is distributed on an "AS IS" BASIS,
15   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   See the License for the specific language governing permissions and
17   limitations under the License.
18 
19 =========================================================================*/
20 
21 // Qt includes
22 #include <QMutexLocker>
23 #include <QThread>
24 #include <QHash>
25 
26 // CTK includes
27 #include "ctkErrorLogContext.h"
28 #include "ctkErrorLogStreamMessageHandler.h"
29 #include "ctkUtils.h"
30 
31 // STD includes
32 #include <iostream>
33 #include <streambuf>
34 #include <string>
35 
36 namespace
37 {
38 
39 // --------------------------------------------------------------------------
40 // ctkStreamHandler
41 
42 //
43 // See http://lists.trolltech.com/qt-interest/2005-06/thread00166-0.html
44 //
45 
46 // --------------------------------------------------------------------------
47 class ctkStreamHandler : public std::streambuf
48 {
49 public:
50   ctkStreamHandler(ctkErrorLogStreamMessageHandler* messageHandler,
51                    ctkErrorLogLevel::LogLevel logLevel,
52                    std::ostream& stream);
53 
54   void setEnabled(bool value);
55 
56 protected:
57   virtual int_type overflow(int_type v);
58   virtual std::streamsize xsputn(const char *p, std::streamsize n);
59 
60 private:
61   void initBuffer();
62   std::string* currentBuffer();
63 
64   ctkErrorLogStreamMessageHandler * MessageHandler;
65   ctkErrorLogLevel::LogLevel LogLevel;
66   ctkErrorLogContext LogContext;
67 
68   bool Enabled;
69 
70   std::ostream&   Stream;
71   std::streambuf* SavedBuffer;
72   QHash<Qt::HANDLE, std::string*> StringBuffers;
73   QMutex Mutex;
74 };
75 
76 // --------------------------------------------------------------------------
77 // ctkStreamHandler methods
78 
79 // --------------------------------------------------------------------------
ctkStreamHandler(ctkErrorLogStreamMessageHandler * messageHandler,ctkErrorLogLevel::LogLevel logLevel,std::ostream & stream)80 ctkStreamHandler::ctkStreamHandler(ctkErrorLogStreamMessageHandler* messageHandler,
81                                    ctkErrorLogLevel::LogLevel logLevel,
82                                    std::ostream& stream) :
83   MessageHandler(messageHandler), LogLevel(logLevel), Stream(stream)
84 {
85   this->Enabled = false;
86 }
87 
88 // --------------------------------------------------------------------------
setEnabled(bool value)89 void ctkStreamHandler::setEnabled(bool value)
90 {
91   if (this->Enabled == value)
92     {
93     return;
94     }
95 
96   if (value)
97     {
98     this->SavedBuffer = this->Stream.rdbuf();
99     this->Stream.rdbuf(this);
100     }
101   else
102     {
103     // Output anything that is left
104 //    if (!this->StringBuffer.empty())
105 //      {
106 //      Q_ASSERT(this->MessageHandler);
107 //      this->MessageHandler->handleMessage(this->LogLevel,
108 //          this->MessageHandler->handlerPrettyName(), this->StringBuffer.c_str());
109 //      }
110     this->Stream.rdbuf(this->SavedBuffer);
111     }
112 
113   this->Enabled = value;
114 }
115 
116 // --------------------------------------------------------------------------
initBuffer()117 void ctkStreamHandler::initBuffer()
118 {
119   Qt::HANDLE threadId = QThread::currentThreadId();
120   if (!this->StringBuffers.contains(threadId))
121     {
122     this->StringBuffers.insert(threadId, new std::string);
123     }
124 }
125 
126 // --------------------------------------------------------------------------
currentBuffer()127 std::string* ctkStreamHandler::currentBuffer()
128 {
129   return this->StringBuffers.value(QThread::currentThreadId());
130 }
131 
132 // --------------------------------------------------------------------------
overflow(std::streambuf::int_type v)133 std::streambuf::int_type ctkStreamHandler::overflow(std::streambuf::int_type v)
134 {
135   QMutexLocker locker(&this->Mutex);
136   this->initBuffer();
137   if (v == '\n')
138     {
139     Q_ASSERT(this->MessageHandler);
140     this->MessageHandler->handleMessage(
141           ctk::qtHandleToString(QThread::currentThreadId()), this->LogLevel,
142           this->MessageHandler->handlerPrettyName(),
143           ctkErrorLogContext(this->currentBuffer()->c_str()), this->currentBuffer()->c_str());
144     this->currentBuffer()->erase(this->currentBuffer()->begin(), this->currentBuffer()->end());
145     }
146   else
147     {
148     *this->currentBuffer() += v;
149     }
150   return v;
151 }
152 
153 // --------------------------------------------------------------------------
xsputn(const char * p,std::streamsize n)154 std::streamsize ctkStreamHandler::xsputn(const char *p, std::streamsize n)
155 {
156   QMutexLocker locker(&this->Mutex);
157   this->initBuffer();
158   this->currentBuffer()->append(p, p + n);
159 
160   std::string::size_type pos = 0;
161   while (pos != std::string::npos)
162     {
163     pos = this->currentBuffer()->find('\n');
164     if (pos != std::string::npos)
165       {
166       std::string tmp(this->currentBuffer()->begin(), this->currentBuffer()->begin() + pos);
167       Q_ASSERT(this->MessageHandler);
168       this->MessageHandler->handleMessage(
169             ctk::qtHandleToString(QThread::currentThreadId()), this->LogLevel,
170             this->MessageHandler->handlerPrettyName(),
171             ctkErrorLogContext(tmp.c_str()), tmp.c_str());
172       this->currentBuffer()->erase(this->currentBuffer()->begin(), this->currentBuffer()->begin() + pos + 1);
173       }
174     }
175   return n;
176 }
177 
178 }
179 
180 // --------------------------------------------------------------------------
181 // ctkErrorLogStreamMessageHandlerPrivate
182 
183 // --------------------------------------------------------------------------
184 class ctkErrorLogStreamMessageHandlerPrivate
185 {
186 public:
187   ctkErrorLogStreamMessageHandlerPrivate();
188   ~ctkErrorLogStreamMessageHandlerPrivate();
189 
190   ctkStreamHandler * CoutStreamHandler;
191   ctkStreamHandler * CerrStreamHandler;
192 };
193 
194 // --------------------------------------------------------------------------
195 // ctkErrorLogStreamMessageHandlerPrivate methods
196 
197 //------------------------------------------------------------------------------
ctkErrorLogStreamMessageHandlerPrivate()198 ctkErrorLogStreamMessageHandlerPrivate::ctkErrorLogStreamMessageHandlerPrivate()
199 {
200 }
201 
202 //------------------------------------------------------------------------------
~ctkErrorLogStreamMessageHandlerPrivate()203 ctkErrorLogStreamMessageHandlerPrivate::~ctkErrorLogStreamMessageHandlerPrivate()
204 {
205   delete this->CoutStreamHandler;
206   delete this->CerrStreamHandler;
207 }
208 
209 //------------------------------------------------------------------------------
210 // ctkErrorLogStreamMessageHandler methods
211 
212 //------------------------------------------------------------------------------
213 QString ctkErrorLogStreamMessageHandler::HandlerName = QLatin1String("Stream");
214 
215 // --------------------------------------------------------------------------
ctkErrorLogStreamMessageHandler()216 ctkErrorLogStreamMessageHandler::ctkErrorLogStreamMessageHandler() :
217   Superclass(), d_ptr(new ctkErrorLogStreamMessageHandlerPrivate())
218 {
219   Q_D(ctkErrorLogStreamMessageHandler);
220   d->CoutStreamHandler = new ctkStreamHandler(this, ctkErrorLogLevel::Info, std::cout);
221   d->CerrStreamHandler = new ctkStreamHandler(this, ctkErrorLogLevel::Critical, std::cerr);
222 }
223 
224 // --------------------------------------------------------------------------
~ctkErrorLogStreamMessageHandler()225 ctkErrorLogStreamMessageHandler::~ctkErrorLogStreamMessageHandler()
226 {
227 }
228 
229 // --------------------------------------------------------------------------
handlerName() const230 QString ctkErrorLogStreamMessageHandler::handlerName()const
231 {
232   return ctkErrorLogStreamMessageHandler::HandlerName;
233 }
234 
235 // --------------------------------------------------------------------------
setEnabledInternal(bool value)236 void ctkErrorLogStreamMessageHandler::setEnabledInternal(bool value)
237 {
238   Q_D(ctkErrorLogStreamMessageHandler);
239   d->CoutStreamHandler->setEnabled(value);
240   d->CerrStreamHandler->setEnabled(value);
241 }
242