Alignment editing columns 0 and 1 fixed
[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         int tSize = 0;\r
370 \r
371         aSize = annotations[a].annotations.length;\r
372         if(insert)\r
373           temp = new Annotation[aSize + command.number];\r
374         else\r
375         {\r
376           if(command.position<aSize)\r
377           {\r
378             if(command.position+command.number>aSize)\r
379               tSize = aSize;\r
380             else\r
381              {\r
382                 tSize = aSize - command.number + command.position;\r
383              }\r
384           }\r
385           else\r
386             tSize = aSize;\r
387 \r
388           if(tSize<0)\r
389           {\r
390             tSize = aSize;\r
391           }\r
392           temp = new Annotation[tSize];\r
393 \r
394         }\r
395 \r
396         if(insert)\r
397         {\r
398           if(command.position < annotations[a].annotations.length)\r
399           {\r
400             System.arraycopy(annotations[a].annotations,\r
401                              0, temp, 0, command.position);\r
402 \r
403             if (command.deletedAnnotations != null\r
404                 &&\r
405                 command.deletedAnnotations.containsKey(annotations[a].annotationId))\r
406             {\r
407               Annotation[] restore = (Annotation[])\r
408                   command.deletedAnnotations.get(annotations[a].annotationId);\r
409 \r
410               System.arraycopy(restore,\r
411                                0,\r
412                                temp,\r
413                                command.position,\r
414                                command.number);\r
415 \r
416             }\r
417 \r
418             System.arraycopy(annotations[a].annotations,\r
419                              command.position, temp,\r
420                              command.position + command.number,\r
421                              aSize - command.position);\r
422           }\r
423           else\r
424           {\r
425             if (command.deletedAnnotations != null\r
426                            &&\r
427                            command.deletedAnnotations.containsKey(annotations[a].annotationId))\r
428             {\r
429               Annotation[] restore = (Annotation[])\r
430                   command.deletedAnnotations.get(annotations[a].annotationId);\r
431 \r
432               temp = new Annotation[annotations[a].annotations.length+\r
433                   restore.length];\r
434               System.arraycopy(annotations[a].annotations,\r
435                                0,temp,0,\r
436                                annotations[a].annotations.length);\r
437               System.arraycopy(restore,0,temp,annotations[a].annotations.length,restore.length);\r
438             }\r
439             else\r
440               temp = annotations[a].annotations;\r
441           }\r
442         }\r
443         else\r
444         {\r
445           if(tSize!=aSize || command.position<2)\r
446           {\r
447             System.arraycopy(annotations[a].annotations,\r
448                              0, temp, 0, command.position);\r
449 \r
450             Annotation[] deleted = new Annotation[command.number];\r
451             System.arraycopy(annotations[a].annotations,\r
452                              command.position, deleted, 0, command.number);\r
453 \r
454             command.deletedAnnotations.put(annotations[a].annotationId,\r
455                                            deleted);\r
456 \r
457             System.arraycopy(annotations[a].annotations,\r
458                              command.position + command.number,\r
459                              temp, command.position,\r
460                              aSize - command.position - command.number);\r
461           }\r
462           else\r
463           {\r
464             int dSize = aSize - command.position;\r
465 \r
466             if(dSize>0)\r
467             {\r
468               Annotation[] deleted = new Annotation[command.number];\r
469               System.arraycopy(annotations[a].annotations,\r
470                                command.position, deleted, 0, dSize);\r
471 \r
472               command.deletedAnnotations.put(annotations[a].annotationId,\r
473                                              deleted);\r
474 \r
475               tSize = Math.min(annotations[a].annotations.length, command.position);\r
476               temp = new Annotation[tSize];\r
477               System.arraycopy(annotations[a].annotations,\r
478                   0, temp, 0, tSize);\r
479             }\r
480             else\r
481               temp = annotations[a].annotations;\r
482           }\r
483         }\r
484 \r
485         annotations[a].annotations = temp;\r
486      }\r
487   }\r
488 \r
489   final void adjustFeatures(Edit command, int index, int i, int j, boolean insert)\r
490   {\r
491     SequenceI seq = command.seqs[index];\r
492     SequenceI sequence = seq.getDatasetSequence();\r
493     if(sequence==null)\r
494       sequence = seq;\r
495 \r
496     if(insert)\r
497     {\r
498       if (command.editedFeatures != null\r
499           && command.editedFeatures.containsKey(seq))\r
500         sequence.setSequenceFeatures(\r
501             (SequenceFeature[]) command.editedFeatures.get(seq)\r
502             );\r
503 \r
504       return;\r
505     }\r
506 \r
507 \r
508     SequenceFeature [] sf = sequence.getSequenceFeatures();\r
509 \r
510 \r
511     if(sf==null)\r
512     {\r
513       return;\r
514     }\r
515 \r
516     SequenceFeature [] oldsf = new SequenceFeature[sf.length];\r
517 \r
518     int cSize = j - i;\r
519 \r
520     for (int s = 0; s < sf.length; s++)\r
521     {\r
522       SequenceFeature copy = new SequenceFeature(sf[s]);\r
523 \r
524       oldsf[s] = copy;\r
525 \r
526       if (sf[s].getEnd() < i)\r
527         continue;\r
528 \r
529       if (sf[s].getBegin() > j)\r
530       {\r
531         sf[s].setBegin(copy.getBegin() - cSize);\r
532         sf[s].setEnd(copy.getEnd() - cSize);\r
533         continue;\r
534       }\r
535 \r
536       if (sf[s].getBegin() >= i)\r
537         sf[s].setBegin(i);\r
538 \r
539       if (sf[s].getEnd() < j)\r
540         sf[s].setEnd(j - 1);\r
541 \r
542       sf[s].setEnd(sf[s].getEnd() - (cSize));\r
543 \r
544       if (sf[s].getBegin() > sf[s].getEnd())\r
545         sequence.deleteFeature(sf[s]);\r
546     }\r
547 \r
548     if (command.editedFeatures == null)\r
549       command.editedFeatures = new Hashtable();\r
550 \r
551     command.editedFeatures.put(seq, oldsf);\r
552 \r
553   }\r
554 \r
555 \r
556   class Edit\r
557   {\r
558     boolean fullAlignmentHeight = false;\r
559     Hashtable deletedAnnotations;\r
560     Hashtable editedFeatures;\r
561     AlignmentI al;\r
562     int command;\r
563     char [][] string;\r
564     SequenceI[] seqs;\r
565     int [] alIndex;\r
566     int position, number;\r
567     char gapChar;\r
568 \r
569     Edit(int command,\r
570          SequenceI[] seqs,\r
571          int position,\r
572          int number,\r
573          char gapChar)\r
574     {\r
575       this.command = command;\r
576       this.seqs = seqs;\r
577       this.position = position;\r
578       this.number = number;\r
579       this.gapChar = gapChar;\r
580     }\r
581 \r
582 \r
583     Edit(int command,\r
584          SequenceI[] seqs,\r
585          int position,\r
586          int number,\r
587          AlignmentI al)\r
588     {\r
589       this.gapChar = al.getGapCharacter();\r
590       this.command = command;\r
591       this.seqs = seqs;\r
592       this.position = position;\r
593       this.number = number;\r
594       this.al = al;\r
595 \r
596       alIndex = new int[seqs.length];\r
597       for(int i=0; i<seqs.length; i++)\r
598       {\r
599         alIndex[i] = al.findIndex(seqs[i]);\r
600       }\r
601 \r
602       fullAlignmentHeight = (al.getHeight()==seqs.length);\r
603     }\r
604   }\r
605 \r
606 }\r