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.biojavax.bio.seq;
023
024import java.util.Iterator;
025import java.util.List;
026import java.util.Set;
027import java.util.TreeSet;
028
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.ProteinTools;
035import org.biojava.bio.seq.SimpleFeatureHolder;
036import org.biojava.bio.symbol.Alphabet;
037import org.biojava.bio.symbol.AlphabetManager;
038import org.biojava.bio.symbol.Edit;
039import org.biojava.bio.symbol.IllegalAlphabetException;
040import org.biojava.bio.symbol.IllegalSymbolException;
041import org.biojava.bio.symbol.Symbol;
042import org.biojava.bio.symbol.SymbolList;
043import org.biojava.ontology.InvalidTermException;
044import org.biojava.utils.ChangeEvent;
045import org.biojava.utils.ChangeSupport;
046import org.biojava.utils.ChangeVetoException;
047import org.biojavax.Namespace;
048import org.biojavax.RichObjectFactory;
049import org.biojavax.bio.SimpleBioEntry;
050
051/**
052 * A simple implementation of RichSequence. It has no sequence data, and
053 * delegates to a RichSequenceHandler to do sequence handling.
054 * 
055 * @author Richard Holland
056 * @since 1.5
057 */
058public class ThinRichSequence extends SimpleBioEntry implements RichSequence {
059
060        private final static String ISCIRCULAR = "X";
061
062        private Set<Feature> features = new TreeSet<Feature>();
063        private Double symListVersion;
064        private boolean circular;
065
066        /**
067         * Creates a new instance of ThinRichSequence. Note the use of Double for
068         * seqversion, which indicates that it is nullable.
069         * 
070         * @param ns
071         *            the namespace for this sequence.
072         * @param name
073         *            the name of the sequence.
074         * @param accession
075         *            the accession of the sequence.
076         * @param version
077         *            the version of the sequence.
078         * @param seqversion
079         *            the version of the symbols for the sequence.
080         */
081        public ThinRichSequence(Namespace ns, String name, String accession,
082                        int version, Alphabet alpha, Double seqversion) {
083                super(ns, name, accession, version);
084                this.symListVersion = seqversion;
085                this.circular = false;
086                this.alphabet = alpha;
087        }
088
089        // Hibernate requirement - not for public use.
090        protected ThinRichSequence() {
091        }
092
093        /**
094         * {@inheritDoc}
095         */
096        public Double getSeqVersion() {
097                return this.symListVersion;
098        }
099
100        /**
101         * {@inheritDoc}
102         */
103        public void setSeqVersion(Double seqVersion) throws ChangeVetoException {
104                if (!this.hasListeners(RichSequence.SYMLISTVERSION)) {
105                        this.symListVersion = seqVersion;
106                } else {
107                        ChangeEvent ce = new ChangeEvent(this, RichSequence.SYMLISTVERSION,
108                                        seqVersion, this.symListVersion);
109                        ChangeSupport cs = this
110                                        .getChangeSupport(RichSequence.SYMLISTVERSION);
111                        synchronized (cs) {
112                                cs.firePreChangeEvent(ce);
113                                this.symListVersion = seqVersion;
114                                cs.firePostChangeEvent(ce);
115                        }
116                }
117        }
118
119        /**
120         * {@inheritDoc}
121         */
122        public void setCircular(boolean circular) throws ChangeVetoException {
123                if (!this.hasListeners(RichSequence.CIRCULAR)) {
124                        this.circular = circular;
125                } else {
126                        ChangeEvent ce = new ChangeEvent(this, RichSequence.CIRCULAR,
127                                        new Boolean(circular), new Boolean(this.circular));
128                        ChangeSupport cs = this.getChangeSupport(RichSequence.CIRCULAR);
129                        synchronized (cs) {
130                                cs.firePreChangeEvent(ce);
131                                this.circular = circular;
132                                cs.firePostChangeEvent(ce);
133                        }
134                }
135        }
136
137        /**
138         * {@inheritDoc}
139         */
140        public boolean getCircular() {
141                return this.circular;
142        }
143
144        // Hibernate requirement - not for public use.
145        String getCircularChar() {
146                return getCircular() ? ISCIRCULAR : null;
147        }
148
149        // Hibernate requirement - not for public use.
150        void setCircularChar(final String isHiddenChar) throws ChangeVetoException {
151                setCircular(isHiddenChar != null
152                                || (isHiddenChar != null && isHiddenChar.length() > 0));// any
153                                                                                                                                                // character
154                                                                                                                                                // will
155                                                                                                                                                // set
156        }
157
158        /**
159         * {@inheritDoc}
160         */
161        public void edit(Edit edit) throws IndexOutOfBoundsException,
162                        IllegalAlphabetException, ChangeVetoException {
163                RichObjectFactory.getDefaultRichSequenceHandler().edit(this, edit);
164        }
165
166        /**
167         * {@inheritDoc}
168         */
169        public Symbol symbolAt(int index) throws IndexOutOfBoundsException {
170                return RichObjectFactory.getDefaultRichSequenceHandler().symbolAt(this,
171                                index);
172        }
173
174        /**
175         * {@inheritDoc}
176         */
177        public List toList() {
178                return RichObjectFactory.getDefaultRichSequenceHandler().toList(this);
179        }
180
181        /**
182         * {@inheritDoc}
183         */
184        public String subStr(int start, int end) throws IndexOutOfBoundsException {
185                return RichObjectFactory.getDefaultRichSequenceHandler().subStr(this,
186                                start, end);
187        }
188
189        /**
190         * {@inheritDoc}
191         */
192        public SymbolList subList(int start, int end)
193                        throws IndexOutOfBoundsException {
194                return RichObjectFactory.getDefaultRichSequenceHandler().subList(this,
195                                start, end);
196        }
197
198        /**
199         * {@inheritDoc}
200         */
201        public String seqString() {
202                return RichObjectFactory.getDefaultRichSequenceHandler()
203                                .seqString(this);
204        }
205
206        /**
207         * {@inheritDoc}
208         */
209        public int length() {
210                return this.length;
211        }
212
213        /**
214         * {@inheritDoc}
215         */
216        public Iterator iterator() {
217                return RichObjectFactory.getDefaultRichSequenceHandler().iterator(this);
218        }
219
220        /**
221         * {@inheritDoc}
222         */
223        public Alphabet getAlphabet() {
224                return this.alphabet;
225        }
226
227        // Hibernate requirement - not for public use.
228        private Alphabet alphabet;
229
230        // Hibernate requirement - not for public use.
231        protected void setAlphabetName(String alphaname)
232                        throws IllegalSymbolException, BioException {
233                if (alphaname.equals("protein"))
234                        alphaname = ProteinTools.getTAlphabet().getName();
235                this.alphabet = AlphabetManager.alphabetForName(alphaname);
236        }
237
238        // Hibernate requirement - not for public use.
239        protected String getAlphabetName() {
240                if (this.alphabet == null)
241                        return null;
242                String name = this.alphabet.getName();
243                if (name.equals(ProteinTools.getTAlphabet().getName()))
244                        return "protein";
245                else
246                        return name;
247        }
248
249        // Hibernate requirement - not for public use.
250        private int length = 0;
251
252        // Hibernate requirement - not for public use.
253        protected void setSequenceLength(int length) {
254                this.length = length;
255        }
256
257        // Hibernate requirement - not for public use.
258        protected int getSequenceLength() {
259                return this.length;
260        }
261
262        /**
263         * {@inheritDoc}
264         */
265        public String getURN() {
266                return this.getName();
267        }
268
269        /**
270         * {@inheritDoc}
271         */
272        public FeatureHolder filter(FeatureFilter fc, boolean recurse) {
273                SimpleFeatureHolder fh = new SimpleFeatureHolder();
274                for (Iterator<Feature> i = this.features.iterator(); i.hasNext();) {
275                        Feature f = (RichFeature) i.next();
276                        try {
277                                if (fc.accept(f))
278                                        fh.addFeature(f);
279                        } catch (ChangeVetoException e) {
280                                throw new RuntimeException(
281                                                "What? You don't like our features??");
282                        }
283                }
284                return fh;
285        }
286
287        /**
288         * {@inheritDoc}
289         */
290        public Feature createFeature(Feature.Template ft) throws BioException,
291                        ChangeVetoException {
292                Feature f;
293                try {
294                        f = new SimpleRichFeature(this, ft);
295                } catch (InvalidTermException e) {
296                        throw new ChangeVetoException("They don't like our term", e);
297                }
298                if (!this.hasListeners(RichSequence.FEATURES)) {
299                        this.features.add(f);
300                } else {
301                        ChangeEvent ce = new ChangeEvent(this, RichSequence.FEATURES, f,
302                                        null);
303                        ChangeSupport cs = this.getChangeSupport(RichSequence.FEATURES);
304                        synchronized (cs) {
305                                cs.firePreChangeEvent(ce);
306                                this.features.add(f);
307                                cs.firePostChangeEvent(ce);
308                        }
309                }
310                return f;
311        }
312
313        /**
314         * {@inheritDoc}
315         */
316        public void removeFeature(Feature f) throws ChangeVetoException,
317                        BioException {
318                if (!(f instanceof RichFeature))
319                        f = RichFeature.Tools.enrich(f);
320                if (!this.hasListeners(RichSequence.FEATURES)) {
321                        this.features.remove(f);
322                } else {
323                        ChangeEvent ce = new ChangeEvent(this, RichSequence.FEATURES, null,
324                                        f);
325                        ChangeSupport cs = this.getChangeSupport(RichSequence.FEATURES);
326                        synchronized (cs) {
327                                cs.firePreChangeEvent(ce);
328                                this.features.remove(f);
329                                cs.firePostChangeEvent(ce);
330                        }
331                }
332        }
333
334        /**
335         * {@inheritDoc}
336         */
337        public boolean containsFeature(Feature f) {
338                try {
339                        if (!(f instanceof RichFeature))
340                                f = RichFeature.Tools.enrich(f);
341                } catch (ChangeVetoException e) {
342                        // We just can't tell!
343                        return false;
344                }
345                return this.features.contains(f);
346        }
347
348        /**
349         * {@inheritDoc}
350         */
351        public FeatureHolder filter(FeatureFilter filter) {
352                boolean recurse = !FilterUtils.areProperSubset(filter,
353                                FeatureFilter.top_level);
354                return this.filter(filter, recurse);
355        }
356
357        /**
358         * {@inheritDoc} <b>Warning</b> this method gives access to the original
359         * Collection not a copy. This is required by Hibernate. If you modify the
360         * object directly the behaviour may be unpredictable.
361         */
362        public Set<Feature> getFeatureSet() {
363                return this.features;
364        } // must be original for Hibernate
365
366        /**
367         * {@inheritDoc} <b>Warning</b> this method gives access to the original
368         * Collection not a copy. This is required by Hibernate. If you modify the
369         * object directly the behaviour may be unpredictable.
370         */
371        public void setFeatureSet(Set<Feature> features) throws ChangeVetoException {
372                this.features = features;
373        } // must be original for Hibernate
374
375        /**
376         * {@inheritDoc}
377         */
378        public FeatureFilter getSchema() {
379                return FeatureFilter.top_level;
380        }
381
382        /**
383         * {@inheritDoc} <b>Warning</b> this method gives access to the original
384         * Collection not a copy. This is required by Hibernate. If you modify the
385         * object directly the behaviour may be unpredictable.
386         */
387        public Iterator<Feature> features() {
388                return this.getFeatureSet().iterator();
389        }
390
391        /**
392         * {@inheritDoc}
393         */
394        public int countFeatures() {
395                return this.features.size();
396        }
397
398        /**
399         * {@inheritDoc}
400         */
401        public SymbolList getInternalSymbolList() {
402                return SymbolList.EMPTY_LIST;
403        }
404}