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