check annotation size when cutting
[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   final 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   final public AlignmentI getAlignment()\r
87   {\r
88     return edits[0].al;\r
89   }\r
90 \r
91 \r
92   final 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   final 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   final public void doCommand()\r
145   {\r
146     performEdit(0);\r
147   }\r
148 \r
149   final 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   final 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   final 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         if(command.seqs[i].getDatasetSequence()!=null\r
207         || command.seqs[i].getSequenceFeatures()!=null)\r
208         {\r
209           for (int s = command.position; s < command.position + command.number; s++)\r
210           {\r
211             if (jalview.schemes.ResidueProperties\r
212                 .aaIndex[command.seqs[i].getCharAt(s)] != 23)\r
213             {\r
214               adjustFeatures(command, i,\r
215                              command.seqs[i].findPosition(command.position),\r
216                              command.seqs[i].findPosition(command.position +\r
217                                                           command.number),\r
218                              false);\r
219               break;\r
220             }\r
221           }\r
222         }\r
223         command.seqs[i].deleteChars(command.position,\r
224                                     command.position + command.number);\r
225       }\r
226 \r
227       if(command.seqs[i].getLength()<1)\r
228       {\r
229         command.al.deleteSequence(command.seqs[i]);\r
230       }\r
231     }\r
232 \r
233     adjustAnnotations(command, false);\r
234   }\r
235 \r
236   void paste(Edit command)\r
237   {\r
238     StringBuffer tmp;\r
239     boolean newDSNeeded;\r
240     int start=0, end=0;\r
241 \r
242     for(int i=0; i<command.seqs.length; i++)\r
243     {\r
244       newDSNeeded = false;\r
245       if(command.seqs[i].getLength()<1)\r
246       {\r
247         // ie this sequence was deleted, we need to\r
248         // read it to the alignment\r
249         if (command.alIndex[i] < command.al.getHeight())\r
250           command.al.getSequences().insertElementAt(command.seqs[i],\r
251               command.alIndex[i]);\r
252         else\r
253           command.al.addSequence(command.seqs[i]);\r
254       }\r
255       tmp = new StringBuffer();\r
256       tmp.append(command.seqs[i].getSequence());\r
257 \r
258       if(command.string!=null && command.string[i]!=null)\r
259       {\r
260         if(command.position>=tmp.length())\r
261         {\r
262           //This occurs if padding is on, and residues\r
263           //are removed from end of alignment\r
264           int length = command.position-tmp.length();\r
265           while (length > 0)\r
266           {\r
267             tmp.append(command.gapChar);\r
268             length--;\r
269           }\r
270         }\r
271         tmp.insert(command.position, command.string[i]);\r
272 \r
273         for (int s = 0; s < command.string[i].length; s++)\r
274         {\r
275           if (jalview.schemes.ResidueProperties.aaIndex[command.string[i][s]] != 23)\r
276           {\r
277             newDSNeeded = true;\r
278             start = command.seqs[i].findPosition(command.position);\r
279             end = command.seqs[i].findPosition(command.position+command.number);\r
280             break;\r
281           }\r
282         }\r
283         command.string[i] = null;\r
284       }\r
285 \r
286 \r
287       command.seqs[i].setSequence(tmp.toString());\r
288 \r
289       if(newDSNeeded)\r
290       {\r
291         if (command.seqs[i].getDatasetSequence() != null)\r
292         {\r
293           Sequence ds = new Sequence(command.seqs[i].getName(),\r
294                                      jalview.analysis.AlignSeq.extractGaps(\r
295                                          jalview.util.Comparison.GapChars,\r
296                                          command.seqs[i].getSequenceAsString()\r
297                                      ),\r
298                                      command.seqs[i].getStart(),\r
299                                      command.seqs[i].getEnd());\r
300           ds.setDescription(command.seqs[i].getDescription());\r
301           command.seqs[i].setDatasetSequence(ds);\r
302         }\r
303 \r
304         adjustFeatures(command, i, start, end, true);\r
305       }\r
306     }\r
307 \r
308 \r
309     adjustAnnotations(command, true);\r
310 \r
311     command.string = null;\r
312   }\r
313 \r
314 \r
315   final void adjustAnnotations(Edit command, boolean insert)\r
316   {\r
317 \r
318     AlignmentAnnotation [] annotations = null;\r
319 \r
320     if (command.fullAlignmentHeight)\r
321     {\r
322       annotations = command.al.getAlignmentAnnotation();\r
323     }\r
324     else\r
325     {\r
326       int aSize = 0;\r
327       AlignmentAnnotation [] tmp;\r
328       for(int s=0; s<command.seqs.length; s++)\r
329       {\r
330         if(command.seqs[s].getAnnotation()==null)\r
331           continue;\r
332 \r
333         if (aSize == 0)\r
334             annotations = command.seqs[s].getAnnotation();\r
335         else\r
336         {\r
337           tmp = new AlignmentAnnotation\r
338               [aSize + command.seqs[s].getAnnotation().length];\r
339 \r
340           System.arraycopy(annotations,0,tmp,0,aSize);\r
341 \r
342           System.arraycopy(command.seqs[s].getAnnotation(),\r
343               0,tmp,aSize,command.seqs[s].getAnnotation().length);\r
344 \r
345           annotations = tmp;\r
346         }\r
347 \r
348 \r
349         aSize = annotations.length;\r
350       }\r
351     }\r
352 \r
353     if(annotations==null)\r
354       return;\r
355 \r
356 \r
357       if(!insert)\r
358         command.deletedAnnotations = new Hashtable();\r
359 \r
360       int aSize;\r
361       Annotation [] temp;\r
362       for (int a = 0; a < annotations.length; a++)\r
363       {\r
364         if(annotations[a].autoCalculated)\r
365         {\r
366           continue;\r
367         }\r
368 \r
369         aSize = annotations[a].annotations.length;\r
370         if(insert)\r
371           temp = new Annotation[aSize + command.number];\r
372         else\r
373         {\r
374           if(command.position<aSize)\r
375             temp = new Annotation[aSize - command.number + command.position];\r
376           else\r
377             temp = new Annotation[aSize];\r
378         }\r
379 \r
380         if(insert)\r
381         {\r
382           if(command.position < annotations[a].annotations.length)\r
383           {\r
384             System.arraycopy(annotations[a].annotations,\r
385                              0, temp, 0, command.position);\r
386 \r
387             if (command.deletedAnnotations != null\r
388                 &&\r
389                 command.deletedAnnotations.containsKey(annotations[a].annotationId))\r
390             {\r
391               Annotation[] restore = (Annotation[])\r
392                   command.deletedAnnotations.get(annotations[a].annotationId);\r
393 \r
394               System.arraycopy(restore,\r
395                                0,\r
396                                temp,\r
397                                command.position,\r
398                                command.number);\r
399 \r
400             }\r
401 \r
402             System.arraycopy(annotations[a].annotations,\r
403                              command.position, temp,\r
404                              command.position + command.number,\r
405                              aSize - command.position);\r
406           }\r
407           else\r
408             temp = annotations[a].annotations;\r
409         }\r
410         else\r
411         {\r
412           if(command.position < aSize)\r
413           {\r
414             System.arraycopy(annotations[a].annotations,\r
415                              0, temp, 0, command.position);\r
416 \r
417             Annotation[] deleted = new Annotation[command.number];\r
418             System.arraycopy(annotations[a].annotations,\r
419                              command.position, deleted, 0, command.number);\r
420 \r
421             command.deletedAnnotations.put(annotations[a].annotationId,\r
422                                            deleted);\r
423 \r
424             System.arraycopy(annotations[a].annotations,\r
425                              command.position + command.number,\r
426                              temp, command.position,\r
427                              aSize - command.position - command.number);\r
428           }\r
429           else\r
430             temp = annotations[a].annotations;\r
431         }\r
432 \r
433         annotations[a].annotations = temp;\r
434      }\r
435   }\r
436 \r
437   final void adjustFeatures(Edit command, int index, int i, int j, boolean insert)\r
438   {\r
439     SequenceI seq = command.seqs[index];\r
440     SequenceI sequence = seq.getDatasetSequence();\r
441     if(sequence==null)\r
442       sequence = seq;\r
443 \r
444     if(insert)\r
445     {\r
446       if (command.editedFeatures != null\r
447           && command.editedFeatures.containsKey(seq))\r
448         sequence.setSequenceFeatures(\r
449             (SequenceFeature[]) command.editedFeatures.get(seq)\r
450             );\r
451 \r
452       return;\r
453     }\r
454 \r
455 \r
456     SequenceFeature [] sf = sequence.getSequenceFeatures();\r
457 \r
458 \r
459     if(sf==null)\r
460     {\r
461       return;\r
462     }\r
463 \r
464     SequenceFeature [] oldsf = new SequenceFeature[sf.length];\r
465 \r
466     int cSize = j - i;\r
467 \r
468     for (int s = 0; s < sf.length; s++)\r
469     {\r
470       SequenceFeature copy = new SequenceFeature(sf[s]);\r
471 \r
472       oldsf[s] = copy;\r
473 \r
474       if (sf[s].getEnd() < i)\r
475         continue;\r
476 \r
477       if (sf[s].getBegin() > j)\r
478       {\r
479         sf[s].setBegin(copy.getBegin() - cSize);\r
480         sf[s].setEnd(copy.getEnd() - cSize);\r
481         continue;\r
482       }\r
483 \r
484       if (sf[s].getBegin() >= i)\r
485         sf[s].setBegin(i);\r
486 \r
487       if (sf[s].getEnd() < j)\r
488         sf[s].setEnd(j - 1);\r
489 \r
490       sf[s].setEnd(sf[s].getEnd() - (cSize));\r
491 \r
492       if (sf[s].getBegin() > sf[s].getEnd())\r
493         sequence.deleteFeature(sf[s]);\r
494     }\r
495 \r
496     if (command.editedFeatures == null)\r
497       command.editedFeatures = new Hashtable();\r
498 \r
499     command.editedFeatures.put(seq, oldsf);\r
500 \r
501   }\r
502 \r
503 \r
504   class Edit\r
505   {\r
506     boolean fullAlignmentHeight = false;\r
507     Hashtable deletedAnnotations;\r
508     Hashtable editedFeatures;\r
509     AlignmentI al;\r
510     int command;\r
511     char [][] string;\r
512     SequenceI[] seqs;\r
513     int [] alIndex;\r
514     int position, number;\r
515     char gapChar;\r
516 \r
517     Edit(int command,\r
518          SequenceI[] seqs,\r
519          int position,\r
520          int number,\r
521          char gapChar)\r
522     {\r
523       this.command = command;\r
524       this.seqs = seqs;\r
525       this.position = position;\r
526       this.number = number;\r
527       this.gapChar = gapChar;\r
528     }\r
529 \r
530 \r
531     Edit(int command,\r
532          SequenceI[] seqs,\r
533          int position,\r
534          int number,\r
535          AlignmentI al)\r
536     {\r
537       this.gapChar = al.getGapCharacter();\r
538       this.command = command;\r
539       this.seqs = seqs;\r
540       this.position = position;\r
541       this.number = number;\r
542       this.al = al;\r
543 \r
544       alIndex = new int[seqs.length];\r
545       for(int i=0; i<seqs.length; i++)\r
546       {\r
547         alIndex[i] = al.findIndex(seqs[i]);\r
548       }\r
549 \r
550       fullAlignmentHeight = (al.getHeight()==seqs.length);\r
551     }\r
552   }\r
553 \r
554 }\r