View Javadoc
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 }