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.search;
023
024import org.biojava.bio.Annotatable;
025import org.biojava.bio.Annotation;
026import org.biojava.bio.alignment.Alignment;
027import org.biojava.bio.seq.StrandedFeature.Strand;
028import org.biojava.utils.AbstractChangeable;
029import org.biojava.utils.ChangeForwarder;
030import org.biojava.utils.ChangeListener;
031import org.biojava.utils.ChangeSupport;
032import org.biojava.utils.ChangeType;
033import org.biojava.utils.ObjectUtil;
034
035/**
036 * <p><code>SimpleSeqSimilaritySearchSubHit</code> objects represent
037 * sub-hits which make up a hit. In the case of Blast these correspond
038 * to HSPs.</p>
039 *
040 * @author Keith James
041 * @author Gerald Loeffler
042 * @since 1.1
043 */
044public class SimpleSeqSimilaritySearchSubHit extends AbstractChangeable
045    implements SeqSimilaritySearchSubHit
046{
047    protected transient ChangeForwarder annotationForwarder;
048
049    private double    score;
050    private double    pValue;
051    private double    eValue;
052    private int       queryStart;
053    private int       queryEnd;
054    private Strand    queryStrand;
055    private int       subjectStart;
056    private int       subjectEnd;
057    private Strand    subjectStrand;
058    private Alignment alignment;
059    private Annotation annotation;
060
061    // Hashcode is cached after first calculation because the data on
062    // which is is based do not change
063    private int hc;
064    private boolean hcCalc;
065
066    /**
067     * Creates a new <code>SimpleSeqSimilaritySearchSubHit</code>
068     * object.
069     *
070     * @param queryStart an <code>int</code> value indicating the
071     * start coordinate of the hit on the query sequence.
072     * @param queryEnd an <code>int</code> value indicating the end
073     * coordinate of the hit on the query sequence.
074     * @param queryStrand a <code>Strand</code> object indicating the
075     * strand of the hit with respect to the query sequence, which may
076     * be null for protein similarities.
077     * @param subjectStart an <code>int</code> value indicating the
078     * start coordinate of the hit on the subject sequence.
079     * @param subjectEnd an <code>int</code> value indicating the end
080     * coordinate of the hit on the query sequence.
081     * @param subjectStrand a <code>Strand</code> object indicating
082     * the strand of the hit with respect to the query sequence, which
083     * may be null for protein similarities.
084     * @param score a <code>double</code> value; the score of the
085     * subhit, which may not be NaN.
086     * @param eValue a <code>double</code> the E-value of the
087     * subhit, which may be NaN.
088     * @param pValue a <code>double</code> value; the P-value of the
089     * hit, which may be NaN.
090     * @param alignment an <code>Alignment</code> object containing
091     * the alignment described by the subhit region, which may not be
092     * null.
093     */
094    public SimpleSeqSimilaritySearchSubHit(double     score,
095                                           double     eValue,
096                                           double     pValue,
097                                           int        queryStart,
098                                           int        queryEnd,
099                                           Strand     queryStrand,
100                                           int        subjectStart,
101                                           int        subjectEnd,
102                                           Strand     subjectStrand,
103                                           Alignment  alignment,
104                                           Annotation annotation)
105    {
106        if (Double.isNaN(score))
107        {
108            throw new IllegalArgumentException("score was NaN");
109        }
110
111        // pValue may be NaN
112        // eValue may be NaN
113        if (alignment == null)
114        {
115            throw new IllegalArgumentException("alignment was null");
116        }
117
118        if (annotation == null)
119        {
120            throw new IllegalArgumentException("annotation was null");
121        }
122
123        // Lock alignment by vetoing all changes
124        alignment.addChangeListener(ChangeListener.ALWAYS_VETO);
125
126        // Lock the annotation by vetoing all changes to properties
127        annotation.addChangeListener(ChangeListener.ALWAYS_VETO);
128
129        this.score         = score;
130        this.eValue        = eValue;
131        this.pValue        = pValue;
132        this.queryStart    = queryStart;
133        this.queryEnd      = queryEnd;
134        this.queryStrand   = queryStrand;
135        this.subjectStart  = subjectStart;
136        this.subjectEnd    = subjectEnd;
137        this.subjectStrand = subjectStrand;
138        this.alignment     = alignment;
139        this.annotation    = annotation;
140
141        hcCalc = false;
142    }
143
144    public double getScore()
145    {
146        return score;
147    }
148
149    public double getPValue()
150    {
151        return pValue;
152    }
153
154    public double getEValue()
155    {
156        return eValue;
157    }
158
159    public int getQueryStart()
160    {
161        return queryStart;
162    }
163
164    public int getQueryEnd()
165    {
166        return queryEnd;
167    }
168
169    public Strand getQueryStrand()
170    {
171        return queryStrand;
172    }
173
174    public int getSubjectStart()
175    {
176        return subjectStart;
177    }
178
179    public int getSubjectEnd()
180    {
181        return subjectEnd;
182    }
183
184    public Strand getSubjectStrand()
185    {
186        return subjectStrand;
187    }
188
189    public Alignment getAlignment()
190    {
191        return alignment;
192    }
193
194    /**
195     * <code>getAnnotation</code> returns the Annotation associated
196     * with this sub-hit.
197     *
198     * @return an <code>Annotation</code>.
199     */
200    public Annotation getAnnotation()
201    {
202        return annotation;
203    }
204
205    public boolean equals(Object other)
206    {
207        if (other == this) return true;
208        if (other == null) return false;
209
210        if (! other.getClass().equals(this.getClass())) return false;
211
212        SimpleSeqSimilaritySearchSubHit that =
213            (SimpleSeqSimilaritySearchSubHit) other;
214
215        if (! ObjectUtil.equals(this.score, that.score))
216            return false;
217        if (! ObjectUtil.equals(this.pValue, that.pValue))
218            return false;
219        if (! ObjectUtil.equals(this.eValue, that.eValue))
220            return false;
221        if (! ObjectUtil.equals(this.queryStart, that.queryStart))
222            return false;
223        if (! ObjectUtil.equals(this.queryEnd, that.queryEnd))
224            return false;
225        if (! ObjectUtil.equals(this.queryStrand, that.queryStrand))
226            return false;
227        if (! ObjectUtil.equals(this.subjectStart, that.subjectStart))
228            return false;
229        if (! ObjectUtil.equals(this.subjectEnd, that.subjectEnd))
230            return false;
231        if (! ObjectUtil.equals(this.subjectStrand, that.subjectStrand))
232            return false;
233        if (! ObjectUtil.equals(this.annotation, that.annotation))
234            return false;
235
236        return true;
237    }
238  
239    public int hashCode()
240    {
241        if (! hcCalc)
242        {
243            hc = ObjectUtil.hashCode(hc, score);
244            hc = ObjectUtil.hashCode(hc, pValue);
245            hc = ObjectUtil.hashCode(hc, eValue);
246            hc = ObjectUtil.hashCode(hc, queryStart);
247            hc = ObjectUtil.hashCode(hc, queryEnd);
248            hc = ObjectUtil.hashCode(hc, queryStrand);
249            hc = ObjectUtil.hashCode(hc, subjectStart);
250            hc = ObjectUtil.hashCode(hc, subjectEnd);
251            hc = ObjectUtil.hashCode(hc, subjectStrand);
252            hc = ObjectUtil.hashCode(hc, annotation);
253            hcCalc = true;
254        }
255
256        return hc;
257    }
258
259    public String toString()
260    {
261        return "SimpleSeqSimilaritySearchSubHit with score " + getScore();
262    }
263
264    protected ChangeSupport getChangeSupport(ChangeType ct)
265    {
266        ChangeSupport cs = super.getChangeSupport(ct);
267
268        if (annotationForwarder == null &&
269            (ct.isMatchingType(Annotatable.ANNOTATION) || Annotatable.ANNOTATION.isMatchingType(ct)))
270        {
271            annotationForwarder =
272                new ChangeForwarder.Retyper(this, cs, Annotation.PROPERTY);
273            getAnnotation().addChangeListener(annotationForwarder,
274                                              Annotatable.ANNOTATION);
275        }
276
277        return cs;
278    }
279}