/*
* BioJava development code
*
* This code may be freely distributed and modified under the
* terms of the GNU Lesser General Public Licence. This should
* be distributed with the code. If you do not have a copy,
* see:
*
* http://www.gnu.org/copyleft/lesser.html
*
* Copyright for this code is held jointly by the individual
* authors. These should be listed in @author doc comments.
*
* For more information on the BioJava project and its aims,
* or to join the biojava-l mailing list, visit the home page
* at:
*
* http://www.biojava.org/
*
*/
package org.biojava.bio.symbol;
import java.util.AbstractList;
import java.util.Collections;
import java.util.Iterator;
import org.biojava.bio.BioError;
/**
* FuzzyPointLocation
represents two types of EMBL-style
* partially-defined locations. These are the '(123.567)' type, which
* represent a single residue somewhere between these coordinates and
* the '<123' or '>123' type, which represent an unbounded location,
* not including the residue at that coordinate.
*
* @author Keith James
* @author Greg Cox
*/
public class FuzzyPointLocation extends AbstractLocation
{
// Use the minimum value
public final static PointResolver RESOLVE_MIN;
// Use the maximum value
public final static PointResolver RESOLVE_MAX;
// Use the arithmetic mean of the two values, unless they are
// unbounded, in which case Integer.MIN_VALUE or Integer.MAX_VALUE
// is returned
public final static PointResolver RESOLVE_AVERAGE;
static
{
RESOLVE_MIN = new MinPointResolver();
RESOLVE_MAX = new MaxPointResolver();
RESOLVE_AVERAGE = new AveragePointResolver();
}
private int min;
private int max;
private PointResolver resolver;
/**
* Creates a new FuzzyPointLocation
object. If the minimum
* value is equal to the maximum value, it is interperted as a swissprot
* point location such as "?24". If the minimum is Integer.MIN_VALUE and
* the maximum is Integer.MAX_VALUE, it is interperted as a swissprot
* location like '?'.
*
* @param min an int
value for the minimum boundary
* of the location, Integer.MIN_VALUE if unbounded.
* @param max an int
value for the minimum boundary
* of the location, Integer.MAX_VALUE if unbounded.
* @param resolver a PointResolver
which defines the
* policy used to calculate the location's min and max
* properties.
*/
public FuzzyPointLocation(int min, int max, PointResolver resolver)
{
this.min = min;
this.max = max;
this.resolver = resolver;
}
public PointResolver getResolver()
{
return resolver;
}
public int getMin()
{
return min;
}
public int getMax()
{
return max;
}
public boolean hasBoundedMin()
{
return min != Integer.MIN_VALUE;
}
public boolean hasBoundedMax()
{
return max != Integer.MAX_VALUE;
}
public boolean overlaps(Location loc)
{
return loc.contains(this);
}
public boolean contains(Location loc)
{
// If the location is unbounded, it is not certain that it
// contains any other specific location
return (hasBoundedMin() && hasBoundedMax()) &&
(resolver.resolve(this) == loc.getMin()) &&
(resolver.resolve(this) == loc.getMax());
}
public boolean contains(int point)
{
// If the location is unbounded, it is not certain that it
// contains any other specific coordinate
return (hasBoundedMin() && hasBoundedMax()) &&
resolver.resolve(this) == point;
}
public boolean equals(Location loc)
{
return this.contains(loc) && loc.contains(this);
}
public int hashCode()
{
return getMin();
}
public Location intersection(Location loc)
{
return loc.contains(this)
? this
: Location.empty;
}
public SymbolList symbols(SymbolList slist)
{
final Symbol sym = slist.symbolAt(resolver.resolve(this));
try
{
return new SimpleSymbolList(slist.getAlphabet(), new AbstractList()
{
public Object get(int index)
throws IndexOutOfBoundsException
{
if (index == 0)
{
return sym;
}
throw new IndexOutOfBoundsException("Index " + index + " greater than 0");
}
public int size()
{
return 1;
}
});
}
catch (IllegalSymbolException ise)
{
throw new BioError(ise);
}
}
public boolean isContiguous()
{
return true;
}
public Iterator blockIterator()
{
return Collections.singleton(this).iterator();
}
public Location translate(int dist)
{
if (dist == 0)
return this;
return new FuzzyPointLocation(this.min + dist,
this.max + dist,
this.resolver);
}
public String toString()
{
if (hasBoundedMin() && hasBoundedMax())
{
if (getMin() == getMax())
{
return("?" + getMin());
}
else
{
return "["
+ Integer.toString(getMin())
+ "."
+ Integer.toString(getMax());
}
}
else if (hasBoundedMin())
{
return "[>"
+ Integer.toString(getMin())
+ "]";
}
else if (hasBoundedMax())
{
return "[<"
+ Integer.toString(getMax())
+ "]";
}
else
{
return "?";
}
}
/**
* Determines how a FuzzyPointLocation
should be treated when used
* as a normal Location
.
*
* Use one of the implementations of this interface when creating a FuzzyPointLocation
* to specify how the fuzzy (inner/outer) properties are translated into the standard
* Location min and max properties.
*
* It is possible to write custom implementations of this to create FuzzyLocations
* with exotic behaviour.
*/
public static interface PointResolver
{
/**
* Return the actual point that the specified location should claim to
* occupy.
*/
public int resolve(FuzzyPointLocation loc);
}
private static class MinPointResolver implements PointResolver
{
public int resolve(FuzzyPointLocation loc)
{
if (loc.hasBoundedMin())
return loc.getMin();
else
return Integer.MIN_VALUE;
}
}
private static class MaxPointResolver implements PointResolver
{
public int resolve(FuzzyPointLocation loc)
{
if (loc.hasBoundedMax())
return loc.getMax();
else
return Integer.MAX_VALUE;
}
}
private static class AveragePointResolver implements PointResolver
{
public int resolve(FuzzyPointLocation loc)
{
// Range of form: (123.567)
if (loc.hasBoundedMin() && loc.hasBoundedMax())
{
return (loc.getMin() + loc.getMax()) / 2;
}
// Swissprot range ?
else if((loc.hasBoundedMin() == false) &&
(loc.hasBoundedMax() == false))
{
return 0;
}
// Range of form: <123 or >123
else
{
return loc.hasBoundedMin() ? Integer.MAX_VALUE : Integer.MIN_VALUE;
}
}
}
}