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}