gatk-3.8/public/java/src/org/broadinstitute/sting/commandline/ArgumentMatch.java

295 lines
11 KiB
Java
Executable File

/*
* Copyright (c) 2010 The Broad Institute
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package org.broadinstitute.sting.commandline;
import org.broadinstitute.sting.gatk.walkers.Multiplexer;
import java.util.*;
/**
* A mapping of all the sites where an argument definition maps to a site on the command line.
*/
public class ArgumentMatch implements Iterable<ArgumentMatch> {
/**
* The argument definition that's been matched.
*/
public final ArgumentDefinition definition;
/**
* The text that's been matched, as it appears in the command line arguments.
*/
public final String label;
/**
* Maps indices of command line arguments to values paired with that argument.
*/
public final SortedMap<ArgumentMatchSite,List<String>> sites = new TreeMap<ArgumentMatchSite,List<String>>();
/**
* An ordered, freeform collection of tags.
*/
public final Tags tags;
/**
* Create a new argument match, defining its properties later. Used to create invalid arguments.
*/
public ArgumentMatch() {
this(null,null);
}
/**
* Minimal constructor for transform function.
* @param label Label of the argument match. Must not be null.
* @param definition The associated definition, if one exists. May be null.
*/
private ArgumentMatch(final String label, final ArgumentDefinition definition) {
this.label = label;
this.definition = definition;
this.tags = new Tags();
}
/**
* A simple way of indicating that an argument with the given label and definition exists at this site.
* @param label Label of the argument match. Must not be null.
* @param definition The associated definition, if one exists. May be null.
* @param site Position of the argument. Must not be null.
* @param tags ordered freeform text tags associated with this argument.
*/
public ArgumentMatch(final String label, final ArgumentDefinition definition, final ArgumentMatchSite site, final Tags tags) {
this( label, definition, site, null, tags );
}
/**
* A simple way of indicating that an argument with the given label and definition exists at this site.
* @param label Label of the argument match. Must not be null.
* @param definition The associated definition, if one exists. May be null.
* @param site Position of the argument. Must not be null.
* @param value Value for the argument at this position.
* @param tags ordered freeform text tags associated with this argument.
*/
private ArgumentMatch(final String label, final ArgumentDefinition definition, final ArgumentMatchSite site, final String value, final Tags tags) {
this.label = label;
this.definition = definition;
ArrayList<String> values = new ArrayList<String>();
if( value != null )
values.add(value);
sites.put(site,values );
this.tags = tags;
}
/**
* Check to see whether two ArgumentMatch objects are equal.
* @param other Object to which this should be compared.
* @return True if objects are equal, false if objects are not equal or incomparable.
*/
@Override
public boolean equals(Object other) {
// this clearly isn't null, since this.equals() when this == null would result in an NPE.
if(other == null)
return false;
if(!(other instanceof ArgumentMatch))
return false;
ArgumentMatch otherArgumentMatch = (ArgumentMatch)other;
return this.definition.equals(otherArgumentMatch.definition) &&
this.label.equals(otherArgumentMatch.label) &&
this.sites.equals(otherArgumentMatch.sites) &&
this.tags.equals(otherArgumentMatch.tags);
}
/**
* Reformat the given entries with the given multiplexer and key.
* TODO: Generify this.
* @param multiplexer Multiplexer that controls the transformation process.
* @param key Key which specifies the transform.
* @return A variant of this ArgumentMatch with all keys transformed.
*/
@SuppressWarnings("unchecked")
ArgumentMatch transform(Multiplexer multiplexer, Object key) {
SortedMap<ArgumentMatchSite,List<String>> newIndices = new TreeMap<ArgumentMatchSite,List<String>>();
for(Map.Entry<ArgumentMatchSite,List<String>> site: sites.entrySet()) {
List<String> newEntries = new ArrayList<String>();
for(String entry: site.getValue())
newEntries.add(multiplexer.transformArgument(key,entry));
newIndices.put(site.getKey(),newEntries);
}
ArgumentMatch newArgumentMatch = new ArgumentMatch(label,definition);
newArgumentMatch.sites.putAll(newIndices);
return newArgumentMatch;
}
/**
* Return a string representation of the given argument match, for debugging purposes.
* @return String representation of the match.
*/
public String toString() {
return label;
}
/**
* Creates an iterator that walks over each individual match at each position of a given argument.
* @return An iterator over the individual matches in this argument. Will not be null.
*/
public Iterator<ArgumentMatch> iterator() {
return new Iterator<ArgumentMatch>() {
/**
* Iterate over each the available site.
*/
private Iterator<ArgumentMatchSite> siteIterator = null;
/**
* Iterate over each available token.
*/
private Iterator<String> tokenIterator = null;
/**
* The next site to return. Null if none remain.
*/
ArgumentMatchSite nextSite = null;
/**
* The next token to return. Null if none remain.
*/
String nextToken = null;
{
siteIterator = sites.keySet().iterator();
prepareNext();
}
/**
* Is there a nextToken available to return?
* @return True if there's another token waiting in the wings. False otherwise.
*/
public boolean hasNext() {
return nextToken != null;
}
/**
* Get the next token, if one exists. If not, throw an IllegalStateException.
* @return The next ArgumentMatch in the series. Should never be null.
*/
public ArgumentMatch next() {
if( nextSite == null || nextToken == null )
throw new IllegalStateException( "No more ArgumentMatches are available" );
ArgumentMatch match = new ArgumentMatch( label, definition, nextSite, nextToken, tags );
prepareNext();
return match;
}
/**
* Initialize the next ArgumentMatch to return. If no ArgumentMatches are available,
* initialize nextSite / nextToken to null.
*/
private void prepareNext() {
if( tokenIterator != null && tokenIterator.hasNext() ) {
nextToken = tokenIterator.next();
}
else {
nextSite = null;
nextToken = null;
// Do a nested loop. While more data is present in the inner loop, grab that data.
// Otherwise, troll the outer iterator looking for more data.
while( siteIterator.hasNext() ) {
nextSite = siteIterator.next();
if( sites.get(nextSite) != null ) {
tokenIterator = sites.get(nextSite).iterator();
if( tokenIterator.hasNext() ) {
nextToken = tokenIterator.next();
break;
}
}
}
}
}
/**
* Remove is unsupported in this context.
*/
public void remove() {
throw new UnsupportedOperationException("Cannot remove an argument match from the collection while iterating.");
}
};
}
/**
* Merge two ArgumentMatches, so that the values for all arguments go into the
* same data structure.
* @param other The other match to merge into.
*/
public void mergeInto( ArgumentMatch other ) {
sites.putAll(other.sites);
}
/**
* Associate a value with this merge maapping.
* @param site site of the command-line argument to which this value is mated.
* @param value Text representation of value to add.
*/
public void addValue( ArgumentMatchSite site, String value ) {
if( !sites.containsKey(site) || sites.get(site) == null )
sites.put(site, new ArrayList<String>() );
sites.get(site).add(value);
}
/**
* Does this argument already have a value at the given site?
* Arguments are only allowed to be single-valued per site, and
* flags aren't allowed a value at all.
* @param site Site at which to check for values.
* @return True if the argument has a value at the given site. False otherwise.
*/
public boolean hasValueAtSite( ArgumentMatchSite site ) {
return (sites.get(site) != null && sites.get(site).size() >= 1) || isArgumentFlag();
}
/**
* Return the values associated with this argument match.
* @return A collection of the string representation of these value.
*/
public List<String> values() {
List<String> values = new ArrayList<String>();
for( ArgumentMatchSite site: sites.keySet() ) {
if( sites.get(site) != null )
values.addAll(sites.get(site));
}
return values;
}
/**
* Convenience method returning true if the definition is a flag.
* @return True if definition is known to be a flag; false if not known to be a flag.
*/
private boolean isArgumentFlag() {
return definition != null && definition.isFlag;
}
}