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.seq; 023 024import java.io.Serializable; 025import java.lang.reflect.Field; 026import java.util.Comparator; 027import java.util.Iterator; 028 029import org.biojava.bio.Annotatable; 030import org.biojava.bio.Annotation; 031import org.biojava.bio.BioError; 032import org.biojava.bio.symbol.Location; 033import org.biojava.bio.symbol.SymbolList; 034import org.biojava.ontology.InvalidTermException; 035import org.biojava.ontology.Term; 036import org.biojava.utils.ChangeType; 037import org.biojava.utils.ChangeVetoException; 038 039/** 040 * A feature within a sequence, or nested within another feature. 041 * 042 * <h2>Common operations</h2> 043 * 044 * <pre> 045 * // loop over all features in a sequence 046 * for(Iterator fi = mySeq.features(); fi.hasNext(); ) { 047 * Feature f = (Feature) fi.next(); 048 * System.out.println(f.getType() + "\t" + f.getLocation()); 049 * } 050 * 051 * // loop over all features that are children of this one, such as exons in a 052 * // gene 053 * for(Iterator cfi = feature.features(); cfi.hasNext(); ) { 054 * ... 055 * 056 * // extract all stranded features that are directly on a sequence 057 * FeatureHolder strandedFeatures = mySeq.filter( 058 * new FeatureFilter.ByClass(StrandedFeature.class), 059 * false 060 * ); 061 * for(fi = strandedFeatures.features(); ... 062 * 063 * // find all features with the type property set to "EXON" no matter how 064 * // far down the feature hierachy they are 065 * FeatureHolder repeats = mySeq.filter( 066 * new FeatureFilter.ByType("EXON"), 067 * true; 068 * ); 069 * </pre> 070 * 071 * <h2>Description</h2> 072 * 073 *<p> Features contain annotation and a location. The type of the 074 * feature is something like 'Repeat' or 'BetaStrand'. Where the 075 * feature has been read from an EMBL or Genbank source the type will 076 * be the same as the feature key in the feature table e.g. 'gene', 077 * 'CDS', 'repeat_unit', 'misc_feature'. The source of the feature is 078 * something like 'genscan', 'repeatmasker' or 'made-up'. </p> 079 * 080 * <p> 081 * Features are <em>always</em> contained by a parent <code>FeatureHolder</code>, 082 * which may either be a <code>Sequence</code> or another <code>Feature</code>. 083 * Feature instances should never be constructed directly by client 084 * code, and the BioJava core does not contain any publicly accessible 085 * implementations of the <code>Feature</code> interface. Instead, you 086 * should create a suitable <code>Feature.Template</code>, then pass this 087 * to the <code>createFeature</code> method of a <code>Sequence</code> 088 * or <code>Feature</code>. 089 * </p> 090 * 091 * <p> 092 * We may need some standardisation for what the fields mean. In particular, we 093 * should be compliant where sensible with GFF. 094 * </p> 095 * @see org.biojavax.bio.seq.RichFeature 096 * 097 * @author Matthew Pocock 098 * @author Thomas Down 099 * @author Keith James 100 */ 101 102public interface Feature extends FeatureHolder, Annotatable { 103 104 /** 105 * This is used as a key in the <code>Annotation</code> where it 106 * identifies internal data. This is not printed when the 107 * <code>Feature</code> is written to a flatfile. E.g. the 108 * original feature's EMBL location string (if it has one) is 109 * stored here. 110 */ 111 public static final String PROPERTY_DATA_KEY = "internal_data"; 112 113 /** 114 * The location of this feature is being altered. 115 */ 116 public static final ChangeType LOCATION = new ChangeType( 117 "Location has altered", 118 Feature.class, 119 "LOCATION" 120 ); 121 122 /** 123 * The type of this feature has altered. 124 */ 125 public static final ChangeType TYPE = new ChangeType( 126 "Type has altered", 127 Feature.class, 128 "TYPE" 129 ); 130 131 /** 132 * The source of this feature has altered 133 */ 134 public static final ChangeType SOURCE = new ChangeType( 135 "Source has altered", 136 Feature.class, 137 "SOURCE" 138 ); 139 140 /** 141 * The ontological type of this feature has altered. 142 */ 143 public static final ChangeType TYPETERM = new ChangeType( 144 "TypeTerm has altered", 145 Feature.class, 146 "TYPETERM" 147 ); 148 149 /** 150 * The ontological source of this feature has altered 151 */ 152 public static final ChangeType SOURCETERM = new ChangeType( 153 "SourceTerm has altered", 154 Feature.class, 155 "SOURCETERM" 156 ); 157 158 /** 159 * The location of this feature. 160 * <p> 161 * The location may be complicated, or simply a range. 162 * The annotation is assumed to apply to all the region contained 163 * within the location. 164 * 165 * @return a Location anchoring this feature 166 */ 167 Location getLocation(); 168 169 /** 170 * The new location for this feature. 171 * <p> 172 * The location may be complicated or simply a range. The annotation is 173 * assumed to apply to the entire region contained within the location. 174 * Any values returned from methods that rely on the old location must 175 * not be affected. 176 * 177 * @param loc the new Location for this feature 178 * @throws ChangeVetoException if the location can't be altered 179 */ 180 void setLocation(Location loc) 181 throws ChangeVetoException; 182 183 /** 184 * The type of the feature. 185 * 186 * @return the type of this sequence 187 */ 188 String getType(); 189 190 /** 191 * Change the type of this feature. 192 * 193 * @param type new type String 194 * @throws ChangeVetoException if the type can't be altered 195 */ 196 void setType(String type) 197 throws ChangeVetoException; 198 199 /** 200 * An ontology term defining the type of feature. This is 201 * optional, and will default to <code>OntoTools.ANY</code> 202 * in implementations which aren't ontology aware. 203 * 204 * @since 1.4 205 */ 206 207 public Term getTypeTerm(); 208 209 /** 210 * Set the type ontology-term for this feature. If this succeeds, 211 * it will generally also change the source name. 212 * 213 * @since 1.4 214 * @throws ChangeVetoException if changes are not allowed 215 * @throws InvalidTermException if the specified term is not an acceptable type 216 * for this feature. 217 */ 218 219 public void setTypeTerm(Term t) throws ChangeVetoException, InvalidTermException; 220 221 /** 222 * An ontology term defining the source of this feature. This is 223 * optional, and will default to <code>OntoTools.ANY</code> 224 * in implementations which aren't ontology aware. 225 * 226 * @since 1.4 227 */ 228 229 public Term getSourceTerm(); 230 231 /** 232 * Set the source ontology-term for this feature. If this succeeds, 233 * it will generally also change the source name. 234 * 235 * @since 1.4 236 * @throws ChangeVetoException if changes are not allowed 237 * @throws InvalidTermException if the specified term is not an acceptable type 238 * for this feature. 239 */ 240 241 public void setSourceTerm(Term t) throws ChangeVetoException, InvalidTermException; 242 243 /** 244 * The source of the feature. This may be a program or process. 245 * 246 * @return the source, or generator 247 */ 248 String getSource(); 249 250 /** 251 * Change the source of the Feature. 252 * 253 * @param source the new source String 254 * @throws ChangeVetoException if the source can't be altered 255 */ 256 void setSource(String source) 257 throws ChangeVetoException; 258 259 /** 260 * Return a list of symbols that are contained in this feature. 261 * <p> 262 * The symbols may not be contiguous in the original sequence, but they 263 * will be concatenated together in the resulting SymbolList. 264 * <p> 265 * The order of the Symbols within the resulting symbol list will be 266 * according to the concept of ordering within the location object. 267 * <p> 268 * If the feature location is modified then this does not modify any 269 * SymbolList produced by earlier invocations of this method. 270 * 271 * @return a SymbolList containing each symbol of the parent sequence contained 272 * within this feature in the order they appear in the parent 273 */ 274 SymbolList getSymbols(); 275 276 /** 277 * Return the <code>FeatureHolder</code> to which this feature has been 278 * attached. This will be a <code>Sequence</code> object for top level 279 * features, and a <code>Feature</code> object for features further 280 * down the tree. 281 */ 282 283 public FeatureHolder getParent(); 284 285 /** 286 * Return the <code>Sequence</code> object to which this feature 287 * is (ultimately) attached. For top level features, this will be 288 * equal to the <code>FeatureHolder</code> returned by 289 * <code>getParent</code>. 290 * 291 * @return the ultimate parent Sequence 292 */ 293 public Sequence getSequence(); 294 295 /** 296 * Iterate over any child features which are held by this 297 * feature. The order of iteration <em>MAY</em> be significant 298 * for some types of Feature. 299 */ 300 301 public Iterator features(); 302 303 /** 304 * Create a new Template that could be used to generate a feature identical 305 * to this one. The fields of the template can be edited without changing 306 * the feature. 307 * 308 * @return a new Template that would make a feature like this one 309 */ 310 public Template makeTemplate(); 311 312 /** 313 * Template class for a plain feature. 314 * <p> 315 * This just has fields for representing the properties of a basic Feature. Each 316 * sub-interface should provide a template class that inherits off this, and 317 * the constructor or factory methods should make a particular feature 318 * implementation from the template. 319 * 320 * <p> 321 * The equals(), hashcode(), toString() and populate() methods are defined 322 * such that two templates are equal if all their fields are equal. These 323 * are implemented by reflection, and automatically pick up any extra fields 324 * added in subclasses. 325 * </p> 326 * 327 * @author Thomas Down 328 * @author Matthew Pocock 329 */ 330 331 public static class Template implements Serializable, Cloneable { 332 public Location location; 333 public String type; 334 public String source; 335 public Term typeTerm; 336 public Term sourceTerm; 337 public Annotation annotation; 338 339 public Object clone() throws CloneNotSupportedException { 340 return super.clone(); 341 } 342 343 public int hashCode() { 344 Class templClazz = getClass(); 345 Field[] fields = templClazz.getFields(); 346 int hc = 0; 347 for (int i = 0; i < fields.length; ++i) { 348 try { 349 Object o = fields[i].get(this); 350 if (o != null) { 351 hc += o.hashCode(); 352 } 353 } catch (Exception ex) { 354 throw new BioError("Can't access template fields", ex); 355 } 356 } 357 358 return hc; 359 } 360 361 public boolean equals(Object b) { 362 Class aClazz = getClass(); 363 Class bClazz = b.getClass(); 364 if (! aClazz.equals(bClazz)) { 365 return false; 366 } 367 368 Field[] fields = aClazz.getFields(); 369 for (int i = 0; i < fields.length; ++i) { 370 try { 371 Object ao = fields[i].get(this); 372 Object bo = fields[i].get(b); 373 if (ao != bo) { 374 if (ao == null) { 375 return false; 376 } else { 377 if (! (ao.equals(bo))) { 378 return false; 379 } 380 } 381 } 382 } catch (Exception ex) { 383 throw new BioError("Can't access template fields", ex); 384 } 385 } 386 387 return true; 388 } 389 390 391 public String toString() { 392 StringBuffer sbuf = new StringBuffer(); 393 394 Class us = getClass(); 395 sbuf.append(us); 396 sbuf.append(":"); 397 398 Field[] fields = us.getFields(); 399 for(int i = 0; i < fields.length; i++) { 400 try { 401 sbuf.append(" "); 402 sbuf.append(fields[i].getName()); 403 sbuf.append("="); 404 sbuf.append(fields[i].get(this)); 405 } catch (Exception e) { 406 throw new BioError("Couldn't access template fields", e); 407 } 408 } 409 410 return sbuf.toString(); 411 } 412 } 413 414 /** 415 * <code>byLocationOrder</code> contains a <code>Feature</code> 416 * comparator which compares by the minimum base position of their 417 * <code>Location</code>. 418 */ 419 public static final ByLocationComparator byLocationOrder = 420 new ByLocationComparator(); 421 422 /** 423 * <code>ByLocationComparator</code> compares 424 * <code>Feature</code>s by the minimum base position of their 425 * <code>Location</code>. 426 * 427 * @author Keith James 428 * @since 1.2 429 */ 430 public static final class ByLocationComparator implements Comparator 431 { 432 public int compare(Object o1, Object o2) 433 { 434 Feature f1 = (Feature) o1; 435 Feature f2 = (Feature) o2; 436 437 // We don't subtract one coordinate from another as one or 438 // both may be set to Integer.MAX_VALUE/Integer.MIN_VALUE 439 // and the result could wrap around. Convert to Long if 440 // necessary. 441 if (f1.getLocation().getMin() > f2.getLocation().getMin()) 442 return 1; 443 else if (f1.getLocation().getMin() < f2.getLocation().getMin()) 444 return -1; 445 else 446 return 0; 447 } 448 } 449}