package org.apache.maven.cli;
/* ====================================================================
* Copyright 2001-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.ParseException;
import org.apache.commons.jelly.JellyException;
import org.apache.commons.jelly.XMLOutput;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.maven.MavenConstants;
import org.apache.maven.MavenException;
import org.apache.maven.MavenSession;
import org.apache.maven.MavenUtils;
import org.apache.maven.UnknownGoalException;
import org.apache.maven.jelly.MavenJellyContext;
import org.apache.maven.project.Project;
import org.apache.maven.verifier.ChecksumVerificationException;
import org.apache.maven.verifier.RepoConfigException;
import org.apache.maven.verifier.UnsatisfiedDependencyException;
import org.apache.maven.werkz.NoActionDefinitionException;
import org.apache.maven.werkz.NoSuchGoalException;
import org.apache.maven.werkz.UnattainableGoalException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.BreakIterator;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
/**
* The CLI wrapper for controlling MavenSession processes which are
* encapsulated in the MavenSession bean.
*
* @author Jason van Zyl
*
* @version $Id: App.java 126378 2005-01-25 12:35:53Z brett $
*
* @todo Separate the computation of the available goals from the display
* of the goals. The goal computation logic needs to be moved
* out of this class and be placed in MavenSession.java proper.
* @todo All logging needs to be done via commons-logging. No
* System.err.* and Jelly needs to be taught to take a
* logger. In an attempt to isolate everything in MavenSession.java.
*/
public class App
{
// ------------------------------------------------------------
// C O N S T A N T S
// ------------------------------------------------------------
/** Convenience constant for new line character. */
private static final String LS = System.getProperty( "line.separator", "\n" );
/** Default file name for an XML-based POM. */
public static final String POM_FILE_NAME = "project.xml";
/** Default wrap indent. */
private static final int WRAP_INDENT = 35;
/** Default console width - for formatting output. */
private static final int CONSOLE_WIDTH = 80;
/** return code for ok processing */
private static final int RC_OK = 0;
/** return code from command prompt when a bad argument is passed */
private static final int RC_BAD_ARG = 10;
/** return code from command prompt when initialization fails */
private static final int RC_INIT_ERROR = 20;
/** return code from command prompt when a goal isn't found */
private static final int RC_NO_GOAL = 30;
/** return code for bad repository configuration */
private static final int RC_BAD_REPO = 40;
/** return code for a goal with no actions */
private static final int RC_EMPTY_GOAL = 50;
/** return code for a goal that failed */
private static final int RC_GOAL_FAILED = 60;
/** return code for a goal failed from jelly exception being thrown */
private static final int RC_JELLY_FAILED = 70;
/** return code for a failure due to Jelly issues */
private static final int RC_BAD_JELLY = 80;
/** return code for a failure due to anything else */
private static final int RC_OTHER_FAILURE = 90;
/** return code for a failure due to failed dependency */
private static final int RC_FAILED_DEPENDENCY = 100;
// O P T I O N S
/** Console banner option. */
private static final String CONSOLE_BANNER = "b";
/** Display stack trace option. */
private static final String DISPLAY_STACKTRACE = "e";
/** Find POM option. */
private static final String FIND_POM_DESCRIPTOR = "f";
/** Display goals options. */
private static final String DISPLAY_GOALS = "g";
/** Display help option. */
private static final String DISPLAY_HELP = "h";
/** Display plugin help option. */
private static final String DISPLAY_PLUGIN_HELP = "P";
/** Display project help option. */
private static final String DISPLAY_USAGE = "u";
/** Display info option. */
private static final String DISPLAY_INFO = "i";
/** Display version option. */
private static final String DISPLAY_VERSION = "v";
/** Work offline option. */
private static final String WORK_OFFLINE = "o";
/** Set POM descriptor option. */
private static final String SET_POM_DESCRIPTOR = "p";
/** System property option. */
private static final String SET_SYSTEM_PROPERTY = "D";
/** Emacs output option. */
private static final String EMACS_OUTPUT = "E";
/** Quiet option. */
private static final String QUIET = "q";
/** Debug option. */
private static final String DEBUG = "X";
/** Working dir option. */
private static final String WORKING_DIR = "d";
/** Links properties */
private static final Properties LINKS_PROPERTIES = new Properties();
// ------------------------------------------------------------
// C L A S S M E M B E R S
// ------------------------------------------------------------
// ------------------------------------------------------------
// I N S T A N C E M E M B E R S
// ------------------------------------------------------------
/** CLI Parser */
private CommandLine commandLine;
/** Jelly's underlying writer. */
private Writer writer;
/** MavenSession Jelly rootContext. */
private MavenJellyContext rootContext;
/** the session to run builds */
private MavenSession mavenSession;
/** logger for output */
private Log log = LogFactory.getLog( App.class );
static {
URL url = App.class.getResource("/links.properties");
if (url == null)
{
throw new RuntimeException("Configuration error: can not find the resource links.properties");
}
try {
LINKS_PROPERTIES.load(url.openStream());
}
catch (IOException e)
{
e.printStackTrace();
}
}
/** Constructor. */
public App()
{
}
// ----------------------------------------------------------------------
// A C C E S S O R S
// ----------------------------------------------------------------------
/**
* Set Jelly rootContext.
*
* @param rootContext The mavenSession jelly rootContext.
*/
public void setRootContext( MavenJellyContext rootContext )
{
this.rootContext = rootContext;
}
/**
* Retrieve the Jelly rootContext.
*
* @return The Jelly rootContext.
*/
public MavenJellyContext getRootContext()
{
return rootContext;
}
/**
* Set the cli parser.
*
* @param commandLine The command line parser.
*/
protected void setCli( CommandLine commandLine )
{
this.commandLine = commandLine;
}
/**
* Get the CLI parser.
*
* @return CommandLine The command line parser.
*/
protected CommandLine getCli()
{
return this.commandLine;
}
// ----------------------------------------------------------------------
// I N S T A N C E M E T H O D S
// ----------------------------------------------------------------------
/** Perform initialization.
*
* @param args The command-line arguments.
*
* @throws ParseException If there is an error parsing the
* command-line.
* @throws IOException If there is an error while
* reading the project descriptor.
* @throws MalformedURLException If any of the the URLs denoting
* the local or remote repositories is malformed.
*/
public void initialize( String[] args )
throws ParseException, MalformedURLException, IOException
{
setCli( CLIManager.parse( args ) );
initializeSystemProperties();
initializeRootContext();
initializeMavenSession();
}
/**
* Get the project descriptor file.
*
* @return The project descriptor file.
* @throws IOException when the project.xml parent can't be resolved
*/
private File getDescriptorFile() throws IOException
{
File descriptorFile = null;
String descriptorName = null;
// Determine what the name of the XML POM descriptor is. If it is
// specified then we will what the user requests, otherwise we will
// default to using 'project.xml'.
if ( getCli().hasOption( SET_POM_DESCRIPTOR ) )
{
descriptorName = getCli().getOptionValue( SET_POM_DESCRIPTOR );
}
else
{
descriptorName = POM_FILE_NAME;
}
if ( getCli().hasOption( FIND_POM_DESCRIPTOR ) )
{
descriptorFile = find( descriptorName );
if ( descriptorFile == null )
{
descriptorFile = new File( descriptorName );
}
}
else
{
descriptorFile = new File(descriptorName);
}
if ( getCli().hasOption( SET_POM_DESCRIPTOR ) && !descriptorFile.exists() ) {
throw new FileNotFoundException( "Project file '" + descriptorName + "' not found" );
}
descriptorFile = descriptorFile.getAbsoluteFile();
if ( !getCli().hasOption( WORKING_DIR ) )
{
System.setProperty("user.dir", descriptorFile.getParentFile().getCanonicalPath());
}
return descriptorFile;
}
/**
* Initialize the IO streams.
*
* @throws IOException on error creating XML output and handling System.err
* and out
*/
protected void initializeRootContext()
throws IOException
{
this.writer = new OutputStreamWriter( System.out );
XMLOutput output = XMLOutput.createXMLOutput( writer, false );
if ( getCli().hasOption( WORKING_DIR ) )
{
String workingDir = getCli().getOptionValue(WORKING_DIR);
File dir = new File(workingDir);
if (!dir.isAbsolute())
{
workingDir = dir.getAbsolutePath();
}
System.setProperty( "user.dir", workingDir );
}
// We will assume here that there might not be a project.xml file present
// and we will create the root context from the directory gleaned from
// the user.dir system property.
File basedir = new File( System.getProperty( "user.dir" ) );
MavenJellyContext c = MavenUtils.createContext( basedir );
setRootContext( c );
c.setXMLOutput( output );
// This is just a start at this mode - there'll still be some output
if ( getCli().hasOption( QUIET ) )
{
// A little bit of log4j specifics needed here
Logger.getLogger( "org.apache.maven" ).setLevel( Level.ERROR );
}
// -X takes precedence over -q
if ( getCli().hasOption( DEBUG ) )
{
getRootContext().setDebugOn( Boolean.TRUE );
// A little bit of log4j specifics needed here
Logger.getLogger( "org.apache.maven" ).setLevel( Level.DEBUG );
}
else
{
getRootContext().setDebugOn( Boolean.FALSE );
}
if ( getCli().hasOption( EMACS_OUTPUT ) )
{
getRootContext().setEmacsModeOn( Boolean.TRUE );
}
else
{
getRootContext().setEmacsModeOn( Boolean.FALSE );
}
if ( getCli().hasOption( WORK_OFFLINE ) )
{
System.setProperty( MavenConstants.ONLINE, "false" );
}
}
/**
* Initialize the mavenSession bean.
* @throws IOException when the descriptor file parent can't be resolved
*/
private void initializeMavenSession() throws IOException
{
// Even though the rootProject contains the rootContext we set both in
// the even that there is no rootProject and the user is requesting goals
// to be attained that do not directly relate to a project. A goaly that
// may, say, generate the skeletal maven application build.
mavenSession = new MavenSession();
mavenSession.setRootContext( getRootContext() );
// note: setRootDescriptor file is a static method
MavenSession.setRootDescriptorFile( getDescriptorFile() );
}
/**
* Setup any system properties that have been specified on
* the CLI.
*/
public void initializeSystemProperties()
{
if ( getCli().hasOption( SET_SYSTEM_PROPERTY ) )
{
String[] defStrs = getCli().getOptionValues( SET_SYSTEM_PROPERTY );
for ( int i = 0; i < defStrs.length; ++i )
{
setCliProperty( defStrs[i] );
}
}
}
/**
* Set a property based upon a commandline name=value string.
*
* @param defStr The name=value string.
*/
private void setCliProperty( String defStr )
{
String name = null;
String value = null;
int equalLoc = defStr.indexOf( "=" );
if ( equalLoc <= 0 )
{
name = defStr.trim();
value = "true";
}
else
{
name = defStr.substring( 0, equalLoc ).trim();
value = defStr.substring( equalLoc + 1 ).trim();
}
System.setProperty( name, value );
}
/**
* Perform main operations in a non-static method.
*
* @param args Arguments passed in from main().
* @param fullStart Date the mavenSession process was started.
*/
public void doMain( String[] args, Date fullStart )
{
initializeMain(args);
displayHelp();
displayVersion();
int returnCode = RC_OK;
if ( !getCli().hasOption( CONSOLE_BANNER ) )
{
printConsoleMavenHeader();
}
boolean failed = false;
try
{
mavenSession.initialize();
displayInfo();
displayProjectHelp();
displayPluginHelp();
if ( getCli().hasOption( DISPLAY_GOALS ) )
{
displayGoals();
exit( returnCode );
}
else
{
mavenSession.attainGoals( mavenSession.getRootProject(), getCli().getArgList() );
}
}
catch ( UnsatisfiedDependencyException e )
{
failed = true;
System.err.println( e.getLocalizedMessage() );
returnCode = RC_FAILED_DEPENDENCY;
}
catch ( ChecksumVerificationException e )
{
failed = true;
System.err.println( e.getLocalizedMessage() );
returnCode = RC_FAILED_DEPENDENCY;
}
catch ( UnknownGoalException e )
{
failed = true;
System.err.println( MavenUtils.getMessage( "build.failed" ) );
System.err.println( MavenUtils.getMessage( "build.unknownGoalException" , e.getGoalName() ) );
returnCode = RC_NO_GOAL;
}
catch ( NoSuchGoalException e )
{
failed = true;
System.err.println( MavenUtils.getMessage( "build.failed" ) );
System.err.println( e.getMessage() );
System.err.println( MavenUtils.getMessage( "build.noSuchGoalException") );
returnCode = RC_NO_GOAL;
}
catch ( RepoConfigException e )
{
failed = true;
System.err.println( e.getLocalizedMessage() );
returnCode = RC_BAD_REPO;
}
catch ( NoActionDefinitionException e )
{
failed = true;
System.err.println( MavenUtils.getMessage( "build.internalError" ) );
System.err.println( MavenUtils.getMessage( "build.noActionDefinitionException", e.getGoal().getName() ) );
System.err.println( "" );
returnCode = RC_EMPTY_GOAL;
if ( getCli().hasOption( DISPLAY_STACKTRACE ) )
{
e.printStackTrace();
}
displayBugReportHelp();
return;
}
catch ( UnattainableGoalException e )
{
failed = true;
returnCode = handleUnattainableGoalException( e );
}
catch ( JellyException e )
{
returnCode = RC_BAD_JELLY;
failed = true;
String fileName = e.getFileName();
String elementName = e.getElementName();
int lineNumber = e.getLineNumber();
int column = e.getColumnNumber();
String msg = e.getReason();
System.err.println( MavenUtils.getMessage( "build.jellyException.file", fileName ) );
System.err.println( MavenUtils.getMessage( "build.jellyException.element", elementName ) );
System.err.println( MavenUtils.getMessage( "build.jellyException.line", new Integer( lineNumber ) ) );
System.err.println( MavenUtils.getMessage( "build.jellyException.column", new Integer( column ) ) );
System.err.println( msg );
if ( getCli().hasOption( DISPLAY_STACKTRACE ) )
{
e.printStackTrace();
}
displayBugReportHelp();
}
catch ( Throwable t )
{
returnCode = RC_OTHER_FAILURE;
failed = true;
t.printStackTrace();
displayBugReportHelp();
}
if ( !failed )
{
log.info( MavenUtils.getMessage( "build.successful" ) );
}
final long mb = 1024 * 1024;
System.gc();
Runtime r = Runtime.getRuntime();
log.debug( MavenUtils.getMessage( "build.final.memory" ) + ((r.totalMemory() - r.freeMemory()) / mb) + "M/" + (r.totalMemory() / mb) + "M");
Date fullStop = new Date();
DateFormat defaultDate = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.LONG);
long fullDiff = fullStop.getTime() - fullStart.getTime();
log.info( MavenUtils.getMessage( "build.total.time" ) + formatTime( fullDiff ) );
log.info( MavenUtils.getMessage( "build.finished.time" ) + defaultDate.format( fullStop ) );
log.info( "" );
exit( returnCode );
}
/**
* Intialize main and exit if failures occur
* @param args command line args
*/
private void initializeMain(String[] args)
{
int returnCode = RC_OK;
try
{
initialize( args );
}
catch ( ParseException e )
{
log.info( e.getLocalizedMessage() );
CLIManager.displayHelp();
returnCode = RC_BAD_ARG;
}
catch ( IOException e )
{
log.info( e.getLocalizedMessage() );
returnCode = RC_INIT_ERROR;
}
catch ( Exception e )
{
e.printStackTrace();
returnCode = RC_INIT_ERROR;
}
if ( returnCode != RC_OK )
{
log.info( "" );
exit( returnCode );
}
}
/**
* Display the command line help if the option is present, then exit
*/
private void displayHelp()
{
if ( getCli().hasOption( DISPLAY_HELP ) )
{
CLIManager.displayHelp();
log.info( "" );
exit( RC_OK );
}
}
/**
* Display the plugin help if the option is present, then exit
*/
private void displayPluginHelp() throws MavenException
{
if ( getCli().hasOption( DISPLAY_PLUGIN_HELP ) )
{
String plugin = getCli().getOptionValue( DISPLAY_PLUGIN_HELP );
displayGoals( true, plugin );
if ( plugin != null )
{
Project project = mavenSession.getPluginProjectFromGoal( plugin );
if ( project != null && project.getDescription() != null )
{
log.info( wrapConsoleMessage( project.getDescription(), 0, CONSOLE_WIDTH ) );
}
}
exit( RC_OK );
}
}
/**
* Display information on how to file a bug report if an unknown error occurs.
*/
private void displayBugReportHelp()
{
StringBuffer sb = new StringBuffer();
sb.append( MavenUtils.getMessage( "displayBugReportHelp.line1" ) ).append('\n');
sb.append( MavenUtils.getMessage( "displayBugReportHelp.line2" ) ).append('\n');
sb.append( MavenUtils.getMessage( "displayBugReportHelp.line3", LINKS_PROPERTIES.get("faqUrl") ) ).append('\n');
sb.append( MavenUtils.getMessage( "displayBugReportHelp.line4" ) ).append('\n');
sb.append( MavenUtils.getMessage( "displayBugReportHelp.line5", LINKS_PROPERTIES.get("usersMailingListUrl") ) ).append('\n');
if ((mavenSession != null) &&
(mavenSession.getRootProject() != null)) {
sb.append( MavenUtils.getMessage( "displayBugReportHelp.line6", mavenSession.getRootProject().getIssueTrackingUrl() ) ).append('\n');
} else {
sb.append( MavenUtils.getMessage( "displayBugReportHelp.line6", "???issueTrackingUrl???" ) ).append('\n');
}
sb.append( MavenUtils.getMessage( "displayBugReportHelp.line7" ) );
log.info( "" );
log.info( sb.toString() );
log.info( "" );
}
/**
* Display the project help if the option is present, then exit
*/
private void displayProjectHelp() throws MavenException
{
if ( getCli().hasOption( DISPLAY_USAGE ) )
{
Set goals = mavenSession.getProjectGoals( mavenSession.getRootProject() );
String title = MavenUtils.getMessage( "displayProjectHelp.title" );
log.info( title );
log.info( format( "", title.length(), '=' ) );
displayGoals( false, null, goals );
String msg = mavenSession.getRootProject().getDescription();
if ( msg != null )
{
log.info( wrapConsoleMessage( msg, 0, CONSOLE_WIDTH ) );
}
exit( RC_OK );
}
}
/**
* Display the command line info if the option is present, then exit
*/
private void displayInfo()
{
if ( getCli().hasOption( DISPLAY_INFO ) )
{
CLIManager.displayInfo();
log.info( "" );
log.info( MavenUtils.getMessage( "displayInfo.info1" ) );
for ( Iterator i = mavenSession.getPluginList().iterator(); i.hasNext(); )
{
log.info( " " + i.next() );
}
Properties buildProperties = new Properties();
try
{
buildProperties.load( new FileInputStream( System.getProperty( "user.home" ) + "/build.properties" ) );
}
catch ( IOException e )
{
log.error( MavenUtils.getMessage( "displayInfo.error" ) + e.getMessage() );
}
log.info( MavenUtils.getMessage( "displayInfo.info2" ) + buildProperties );
exit( RC_OK );
}
}
/**
* Display the Maven version info if the option is present, then exit
*/
private void displayVersion()
{
if ( getCli().hasOption( DISPLAY_VERSION ) )
{
printConsoleMavenHeader();
exit( RC_OK );
}
}
/** Handle an UnattainableGoalException.
*
* @param e The exception.
*
* @return The return code.
*/
private int handleUnattainableGoalException( UnattainableGoalException e )
{
int returnCode = RC_GOAL_FAILED;
System.err.println( "" );
System.err.println( MavenUtils.getMessage( "build.failed" ) );
String msg = null;
String fileName = null;
String elementName = null;
int lineNumber = -1;
int column = -1;
Throwable rootCause = e.getRootCause();
if ( rootCause != null )
{
if ( rootCause instanceof JellyException )
{
returnCode = RC_JELLY_FAILED;
JellyException jellyEx = (JellyException) rootCause;
fileName = jellyEx.getFileName();
elementName = jellyEx.getElementName();
lineNumber = jellyEx.getLineNumber();
column = jellyEx.getColumnNumber();
rootCause = jellyEx.getCause();
if ( rootCause == null )
{
msg = jellyEx.getReason();
}
else
{
if ( getCli().hasOption( DISPLAY_STACKTRACE ) )
{
rootCause.printStackTrace();
}
msg = rootCause.getLocalizedMessage();
if ( msg == null )
{
msg = rootCause.getClass().getName();
}
}
System.err.println( MavenUtils.getMessage( "build.jellyException.file", fileName ) );
System.err.println( MavenUtils.getMessage( "build.jellyException.element", elementName ) );
System.err.println( MavenUtils.getMessage( "build.jellyException.line", new Integer( lineNumber ) ) );
System.err.println( MavenUtils.getMessage( "build.jellyException.column", new Integer( column ) ) );
}
else
{
if ( getCli().hasOption( DISPLAY_STACKTRACE ) )
{
rootCause.printStackTrace();
}
msg = rootCause.getLocalizedMessage();
if ( msg == null )
{
msg = rootCause.getClass().getName();
}
}
}
else
{
msg = e.getLocalizedMessage();
}
System.err.println( msg );
if ( getCli().hasOption( DEBUG ) )
{
e.printStackTrace();
}
return returnCode;
}
/**
* From the CWD search through the directory hierarchy for
* an XML-based POM.
*
* @param filename The filename to find.
* @return The found file.
*/
private File find( String filename )
{
// An empty string should resolve to the current directory (user.dir)
return find( new File( "" ), filename );
}
/**
* From the CWD search through the directory hierarchy for
* an XML-based POM.
*
* @param start The starting directory for the POM search.
* @param suffix The suffix for the file to be searched for.
* @return The found project.xml file.
*/
private File find( File start, String suffix )
{
if ( start == null )
{
return null;
}
File dir = start.getAbsoluteFile();
File file = new File( dir, suffix );
return file.exists() ? file : find( dir.getParentFile(), suffix );
}
/**
* Prints the MavenSession header.
*/
protected void printConsoleMavenHeader()
{
Properties p = new Properties();
InputStream is = getClass().getResourceAsStream( "/driver.properties" );
try
{
p.load( is );
}
catch ( IOException e )
{
log.error( MavenUtils.getMessage( "printConsoleMavenHeader.error" ) + e );
}
finally
{
try { is.close(); } catch ( IOException e ) {}
}
log.info( " __ __" );
log.info( "| \\/ |__ _Apache__ ___" );
log.info( "| |\\/| / _` \\ V / -_) ' \\ ~ intelligent projects ~" );
log.info( "|_| |_\\__,_|\\_/\\___|_||_| v. " + p.getProperty( "maven.application.version" ) );
log.info( "" );
}
/**
* Display helpful information regarding
* all documented goals.
*/
protected void displayGoals()
{
displayGoals( false, null );
}
/**
* Display helpful information regarding all documented goals.
* @param pluginOnly show information for the given plugin only
* @param plugin plugin to show info for
*/
protected void displayGoals( boolean pluginOnly, String plugin )
{
String title = MavenUtils.getMessage( "displayGoals.title" );
if ( pluginOnly )
{
title = ( plugin == null ? MavenUtils.getMessage( "displayGoals.title.pluginOnly.null" ) : MavenUtils.getMessage( "displayGoals.title.pluginOnly.notNull" ) + plugin );
}
log.info( title );
log.info( format( "", title.length(), '=' ) );
Set goals = mavenSession.getAllGoalNames();
displayGoals( pluginOnly, plugin, goals );
}
private void displayGoals( boolean pluginOnly, String plugin, Set goals )
{
Map map = new HashMap();
for ( Iterator i = goals.iterator(); i.hasNext(); )
{
String goal = ( String ) i.next();
String pluginName = "";
int index = goal.indexOf( ':' );
if ( index >= 0 )
{
pluginName = goal.substring( 0, index );
}
List l = ( List ) map.get( pluginName );
if ( l == null )
{
l = new ArrayList();
map.put( pluginName, l );
}
l.add( goal.substring( index + 1 ) );
}
List goalList = ( List ) map.get( "" );
if ( goalList != null )
{
for ( Iterator i = goalList.iterator(); i.hasNext(); )
{
String goal = ( String ) i.next();
if ( map.containsKey( goal ) )
{
i.remove();
List pluginGoals = ( List ) map.get( goal );
if ( pluginGoals == null )
{
pluginGoals = new ArrayList();
map.put( goal, pluginGoals );
}
// don't add the empty goal as we always attempt to display the default
}
}
}
List keys = new ArrayList( map.keySet() );
Collections.sort( keys );
List undocumentedGoals = new ArrayList();
for ( Iterator i = keys.iterator(); i.hasNext(); )
{
String pluginName = ( String ) i.next();
if ( pluginOnly )
{
if ( plugin == null )
{
// only show default goal
displayDefaultGoal( pluginName, mavenSession.getGoalDescription( pluginName ), false );
continue;
}
else if ( !pluginName.equals( plugin ) )
{
continue;
}
}
displayDefaultGoal( pluginName, mavenSession.getGoalDescription( pluginName ), true );
List l = ( List ) map.get( pluginName );
Collections.sort( l );
for ( Iterator j = l.iterator(); j.hasNext(); )
{
String goalName = ( String ) j.next();
String fullGoalName = pluginName.length() == 0 ? goalName : pluginName + ":" + goalName;
String goalDescription = mavenSession.getGoalDescription( fullGoalName );
if ( goalDescription != null )
{
displayGoal( goalName, goalDescription );
}
else
{
undocumentedGoals.add( fullGoalName );
}
}
}
if ( undocumentedGoals.isEmpty() == false )
{
displayGoalsWithoutDescriptions( undocumentedGoals );
}
log.info( "" );
}
private void displayGoal( String goalName, String goalDescription )
{
if ( "".equals( goalName ) )
{
goalName = "( NO GOAL )";
}
String msgPrefix = format( " " + goalName + " ", WRAP_INDENT, '.' ) + " ";
log.info( msgPrefix + wrapConsoleMessage( goalDescription, WRAP_INDENT + 1, CONSOLE_WIDTH ) );
}
private void displayDefaultGoal( String goalName, String goalDescription, boolean newLine )
{
if ( "".equals( goalName ) )
{
return;
}
String msgPrefix = format( "[" + goalName + "]", WRAP_INDENT, ' ' ) + " ";
if ( goalDescription == null )
{
goalDescription = "( NO DEFAULT GOAL )";
}
if ( newLine )
{
msgPrefix = LS + msgPrefix;
}
log.info( msgPrefix + wrapConsoleMessage( goalDescription, WRAP_INDENT + 1, CONSOLE_WIDTH ) );
}
/** Display goals without descriptions.
*
* @param list List of undocument goal names.
*/
private void displayGoalsWithoutDescriptions( List list )
{
log.info( "" );
log.info( MavenUtils.getMessage( "displayGoalsWithoutDescriptions.info" ) );
log.info( "" );
for ( Iterator i = list.iterator(); i.hasNext();)
{
String goalName = ( String ) i.next();
log.info( " " + goalName );
}
}
/** Produce a formatted/padded string.
*
* @param orig The string to format.
* @param width The width of the resulting formatted string.
* @param pad The trailing pad character.
*
* @return The formatted string, or the original string
* if the length is already >= width.
*/
protected String format( String orig,
int width,
char pad )
{
if ( orig.length() >= width )
{
return orig;
}
StringBuffer buf = new StringBuffer().append( orig );
int diff = width - orig.length();
for ( int i = 0; i < diff; ++i )
{
buf.append( pad );
}
return buf.toString();
}
/**
* Nicely wraps a message for console output, using a word BreakIterator
* instance to determine wrapping breaks.
*
* @param msg the string message for the console
* @param wrapIndent the number of characters to indent all lines
* after the first one
* @param lineWidth the console width that determines where to wrap
* @return the message wrapped for the console
*/
protected String wrapConsoleMessage( String msg,
int wrapIndent,
int lineWidth )
{
if ( msg.indexOf( '\n' ) < 0 && msg.indexOf( '\r' ) < 0 )
{
return wrapConsoleLine( msg, wrapIndent, lineWidth );
}
StringBuffer buf = new StringBuffer();
StringTokenizer tok = new StringTokenizer( msg, "\n\r" );
while ( tok.hasMoreTokens() )
{
String token = tok.nextToken().trim();
if ( token.length() > 0 )
{
buf.append( wrapConsoleLine( token, wrapIndent, lineWidth ) );
buf.append( LS );
}
}
return buf.toString();
}
private String wrapConsoleLine( String msg, int wrapIndent, int lineWidth )
{
int offset = lineWidth - wrapIndent;
if ( msg.length() <= offset )
{
return msg;
}
BreakIterator bIter = BreakIterator.getWordInstance();
StringBuffer buf = new StringBuffer();
String pad = " ";
int currentPos = 0;
bIter.setText( msg );
while ( offset < bIter.getText().getEndIndex() )
{
if ( Character.isWhitespace( bIter.getText().first() ) )
{
// remove leading whitespace and continue
msg = msg.substring( 1 );
bIter.setText( msg );
continue;
}
// get the last boundary before the specified offset
currentPos = bIter.preceding( offset );
// append from the start to currentPos
buf.append( msg.substring( 0, currentPos ) );
// start next line
buf.append( LS );
//pad with spaces to create indent
for ( int i = 0; i != wrapIndent && i < lineWidth; i++ )
{
buf.append( pad );
}
// set the text of the break iterator to be the rest
// of the string not already appended
msg = msg.substring( currentPos );
//reset the text for another go
bIter.setText( msg );
}
// remove leading whitespace and continue
while ( Character.isWhitespace( msg.charAt( 0 ) ) )
{
msg = msg.substring( 1 );
}
buf.append( msg );
return buf.toString();
}
/**
* Format a time string.
*
* @param ms Duration in ms.
* @return String The formatted time string.
*/
protected static String formatTime( long ms )
{
long secs = ms / 1000;
long min = secs / 60;
secs = secs % 60;
if ( min > 0 )
{
return min + MavenUtils.getMessage( "formatTime.minutes" ) + secs + MavenUtils.getMessage( "formatTime.seconds" );
}
else
{
return secs + MavenUtils.getMessage( "formatTime.seconds" );
}
}
/**
* Main CLI entry point for MavenSession.
*
* @param args CLI arguments.
*/
public static void main( String[] args )
{
Date start = new Date();
App app = new App();
app.doMain( args, start );
}
/**
* To allow subclasses stop the app from exiting
* @param status the value to exit with
*/
protected void exit( int status )
{
System.exit( status );
}
}