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.distributed;
023
024import java.util.HashSet;
025import java.util.Iterator;
026import java.util.Set;
027
028import org.biojava.bio.BioException;
029import org.biojava.bio.seq.Sequence;
030import org.biojava.bio.seq.db.AbstractSequenceDB;
031import org.biojava.bio.seq.db.IllegalIDException;
032import org.biojava.bio.seq.db.SequenceDB;
033import org.biojava.utils.ChangeEvent;
034import org.biojava.utils.ChangeSupport;
035import org.biojava.utils.ChangeType;
036import org.biojava.utils.ChangeVetoException;
037
038/**
039 * Sequence database from the meta-DAS system.
040 *
041 * @author Thomas Down
042 * @author Matthew Pocock
043 * @since 1.2
044 *
045 * Once you've made one of these and populated it with a few DistDataSource instances,
046 * you should be able to prety much forget about it and use it directly as a normal
047 * SequenceDB implementation.
048 *
049 * DataSources can be added and removed while the object is live. 
050 */
051
052public class DistributedSequenceDB extends AbstractSequenceDB implements SequenceDB {
053    public static final ChangeType DATASOURCE = new ChangeType(
054            "Data sources have changes in a Distributed Sequence DB",
055            "org.biojava.bio.seq.distributed.DistributedSequenceDB",
056            "DATASOURCE",
057            ChangeType.UNKNOWN
058    );
059
060    public static final ChangeType DATASOURCE_SELECTION = new ChangeType(
061            "The set of available data sources has changes in a Distributed Sequence DB",
062            "org.biojava.bio.seq.distributed.DistributedSequenceDB",
063            "DATASOURCE_SELECTION",
064            DistributedSequenceDB.DATASOURCE
065    );
066
067    private Set datasources;
068    private transient ChangeSupport changeSupport;
069
070    protected boolean hasChangeSupport() {
071        return (changeSupport != null);
072    }
073
074    protected ChangeSupport getChangeSupport() {
075        if (changeSupport == null) {
076            changeSupport = new ChangeSupport();
077        }
078        return changeSupport;
079    }
080
081    {
082        datasources = new HashSet();
083    }
084
085    /**
086     * Get the current set of all currently registered data sources.
087     *
088     * <p>
089     * The returned Set is totaly independant of any future changes made to the
090     * distributed sequence DB.
091     * </p>
092     *
093     * @return a new Set containing all DistDataSource instances registered at
094     *    the time
095     */ 
096    public Set getDataSources() {
097        return new HashSet(datasources);
098    }
099
100    /**
101     * Add a distributed data source.
102     *
103     * @param dds  the DistDataSource to add
104     * @throws ChangeVetoException if the data source could not be added
105     */
106    public void addDataSource(DistDataSource dds) 
107        throws ChangeVetoException
108    {
109        if (datasources.contains(dds)) {
110            return;
111        }
112
113        if (hasChangeSupport()) {
114            ChangeSupport cs = getChangeSupport();
115            synchronized (cs) {
116                ChangeEvent cev = new ChangeEvent(this,
117                                                  DATASOURCE_SELECTION,
118                                                  dds,
119                                                  null);
120                cs.firePreChangeEvent(cev);
121                _addDataSource(dds);
122                cs.firePostChangeEvent(cev);
123            }
124        } else {
125            _addDataSource(dds);
126        }
127    }
128
129    private void _addDataSource(DistDataSource dds) {
130        datasources.add(dds);
131    }
132
133    /**
134     * Remove a distributed data source.
135     *
136     * @param dds  the DistributedDataSource to remove
137     * @throws ChangeVetoException  if the data source could not be removed
138     */
139    public void removeDataSource(DistDataSource dds) 
140        throws ChangeVetoException
141    {
142        if (!datasources.contains(dds)) {
143            throw new ChangeVetoException("That datasource isn't currently installed");
144        }
145
146        if (hasChangeSupport()) {
147            ChangeSupport cs = getChangeSupport();
148            synchronized (cs) {
149                ChangeEvent cev = new ChangeEvent(this,
150                                                  DATASOURCE_SELECTION,
151                                                  null,
152                                                  dds);
153                cs.firePreChangeEvent(cev);
154                _removeDataSource(dds);
155                cs.firePostChangeEvent(cev);
156            }
157        } else {
158            _removeDataSource(dds);
159        }
160    }
161
162    private void _removeDataSource(DistDataSource dds) {
163        datasources.remove(dds);
164    }
165
166    public String getName() {
167        return "<unknown meta-das>";
168    }
169
170    public void addSequence(Sequence seq)
171        throws ChangeVetoException
172    {
173        throw new ChangeVetoException("Can't add sequences to meta-das");
174    }
175
176    public void removeSequence(String id)
177        throws ChangeVetoException
178    {
179        throw new ChangeVetoException("Can't add sequences to meta-das");
180    }
181
182    public Sequence getSequence(String id)
183        throws IllegalIDException, BioException
184    {
185        Set featureSources = new HashSet();
186        DistDataSource seqSource = null;
187
188        for (Iterator i = datasources.iterator(); i.hasNext(); ) {
189            DistDataSource dds = (DistDataSource) i.next();
190            if (dds.hasSequence(id) && seqSource == null) {
191                seqSource = dds;
192            }
193
194            if (dds.hasFeatures(id)) {
195                featureSources.add(dds);
196            }
197        }
198
199        if (seqSource == null) {
200            throw new IllegalIDException("No sequence source for ID: " + id);
201        }
202
203        return new DistributedSequence(id, this, seqSource, featureSources);
204    }
205
206    public Set ids() {
207        Set ids = new HashSet();
208        for (Iterator i = datasources.iterator(); i.hasNext(); ) {
209            DistDataSource dds = (DistDataSource) i.next();
210            try {
211                ids.addAll(dds.ids(true));
212            } catch (BioException ex) {
213            }
214        }
215        return ids;
216    }
217
218}