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>SimpleSeqSimilaritySearchHit</code> objects represent a 039 * similarity search hit of a query sequence to a sequence referenced 040 * in a SequenceDB object. The core data (score, E-value, P-value) 041 * have accessors, while supplementary data are stored in the 042 * Annotation object. Supplementary data are typically the more 043 * loosely formatted details which vary from one search program to 044 * another (and between 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 Gerald Loeffler 063 * @since 1.1 064 * 065 * @see AbstractChangeable 066 * @see SeqSimilaritySearchHit 067 * @see Annotatable 068 */ 069public class SimpleSeqSimilaritySearchHit extends AbstractChangeable 070 implements SeqSimilaritySearchHit 071{ 072 protected transient ChangeForwarder annotationForwarder; 073 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>SimpleSeqSimilaritySearchHit</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 SimpleSeqSimilaritySearchHit(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 // Lock the annotation by vetoing all changes 162 annotation.addChangeListener(ChangeListener.ALWAYS_VETO); 163 164 this.score = score; 165 this.eValue = eValue; 166 this.pValue = pValue; 167 this.queryStart = queryStart; 168 this.queryEnd = queryEnd; 169 this.queryStrand = queryStrand; 170 this.subjectStart = subjectStart; 171 this.subjectEnd = subjectEnd; 172 this.subjectStrand = subjectStrand; 173 this.subjectID = subjectID; 174 this.annotation = annotation; 175 this.subHits = Collections.unmodifiableList(subHits); 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 SimpleSeqSimilaritySearchHit that = 254 (SimpleSeqSimilaritySearchHit) other; 255 256 if (! ObjectUtil.equals(this.score, that.score)) 257 return false; 258 if (! ObjectUtil.equals(this.pValue, that.pValue)) 259 return false; 260 if (! ObjectUtil.equals(this.eValue, that.eValue)) 261 return false; 262 if (! ObjectUtil.equals(this.subjectID, that.subjectID)) 263 return false; 264 if (! ObjectUtil.equals(this.subHits, that.subHits)) 265 return false; 266 267 return true; 268 } 269 270 public int hashCode() 271 { 272 if (! hcCalc) 273 { 274 hc = ObjectUtil.hashCode(hc, score); 275 hc = ObjectUtil.hashCode(hc, pValue); 276 hc = ObjectUtil.hashCode(hc, eValue); 277 hc = ObjectUtil.hashCode(hc, subjectID); 278 hc = ObjectUtil.hashCode(hc, subHits); 279 hcCalc = true; 280 } 281 282 return hc; 283 } 284 285 public String toString() 286 { 287 return "SimpleSeqSimilaritySearchHit to " + getSubjectID() 288 + " with score " + getScore(); 289 } 290 291 protected ChangeSupport getChangeSupport(ChangeType ct) 292 { 293 ChangeSupport cs = super.getChangeSupport(ct); 294 295 if (annotationForwarder == null && 296 (ct.isMatchingType(Annotatable.ANNOTATION) || Annotatable.ANNOTATION.isMatchingType(ct))) 297 { 298 annotationForwarder = 299 new ChangeForwarder.Retyper(this, cs, Annotation.PROPERTY); 300 getAnnotation().addChangeListener(annotationForwarder, 301 Annotatable.ANNOTATION); 302 } 303 304 return cs; 305 } 306}