Adjusts annotations for sequence edits
[jalview.git] / src / jalview / commands / EditCommand.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2006 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
4  *\r
5  * This program is free software; you can redistribute it and/or\r
6  * modify it under the terms of the GNU General Public License\r
7  * as published by the Free Software Foundation; either version 2\r
8  * of the License, or (at your option) any later version.\r
9  *\r
10  * This program is distributed in the hope that it will be useful,\r
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13  * GNU General Public License for more details.\r
14  *\r
15  * You should have received a copy of the GNU General Public License\r
16  * along with this program; if not, write to the Free Software\r
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
18  */\r
19 package jalview.commands;\r
20 \r
21 import jalview.datamodel.*;\r
22 \r
23 import java.util.Hashtable;\r
24 \r
25 /**\r
26  *\r
27  * <p>Title: EditCommmand</p>\r
28  *\r
29  * <p>Description: Essential information for performing\r
30  * undo and redo for cut/paste insert/delete gap\r
31  * which can be stored in the HistoryList </p>\r
32  *\r
33  * <p>Copyright: Copyright (c) 2006</p>\r
34  *\r
35  * <p>Company: Dundee University</p>\r
36  *\r
37  * @author not attributable\r
38  * @version 1.0\r
39  */\r
40 public class EditCommand implements CommandI\r
41 {\r
42   public static final int INSERT_GAP = 0;\r
43   public static final int DELETE_GAP = 1;\r
44   public static final int CUT = 2;\r
45   public static final int PASTE = 3;\r
46 \r
47   Edit[] edits;\r
48 \r
49   String description;\r
50 \r
51   public EditCommand()\r
52   {}\r
53 \r
54   public EditCommand(String description)\r
55   {\r
56     this.description = description;\r
57   }\r
58 \r
59   public EditCommand( String description,\r
60                       int command,\r
61                       SequenceI[] seqs,\r
62                       int position,\r
63                       int number,\r
64                       AlignmentI al)\r
65    {\r
66      this.description = description;\r
67      if ( command==CUT || command==PASTE)\r
68      {\r
69        edits = new Edit[]{new Edit(command, seqs, position, number, al)};\r
70      }\r
71 \r
72      performEdit(0);\r
73   }\r
74 \r
75 \r
76   public String getDescription()\r
77   {\r
78     return description;\r
79   }\r
80 \r
81   public int getSize()\r
82   {\r
83     return edits==null?0:edits.length;\r
84   }\r
85 \r
86   public AlignmentI getAlignment()\r
87   {\r
88     return edits[0].al;\r
89   }\r
90 \r
91 \r
92   public void appendEdit(int command,\r
93                          SequenceI[] seqs,\r
94                          int position,\r
95                          int number,\r
96                          AlignmentI al,\r
97                          boolean performEdit)\r
98   {\r
99     Edit edit = new Edit(command, seqs, position, number, al.getGapCharacter());\r
100     if(al.getHeight()==seqs.length)\r
101     {\r
102       edit.al = al;\r
103       edit.fullAlignmentHeight = true;\r
104     }\r
105 \r
106     if (edits != null)\r
107     {\r
108       Edit[] temp = new Edit[edits.length + 1];\r
109       System.arraycopy(edits, 0, temp, 0, edits.length);\r
110       edits = temp;\r
111       edits[edits.length - 1] = edit;\r
112     }\r
113     else\r
114       edits = new Edit[] {  edit  };\r
115 \r
116     if (performEdit)\r
117       performEdit(edits.length - 1);\r
118   }\r
119 \r
120   void performEdit(int commandIndex)\r
121   {\r
122     int eSize = edits.length;\r
123     for (int e = commandIndex; e < eSize; e++)\r
124     {\r
125       if (edits[e].command==INSERT_GAP)\r
126       {\r
127         insertGap(edits[e]);\r
128       }\r
129       else if (edits[e].command==DELETE_GAP)\r
130       {\r
131         deleteGap(edits[e]);\r
132       }\r
133       else if(edits[e].command==CUT)\r
134       {\r
135         cut(edits[e]);\r
136       }\r
137       else if(edits[e].command==PASTE)\r
138       {\r
139         paste(edits[e]);\r
140       }\r
141     }\r
142   }\r
143 \r
144   public void doCommand()\r
145   {\r
146     performEdit(0);\r
147   }\r
148 \r
149   public void undoCommand()\r
150   {\r
151     int e = 0, eSize = edits.length;\r
152     for (e = eSize-1; e > -1; e--)\r
153     {\r
154       if (edits[e].command==INSERT_GAP)\r
155       {\r
156         deleteGap(edits[e]);\r
157       }\r
158       else if (edits[e].command==DELETE_GAP)\r
159       {\r
160         insertGap(edits[e]);\r
161       }\r
162       else if (edits[e].command==CUT)\r
163       {\r
164         paste(edits[e]);\r
165       }\r
166       else if (edits[e].command==PASTE)\r
167       {\r
168         cut(edits[e]);\r
169       }\r
170     }\r
171   }\r
172 \r
173   void insertGap(Edit command)\r
174   {\r
175     for(int s=0; s<command.seqs.length; s++)\r
176     {\r
177         command.seqs[s].insertCharAt(command.position,\r
178                                      command.number,\r
179                                      command.gapChar);\r
180     }\r
181 \r
182     adjustAnnotations(command, true);\r
183   }\r
184 \r
185   void deleteGap(Edit command)\r
186   {\r
187     for (int s = 0; s < command.seqs.length; s++)\r
188     {\r
189       command.seqs[s].deleteChars(command.position, command.position+command.number);\r
190     }\r
191 \r
192     adjustAnnotations(command, false);\r
193   }\r
194 \r
195   void cut(Edit command)\r
196   {\r
197     command.string = new char [command.seqs.length][];\r
198 \r
199     for(int i=0; i<command.seqs.length; i++)\r
200     {\r
201       if(command.seqs[i].getLength()>command.position)\r
202       {\r
203         command.string[i] = command.seqs[i].getSequence(command.position,\r
204             command.position + command.number);\r
205 \r
206         command.seqs[i].deleteChars(command.position,\r
207                                     command.position + command.number);\r
208       }\r
209 \r
210       if(command.seqs[i].getLength()<1)\r
211       {\r
212         command.al.deleteSequence(command.seqs[i]);\r
213       }\r
214     }\r
215 \r
216     adjustAnnotations(command, false);\r
217   }\r
218 \r
219   void paste(Edit command)\r
220   {\r
221     StringBuffer tmp;\r
222     for(int i=0; i<command.seqs.length; i++)\r
223     {\r
224       if(command.seqs[i].getLength()<1)\r
225       {\r
226         // ie this sequence was deleted, we need to\r
227         // read it to the alignment\r
228         if (command.alIndex[i] < command.al.getHeight())\r
229           command.al.getSequences().insertElementAt(command.seqs[i],\r
230               command.alIndex[i]);\r
231         else\r
232           command.al.addSequence(command.seqs[i]);\r
233       }\r
234       tmp = new StringBuffer();\r
235       tmp.append(command.seqs[i].getSequence());\r
236 \r
237       if(command.string!=null && command.string[i]!=null)\r
238       {\r
239         if(command.position>=tmp.length())\r
240         {\r
241           //This occurs if padding is on, and residues\r
242           //are removed from end of alignment\r
243           int length = command.position-tmp.length();\r
244           while (length > 0)\r
245           {\r
246             tmp.append(command.gapChar);\r
247             length--;\r
248           }\r
249         }\r
250         tmp.insert(command.position, command.string[i]);\r
251         command.string[i] = null;\r
252       }\r
253       command.seqs[i].setSequence(tmp.toString());\r
254     }\r
255 \r
256 \r
257     adjustAnnotations(command, true);\r
258 \r
259     command.string = null;\r
260   }\r
261 \r
262 \r
263   void adjustAnnotations(Edit command, boolean insert)\r
264   {\r
265 \r
266     AlignmentAnnotation [] annotations = null;\r
267 \r
268     if (command.fullAlignmentHeight)\r
269     {\r
270       annotations = command.al.getAlignmentAnnotation();\r
271     }\r
272     else\r
273     {\r
274       int aSize = 0;\r
275       AlignmentAnnotation [] tmp;\r
276       for(int s=0; s<command.seqs.length; s++)\r
277       {\r
278         if(command.seqs[s].getAnnotation()==null)\r
279           continue;\r
280 \r
281         if (aSize == 0)\r
282             annotations = command.seqs[s].getAnnotation();\r
283         else\r
284         {\r
285           tmp = new AlignmentAnnotation\r
286               [aSize + command.seqs[s].getAnnotation().length];\r
287 \r
288           System.arraycopy(annotations,0,tmp,0,aSize);\r
289 \r
290           System.arraycopy(command.seqs[s].getAnnotation(),\r
291               0,tmp,aSize,command.seqs[s].getAnnotation().length);\r
292 \r
293           annotations = tmp;\r
294         }\r
295 \r
296 \r
297         aSize = annotations.length;\r
298       }\r
299     }\r
300 \r
301     if(annotations==null)\r
302       return;\r
303 \r
304 \r
305       if(!insert)\r
306         command.deletedAnnotations = new Hashtable();\r
307 \r
308       int aSize, tSize;\r
309       Annotation [] temp;\r
310       for (int a = 0; a < annotations.length; a++)\r
311       {\r
312         if(annotations[a].autoCalculated)\r
313         {\r
314           continue;\r
315         }\r
316 \r
317         aSize = annotations[a].annotations.length;\r
318         if(insert)\r
319           tSize = aSize + command.number;\r
320         else\r
321           tSize = aSize - command.number;\r
322 \r
323         temp = new Annotation[tSize];\r
324 \r
325         if(insert)\r
326         {\r
327           if(command.position < annotations[a].annotations.length)\r
328           {\r
329             System.arraycopy(annotations[a].annotations,\r
330                              0, temp, 0, command.position);\r
331 \r
332             if (command.deletedAnnotations != null\r
333                 &&\r
334                 command.deletedAnnotations.containsKey(annotations[a].annotationId))\r
335             {\r
336               Annotation[] restore = (Annotation[])\r
337                   command.deletedAnnotations.get(annotations[a].annotationId);\r
338 \r
339               System.arraycopy(restore,\r
340                                0,\r
341                                temp,\r
342                                command.position,\r
343                                command.number);\r
344 \r
345             }\r
346 \r
347             System.arraycopy(annotations[a].annotations,\r
348                              command.position, temp,\r
349                              command.position + command.number,\r
350                              aSize - command.position);\r
351           }\r
352           else\r
353             temp = annotations[a].annotations;\r
354         }\r
355         else\r
356         {\r
357           if(command.position < annotations[a].annotations.length)\r
358           {\r
359             System.arraycopy(annotations[a].annotations,\r
360                              0, temp, 0, command.position);\r
361 \r
362             Annotation[] deleted = new Annotation[command.number];\r
363             System.arraycopy(annotations[a].annotations,\r
364                              command.position, deleted, 0, command.number);\r
365 \r
366             command.deletedAnnotations.put(annotations[a].annotationId,\r
367                                            deleted);\r
368 \r
369             System.arraycopy(annotations[a].annotations,\r
370                              command.position + command.number,\r
371                              temp, command.position,\r
372                              aSize - command.position - command.number);\r
373           }\r
374           else\r
375             temp = annotations[a].annotations;\r
376         }\r
377 \r
378         annotations[a].annotations = temp;\r
379      }\r
380   }\r
381 \r
382 \r
383   class Edit\r
384   {\r
385     boolean fullAlignmentHeight = false;\r
386     Hashtable deletedAnnotations;\r
387     AlignmentI al;\r
388     int command;\r
389     char [][] string;\r
390     SequenceI[] seqs;\r
391     int [] alIndex;\r
392     int position, number;\r
393     char gapChar;\r
394 \r
395     Edit(int command,\r
396          SequenceI[] seqs,\r
397          int position,\r
398          int number,\r
399          char gapChar)\r
400     {\r
401       this.command = command;\r
402       this.seqs = seqs;\r
403       this.position = position;\r
404       this.number = number;\r
405       this.gapChar = gapChar;\r
406     }\r
407 \r
408 \r
409     Edit(int command,\r
410          SequenceI[] seqs,\r
411          int position,\r
412          int number,\r
413          AlignmentI al)\r
414     {\r
415       this.gapChar = al.getGapCharacter();\r
416       this.command = command;\r
417       this.seqs = seqs;\r
418       this.position = position;\r
419       this.number = number;\r
420       this.al = al;\r
421 \r
422       alIndex = new int[seqs.length];\r
423       for(int i=0; i<seqs.length; i++)\r
424       {\r
425         alIndex[i] = al.findIndex(seqs[i]);\r
426       }\r
427 \r
428       fullAlignmentHeight = (al.getHeight()==seqs.length);\r
429     }\r
430   }\r
431 \r
432 }\r