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 | |
54 | class CoinOneMessage { |
55 | |
56 | public: |
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 | |
124 | class CoinMessages { |
125 | |
126 | public: |
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 |
213 | enum 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 | |
313 | class CoinMessageHandler { |
314 | |
315 | friend bool CoinMessageHandlerUnitTest () ; |
316 | |
317 | private: |
318 | /** The body of the copy constructor and the assignment operator */ |
319 | void gutsOfCopy(const CoinMessageHandler& rhs); |
320 | |
321 | public: |
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 * , |
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 |
573 | protected: |
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. */ |
623 | bool |
624 | CoinMessageHandlerUnitTest(); |
625 | |
626 | #endif |
627 | |