001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021
022package org.biojava.bio.symbol;
023
024import java.util.AbstractList;
025import java.util.Collections;
026import java.util.Iterator;
027
028import org.biojava.bio.BioError;
029
030/**
031 * <code>FuzzyPointLocation</code> represents two types of EMBL-style
032 * partially-defined locations. These are the '(123.567)' type, which
033 * represent a single residue somewhere between these coordinates and
034 * the '<123' or '>123' type, which represent an unbounded location,
035 * not including the residue at that coordinate.
036 *
037 * @author <a href="mailto:kdj@sanger.ac.uk">Keith James</a>
038 * @author Greg Cox
039 */
040public class FuzzyPointLocation extends AbstractLocation
041{
042    // Use the minimum value
043    public final static PointResolver RESOLVE_MIN;
044
045    // Use the maximum value
046    public final static PointResolver RESOLVE_MAX;
047
048    // Use the arithmetic mean of the two values, unless they are
049    // unbounded, in which case Integer.MIN_VALUE or Integer.MAX_VALUE
050    // is returned
051    public final static PointResolver RESOLVE_AVERAGE;
052
053    static
054    {
055        RESOLVE_MIN     = new MinPointResolver();
056        RESOLVE_MAX     = new MaxPointResolver();
057        RESOLVE_AVERAGE = new AveragePointResolver();
058    }
059
060    private int           min;
061    private int           max;
062    private PointResolver resolver;
063
064    /**
065     * Creates a new <code>FuzzyPointLocation</code> object.  If the minimum
066     * value is equal to the maximum value, it is interperted as a swissprot
067     * point location such as "?24".  If the minimum is Integer.MIN_VALUE and
068     * the maximum is Integer.MAX_VALUE, it is interperted as a swissprot
069     * location like '?'.
070     *
071     * @param min an <code>int</code> value for the minimum boundary
072     * of the location, Integer.MIN_VALUE if unbounded.
073     * @param max an <code>int</code> value for the minimum boundary
074     * of the location, Integer.MAX_VALUE if unbounded.
075     * @param resolver a <code>PointResolver</code> which defines the
076     * policy used to calculate the location's min and max
077     * properties.
078     */
079    public FuzzyPointLocation(int min, int max, PointResolver resolver)
080    {
081        this.min      = min;
082        this.max      = max;
083        this.resolver = resolver;
084    }
085
086    public PointResolver getResolver()
087    {
088        return resolver;
089    }
090
091    public int getMin()
092    {
093        return min;
094    }
095
096    public int getMax()
097    {
098        return max;
099    }
100
101    public boolean hasBoundedMin()
102    {
103        return min != Integer.MIN_VALUE;
104    }
105
106    public boolean hasBoundedMax()
107    {
108        return max != Integer.MAX_VALUE;
109    }
110
111    public boolean overlaps(Location loc)
112    {
113        return loc.contains(this);
114    }
115
116    public boolean contains(Location loc)
117    {
118        // If the location is unbounded, it is not certain that it
119        // contains any other specific location
120        return (hasBoundedMin() && hasBoundedMax()) &&
121            (resolver.resolve(this) == loc.getMin()) &&
122            (resolver.resolve(this) == loc.getMax());
123    }
124
125    public boolean contains(int point)
126    {
127        // If the location is unbounded, it is not certain that it
128        // contains any other specific coordinate
129        return (hasBoundedMin() && hasBoundedMax()) &&
130            resolver.resolve(this) == point;
131    }
132
133    public boolean equals(Location loc)
134    {
135        return this.contains(loc) && loc.contains(this);
136    }
137
138    public int hashCode()
139    {
140        return getMin();
141    }
142
143    public Location intersection(Location loc)
144    {
145        return loc.contains(this)
146            ? this
147            : Location.empty;
148    }
149
150
151    public SymbolList symbols(SymbolList slist)
152    {
153        final Symbol sym = slist.symbolAt(resolver.resolve(this));
154        try
155        {
156            return new SimpleSymbolList(slist.getAlphabet(), new AbstractList()
157                {
158                    public Object get(int index)
159                        throws IndexOutOfBoundsException
160                    {
161                        if (index == 0)
162                        {
163                            return sym;
164                        }
165
166                        throw new IndexOutOfBoundsException("Index " + index + " greater than 0");
167                    }
168
169                    public int size()
170                    {
171                        return 1;
172                    }
173                });
174        }
175        catch (IllegalSymbolException ise)
176        {
177            throw new BioError(ise);
178        }
179    }
180
181    public boolean isContiguous()
182    {
183        return true;
184    }
185
186    public Iterator blockIterator()
187    {
188        return Collections.singleton(this).iterator();
189    }
190
191    public Location translate(int dist)
192    {
193        if (dist == 0)
194            return this;
195
196        return new FuzzyPointLocation(this.min + dist,
197                                      this.max + dist,
198                                      this.resolver);
199    }
200
201    public String toString()
202    {
203        if (hasBoundedMin() && hasBoundedMax())
204        {
205            if (getMin() == getMax())
206            {
207                return("?" + getMin());
208            }
209            else
210            {
211                    return "["
212                        + Integer.toString(getMin())
213                        + "."
214                        + Integer.toString(getMax());
215                }
216        }
217        else if (hasBoundedMin())
218        {
219            return "[>"
220                + Integer.toString(getMin())
221                + "]";
222        }
223        else if (hasBoundedMax())
224        {
225            return "[<"
226                + Integer.toString(getMax())
227                + "]";
228        }
229        else
230        {
231            return "?";
232        }
233    }
234
235     
236    /**
237     * Determines how a <code>FuzzyPointLocation</code> should be treated when used
238     * as a normal <code>Location</code>.
239     *
240     * Use one of the implementations of this interface when creating a <code>FuzzyPointLocation</code>
241     * to specify how the fuzzy (inner/outer) properties are translated into the standard
242     * Location min and max properties.
243     *
244     * It is possible to write custom implementations of this to create <code>FuzzyLocations</code>
245     * with exotic behaviour.
246     */
247    
248    public static interface PointResolver
249    {
250        /**
251         * Return the actual point that the specified location should claim to
252         * occupy.
253         */
254        
255        public int resolve(FuzzyPointLocation loc);
256    }
257
258    private static class MinPointResolver implements PointResolver
259    {
260        public int resolve(FuzzyPointLocation loc)
261        {
262            if (loc.hasBoundedMin())
263                return loc.getMin();
264            else
265                return Integer.MIN_VALUE;
266        }
267    }
268
269    private static class MaxPointResolver implements PointResolver
270    {
271        public int resolve(FuzzyPointLocation loc)
272        {
273            if (loc.hasBoundedMax())
274                return loc.getMax();
275            else
276                return Integer.MAX_VALUE;
277        }
278    }
279
280    private static class AveragePointResolver implements PointResolver
281    {
282        public int resolve(FuzzyPointLocation loc)
283        {
284            // Range of form: (123.567)
285            if (loc.hasBoundedMin() && loc.hasBoundedMax())
286            {
287                return (loc.getMin() + loc.getMax()) / 2;
288            }
289                // Swissprot range ?
290                else if((loc.hasBoundedMin() == false) &&
291                                (loc.hasBoundedMax() == false))
292                {
293                        return 0;
294                }
295            // Range of form: <123 or >123
296            else
297            {
298                return loc.hasBoundedMin() ? Integer.MAX_VALUE : Integer.MIN_VALUE;
299            }
300        }
301    }
302}