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 }