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.util.Iterator;
025import java.util.List;
026
027import org.biojava.bio.Annotatable;
028import org.biojava.bio.Annotation;
029import org.biojava.bio.BioError;
030import org.biojava.bio.BioException;
031import org.biojava.bio.SimpleAnnotation;
032import org.biojava.bio.seq.impl.AssembledSymbolList;
033import org.biojava.bio.symbol.Alphabet;
034import org.biojava.bio.symbol.Edit;
035import org.biojava.bio.symbol.IllegalAlphabetException;
036import org.biojava.bio.symbol.Location;
037import org.biojava.bio.symbol.Symbol;
038import org.biojava.bio.symbol.SymbolList;
039import org.biojava.utils.AbstractChangeable;
040import org.biojava.utils.ChangeForwarder;
041import org.biojava.utils.ChangeSupport;
042import org.biojava.utils.ChangeType;
043import org.biojava.utils.ChangeVetoException;
044
045/**
046 * A Sequence which is assembled from other sequences contained
047 * in a set of ComponentFeature objects.
048 *
049 * <p>
050 * There is still some potential for optimising SymbolList
051 * operations on this class.
052 * </p>
053 *
054 * @author Thomas Down
055 * @author Matthew Pocock
056 * @since 1.1
057 */
058
059public class SimpleAssembly
060  extends
061    AbstractChangeable
062  implements
063    Sequence,
064    RealizingFeatureHolder
065{
066    private final String name;
067    private final String uri;
068    private final Annotation annotation = new SimpleAnnotation();
069    private final SimpleFeatureHolder features;
070    private final AssembledSymbolList assembly;
071
072    private final FeatureRealizer featureRealizer = org.biojava.bio.seq.impl.FeatureImpl.DEFAULT;
073
074    protected transient ChangeForwarder annotationForwarder;
075
076    {
077        assembly = new AssembledSymbolList();
078        features = new SimpleFeatureHolder();
079    }
080
081    /**
082     * Construct a new SimpleAssembly using the DNA alphabet.
083     * Initially, the sequence will just contain a list of `N's.
084     * Sequence data can be added by adding one or more
085     * ComponentFeatures.
086     *
087     * @param length The length of the sequence
088     * @param name The name of the sequence (returned by getName())
089     * @param uri The identifier of the sequence (returned by getURN());
090     */
091
092    public SimpleAssembly(int length, String name, String uri) {
093        this.name = name;
094        this.uri = uri;
095        assembly.setLength(length);
096    }
097
098    /**
099     * Construct a new SimpleAssembly using the DNA alphabet.
100     * Initially, the sequence will just contain a list of `N's.
101     * Sequence data can be added by adding one or more
102     * ComponentFeatures.
103     *
104     * @param name The name of the sequence (returned by getName())
105     * @param uri The identifier of the sequence (returned by getURN());
106     */
107
108    public SimpleAssembly(String name, String uri) {
109        this.name = name;
110        this.uri = uri;
111    }
112
113    //
114    // SymbolList
115    //
116
117    public Alphabet getAlphabet() {
118        return assembly.getAlphabet();
119    }
120
121    public int length() {
122        return assembly.length();
123    }
124
125    public Symbol symbolAt(int pos) {
126        return assembly.symbolAt(pos);
127    }
128
129    public SymbolList subList(int start, int end) {
130        return assembly.subList(start, end);
131    }
132
133    public String seqString() {
134        return assembly.seqString();
135    }
136
137    public String subStr(int start, int end) {
138        return assembly.subStr(start, end);
139    }
140
141    public Iterator iterator() {
142        return assembly.iterator();
143    }
144
145    public List toList() {
146        return assembly.toList();
147    }
148
149    public void edit(Edit e)
150        throws IllegalAlphabetException, ChangeVetoException
151    {
152        assembly.edit(e);
153    }
154
155    //
156    // Sequence identification
157    //
158
159    public String getName() {
160        return name;
161    }
162
163    public String getURN() {
164        return uri;
165    }
166
167    //
168    // Annotatable
169    //
170
171    public Annotation getAnnotation() {
172        return annotation;
173    }
174
175    //
176    // FeatureHolder
177    //
178
179    public Iterator features() {
180        return features.features();
181    }
182
183    public int countFeatures() {
184        return features.countFeatures();
185    }
186
187    public FeatureHolder filter(FeatureFilter ff, boolean recurse) {
188            return features.filter(ff, recurse);
189    }
190
191    public FeatureHolder filter(FeatureFilter ff) {
192            return features.filter(ff);
193    }
194
195    public boolean containsFeature(Feature f) {
196      return features.containsFeature(f);
197    }
198
199    public Feature createFeature(Feature.Template temp)
200        throws BioException, ChangeVetoException
201    {
202        if (temp.location.getMin() < 1)
203            throw new BioException("Coordinates out of range");
204
205        if (temp instanceof ComponentFeature.Template) {
206            for (Iterator i = assembly.getComponentLocationSet().iterator(); i.hasNext(); ) {
207                Location l = (Location) i.next();
208                if (l.overlaps(temp.location))
209                    throw new BioError("Can't create overlapping ComponentFeature");
210            }
211        }
212
213        Feature f = realizeFeature(this, temp);
214        features.addFeature(f);
215        if (f instanceof ComponentFeature) {
216            ComponentFeature cf = (ComponentFeature) f;
217            Location loc = cf.getLocation();
218            if (loc.getMax() > assembly.length()) {
219                assembly.setLength(loc.getMax());
220            }
221            assembly.putComponent(cf);
222        }
223        return f;
224    }
225
226    public void removeFeature(Feature f)
227    throws ChangeVetoException {
228      if (f instanceof ComponentFeature) {
229        assembly.removeComponent(f.getLocation());
230      }
231      features.removeFeature(f);
232    }
233
234    //
235    // Feature realization
236    //
237
238    public Feature realizeFeature(FeatureHolder fh, Feature.Template temp)
239            throws BioException {
240      if (temp instanceof ComponentFeature.Template) {
241        if (fh != this) {
242          throw new BioException("ComponentFeatures can only be attached directly to SimpleAssembly objects");
243        }
244        ComponentFeature.Template cft = (ComponentFeature.Template) temp;
245        return new SimpleComponentFeature(this, cft);
246      } else {
247        FeatureHolder gopher = fh;
248        while (gopher instanceof Feature) {
249          if (gopher instanceof ComponentFeature) {
250            // fixme: this should delegate onto the ComponentFeature, which
251            // should in turn delegate to its ProjectedFeatureHolder and let
252            // the projection magic sort this out
253            throw new BioException("Cannot [currently] realize features on components of SimpleAssemblies");
254          }
255          gopher = ((Feature) gopher).getParent();
256        }
257        return featureRealizer.realizeFeature(this, fh, temp);
258      }
259    }
260
261    protected ChangeSupport getChangeSupport(ChangeType ct){
262      ChangeSupport cs = super.getChangeSupport(ct);
263
264      if(annotationForwarder == null &&
265        (ct == null || ct == Annotatable.ANNOTATION)){
266        annotationForwarder =
267                new ChangeForwarder.Retyper(this, cs, Annotation.PROPERTY);
268        getAnnotation().addChangeListener(
269            annotationForwarder,
270            Annotatable.ANNOTATION);
271      }
272      return cs;
273    }
274
275    public FeatureFilter getSchema() {
276        return FeatureFilter.top_level;
277    }
278}