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