1/* $Id: CoinMessageHandler.hpp 1448 2011-06-19 15:34:41Z stefan $ */
2// Copyright (C) 2002, International Business Machines
3// Corporation and others. All Rights Reserved.
4// This code is licensed under the terms of the Eclipse Public License (EPL).
5
6#ifndef CoinMessageHandler_H
7#define CoinMessageHandler_H
8
9#include "CoinUtilsConfig.h"
10#include "CoinPragma.hpp"
11
12#include <iostream>
13#include <cstdio>
14#include <string>
15#include <vector>
16
17/** \file CoinMessageHandler.hpp
18 \brief This is a first attempt at a message handler.
19
20 The COIN Project is in favo(u)r of multi-language support. This implementation
21 of a message handler tries to make it as lightweight as possible in the sense
22 that only a subset of messages need to be defined --- the rest default to US
23 English.
24
25 The default handler at present just prints to stdout or to a FILE pointer
26
27 \todo
28 This needs to be worked over for correct operation with ISO character codes.
29*/
30
31/*
32 I (jjf) am strongly in favo(u)r of language support for an open
33 source project, but I have tried to make it as lightweight as
34 possible in the sense that only a subset of messages need to be
35 defined - the rest default to US English. There will be different
36 sets of messages for each component - so at present there is a
37 Clp component and a Coin component.
38
39 Because messages are only used in a controlled environment and have no
40 impact on code and are tested by other tests I have include tests such
41 as language and derivation in other unit tests.
42*/
43
44/** \brief Class for one massaged message.
45
46 A message consists of a text string with formatting codes (#message_),
47 an integer identifier (#externalNumber_) which also determines the severity
48 level (#severity_) of the message, and a detail (logging) level (#detail_).
49
50 CoinOneMessage is just a container to hold this information. The
51 interpretation is set by CoinMessageHandler, which see.
52 */
53
54class CoinOneMessage {
55
56public:
57 /**@name Constructors etc */
58 //@{
59 /** Default constructor. */
60 CoinOneMessage();
61 /** Normal constructor */
62 CoinOneMessage(int externalNumber, char detail,
63 const char * message);
64 /** Destructor */
65 ~CoinOneMessage();
66 /** The copy constructor */
67 CoinOneMessage(const CoinOneMessage&);
68 /** assignment operator. */
69 CoinOneMessage& operator=(const CoinOneMessage&);
70 //@}
71
72 /**@name Useful stuff */
73 //@{
74 /// Replace message text (<i>e.g.</i>, text in a different language)
75 void replaceMessage(const char * message);
76 //@}
77
78 /**@name Get and set methods */
79 //@{
80 /** Get message ID number */
81 inline int externalNumber() const
82 {return externalNumber_;}
83 /** \brief Set message ID number
84
85 In the default CoinMessageHandler, this number is printed in the message
86 prefix and is used to determine the message severity level.
87 */
88 inline void setExternalNumber(int number)
89 {externalNumber_=number;}
90 /// Severity
91 inline char severity() const
92 {return severity_;}
93 /// Set detail level
94 inline void setDetail(int level)
95 {detail_=static_cast<char> (level);}
96 /// Get detail level
97 inline int detail() const
98 {return detail_;}
99 /// Return the message text
100 inline char * message() const
101 {return message_;}
102 //@}
103
104 /**@name member data */
105 //@{
106 /// number to print out (also determines severity)
107 int externalNumber_;
108 /// Will only print if detail matches
109 char detail_;
110 /// Severity
111 char severity_;
112 /// Messages (in correct language) (not all 400 may exist)
113 mutable char message_[400];
114 //@}
115};
116
117/** \brief Class to hold and manipulate an array of massaged messages.
118
119 Note that the message index used to reference a message in the array of
120 messages is completely distinct from the external ID number stored with the
121 message.
122*/
123
124class CoinMessages {
125
126public:
127 /** \brief Supported languages
128
129 These are the languages that are supported. At present only
130 us_en is serious and the rest are for testing.
131 */
132 enum Language {
133 us_en = 0,
134 uk_en,
135 it
136 };
137
138 /**@name Constructors etc */
139 //@{
140 /** Constructor with number of messages. */
141 CoinMessages(int numberMessages=0);
142 /** Destructor */
143 ~CoinMessages();
144 /** The copy constructor */
145 CoinMessages(const CoinMessages&);
146 /** assignment operator. */
147 CoinMessages& operator=(const CoinMessages&);
148 //@}
149
150 /**@name Useful stuff */
151 //@{
152 /*! \brief Installs a new message in the specified index position
153
154 Any existing message is replaced, and a copy of the specified message is
155 installed.
156 */
157 void addMessage(int messageNumber, const CoinOneMessage & message);
158 /*! \brief Replaces the text of the specified message
159
160 Any existing text is deleted and the specified text is copied into the
161 specified message.
162 */
163 void replaceMessage(int messageNumber, const char * message);
164 /** Language. Need to think about iso codes */
165 inline Language language() const
166 {return language_;}
167 /** Set language */
168 void setLanguage(Language newlanguage)
169 {language_ = newlanguage;}
170 /// Change detail level for one message
171 void setDetailMessage(int newLevel, int messageNumber);
172 /** \brief Change detail level for several messages
173
174 messageNumbers is expected to contain the indices of the messages to be
175 changed.
176 If numberMessages >= 10000 or messageNumbers is NULL, the detail level
177 is changed on all messages.
178 */
179 void setDetailMessages(int newLevel, int numberMessages,
180 int * messageNumbers);
181 /** Change detail level for all messages with low <= ID number < high */
182 void setDetailMessages(int newLevel, int low, int high);
183
184 /// Returns class
185 inline int getClass() const
186 { return class_;}
187 /// Moves to compact format
188 void toCompact();
189 /// Moves from compact format
190 void fromCompact();
191 //@}
192
193 /**@name member data */
194 //@{
195 /// Number of messages
196 int numberMessages_;
197 /// Language
198 Language language_;
199 /// Source (null-terminated string, maximum 4 characters).
200 char source_[5];
201 /// Class - see later on before CoinMessageHandler
202 int class_;
203 /** Length of fake CoinOneMessage array.
204 First you get numberMessages_ pointers which point to stuff
205 */
206 int lengthMessages_;
207 /// Messages
208 CoinOneMessage ** message_;
209 //@}
210};
211
212// for convenience eol
213enum CoinMessageMarker {
214 CoinMessageEol = 0,
215 CoinMessageNewline = 1
216};
217
218/** Base class for message handling
219
220 The default behavior is described here: messages are printed, and (if the
221 severity is sufficiently high) execution will be aborted. Inherit and
222 redefine the methods #print and #checkSeverity to augment the behaviour.
223
224 Messages can be printed with or without a prefix; the prefix will consist
225 of a source string, the external ID number, and a letter code,
226 <i>e.g.</i>, Clp6024W.
227 A prefix makes the messages look less nimble but is very useful
228 for "grep" <i>etc</i>.
229
230 <h3> Usage </h3>
231
232 The general approach to using the COIN messaging facility is as follows:
233 <ul>
234 <li> Define your messages. For each message, you must supply an external
235 ID number, a log (detail) level, and a format string. Typically, you
236 define a convenience structure for this, something that's easy to
237 use to create an array of initialised message definitions at compile
238 time.
239 <li> Create a CoinMessages object, sized to accommodate the number of
240 messages you've defined. (Incremental growth will happen if
241 necessary as messages are loaded, but it's inefficient.)
242 <li> Load the messages into the CoinMessages object. Typically this
243 entails creating a CoinOneMessage object for each message and
244 passing it as a parameter to CoinMessages::addMessage(). You specify
245 the message's internal ID as the other parameter to addMessage.
246 <li> Create and use a CoinMessageHandler object to print messages.
247 </ul>
248 See, for example, CoinMessage.hpp and CoinMessage.cpp for an example of
249 the first three steps. `Format codes' below has a simple example of
250 printing a message.
251
252 <h3> External ID numbers and severity </h3>
253
254 CoinMessageHandler assumes the following relationship between the
255 external ID number of a message and the severity of the message:
256 \li <3000 are informational ('I')
257 \li <6000 warnings ('W')
258 \li <9000 non-fatal errors ('E')
259 \li >=9000 aborts the program (after printing the message) ('S')
260
261 <h3> Format codes </h3>
262
263 CoinMessageHandler can print integers (normal, long, and long long),
264 doubles, characters, and strings. See the descriptions of the
265 various << operators.
266
267 When processing a standard message with a format string, the formatting
268 codes specified in the format string will be passed to the sprintf
269 function, along with the argument. When generating a message with no
270 format string, each << operator uses a simple format code appropriate for
271 its argument. Consult the documentation for the standard printf facility
272 for further information on format codes.
273
274 The special format code `%?' provides a hook to enable or disable
275 printing. For each `%?' code, there must be a corresponding call to
276 printing(bool). This provides a way to define optional parts in
277 messages, delineated by the code `%?' in the format string. Printing can
278 be suppressed for these optional parts, but any operands must still be
279 supplied. For example, given the message string
280 \verbatim
281 "A message with%? an optional integer %d and%? a double %g."
282 \endverbatim
283 installed in CoinMessages \c exampleMsgs with index 5, and
284 \c CoinMessageHandler \c hdl, the code
285 \code
286 hdl.message(5,exampleMsgs) ;
287 hdl.printing(true) << 42 ;
288 hdl.printing(true) << 53.5 << CoinMessageEol ;
289 \endcode
290 will print
291 \verbatim
292 A message with an optional integer 42 and a double 53.5.
293 \endverbatim
294 while
295 \code
296 hdl.message(5,exampleMsgs) ;
297 hdl.printing(false) << 42 ;
298 hdl.printing(true) << 53.5 << CoinMessageEol ;
299 \endcode
300 will print
301 \verbatim
302 A message with a double 53.5.
303 \endverbatim
304
305 For additional examples of usage, see CoinMessageHandlerUnitTest in
306 CoinMessageHandlerTest.cpp.
307*/
308
309/*
310 Where there are derived classes I (jjf) have started message numbers at 1001.
311*/
312
313class CoinMessageHandler {
314
315friend bool CoinMessageHandlerUnitTest () ;
316
317private:
318 /** The body of the copy constructor and the assignment operator */
319 void gutsOfCopy(const CoinMessageHandler& rhs);
320
321public:
322 /**@name Virtual methods that the derived classes may provide */
323 //@{
324 /** Print message, return 0 normally.
325 */
326 virtual int print() ;
327 /** Check message severity - if too bad then abort
328 */
329 virtual void checkSeverity() ;
330 //@}
331
332 /**@name Constructors etc */
333 //@{
334 /// Constructor
335 CoinMessageHandler();
336 /// Constructor to put to file pointer (won't be closed)
337 CoinMessageHandler(FILE *fp);
338 /** Destructor */
339 virtual ~CoinMessageHandler();
340 /** The copy constructor */
341 CoinMessageHandler(const CoinMessageHandler&);
342 /** Assignment operator. */
343 CoinMessageHandler& operator=(const CoinMessageHandler&);
344 /// Clone
345 virtual CoinMessageHandler * clone() const;
346 //@}
347 /**@name Get and set methods */
348 //@{
349 /// Get detail level of a message.
350 inline int detail(int messageNumber, const CoinMessages &normalMessage) const
351 { return normalMessage.message_[messageNumber]->detail();}
352 /** Get current log (detail) level. */
353 inline int logLevel() const
354 { return logLevel_;}
355 /** \brief Set current log (detail) level.
356
357 If the log level is equal or greater than the detail level of a message,
358 the message will be printed. A rough convention for the amount of output
359 expected is
360 - 0 - none
361 - 1 - minimal
362 - 2 - normal low
363 - 3 - normal high
364 - 4 - verbose
365
366 Please assign log levels to messages accordingly. Log levels of 8 and
367 above (8,16,32, <i>etc</i>.) are intended for selective debugging.
368 The logical AND of the log level specified in the message and the current
369 log level is used to determine if the message is printed. (In other words,
370 you're using individual bits to determine which messages are printed.)
371 */
372 void setLogLevel(int value);
373 /** Get alternative log level. */
374 inline int logLevel(int which) const
375 { return logLevels_[which];}
376 /*! \brief Set alternative log level value.
377
378 Can be used to store alternative log level information within the handler.
379 */
380 void setLogLevel(int which, int value);
381
382 /// Set the number of significant digits for printing floating point numbers
383 void setPrecision(unsigned int new_precision);
384 /// Current number of significant digits for printing floating point numbers
385 inline int precision() { return (g_precision_) ; }
386
387 /// Switch message prefix on or off.
388 void setPrefix(bool yesNo);
389 /// Current setting for printing message prefix.
390 bool prefix() const;
391 /*! \brief Values of double fields already processed.
392
393 As the parameter for a double field is processed, the value is saved
394 and can be retrieved using this function.
395 */
396 inline double doubleValue(int position) const
397 { return doubleValue_[position];}
398 /*! \brief Number of double fields already processed.
399
400 Incremented each time a field of type double is processed.
401 */
402 inline int numberDoubleFields() const
403 {return static_cast<int>(doubleValue_.size());}
404 /*! \brief Values of integer fields already processed.
405
406 As the parameter for a integer field is processed, the value is saved
407 and can be retrieved using this function.
408 */
409 inline int intValue(int position) const
410 { return longValue_[position];}
411 /*! \brief Number of integer fields already processed.
412
413 Incremented each time a field of type integer is processed.
414 */
415 inline int numberIntFields() const
416 {return static_cast<int>(longValue_.size());}
417 /*! \brief Values of char fields already processed.
418
419 As the parameter for a char field is processed, the value is saved
420 and can be retrieved using this function.
421 */
422 inline char charValue(int position) const
423 { return charValue_[position];}
424 /*! \brief Number of char fields already processed.
425
426 Incremented each time a field of type char is processed.
427 */
428 inline int numberCharFields() const
429 {return static_cast<int>(charValue_.size());}
430 /*! \brief Values of string fields already processed.
431
432 As the parameter for a string field is processed, the value is saved
433 and can be retrieved using this function.
434 */
435 inline std::string stringValue(int position) const
436 { return stringValue_[position];}
437 /*! \brief Number of string fields already processed.
438
439 Incremented each time a field of type string is processed.
440 */
441 inline int numberStringFields() const
442 {return static_cast<int>(stringValue_.size());}
443
444 /// Current message
445 inline CoinOneMessage currentMessage() const
446 {return currentMessage_;}
447 /// Source of current message
448 inline std::string currentSource() const
449 {return source_;}
450 /// Output buffer
451 inline const char * messageBuffer() const
452 {return messageBuffer_;}
453 /// Highest message number (indicates any errors)
454 inline int highestNumber() const
455 {return highestNumber_;}
456 /// Get current file pointer
457 inline FILE * filePointer() const
458 { return fp_;}
459 /// Set new file pointer
460 inline void setFilePointer(FILE * fp)
461 { fp_ = fp;}
462 //@}
463
464 /**@name Actions to create a message */
465 //@{
466 /*! \brief Start a message
467
468 Look up the specified message. A prefix will be generated if enabled.
469 The message will be printed if the current log level is equal or greater
470 than the log level of the message.
471 */
472 CoinMessageHandler & message(int messageNumber,
473 const CoinMessages & messages);
474 /*! \brief Start or continue a message
475
476 Does nothing except return a reference to the handler. This can be used
477 with any of the << operators. One use is to start a message which will be
478 constructed entirely from scratch. Another use is continuation of a
479 message after code that interrupts the usual sequence of << operators.
480 */
481 CoinMessageHandler & message();
482 /*! \brief Generate a standard prefix and append \c msg `as is'.
483
484 Intended as a transition mechanism. The standard prefix is generated (if
485 enabled), and \c msg is appended. Only the operator<<(CoinMessageMarker)
486 operator can be used with a message started with this call.
487 */
488 CoinMessageHandler & message(int externalNumber,const char * header,
489 const char * msg,char severity);
490 /*! \brief Process an integer parameter value.
491
492 The default format code is `%d'.
493 */
494 CoinMessageHandler & operator<< (int intvalue);
495#if COIN_BIG_INDEX==1
496 /*! \brief Process a long integer parameter value.
497
498 The default format code is `%ld'.
499 */
500 CoinMessageHandler & operator<< (long longvalue);
501#endif
502#if COIN_BIG_INDEX==2
503 /*! \brief Process a long long integer parameter value.
504
505 The default format code is `%ld'.
506 */
507 CoinMessageHandler & operator<< (long long longvalue);
508#endif
509 /*! \brief Process a double parameter value.
510
511 The default format code is `%d'.
512 */
513 CoinMessageHandler & operator<< (double doublevalue);
514 /*! \brief Process a STL string parameter value.
515
516 The default format code is `%g'.
517 */
518 CoinMessageHandler & operator<< (const std::string& stringvalue);
519 /*! \brief Process a char parameter value.
520
521 The default format code is `%s'.
522 */
523 CoinMessageHandler & operator<< (char charvalue);
524 /*! \brief Process a C-style string parameter value.
525
526 The default format code is `%c'.
527 */
528 CoinMessageHandler & operator<< (const char *stringvalue);
529 /*! \brief Process a marker.
530
531 The default format code is `%s'.
532 */
533 CoinMessageHandler & operator<< (CoinMessageMarker);
534 /** Finish (and print) the message.
535
536 Equivalent to using the CoinMessageEol marker.
537 */
538 int finish();
539 /*! \brief Enable or disable printing of an optional portion of a message.
540
541 Optional portions of a message are delimited by `%?' markers, and
542 printing processes one %? marker. If \c onOff is true, the subsequent
543 portion of the message (to the next %? marker or the end of the format
544 string) will be printed. If \c onOff is false, printing is suppressed.
545 Parameters must still be supplied, whether printing is suppressed or not.
546 See the class documentation for an example.
547 */
548 CoinMessageHandler & printing(bool onOff);
549
550 /*! \brief Internal function to locate next format code.
551
552 Intended for internal use. Side effects modify the format string.
553 */
554 char * nextPerCent(char * start , const bool initial=false);
555 /*! \brief Internal printing function.
556
557 Makes it easier to split up print into clean, print and check severity
558 */
559 int internalPrint();
560 //@}
561
562 /** Log levels will be by type and will then use type
563 given in CoinMessage::class_
564
565 - 0 - Branch and bound code or similar
566 - 1 - Solver
567 - 2 - Stuff in Coin directory
568 - 3 - Cut generators
569 */
570#define COIN_NUM_LOG 4
571/// Maximum length of constructed message (characters)
572#define COIN_MESSAGE_HANDLER_MAX_BUFFER_SIZE 1000
573protected:
574 /**@name Protected member data */
575 //@{
576 /// values in message
577 std::vector<double> doubleValue_;
578 std::vector<int> longValue_;
579 std::vector<char> charValue_;
580 std::vector<std::string> stringValue_;
581 /// Log level
582 int logLevel_;
583 /// Log levels
584 int logLevels_[COIN_NUM_LOG];
585 /// Whether we want prefix (may get more subtle so is int)
586 int prefix_;
587 /// Current message
588 CoinOneMessage currentMessage_;
589 /// Internal number for use with enums
590 int internalNumber_;
591 /// Format string for message (remainder)
592 char * format_;
593 /// Output buffer
594 char messageBuffer_[COIN_MESSAGE_HANDLER_MAX_BUFFER_SIZE];
595 /// Position in output buffer
596 char * messageOut_;
597 /// Current source of message
598 std::string source_;
599 /** 0 - normal,
600 1 - put in values, move along format, no print
601 2 - put in values, no print
602 3 - skip message
603 */
604 int printStatus_;
605 /// Highest message number (indicates any errors)
606 int highestNumber_;
607 /// File pointer
608 FILE * fp_;
609 /// Current format for floating point numbers
610 char g_format_[8];
611 /// Current number of significant digits for floating point numbers
612 int g_precision_ ;
613 //@}
614
615};
616
617//#############################################################################
618/** A function that tests the methods in the CoinMessageHandler class. The
619 only reason for it not to be a member method is that this way it doesn't
620 have to be compiled into the library. And that's a gain, because the
621 library should be compiled with optimization on, but this method should be
622 compiled with debugging. */
623bool
624CoinMessageHandlerUnitTest();
625
626#endif
627