1 package org.apache.maven.shared.utils.logging;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import org.fusesource.jansi.Ansi;
23 import org.fusesource.jansi.AnsiConsole;
24
25 /**
26 * Colored message utils, to manage colors consistently across plugins (only if Maven version is at least 3.5.0).
27 * For Maven version before 3.5.0, message built with this util will never add color.
28 * <p>
29 * Internally, <a href="http://fusesource.github.io/jansi/">Jansi</a> is used to render
30 * <a href="https://en.wikipedia.org/wiki/ANSI_escape_code#Colors">ANSI colors</a> on any platform.
31 * @since 3.1.0
32 */
33 public class MessageUtils
34 {
35 private static final boolean JANSI;
36
37 /** Reference to the JVM shutdown hook, if registered */
38 private static Thread shutdownHook;
39
40 /** Synchronization monitor for the "uninstall" */
41 private static final Object STARTUP_SHUTDOWN_MONITOR = new Object();
42
43 static
44 {
45 boolean jansi = true;
46 try
47 {
48 // JAnsi is provided by Maven core since 3.5.0
49 Class.forName( "org.fusesource.jansi.Ansi" );
50 }
51 catch ( ClassNotFoundException cnfe )
52 {
53 jansi = false;
54 }
55 JANSI = jansi;
56 }
57
58 /**
59 * Install color support.
60 * This method is called by Maven core, and calling it is not necessary in plugins.
61 */
62 public static void systemInstall()
63 {
64 if ( JANSI )
65 {
66 AnsiConsole.systemInstall();
67 }
68 }
69
70 /**
71 * Undo a previous {@link #systemInstall()}. If {@link #systemInstall()} was called
72 * multiple times, {@link #systemUninstall()} must be called call the same number of times before
73 * it is actually uninstalled.
74 */
75 public static void systemUninstall()
76 {
77 synchronized ( STARTUP_SHUTDOWN_MONITOR )
78 {
79 doSystemUninstall();
80
81 // hook can only set when JANSI is true
82 if ( shutdownHook != null )
83 {
84 // if out and system_out are same instance again, ansi is assumed to be uninstalled
85 if ( AnsiConsole.out == AnsiConsole.system_out )
86 {
87 try
88 {
89 Runtime.getRuntime().removeShutdownHook( shutdownHook );
90 }
91 catch ( IllegalStateException ex )
92 {
93 // ignore - VM is already shutting down
94 }
95 }
96 }
97 }
98 }
99
100 private static void doSystemUninstall()
101 {
102 if ( JANSI )
103 {
104 AnsiConsole.systemUninstall();
105 }
106 }
107
108 /**
109 * Enables message color (if JAnsi is available).
110 * @param flag
111 */
112 public static void setColorEnabled( boolean flag )
113 {
114 if ( JANSI )
115 {
116 Ansi.setEnabled( flag );
117 }
118 }
119
120 /**
121 * Is message color enabled: requires JAnsi available (through Maven) and the color has not been disabled.
122 */
123 public static boolean isColorEnabled()
124 {
125 return JANSI ? Ansi.isEnabled() : false;
126 }
127
128 /**
129 * Create a default message buffer.
130 * @return a new buffer
131 */
132 public static MessageBuilder buffer()
133 {
134 return JANSI ? new AnsiMessageBuilder() : new PlainMessageBuilder();
135 }
136
137 /**
138 * Create a message buffer with defined String builder.
139 * @return a new buffer
140 */
141 public static MessageBuilder buffer( StringBuilder builder )
142 {
143 return JANSI ? new AnsiMessageBuilder( builder ) : new PlainMessageBuilder( builder );
144 }
145
146 /**
147 * Create a message buffer with an internal buffer of defined size.
148 * @return a new buffer
149 */
150 public static MessageBuilder buffer( int size )
151 {
152 return JANSI ? new AnsiMessageBuilder( size ) : new PlainMessageBuilder( size );
153 }
154
155 /**
156 * Create a logger level renderer.
157 * @return a logger level renderer
158 * @since 3.2.0
159 */
160 @SuppressWarnings( "checkstyle:magicnumber" )
161 public static LoggerLevelRenderer level()
162 {
163 return JANSI ? new AnsiMessageBuilder( 20 ) : new PlainMessageBuilder( 7 );
164 }
165
166 /**
167 * Remove any ANSI code from a message (colors or other escape sequences).
168 * @param msg message eventually containing ANSI codes
169 * @return the message with ANSI codes removed
170 */
171 public static String stripAnsiCodes( String msg )
172 {
173 return msg.replaceAll( "\u001B\\[[;\\d]*[ -/]*[@-~]", "" );
174 }
175
176 /**
177 * Register a shutdown hook with the JVM runtime, uninstalling Ansi support on
178 * JVM shutdown unless is has already been uninstalled at that time.
179 * <p>Delegates to {@link #doSystemUninstall()} for the actual uninstall procedure
180 *
181 * @see Runtime#addShutdownHook(Thread)
182 * @see MessageUtils#systemUninstall()
183 * @see #doSystemUninstall()
184 */
185 public static void registerShutdownHook()
186 {
187 if ( JANSI && shutdownHook == null )
188 {
189 // No shutdown hook registered yet.
190 shutdownHook = new Thread()
191 {
192 @Override
193 public void run()
194 {
195 synchronized ( STARTUP_SHUTDOWN_MONITOR )
196 {
197 doSystemUninstall();
198 }
199 }
200 };
201 Runtime.getRuntime().addShutdownHook( shutdownHook );
202 }
203 }
204 }