package org.neo4j.util.shell;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
/**
* Completely server-side
*/
public class AppCommandParser
{
private AppShellServer ui;
private String line;
private String appName;
private App app;
private Map<String, String> options = new HashMap<String, String>();
private List<String> arguments = new ArrayList<String>();
public AppCommandParser( AppShellServer ui, String line )
throws ShellException
{
this.ui = ui;
if ( line != null )
{
line = line.trim();
}
this.line = line;
this.parse();
}
private void parse() throws ShellException
{
if ( this.line == null || this.line.trim().length() == 0 )
{
return;
}
this.parseApp();
this.parseParameters();
}
private void parseApp() throws ShellException
{
int index = findNextWhiteSpace( this.line, 0 );
this.appName = index == -1 ?
this.line : this.line.substring( 0, index );
try
{
this.app = this.ui.findApp( this.appName );
}
catch ( Exception e )
{
throw new ShellException( e );
}
if ( this.app == null )
{
throw new ShellException(
"Unknown command '" + this.appName + "'" );
}
}
private void parseParameters() throws ShellException
{
String rest = this.line.substring( this.appName.length() ).trim();
String[] parsed = tokenizeStringWithQuotes( rest, false );
for ( int i = 0; i < parsed.length; i++ )
{
String string = parsed[ i ];
if ( string.startsWith( "--" ) )
{
// It is one long name
String name = string.substring( 2 );
i = this.fetchArguments( parsed, i, name );
}
else if ( this.isOption( string ) )
{
String options = string.substring( 1 );
for ( int o = 0; o < options.length(); o++ )
{
String name = String.valueOf( options.charAt( o ) );
i = this.fetchArguments( parsed, i, name );
}
}
else
{
this.arguments.add( string );
}
}
}
private boolean isOption( String string )
{
return string.startsWith( "-" );
}
private int fetchArguments( String[] parsed, int whereAreWe,
String optionName ) throws ShellException
{
String value = null;
OptionValueType type = this.app.getOptionValueType( optionName );
if ( type == OptionValueType.MUST )
{
whereAreWe++;
String message = "Value required for '" + optionName + "'";
this.assertHasIndex( parsed, whereAreWe, message );
value = parsed[ whereAreWe ];
if ( this.isOption( value ) )
{
throw new ShellException( message );
}
}
else if ( type == OptionValueType.MAY )
{
if ( this.hasIndex( parsed, whereAreWe + 1 ) &&
!this.isOption( parsed[ whereAreWe + 1 ] ) )
{
whereAreWe++;
value = parsed[ whereAreWe ];
}
}
this.options.put( optionName, value );
return whereAreWe;
}
private boolean hasIndex( String[] array, int index )
{
return index >= 0 && index < array.length;
}
private void assertHasIndex( String[] array, int index, String message )
throws ShellException
{
if ( !this.hasIndex( array, index ) )
{
throw new ShellException( message );
}
}
private static int findNextWhiteSpace( String line, int fromIndex )
{
int index = line.indexOf( ' ', fromIndex );
return index == -1 ? line.indexOf( '\t', fromIndex ) : index;
}
public String getAppName()
{
return this.appName;
}
public App app()
{
return this.app;
}
public Map<String, String> options()
{
return this.options;
}
public List<String> arguments()
{
return this.arguments;
}
public String getLine()
{
return this.line;
}
public String getLineWithoutCommand()
{
return this.line.substring( this.appName.length() ).trim();
}
public static String[] tokenizeStringWithQuotes( String string )
{
return tokenizeStringWithQuotes( string, true );
}
public static String[] tokenizeStringWithQuotes( String string,
boolean trim )
{
if ( trim )
{
string = string.trim();
}
ArrayList<String> result = new ArrayList<String>();
string = string.trim();
boolean inside = string.startsWith( "\"" );
StringTokenizer quoteTokenizer = new StringTokenizer( string, "\"" );
while ( quoteTokenizer.hasMoreTokens() )
{
String token = quoteTokenizer.nextToken();
if ( trim )
{
token = token.trim();
}
if ( token.length() == 0 )
{
// Skip it
}
else if ( inside )
{
// Don't split
result.add( token );
}
else
{
// Split
StringTokenizer spaceTokenizer =
new StringTokenizer( token, " " );
while ( spaceTokenizer.hasMoreTokens() )
{
String spaceToken = spaceTokenizer.nextToken();
if ( trim )
{
spaceToken = spaceToken.trim();
}
result.add( spaceToken );
}
}
inside = !inside;
}
return result.toArray( new String[ result.size() ] );
}
}