JAL-653 JAL-1968 FeaturesFile now handles Jalview or GFF2 or GFF3
[jalview.git] / src / jalview / io / packed / JalviewDataset.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.io.packed;
22
23 import jalview.datamodel.AlignmentI;
24 import jalview.datamodel.SequenceI;
25 import jalview.io.NewickFile;
26
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.Hashtable;
30 import java.util.List;
31 import java.util.Map;
32
33 public class JalviewDataset
34 {
35   /**
36    * dataset that new data (sequences, alignments) will be added to
37    */
38   AlignmentI parentDataset;
39
40   /**
41    * @return the parentDataset
42    */
43   public AlignmentI getParentDataset()
44   {
45     return parentDataset;
46   }
47
48   /**
49    * @param parentDataset
50    *          the parentDataset to set
51    */
52   public void setParentDataset(AlignmentI parentDataset)
53   {
54     this.parentDataset = parentDataset;
55   }
56
57   /**
58    * @return the featureColours
59    */
60   public Map<String, Object> getFeatureColours()
61   {
62     return featureColours;
63   }
64
65   /**
66    * @param featureColours
67    *          the featureColours to set
68    */
69   public void setFeatureColours(Map<String, Object> featureColours)
70   {
71     this.featureColours = featureColours;
72   }
73
74   /**
75    * @return the seqDetails
76    */
77   public Hashtable getSeqDetails()
78   {
79     return seqDetails;
80   }
81
82   /**
83    * @param seqDetails
84    *          the seqDetails to set
85    */
86   public void setSeqDetails(Hashtable seqDetails)
87   {
88     this.seqDetails = seqDetails;
89   }
90
91   /**
92    * @return the al
93    */
94   public List<AlignmentSet> getAl()
95   {
96     return (al == null) ? new ArrayList<AlignmentSet>() : al;
97   }
98
99   /**
100    * current alignment being worked on.
101    */
102   List<AlignmentSet> al;
103
104   public class AlignmentSet
105   {
106     public AlignmentI al;
107
108     public List<jalview.io.NewickFile> trees;
109
110     AlignmentSet(AlignmentI a)
111     {
112       al = a;
113       trees = new ArrayList<jalview.io.NewickFile>();
114     }
115
116     /**
117      * deuniquify the current alignment in the context, merging any new
118      * annotation/features with the existing set
119      * 
120      * @param context
121      */
122     void deuniquifyAlignment()
123     {
124       if (seqDetails == null || seqDetails.size() == 0)
125       {
126         // nothing to do
127         return;
128       }
129       // 1. recover correct names and attributes for each sequence in alignment.
130       /*
131        * TODO: housekeeping w.r.t. recovery of dataset and annotation references
132        * for input sequences, and then dataset sequence creation for new
133        * sequences retrieved from service // finally, attempt to de-uniquify to
134        * recover input sequence identity, and try to map back onto dataset Note:
135        * this jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs, true);
136        * will NOT WORK - the returned alignment may contain multiple versions of
137        * the input sequence, each being a subsequence of the original.
138        * deuniquify also removes existing annotation and features added in the
139        * previous step... al.setDataset(dataset); // add in new sequences
140        * retrieved from sequence search which are not already in dataset. //
141        * trigger a 'fetchDBids' to annotate sequences with database ids...
142        */
143       // jalview.analysis.SeqsetUtils.deuniquifyAndMerge(parentDataset,
144       // seqDetails, al,true);
145
146       jalview.analysis.SeqsetUtils.deuniquify(seqDetails,
147               al.getSequencesArray(), true);
148       // 2. Update names of associated nodes in any trees
149       for (NewickFile nf : trees)
150       {
151         // the following works because all trees are already had node/SequenceI
152         // associations created.
153         jalview.analysis.NJTree njt = new jalview.analysis.NJTree(
154                 al.getSequencesArray(), nf);
155         // this just updates the displayed leaf name on the tree according to
156         // the SequenceIs.
157         njt.renameAssociatedNodes();
158       }
159
160     }
161
162     /**
163      * set modification flag. If anything modifies the alignment in the current
164      * set, this flag should be true
165      */
166     private boolean modified = false;
167
168     /**
169      * @return the modified
170      */
171     public boolean isModified()
172     {
173       return modified;
174     }
175
176     /**
177      * or the modification state with the given state
178      * 
179      * @param modifiedFromAction
180      */
181     public void updateSetModified(boolean modifiedFromAction)
182     {
183       modified |= modifiedFromAction;
184     }
185   }
186
187   /**
188    * current set of feature colours
189    */
190   Map<String, Object> featureColours;
191
192   /**
193    * original identity of each sequence in results
194    */
195   Hashtable seqDetails;
196
197   public boolean relaxedIdMatching = false;
198
199   public JalviewDataset()
200   {
201     seqDetails = new Hashtable();
202     al = new ArrayList<AlignmentSet>();
203     parentDataset = null;
204     featureColours = new HashMap<String, Object>();
205   }
206
207   /**
208    * context created from an existing alignment.
209    * 
210    * @param parentAlignment
211    */
212   public JalviewDataset(AlignmentI aldataset, Map<String, Object> fc,
213           Hashtable seqDets)
214   {
215     // TODO not used - remove?
216     this(aldataset, fc, seqDets, null);
217   }
218
219   /**
220    * 
221    * @param aldataset
222    *          - parent dataset for any new alignment/sequence data (must not be
223    *          null)
224    * @param fc
225    *          (may be null) feature settings for the alignment where new feature
226    *          renderstyles are stored
227    * @param seqDets
228    *          - (may be null) anonymised sequence information created by
229    *          Sequence uniquifier
230    * @param parentAlignment
231    *          (may be null) alignment to associate new annotation and trees
232    *          with.
233    */
234   public JalviewDataset(AlignmentI aldataset, Map<String, Object> fc,
235           Hashtable seqDets, AlignmentI parentAlignment)
236   {
237     this();
238     parentDataset = aldataset;
239     if (parentAlignment != null)
240     {
241       parentDataset = parentAlignment.getDataset();
242       if (parentDataset == null)
243       {
244         parentDataset = parentAlignment;
245       }
246       else
247       {
248         addAlignment(parentAlignment);
249       }
250     }
251     if (seqDets != null)
252     {
253       seqDetails = seqDets;
254     }
255     if (fc != null)
256     {
257       featureColours = fc;
258     }
259
260   }
261
262   public boolean hasAlignments()
263   {
264     return al != null && al.size() > 0;
265   }
266
267   public AlignmentI getLastAlignment()
268   {
269     return (al == null || al.size() < 1) ? null : al.get(al.size() - 1).al;
270   }
271
272   public AlignmentSet getLastAlignmentSet()
273   {
274     return (al == null || al.size() < 1) ? null : al.get(al.size() - 1);
275   }
276
277   /**
278    * post process (deuniquify) the current alignment and its dependent data, and
279    * then add newal to the dataset.
280    * 
281    * @param newal
282    */
283   public void addAlignment(AlignmentI newal)
284   {
285     if (!hasAlignments())
286     {
287       al = new ArrayList<AlignmentSet>();
288     }
289     AlignmentSet last = getLastAlignmentSet();
290     if (last != null)
291     {
292       System.err.println("Deuniquifying last alignment set.");
293       last.deuniquifyAlignment();
294     }
295     al.add(new AlignmentSet(newal));
296   }
297
298   public void addTreeFromFile(NewickFile nf)
299   {
300     AlignmentSet lal = getLastAlignmentSet();
301     lal.trees.add(nf);
302   }
303
304   public boolean hasSequenceAssoc()
305   {
306     // TODO: discover where sequence associated data should be put.
307     return false;
308   }
309
310   public SequenceI getLastAssociatedSequence()
311   {
312     // TODO: delineate semantics for associating uniquified data with
313     // potentially de-uniquified sequence.
314     return null;
315   }
316
317   /**
318    * update the modified state flag for the current set with the given
319    * modification state
320    * 
321    * @param modified
322    *          - this will be ored with current modification state
323    */
324   public void updateSetModified(boolean modified)
325   {
326     getLastAlignmentSet().updateSetModified(modified);
327   }
328 }