/**
* Copyright (c) 2007-2011, JAGaToo Project Group all rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the 'Xith3D Project Group' nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) A
* RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE
*/
package org.jagatoo.commandline;
import java.util.ArrayList;
import org.jagatoo.util.strings.SimpleStringTokenizer;
/**
* <p>
* Parses standard command lines.
* </p>
* <p>
* These commandlines ca have the form:
* -a -xf 0 --long-option "option value" -long-option2 value2
* </p>
*
* @author Marvin Froehlich (aka Qudus)
*/
public class CommandlineParser
{
private final ArgumentsRegistry argReg;
private final ArgumentsHandler handler;
private void onError( int chunk, String message ) throws CommandlineParsingException
{
handler.onError( chunk, message );
}
private Argument onArgument( int chunk, String argName ) throws CommandlineParsingException
{
Argument arg = argReg.getArgument( argName );
if ( arg == null )
throw new CommandlineParsingException( chunk, "There is no argument \"" + argName + "\"." );
if ( arg.needsValue() )
{
return ( arg );
}
onArgumentComplete( arg, null );
return ( null );
}
private void onArgumentComplete( Argument arg, String rawValue ) throws CommandlineParsingException
{
if ( rawValue == null )
handler._handleArgument( arg, null );
else if ( arg == null )
handler._handleArgument( null, rawValue );
else
handler._handleArgument( arg, arg.parseValue( rawValue ) );
}
private Argument parseSingleCharArguments( int chunkNum, String chunk ) throws CommandlineParsingException
{
Argument lastArg = null;
for ( int i = 1; i < chunk.length(); i++ )
{
char ch = chunk.charAt( i );
lastArg = argReg.getArgument( ch );
if ( lastArg == null )
throw new CommandlineParsingException( chunkNum, "There is no argument '" + ch + "'." );
if ( !lastArg.needsValue() )
{
onArgumentComplete( lastArg, null );
lastArg = null;
}
else if ( i < chunk.length() - 1 )
{
onError( chunkNum, "No value provided for argument " + lastArg );
}
}
return ( lastArg );
}
/**
* Parses a command line from an array of chunks.
*
* @param chunks the chunks as passed to a main() method (a String split by white spaces, quoted Strings kept together omitting quotes)
*
* @throws CommandlineParsingException
*/
public void parseCommandline( String[] chunks ) throws CommandlineParsingException
{
int minusCount = 0;
char lastChar = '\0';
int firstNameChar = 0;
Argument lastValueArg = null;
for ( int j = 0; j < chunks.length; j++ )
{
String chunk = chunks[j];
if ( lastValueArg != null )
{
onArgumentComplete( lastValueArg, chunk );
lastValueArg = null;
continue;
}
minusCount = 0;
lastChar = '\0';
firstNameChar = 0;
for ( int i = 0; i < chunk.length(); i++ )
{
char ch = chunk.charAt( i );
if ( ch == '-' )
{
if ( ( lastChar == '\0' ) || ( lastChar == '-' ) )
{
minusCount++;
firstNameChar++;
}
}
else if ( minusCount == 0 )
{
// loose value detected
onArgumentComplete( null, chunk );
break;
}
else if ( minusCount == 1 )
{
lastValueArg = parseSingleCharArguments( j, chunk );
break;
}
else if ( minusCount == 2 )
{
lastValueArg = onArgument( j, chunk.substring( 2 ) );
break;
}
else
{
onError( j, "invalid chunk " + j );
}
lastChar = ch;
}
if ( firstNameChar == chunk.length() )
{
onError( j, "invalid chunk " + j );
}
}
if ( lastValueArg != null )
{
onError( chunks.length, "No value provided for argument " + lastValueArg );
}
handler._validate();
}
/**
* Parses a command line from a single String.
*
* @param commandline the complete commandline as one String
*
* @throws CommandlineParsingException
*/
public void parseCommandline( String commandline ) throws CommandlineParsingException
{
ArrayList<String> argsList = new ArrayList<String>();
SimpleStringTokenizer tokenizer = new SimpleStringTokenizer( commandline );
tokenizer.useQuotes( true );
while ( tokenizer.hasMoreTokens() )
{
argsList.add( tokenizer.nextToken() );
}
parseCommandline( argsList.toArray( new String[ argsList.size() ] ) );
}
/**
* Creates a new {@link CommandlineParser}.
*
* @param argReg
* @param handler
*/
public CommandlineParser( ArgumentsRegistry argReg, ArgumentsHandler handler )
{
if ( argReg == null )
throw new IllegalArgumentException( "argReg must not be null." );
if ( handler == null )
throw new IllegalArgumentException( "handler must not be null." );
this.argReg = argReg;
this.handler = handler;
}
}