Cleanup parsing method to make it less generic.
git-svn-id: file:///humgen/gsa-scr1/gsa-engineering/svn_contents/trunk@619 348d0f76-0448-11de-a6fe-93d51630548a
This commit is contained in:
parent
d725c6cf1c
commit
4f85062004
|
|
@ -142,34 +142,6 @@ class ArgumentDefinitions implements Iterable<ArgumentDefinition> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static AliasProvider ShortNameAliasProvider = new AliasProvider() {
|
|
||||||
/**
|
|
||||||
* Short names can come in the form -Ofoo.txt, -O foo.txt, or -out (multi-character short name).
|
|
||||||
* Given the argument name and built-in provided, see if these can be formed into some other argument
|
|
||||||
* name.
|
|
||||||
* @param argument Name of the argument, as parsed. For a short name, will be a single letter.
|
|
||||||
* @param value Value of the argument, as parsed.
|
|
||||||
* @return Any potential aliases for the given shortname.
|
|
||||||
*/
|
|
||||||
public List<String> getAliases( String argument, String value ) {
|
|
||||||
List<String> aliases = new ArrayList<String>();
|
|
||||||
aliases.add(argument+value);
|
|
||||||
aliases.add(argument);
|
|
||||||
return aliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is the value part of the given alias, or something separate that should be treated as an argument value.
|
|
||||||
* @param alias The alias to use.
|
|
||||||
* @param argument The parsed argument.
|
|
||||||
* @param value The parsed value.
|
|
||||||
* @return True if this alias should be used instead of the given value.
|
|
||||||
*/
|
|
||||||
public boolean doesAliasConsumeValue( String alias, String argument, String value ) {
|
|
||||||
return alias.equals(argument + value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find all required definitions.
|
* Find all required definitions.
|
||||||
*/
|
*/
|
||||||
|
|
@ -289,24 +261,3 @@ interface DefinitionMatcher {
|
||||||
*/
|
*/
|
||||||
boolean matches( ArgumentDefinition definition, Object key );
|
boolean matches( ArgumentDefinition definition, Object key );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A way to get alternate names for the argument given the recognized name and value.
|
|
||||||
*/
|
|
||||||
interface AliasProvider {
|
|
||||||
/**
|
|
||||||
* Give all alternate names for the given argument / value pair. The aliases should
|
|
||||||
* be returned in 'preferred order'.
|
|
||||||
* @param argument The argument.
|
|
||||||
* @param value The value.
|
|
||||||
* @return All possible names.
|
|
||||||
*/
|
|
||||||
List<String> getAliases( String argument, String value );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* True if this alias 'consumes' the value, meaning that the argument + value together
|
|
||||||
* represent some other alias.
|
|
||||||
* @return True if the value should still be used. False otherwise.
|
|
||||||
*/
|
|
||||||
boolean doesAliasConsumeValue( String alias, String argument, String value );
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,6 @@ import java.lang.reflect.Modifier;
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
@ -60,10 +58,8 @@ public class ParsingEngine {
|
||||||
protected static Logger logger = Logger.getLogger(ParsingEngine.class);
|
protected static Logger logger = Logger.getLogger(ParsingEngine.class);
|
||||||
|
|
||||||
public ParsingEngine() {
|
public ParsingEngine() {
|
||||||
parsingMethods.add( new ParsingMethod(Pattern.compile("\\s*--([\\w\\.]+)\\s*"), ArgumentDefinitions.FullNameDefinitionMatcher) );
|
parsingMethods.add( new FullNameParsingMethod() );
|
||||||
parsingMethods.add( new ParsingMethod(Pattern.compile("\\s*-([\\w\\.])([\\w\\.\\/:\\-]*)\\s*"),
|
parsingMethods.add( new ShortNameParsingMethod() );
|
||||||
ArgumentDefinitions.ShortNameDefinitionMatcher,
|
|
||||||
ArgumentDefinitions.ShortNameAliasProvider) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -242,7 +238,7 @@ public class ParsingEngine {
|
||||||
*/
|
*/
|
||||||
private boolean isArgumentForm( String token ) {
|
private boolean isArgumentForm( String token ) {
|
||||||
for( ParsingMethod parsingMethod: parsingMethods ) {
|
for( ParsingMethod parsingMethod: parsingMethods ) {
|
||||||
if( parsingMethod.pattern.matcher(token).matches() )
|
if( parsingMethod.matches(token) )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -259,7 +255,7 @@ public class ParsingEngine {
|
||||||
throw new IllegalArgumentException( "Token is not recognizable as an argument: " + token );
|
throw new IllegalArgumentException( "Token is not recognizable as an argument: " + token );
|
||||||
|
|
||||||
for( ParsingMethod parsingMethod: parsingMethods ) {
|
for( ParsingMethod parsingMethod: parsingMethods ) {
|
||||||
if( parsingMethod.matches( argumentDefinitions, token ) )
|
if( parsingMethod.matches( token ) )
|
||||||
return parsingMethod.match( argumentDefinitions, token, position );
|
return parsingMethod.match( argumentDefinitions, token, position );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -413,79 +409,6 @@ public class ParsingEngine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds a pattern, along with how to get to the argument definitions that could match that pattern.
|
|
||||||
*/
|
|
||||||
private class ParsingMethod {
|
|
||||||
public final Pattern pattern;
|
|
||||||
public final DefinitionMatcher definitionMatcher;
|
|
||||||
public final AliasProvider aliasProvider;
|
|
||||||
|
|
||||||
public ParsingMethod( Pattern pattern, DefinitionMatcher definitionMatcher ) {
|
|
||||||
this( pattern, definitionMatcher, null );
|
|
||||||
}
|
|
||||||
|
|
||||||
public ParsingMethod( Pattern pattern, DefinitionMatcher definitionMatcher, AliasProvider aliasProvider ) {
|
|
||||||
this.pattern = pattern;
|
|
||||||
this.definitionMatcher = definitionMatcher;
|
|
||||||
this.aliasProvider = aliasProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean matches( ArgumentDefinitions definitions, String token ) {
|
|
||||||
Matcher matcher = pattern.matcher(token);
|
|
||||||
return matcher.matches();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArgumentMatch match( ArgumentDefinitions definitions, String token, int position ) {
|
|
||||||
Matcher matcher = pattern.matcher(token);
|
|
||||||
|
|
||||||
// Didn't match? Must be bad input.
|
|
||||||
if( !matcher.matches() )
|
|
||||||
throw new IllegalArgumentException( String.format("Unable to parse token %s with pattern %s", token, pattern.pattern()) );
|
|
||||||
|
|
||||||
// If the argument is valid, parse out the argument and value (if present).
|
|
||||||
String argument = matcher.group(1);
|
|
||||||
String value = null;
|
|
||||||
if( matcher.groupCount() > 1 && matcher.group(2).trim().length() > 0)
|
|
||||||
value = matcher.group(2).trim();
|
|
||||||
|
|
||||||
// If an alias provider has been provided, determine the possible list of argument names that this
|
|
||||||
// argument / value pair can represent.
|
|
||||||
ArgumentDefinition bestMatchArgumentDefinition = null;
|
|
||||||
if( aliasProvider != null ) {
|
|
||||||
List<String> aliases = aliasProvider.getAliases( argument, value );
|
|
||||||
String bestAlias = null;
|
|
||||||
|
|
||||||
for( String alias: aliases ) {
|
|
||||||
if( definitions.findArgumentDefinition(alias,definitionMatcher) != null ) {
|
|
||||||
bestAlias = alias;
|
|
||||||
bestMatchArgumentDefinition = definitions.findArgumentDefinition(alias,definitionMatcher);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Couldn't find anything appropriate? The aliases should be in best-to-worst order, so
|
|
||||||
if( bestAlias == null ) {
|
|
||||||
bestAlias = aliases.get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if( aliasProvider.doesAliasConsumeValue(bestAlias,argument,value) ) value = null;
|
|
||||||
argument = bestAlias;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
bestMatchArgumentDefinition = definitions.findArgumentDefinition( argument, definitionMatcher );
|
|
||||||
|
|
||||||
// Try to find a matching argument. If found, label that as the match. If not found, add the argument
|
|
||||||
// with a null definition.
|
|
||||||
ArgumentMatch argumentMatch = new ArgumentMatch( argument, bestMatchArgumentDefinition, position );
|
|
||||||
if( value != null )
|
|
||||||
argumentMatch.addValue( position, value );
|
|
||||||
return argumentMatch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
package org.broadinstitute.sting.utils.cmdLine;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds a pattern, along with how to get to the argument definitions that could match that pattern.
|
||||||
|
*/
|
||||||
|
interface ParsingMethod {
|
||||||
|
/**
|
||||||
|
* Can the given token be parsed by this parsing method?
|
||||||
|
* @param token Token to validate.
|
||||||
|
* @return True if the given token matches.
|
||||||
|
*/
|
||||||
|
public abstract boolean matches( String token );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the best match for a given token at a given position from among the provided
|
||||||
|
* argument definitions.
|
||||||
|
* @param definitions List of argument definitions.
|
||||||
|
* @param token The token from the command line to match. Should be validated using
|
||||||
|
* ParsingMethod's matches() tester.
|
||||||
|
* @param position Position at which this command-line argument occurs. Will be used
|
||||||
|
* for validation later.
|
||||||
|
* @return An argument match. Definition field will be populated if a match was found or
|
||||||
|
* empty if no appropriate definition could be found.
|
||||||
|
*/
|
||||||
|
public abstract ArgumentMatch match( ArgumentDefinitions definitions, String token, int position );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instructions for how to parse a command-line argument passed by full name into a match.
|
||||||
|
*/
|
||||||
|
class FullNameParsingMethod implements ParsingMethod {
|
||||||
|
private static final Pattern pattern = Pattern.compile("\\s*--([\\w\\.\\-]+)\\s*");
|
||||||
|
private static final DefinitionMatcher definitionMatcher = ArgumentDefinitions.FullNameDefinitionMatcher;
|
||||||
|
|
||||||
|
public boolean matches( String token ) {
|
||||||
|
Matcher matcher = pattern.matcher(token);
|
||||||
|
return matcher.matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArgumentMatch match( ArgumentDefinitions definitions, String token, int position ) {
|
||||||
|
// If the argument is valid, parse out the argument.
|
||||||
|
Matcher matcher = pattern.matcher(token);
|
||||||
|
|
||||||
|
// Didn't match? Must be bad input.
|
||||||
|
if( !matcher.matches() )
|
||||||
|
throw new IllegalArgumentException( String.format("Unable to parse token %s with pattern %s", token, pattern.pattern()) );
|
||||||
|
|
||||||
|
String argument = matcher.group(1).trim();
|
||||||
|
|
||||||
|
// Find the most appropriate argument definition for the given argument.
|
||||||
|
ArgumentDefinition argumentDefinition = definitions.findArgumentDefinition( argument, definitionMatcher );
|
||||||
|
|
||||||
|
// Try to find a matching argument. If found, label that as the match. If not found, add the argument
|
||||||
|
// with a null definition.
|
||||||
|
ArgumentMatch argumentMatch = new ArgumentMatch( argument, argumentDefinition, position );
|
||||||
|
|
||||||
|
return argumentMatch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instructions for how to parse a command-line argument passed by short name into a match.
|
||||||
|
*/
|
||||||
|
class ShortNameParsingMethod implements ParsingMethod {
|
||||||
|
private static final Pattern standalonePattern = Pattern.compile("\\s*-([\\w\\-]+)\\s*");
|
||||||
|
private static final Pattern embeddedValuePattern = Pattern.compile("\\s*-([\\w\\.])([\\w/:\\.\\-]+)\\s*");
|
||||||
|
private static final DefinitionMatcher definitionMatcher = ArgumentDefinitions.ShortNameDefinitionMatcher;
|
||||||
|
|
||||||
|
public boolean matches( String token ) {
|
||||||
|
return standalonePattern.matcher(token).matches() || embeddedValuePattern.matcher(token).matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArgumentMatch match( ArgumentDefinitions definitions, String token, int position ) {
|
||||||
|
// Didn't match? Must be bad input.
|
||||||
|
if( !matches(token) )
|
||||||
|
throw new IllegalArgumentException( String.format("Unable to parse token %s with pattern %s", token, embeddedValuePattern.pattern()) );
|
||||||
|
|
||||||
|
// Build the best possible standalone match given the available data.
|
||||||
|
Matcher standaloneMatcher = standalonePattern.matcher(token);
|
||||||
|
ArgumentMatch standaloneMatch = null;
|
||||||
|
|
||||||
|
if( standaloneMatcher.matches() ) {
|
||||||
|
String argument = standaloneMatcher.group(1).trim();
|
||||||
|
|
||||||
|
ArgumentDefinition argumentDefinition = definitions.findArgumentDefinition(argument,definitionMatcher);
|
||||||
|
standaloneMatch = new ArgumentMatch( argument, argumentDefinition, position );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the best possible embedded value match given the available data.
|
||||||
|
Matcher embeddedValueMatcher = embeddedValuePattern.matcher(token);
|
||||||
|
ArgumentMatch embeddedValueMatch = null;
|
||||||
|
|
||||||
|
if( embeddedValueMatcher.matches() ) {
|
||||||
|
String argument = embeddedValueMatcher.group(1).trim();
|
||||||
|
String value = embeddedValueMatcher.group(2).trim();
|
||||||
|
|
||||||
|
ArgumentDefinition argumentDefinition = definitions.findArgumentDefinition(argument,definitionMatcher);
|
||||||
|
embeddedValueMatch = new ArgumentMatch( argument, argumentDefinition, position );
|
||||||
|
|
||||||
|
if( embeddedValueMatch != null && value != null )
|
||||||
|
embeddedValueMatch.addValue( position, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefer the standalone match...
|
||||||
|
ArgumentMatch bestMatch = standaloneMatch;
|
||||||
|
|
||||||
|
// ...But if the embedded value match is clearly better, choose it as the best match instead.
|
||||||
|
if( (standaloneMatch == null || standaloneMatch.definition == null) &&
|
||||||
|
(embeddedValueMatch != null && embeddedValueMatch.definition != null) )
|
||||||
|
bestMatch = embeddedValueMatch;
|
||||||
|
|
||||||
|
return bestMatch;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue