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.projection;
023
024import java.util.ArrayList;
025import java.util.Iterator;
026import java.util.List;
027
028import org.biojava.bio.BioError;
029import org.biojava.bio.BioException;
030import org.biojava.bio.seq.Feature;
031import org.biojava.bio.seq.FeatureFilter;
032import org.biojava.bio.seq.FeatureHolder;
033import org.biojava.bio.seq.FilterUtils;
034import org.biojava.bio.seq.Sequence;
035import org.biojava.bio.seq.impl.TemplateUtils;
036import org.biojava.bio.symbol.IllegalSymbolException;
037import org.biojava.bio.symbol.Location;
038import org.biojava.bio.symbol.SimpleSymbolList;
039import org.biojava.bio.symbol.SymbolList;
040import org.biojava.utils.AssertionFailure;
041import org.biojava.utils.ChangeListener;
042import org.biojava.utils.ChangeType;
043import org.biojava.utils.ChangeVetoException;
044
045/**
046 * Internal class used by ProjectionEngine to wrap Feature objects.
047 *
048 * This is not for you.
049 *
050 * This is the base class that the projection engine extends. If you modify this
051 * you will modify every projected feature. That is probably a bad thing.
052 *
053 * @author Thomas Down
054 * @author Matthew Pocock
055 * @since 1.1
056 */
057
058public abstract class ProjectedFeature
059        implements
060        Feature,
061        Projection {
062  private final Feature feature;
063  private final ProjectionContext context;
064
065  public ProjectedFeature(
066          Feature f,
067          ProjectionContext ctx
068          ) {
069    this.feature = f;
070    this.context = ctx;
071  }
072
073  public Feature getViewedFeature() {
074    return feature;
075  }
076
077  public ProjectionContext getProjectionContext() {
078    return context;
079  }
080  
081  public Sequence getSequence() {
082      return context.getSequence(getViewedFeature());
083  }
084
085  public FeatureHolder getParent() {
086    return context.getParent(feature);
087  }
088
089  public SymbolList getSymbols() {
090    Location loc = getLocation();
091    Sequence seq = getSequence();
092
093    if (loc.isContiguous()) {
094      return seq.subList(loc.getMin(), loc.getMax());
095    }
096
097    List res = new ArrayList();
098    for (Iterator i = loc.blockIterator(); i.hasNext();) {
099      Location l = (Location) i.next();
100      res.add(seq.subList(l.getMin(), l.getMax()));
101    }
102
103    try {
104      return new SimpleSymbolList(seq.getAlphabet(), res);
105    } catch (IllegalSymbolException ex) {
106      throw new BioError(ex);
107    }
108  }
109
110  public int countFeatures() {
111    return getProjectedFeatures().countFeatures();
112  }
113
114  public boolean containsFeature(Feature f) {
115    return getProjectedFeatures().containsFeature(f);
116  }
117
118  protected FeatureHolder getProjectedFeatures() {
119    return context.projectChildFeatures(feature, this);
120  }
121
122  public Iterator features() {
123    return getProjectedFeatures().features();
124  }
125
126  public FeatureHolder filter(FeatureFilter ff) {
127    FeatureFilter membershipFilter = new FeatureFilter.And(
128            new FeatureFilter.Not(FeatureFilter.top_level),
129            new FeatureFilter.ContainedByLocation(getLocation()));
130    if (FilterUtils.areDisjoint(ff, membershipFilter)) {
131      return FeatureHolder.EMPTY_FEATURE_HOLDER;
132    }
133
134    return getProjectedFeatures().filter(ff);
135  }
136
137  public FeatureHolder filter(FeatureFilter ff, boolean recurse) {
138    FeatureFilter membershipFilter =
139            new FeatureFilter.ContainedByLocation(getLocation());
140    if (FilterUtils.areDisjoint(ff, membershipFilter)) {
141      return FeatureHolder.EMPTY_FEATURE_HOLDER;
142    }
143
144    return getProjectedFeatures().filter(ff, recurse);
145  }
146
147  public Feature createFeature(Feature.Template temp)
148          throws ChangeVetoException, BioException {
149    return context.createFeature(feature, temp);
150  }
151
152  public void removeFeature(Feature f)
153          throws ChangeVetoException, BioException {
154    context.removeFeature(feature, f);
155  }
156
157  public Feature.Template makeTemplate() {
158    try {
159      return TemplateUtils.makeTemplate(this);
160    } catch (BioException be) {
161      throw new AssertionFailure("Could not build/populate template for: " +
162                                 this.toString() + " ", be);
163    }
164  }
165
166  public int hashCode() {
167    return makeTemplate().hashCode();
168  }
169
170  public boolean equals(Object o) {
171    if (o instanceof Feature) {
172      Feature fo = (Feature) o;
173      if (fo.getSequence().equals(getSequence())) {
174        return makeTemplate().equals(fo.makeTemplate());
175      }
176    }
177    return false;
178  }
179
180  public void addChangeListener(ChangeListener cl) {
181    addChangeListener(cl, ChangeType.UNKNOWN);
182  }
183
184  public void removeChangeListener(ChangeListener cl) {
185    removeChangeListener(cl, ChangeType.UNKNOWN);
186  }
187
188  public void addChangeListener(ChangeListener cl, ChangeType ct) {
189    context.addChangeListener(feature, cl, ct);
190  }
191
192  public void removeChangeListener(ChangeListener cl, ChangeType ct) {
193    context.removeChangeListener(feature, cl, ct);
194  }
195
196  public boolean isUnchanging(ChangeType ct) {
197    return feature.isUnchanging(ct);
198  }
199}