1<?php
2/*
3 * $Id: f4e92d98e6607c40a11d6f7f7b71ee81c5c6b995 $
4 *
5 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
6 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
7 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
8 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
9 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
10 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
11 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
12 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
13 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16 *
17 * This software consists of voluntary contributions made by many individuals
18 * and is licensed under the LGPL. For more information please see
19 * <http://phing.info>.
20 */
21
22require_once 'phing/listener/DefaultLogger.php';
23include_once 'phing/system/util/Properties.php';
24
25/**
26 * Uses ANSI Color Code Sequences to colorize messages
27 * sent to the console.
28 *
29 * If used with the -logfile option, the output file
30 * will contain all the necessary escape codes to
31 * display the text in colorized mode when displayed
32 * in the console using applications like cat, more,
33 * etc.
34 *
35 * This is designed to work on terminals that support ANSI
36 * color codes.  It works on XTerm, ETerm, Mindterm, etc.
37 * It also works on Win9x (with ANSI.SYS loaded.)
38 *
39 * NOTE:
40 * It doesn't work on WinNT's COMMAND.COM even with
41 * ANSI.SYS loaded.
42 *
43 * The default colors used for differentiating
44 * the message levels can be changed by editing the
45 * phing/listener/defaults.properties file.
46 *
47 * This file contains 5 key/value pairs:
48 * AnsiColorLogger.ERROR_COLOR=2;31
49 * AnsiColorLogger.WARNING_COLOR=2;35
50 * AnsiColorLogger.INFO_COLOR=2;36
51 * AnsiColorLogger.VERBOSE_COLOR=2;32
52 * AnsiColorLogger.DEBUG_COLOR=2;34
53 *
54 * Another option is to pass a system variable named
55 * ant.logger.defaults, with value set to the path of
56 * the file that contains user defined Ansi Color
57 * Codes, to the <B>java</B> command using -D option.
58 *
59 * To change these colors use the following chart:
60 *
61 *      <B>ANSI COLOR LOGGER CONFIGURATION</B>
62 *
63 * Format for AnsiColorLogger.*=
64 *  Attribute;Foreground;Background
65 *
66 *  Attribute is one of the following:
67 *  0 -> Reset All Attributes (return to normal mode)
68 *  1 -> Bright (Usually turns on BOLD)
69 *  2 -> Dim
70 *  3 -> Underline
71 *  5 -> link
72 *  7 -> Reverse
73 *  8 -> Hidden
74 *
75 *  Foreground is one of the following:
76 *  30 -> Black
77 *  31 -> Red
78 *  32 -> Green
79 *  33 -> Yellow
80 *  34 -> Blue
81 *  35 -> Magenta
82 *  36 -> Cyan
83 *  37 -> White
84 *
85 *  Background is one of the following:
86 *  40 -> Black
87 *  41 -> Red
88 *  42 -> Green
89 *  43 -> Yellow
90 *  44 -> Blue
91 *  45 -> Magenta
92 *  46 -> Cyan
93 *  47 -> White
94 *
95 * @author     Hans Lellelid <hans@xmpl.org> (Phing)
96 * @author     Magesh Umasankar (Ant)
97 * @package    phing.listener
98 * @version    $Id: f4e92d98e6607c40a11d6f7f7b71ee81c5c6b995 $
99 */
100class AnsiColorLogger extends DefaultLogger {
101
102    const ATTR_NORMAL = 0;
103    const ATTR_BRIGHT = 1;
104    const ATTR_DIM = 2;
105    const ATTR_UNDERLINE = 3;
106    const ATTR_BLINK = 5;
107    const ATTR_REVERSE = 7;
108    const ATTR_HIDDEN = 8;
109
110    const FG_BLACK = 30;
111    const FG_RED = 31;
112    const FG_GREEN = 32;
113    const FG_YELLOW = 33;
114    const FG_BLUE = 34;
115    const FG_MAGENTA = 35;
116    const FG_CYAN = 36;
117    const FG_WHITE = 37;
118
119    const BG_BLACK = 40;
120    const BG_RED = 41;
121    const BG_GREEN = 42;
122    const BG_YELLOW = 44;
123    const BG_BLUE = 44;
124    const BG_MAGENTA = 45;
125    const BG_CYAN = 46;
126    const BG_WHITE = 47;
127
128    const PREFIX = "\x1b[";
129    const SUFFIX = "m";
130    const SEPARATOR = ';';
131    const END_COLOR = "\x1b[0m"; // self::PREFIX . self::SUFFIX;
132
133    private $errColor;
134    private $warnColor;
135    private $infoColor;
136    private $verboseColor;
137    private $debugColor;
138
139    private $colorsSet = false;
140
141    /**
142     * Construct new AnsiColorLogger
143     * Perform initializations that cannot be done in var declarations.
144     */
145    public function __construct() {
146        parent::__construct();
147        $this->errColor = self::PREFIX . self::ATTR_NORMAL . self::SEPARATOR . self::FG_RED . self::SUFFIX;
148        $this->warnColor = self::PREFIX . self::ATTR_NORMAL . self::SEPARATOR . self::FG_MAGENTA . self::SUFFIX;
149        $this->infoColor = self::PREFIX . self::ATTR_NORMAL . self::SEPARATOR . self::FG_CYAN . self::SUFFIX;
150        $this->verboseColor = self::PREFIX . self::ATTR_NORMAL . self::SEPARATOR . self::FG_GREEN . self::SUFFIX;
151        $this->debugColor = self::PREFIX . self::ATTR_NORMAL . self::SEPARATOR . self::FG_BLUE . self::SUFFIX;
152    }
153
154    /**
155     * Set the colors to use from a property file specified by the
156     * special ant property ant.logger.defaults
157     */
158    private final function setColors() {
159
160        $userColorFile = Phing::getProperty("phing.logger.defaults");
161        $systemColorFile = new PhingFile(Phing::getResourcePath("phing/listener/defaults.properties"));
162
163        $in = null;
164
165        try {
166            $prop = new Properties();
167
168            if ($userColorFile !== null) {
169                $prop->load($userColorFile);
170            } else {
171                $prop->load($systemColorFile);
172            }
173
174            $err = $prop->getProperty("AnsiColorLogger.ERROR_COLOR");
175            $warn = $prop->getProperty("AnsiColorLogger.WARNING_COLOR");
176            $info = $prop->getProperty("AnsiColorLogger.INFO_COLOR");
177            $verbose = $prop->getProperty("AnsiColorLogger.VERBOSE_COLOR");
178            $debug = $prop->getProperty("AnsiColorLogger.DEBUG_COLOR");
179            if ($err !== null) {
180                $this->errColor = self::PREFIX . $err . self::SUFFIX;
181            }
182            if ($warn !== null) {
183                $this->warnColor = self::PREFIX . $warn . self::SUFFIX;
184            }
185            if ($info !== null) {
186                $this->infoColor = self::PREFIX . $info . self::SUFFIX;
187            }
188            if ($verbose !== null) {
189                $this->verboseColor = self::PREFIX . $verbose . self::SUFFIX;
190            }
191            if ($debug !== null) {
192                $this->debugColor = self::PREFIX . $debug . self::SUFFIX;
193            }
194        } catch (IOException $ioe) {
195            //Ignore exception - we will use the defaults.
196        }
197    }
198
199    /**
200     * @see DefaultLogger#printMessage
201     * @param string $message
202     * @param OutputStream $stream
203     * @param int $priority
204     */
205    protected final function printMessage($message, OutputStream $stream, $priority) {
206        if ($message !== null) {
207
208            if (!$this->colorsSet) {
209                $this->setColors();
210                $this->colorsSet = true;
211            }
212
213            switch ($priority) {
214                case Project::MSG_ERR:
215                    $message = $this->errColor . $message . self::END_COLOR;
216                    break;
217                case Project::MSG_WARN:
218                    $message = $this->warnColor . $message . self::END_COLOR;
219                    break;
220                case Project::MSG_INFO:
221                    $message = $this->infoColor . $message . self::END_COLOR;
222                    break;
223                case Project::MSG_VERBOSE:
224                    $message = $this->verboseColor . $message . self::END_COLOR;
225                    break;
226                case Project::MSG_DEBUG:
227                    $message = $this->debugColor . $message . self::END_COLOR;
228                    break;
229            }
230
231            $stream->write($message . PHP_EOL);
232        }
233    }
234}
235