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.io.Serializable;
025
026/**
027 * A 'fuzzy' location a-la Embl fuzzy locations.
028 * <p>
029 * Fuzzy locations have propreties that indicate that they may start before min
030 * and end after max. However, this in no way affects how they interact with
031 * other locations.
032 * </p>
033 *
034 * @author Matthew Pocock
035 * @author Thomas Down
036 * @author Greg Cox
037 */
038public class FuzzyLocation
039extends AbstractRangeLocation
040implements Serializable {
041    /**
042     * Always use the `inner' values.
043     */
044
045    public final static RangeResolver RESOLVE_INNER;
046
047    /**
048     * Use the `outer' values, unless they are unbounded in which case the
049     * `inner' values are used.
050     */
051
052    public final static RangeResolver RESOLVE_OUTER;
053
054    /**
055     * Use the arithmetic mean of the `inner' and `outer' values, unless the
056     * outer value is unbounded.
057     */
058
059    public final static RangeResolver RESOLVE_AVERAGE;
060
061    static {
062        RESOLVE_INNER = new InnerRangeResolver();
063        RESOLVE_OUTER = new OuterRangeResolver();
064        RESOLVE_AVERAGE = new AverageRangeResolver();
065    }
066
067    private int outerMin;
068    private int innerMin;
069    private int innerMax;
070    private int outerMax;
071    private boolean mIsMinFuzzy;
072    private boolean mIsMaxFuzzy;
073    private RangeResolver resolver;
074
075  /**
076   * Create a new FuzzyLocation with endpoints (outerMin.innerMin) and (innerMax.outerMax).
077   *
078   * @param outerMin the lower bound on the location's min value.
079   *                             Integer.MIN_VALUE indicates unbounded.
080   * @param outerMax the upper bound on the location's max value.
081   *                             Integer.MAX_VALUE indicates unbounded.
082   * @param innerMin the upper bound on the location's min value.
083   * @param innerMax the lower bound on the location's max value.
084   * @param resolver a RangeResolver object which defines the policy used to calculate
085   *                 the location's min and max properties.
086   */
087
088  public FuzzyLocation(
089    int outerMin, int outerMax,
090    int innerMin, int innerMax,
091    RangeResolver resolver
092  ) {
093        boolean isMinFuzzy = false;
094        boolean isMaxFuzzy = false;
095        if (outerMin != innerMin)
096        {
097            isMinFuzzy = true;
098        }
099        if (outerMax != innerMax)
100        {
101            isMaxFuzzy = true;
102        }
103        this.initializeVariables(outerMin, outerMax, innerMin, innerMax, isMinFuzzy, isMaxFuzzy, resolver);
104  }
105
106    /**
107     * Create a new FuzzyLocation with endpoints (outerMin.innerMin) and
108     * (innerMax.outerMax).  This constructor allows you to explicitly mark an
109     * endpoint as fuzzy, even if there is no other information about it.  For
110     * example, a valid swissprot location "?5 10" would be a fuzzy location 5
111     * to 10 where the min is fuzzy and the max is not.
112     * <p>
113     * Note that it is not logical to specify inner and outer values that
114     * clearly denote fuzzy boundaries and the set the <code>isMinFuzzy</code> or
115     * <code>isMaxFuzzy</code> value to false. This object makes
116     * no specific check of your logic so be careful.
117     *
118     * @param outerMin the lower bound on the location's min value.
119     *                           Integer.MIN_VALUE indicates unbounded.
120     * @param outerMax the upper bound on the location's max value.
121     *                           Integer.MAX_VALUE indicates unbounded.
122     * @param innerMin the upper bound on the location's min value.
123     * @param innerMax the lower bound on the location's max value.
124     * @param isMinFuzzy Explictly state if the minimum is fuzzy
125     * @param isMaxFuzzy Explictly state if the maximum is fuzzy
126     * @param resolver a RangeResolver object which defines the policy used to
127     *                             calculate the location's min and max properties.
128     */
129
130    public FuzzyLocation(int outerMin, int outerMax,
131                         int innerMin, int innerMax,
132                         boolean isMinFuzzy, boolean isMaxFuzzy,
133                         RangeResolver resolver)
134    {
135        this.initializeVariables(outerMin, outerMax, innerMin, innerMax, isMinFuzzy, isMaxFuzzy, resolver);
136    }
137
138  public Location translate(int dist) {
139    return new FuzzyLocation(
140      outerMin + dist,
141      outerMax + dist,
142      innerMin + dist,
143      innerMax + dist,
144      resolver
145    );
146  }
147
148  /**
149   * Retrieve the Location that this decorates.
150   *
151   * @return the Location instance that stores all of the Location interface
152   *         data
153   */
154
155  public RangeResolver getResolver() {
156    return resolver;
157  }
158
159  public int getOuterMin() {
160    return outerMin;
161  }
162
163
164  public int getOuterMax() {
165    return outerMax;
166  }
167
168  public int getInnerMin() {
169    return innerMin;
170  }
171
172
173  public int getInnerMax() {
174    return innerMax;
175  }
176
177  public int getMin() {
178    return resolver.resolveMin(this);
179  }
180
181  public int getMax() {
182    return resolver.resolveMax(this);
183  }
184
185  public boolean hasBoundedMin() {
186    return outerMin != Integer.MIN_VALUE;
187  }
188
189  public boolean hasBoundedMax() {
190    return outerMax != Integer.MAX_VALUE;
191  }
192
193    public String toString()
194    {
195        return "["
196            + (hasBoundedMin() ? Integer.toString(getMin()) : "<" + Integer.toString(getMin()))
197            + ", "
198            + (hasBoundedMax() ? Integer.toString(getMax()) : ">" + Integer.toString(getMax()))
199            + "]";
200    }
201
202    /**
203     * Determines how a <code>FuzzyLocation</code> should be treated when used
204     * as a normal <code>Location</code>.
205     *
206     * Use one of the implementations of this interface when creating a <code>FuzzyLocation</code>
207     * to specify how the fuzzy (inner/outer) properties are translated into the standard
208     * Location min and max properties.
209     *
210     * It is possible to write custom implementations of this to create <code>FuzzyLocations</code>
211     * with exotic behaviour.
212     */
213
214    public static interface RangeResolver extends Serializable {
215        /**
216         * Delegate for the getMin() method.
217         * @param loc The Location to resolve
218         * @return the resolved Min
219         */
220
221        public int resolveMin(FuzzyLocation loc);
222
223        /**
224         * Delegate for the getMax() method.
225         * @param loc The Location to resolve
226         * @return the resolved Max
227         */
228
229        public int resolveMax(FuzzyLocation loc);
230    }
231
232    private static class InnerRangeResolver implements RangeResolver {
233        public int resolveMin(FuzzyLocation loc) {
234            return loc.getInnerMin();
235        }
236
237        public int resolveMax(FuzzyLocation loc) {
238            return loc.getInnerMax();
239        }
240    }
241
242    private static class OuterRangeResolver implements RangeResolver {
243        public int resolveMin(FuzzyLocation loc) {
244            if (loc.hasBoundedMin()) {
245                return loc.getOuterMin();
246            } else {
247                return loc.getInnerMin();
248            }
249        }
250
251        public int resolveMax(FuzzyLocation loc) {
252            if (loc.hasBoundedMax()) {
253                return loc.getOuterMax();
254            } else {
255                return loc.getInnerMax();
256            }
257        }
258    }
259
260    private static class AverageRangeResolver implements RangeResolver {
261        public int resolveMin(FuzzyLocation loc) {
262            if (loc.hasBoundedMin()) {
263                return (loc.getOuterMin() + loc.getInnerMin()) / 2;
264            } else {
265                return loc.getInnerMin();
266            }
267        }
268
269        public int resolveMax(FuzzyLocation loc) {
270            if (loc.hasBoundedMax()) {
271                return (loc.getOuterMax() + loc.getInnerMax()) / 2;
272            } else {
273                return loc.getInnerMax();
274            }
275        }
276    }
277
278    public boolean isMinFuzzy()
279    {
280        return mIsMinFuzzy;
281    }
282
283    public boolean isMaxFuzzy()
284    {
285        return mIsMaxFuzzy;
286    }
287
288    /**
289     * Refactored initialization code from the constructors.
290     *
291     * @param outerMin the lower bound on the location's min value.
292     *                           Integer.MIN_VALUE indicates unbounded.
293     * @param outerMax the upper bound on the location's max value.
294     *                           Integer.MAX_VALUE indicates unbounded.
295     * @param innerMin the upper bound on the location's min value.
296     * @param innerMax the lower bound on the location's max value.
297     * @param resolver a RangeResolver object which defines the policy used to calculate
298     *                 the location's min and max properties.
299     */
300    protected void initializeVariables(int outerMin, int outerMax,
301                                       int innerMin, int innerMax,
302                                       boolean isMinFuzzy, boolean isMaxFuzzy,
303                                       RangeResolver resolver)
304    {
305        this.outerMin = outerMin;
306        this.outerMax = outerMax;
307        this.innerMin = innerMin;
308        this.innerMax = innerMax;
309        this.resolver = resolver;
310        this.mIsMinFuzzy = isMinFuzzy;
311        this.mIsMaxFuzzy = isMaxFuzzy;
312    }
313}