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 java.util.Collections; 025import java.util.List; 026 027import org.biojava.bio.Annotatable; 028import org.biojava.bio.Annotation; 029import org.biojava.bio.seq.StrandedFeature.Strand; 030import org.biojava.utils.AbstractChangeable; 031import org.biojava.utils.ChangeForwarder; 032import org.biojava.utils.ChangeListener; 033import org.biojava.utils.ChangeSupport; 034import org.biojava.utils.ChangeType; 035import org.biojava.utils.ObjectUtil; 036 037/** 038 * <p><code>SequenceDBSearchHit</code> objects represent a similarity 039 * search hit of a query sequence to a sequence referenced in a 040 * SequenceDB object. The core data (score, E-value, P-value) have 041 * accessors, while supplementary data are stored in the Annotation 042 * object. Supplementary data are typically the more loosely formatted 043 * details which vary from one search program to another (and between 044 * versions of those programs).</p> 045 * 046 * <p>It is up to the user to define the meaning of the hit's 047 * query/subject start/end/strand with respect to its constituent 048 * sub-hits. One approach could be:</p> 049 * 050 * <ul> 051 * <li>Hit query/subject start == start of first sub-hit</li> 052 * <li>Hit query/subject end == end of last sub-hit</li> 053 * <li>Hit strand == POSITIVE if all sub-hits have strand POSITIVE</li> 054 * <li>Hit strand == NEGATIVE if all sub-hits have strand NEGATIVE</li> 055 * <li>Hit strand == UNKNOWN if sub-hits have either mixed or any UNKNOWN 056 * strands</li> 057 * <li>Hit strand == null if the concept of strandedness is inappropriate 058 * for the sequence type i.e. for protein</li> 059 * </ul> 060 * 061 * @author Keith James 062 * @author Matthew Pocock 063 * @since 1.1 064 * @deprecated SimpleSeqSimilaritySearchHit has been made Annotatable 065 * and is now functionally identical. 066 * @see AbstractChangeable 067 * @see SeqSimilaritySearchHit 068 * @see Annotatable 069 */ 070public class SequenceDBSearchHit extends AbstractChangeable 071 implements SeqSimilaritySearchHit, Annotatable 072{ 073 protected transient ChangeForwarder annotationForwarder; 074 private double score; 075 private double pValue; 076 private double eValue; 077 private int queryStart; 078 private int queryEnd; 079 private Strand queryStrand; 080 private int subjectStart; 081 private int subjectEnd; 082 private Strand subjectStrand; 083 private String subjectID; 084 private Annotation annotation; 085 private List subHits; 086 087 // Hashcode is cached after first calculation because the data on 088 // which is is based do not change 089 private int hc; 090 private boolean hcCalc; 091 092 /** 093 * Creates a new <code>SequenceDBSearchHit</code> object. 094 * 095 * @param score a <code>double</code> value; the score of the hit, 096 * which may not be NaN. 097 * @param eValue a <code>double</code> value; the E-value of the 098 * hit, which may be NaN. 099 * @param pValue a <code>double</code> value; the P-value of the 100 * hit, which may be NaN. 101 * @param queryStart the start of the first sub-hit on the query 102 * sequence. 103 * @param queryEnd the end of the last sub-hit on the query 104 * sequence. 105 * @param queryStrand the strand of the sub-hits on the query 106 * sequence, which may be null for protein similarities. If they 107 * are not all positive or all negative, then this should be the 108 * unknown strand. 109 * @param subjectStart the start of the first sub-hit on the subject 110 * sequence. 111 * @param subjectEnd the end of the last sub-hit on the subject 112 * sequence. 113 * @param subjectStrand the strand of the sub-hits on the subject 114 * sequence, which may be null for protein similarities. If they 115 * are not all positive or all negative, then this should be the 116 * unknown strand. 117 * @param subjectID a <code>String</code> representing the ID in 118 * the SequenceDB of the sequence which was hit, which may not be 119 * null. 120 * @param annotation an <code>Annotation</code> object, which may 121 * not be null. 122 * @param subHits a <code>List</code> object containing the 123 * subhits, which may not be null. They should be sorted in the 124 * order specified by the search program. 125 */ 126 public SequenceDBSearchHit(double score, 127 double eValue, 128 double pValue, 129 int queryStart, 130 int queryEnd, 131 Strand queryStrand, 132 int subjectStart, 133 int subjectEnd, 134 Strand subjectStrand, 135 String subjectID, 136 Annotation annotation, 137 List subHits) 138 { 139 if (Double.isNaN(score)) 140 { 141 throw new IllegalArgumentException("score was NaN"); 142 } 143 144 // pValue may be NaN 145 // eValue may be NaN 146 if (subjectID == null) 147 { 148 throw new IllegalArgumentException("subjectID was null"); 149 } 150 151 if (annotation == null) 152 { 153 throw new IllegalArgumentException("annotation was null"); 154 } 155 156 if (subHits == null) 157 { 158 throw new IllegalArgumentException("subHits was null"); 159 } 160 161 this.score = score; 162 this.eValue = eValue; 163 this.pValue = pValue; 164 this.queryStart = queryStart; 165 this.queryEnd = queryEnd; 166 this.queryStrand = queryStrand; 167 this.subjectStart = subjectStart; 168 this.subjectEnd = subjectEnd; 169 this.subjectStrand = subjectStrand; 170 this.subjectID = subjectID; 171 this.annotation = annotation; 172 this.subHits = Collections.unmodifiableList(subHits); 173 174 // Lock the annotation by vetoing all changes 175 this.annotation.addChangeListener(ChangeListener.ALWAYS_VETO); 176 177 hcCalc = false; 178 } 179 180 public double getScore() 181 { 182 return score; 183 } 184 185 public double getPValue() 186 { 187 return pValue; 188 } 189 190 public double getEValue() 191 { 192 return eValue; 193 } 194 195 public int getQueryStart() 196 { 197 return queryStart; 198 } 199 200 public int getQueryEnd() 201 { 202 return queryEnd; 203 } 204 205 public Strand getQueryStrand() 206 { 207 return queryStrand; 208 } 209 210 public int getSubjectStart() 211 { 212 return subjectStart; 213 } 214 215 public int getSubjectEnd() 216 { 217 return subjectEnd; 218 } 219 220 public Strand getSubjectStrand() 221 { 222 return subjectStrand; 223 } 224 225 public String getSubjectID() 226 { 227 return subjectID; 228 } 229 230 public List getSubHits() 231 { 232 return subHits; 233 } 234 235 /** 236 * <code>getAnnotation</code> returns the Annotation associated 237 * with this hit. 238 * 239 * @return an <code>Annotation</code>. 240 */ 241 public Annotation getAnnotation() 242 { 243 return annotation; 244 } 245 246 public boolean equals(Object other) 247 { 248 if (other == this) return true; 249 if (other == null) return false; 250 251 if (! other.getClass().equals(this.getClass())) return false; 252 253 SequenceDBSearchHit that = (SequenceDBSearchHit) other; 254 255 if (! ObjectUtil.equals(this.score, that.score)) 256 return false; 257 if (! ObjectUtil.equals(this.pValue, that.pValue)) 258 return false; 259 if (! ObjectUtil.equals(this.eValue, that.eValue)) 260 return false; 261 if (! ObjectUtil.equals(this.subjectID, that.subjectID)) 262 return false; 263 if (! ObjectUtil.equals(this.subHits, that.subHits)) 264 return false; 265 266 return true; 267 } 268 269 public int hashCode() 270 { 271 if (! hcCalc) 272 { 273 hc = ObjectUtil.hashCode(hc, score); 274 hc = ObjectUtil.hashCode(hc, pValue); 275 hc = ObjectUtil.hashCode(hc, eValue); 276 hc = ObjectUtil.hashCode(hc, subjectID); 277 hc = ObjectUtil.hashCode(hc, subHits); 278 hcCalc = true; 279 } 280 281 return hc; 282 } 283 284 public String toString() 285 { 286 return "SequenceDBSearchHit to " + getSubjectID() 287 + " with score " + getScore(); 288 } 289 290 protected ChangeSupport getChangeSupport(ChangeType ct) 291 { 292 ChangeSupport cs = super.getChangeSupport(ct); 293 294 if (annotationForwarder == null && 295 (ct.isMatchingType(Annotatable.ANNOTATION) || Annotatable.ANNOTATION.isMatchingType(ct))) 296 { 297 annotationForwarder = 298 new ChangeForwarder.Retyper(this, cs, Annotation.PROPERTY); 299 getAnnotation().addChangeListener(annotationForwarder, 300 Annotatable.ANNOTATION); 301 } 302 303 return cs; 304 } 305}