Formatting
[jalview.git] / src / jalview / commands / EditCommand.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2007 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 java.util.*;\r
22 \r
23 import jalview.datamodel.*;\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\r
41     implements CommandI\r
42 {\r
43   public static final int INSERT_GAP = 0;\r
44   public static final int DELETE_GAP = 1;\r
45   public static final int CUT = 2;\r
46   public static final int PASTE = 3;\r
47 \r
48   Edit[] edits;\r
49 \r
50   String description;\r
51 \r
52   public EditCommand()\r
53   {}\r
54 \r
55   public EditCommand(String description)\r
56   {\r
57     this.description = description;\r
58   }\r
59 \r
60   public EditCommand(String description,\r
61                      int command,\r
62                      SequenceI[] seqs,\r
63                      int position,\r
64                      int number,\r
65                      AlignmentI al)\r
66   {\r
67     this.description = description;\r
68     if (command == CUT || command == PASTE)\r
69     {\r
70       edits = new Edit[]\r
71           {\r
72           new Edit(command, seqs, position, number, al)};\r
73     }\r
74 \r
75     performEdit(0);\r
76   }\r
77 \r
78   final public String getDescription()\r
79   {\r
80     return description;\r
81   }\r
82 \r
83   public int getSize()\r
84   {\r
85     return edits == null ? 0 : edits.length;\r
86   }\r
87 \r
88   final public AlignmentI getAlignment()\r
89   {\r
90     return edits[0].al;\r
91   }\r
92 \r
93   final public void appendEdit(int command,\r
94                                SequenceI[] seqs,\r
95                                int position,\r
96                                int number,\r
97                                AlignmentI al,\r
98                                boolean performEdit)\r
99   {\r
100     Edit edit = new Edit(command, seqs, position, number, al.getGapCharacter());\r
101     if (al.getHeight() == seqs.length)\r
102     {\r
103       edit.al = al;\r
104       edit.fullAlignmentHeight = true;\r
105     }\r
106 \r
107     if (edits != null)\r
108     {\r
109       Edit[] temp = new Edit[edits.length + 1];\r
110       System.arraycopy(edits, 0, temp, 0, edits.length);\r
111       edits = temp;\r
112       edits[edits.length - 1] = edit;\r
113     }\r
114     else\r
115     {\r
116       edits = new Edit[]\r
117           {\r
118           edit};\r
119     }\r
120 \r
121     if (performEdit)\r
122     {\r
123       performEdit(edits.length - 1);\r
124     }\r
125   }\r
126 \r
127   final void performEdit(int commandIndex)\r
128   {\r
129     int eSize = edits.length;\r
130     for (int e = commandIndex; e < eSize; e++)\r
131     {\r
132       if (edits[e].command == INSERT_GAP)\r
133       {\r
134         insertGap(edits[e]);\r
135       }\r
136       else if (edits[e].command == DELETE_GAP)\r
137       {\r
138         deleteGap(edits[e]);\r
139       }\r
140       else if (edits[e].command == CUT)\r
141       {\r
142         cut(edits[e]);\r
143       }\r
144       else if (edits[e].command == PASTE)\r
145       {\r
146         paste(edits[e]);\r
147       }\r
148     }\r
149   }\r
150 \r
151   final public void doCommand()\r
152   {\r
153     performEdit(0);\r
154   }\r
155 \r
156   final public void undoCommand()\r
157   {\r
158     int e = 0, eSize = edits.length;\r
159     for (e = eSize - 1; e > -1; e--)\r
160     {\r
161       if (edits[e].command == INSERT_GAP)\r
162       {\r
163         deleteGap(edits[e]);\r
164       }\r
165       else if (edits[e].command == DELETE_GAP)\r
166       {\r
167         insertGap(edits[e]);\r
168       }\r
169       else if (edits[e].command == CUT)\r
170       {\r
171         paste(edits[e]);\r
172       }\r
173       else if (edits[e].command == PASTE)\r
174       {\r
175         cut(edits[e]);\r
176       }\r
177     }\r
178   }\r
179 \r
180   final void insertGap(Edit command)\r
181   {\r
182     for (int s = 0; s < command.seqs.length; s++)\r
183     {\r
184       command.seqs[s].insertCharAt(command.position,\r
185                                    command.number,\r
186                                    command.gapChar);\r
187     }\r
188 \r
189     adjustAnnotations(command, true);\r
190   }\r
191 \r
192   final void deleteGap(Edit command)\r
193   {\r
194     for (int s = 0; s < command.seqs.length; s++)\r
195     {\r
196       command.seqs[s].deleteChars(command.position,\r
197                                   command.position + command.number);\r
198     }\r
199 \r
200     adjustAnnotations(command, false);\r
201   }\r
202 \r
203   void cut(Edit command)\r
204   {\r
205     command.string = new char[command.seqs.length][];\r
206 \r
207     for (int i = 0; i < command.seqs.length; i++)\r
208     {\r
209       if (command.seqs[i].getLength() > command.position)\r
210       {\r
211         command.string[i] = command.seqs[i].getSequence(command.position,\r
212             command.position + command.number);\r
213 \r
214         if (command.seqs[i].getDatasetSequence() != null\r
215             || command.seqs[i].getSequenceFeatures() != null)\r
216         {\r
217           for (int s = command.position; s < command.position + command.number;\r
218                s++)\r
219           {\r
220             if (jalview.schemes.ResidueProperties\r
221                 .aaIndex[command.seqs[i].getCharAt(s)] != 23)\r
222             {\r
223               adjustFeatures(command, i,\r
224                              command.seqs[i].findPosition(command.position),\r
225                              command.seqs[i].findPosition(command.position +\r
226                   command.number),\r
227                              false);\r
228               break;\r
229             }\r
230           }\r
231         }\r
232         command.seqs[i].deleteChars(command.position,\r
233                                     command.position + command.number);\r
234       }\r
235 \r
236       if (command.seqs[i].getLength() < 1)\r
237       {\r
238         command.al.deleteSequence(command.seqs[i]);\r
239       }\r
240     }\r
241 \r
242     adjustAnnotations(command, false);\r
243   }\r
244 \r
245   void paste(Edit command)\r
246   {\r
247     StringBuffer tmp;\r
248     boolean newDSNeeded;\r
249     int start = 0, end = 0;\r
250 \r
251     for (int i = 0; i < command.seqs.length; i++)\r
252     {\r
253       newDSNeeded = false;\r
254       if (command.seqs[i].getLength() < 1)\r
255       {\r
256         // ie this sequence was deleted, we need to\r
257         // read it to the alignment\r
258         if (command.alIndex[i] < command.al.getHeight())\r
259         {\r
260           command.al.getSequences().insertElementAt(command.seqs[i],\r
261               command.alIndex[i]);\r
262         }\r
263         else\r
264         {\r
265           command.al.addSequence(command.seqs[i]);\r
266         }\r
267       }\r
268       tmp = new StringBuffer();\r
269       tmp.append(command.seqs[i].getSequence());\r
270 \r
271       if (command.string != null && command.string[i] != null)\r
272       {\r
273         if (command.position >= tmp.length())\r
274         {\r
275           //This occurs if padding is on, and residues\r
276           //are removed from end of alignment\r
277           int length = command.position - tmp.length();\r
278           while (length > 0)\r
279           {\r
280             tmp.append(command.gapChar);\r
281             length--;\r
282           }\r
283         }\r
284         tmp.insert(command.position, command.string[i]);\r
285 \r
286         for (int s = 0; s < command.string[i].length; s++)\r
287         {\r
288           if (jalview.schemes.ResidueProperties.aaIndex[command.string[i][s]] !=\r
289               23)\r
290           {\r
291             newDSNeeded = true;\r
292             start = command.seqs[i].findPosition(command.position);\r
293             end = command.seqs[i].findPosition(command.position +\r
294                                                command.number);\r
295             break;\r
296           }\r
297         }\r
298         command.string[i] = null;\r
299       }\r
300 \r
301       command.seqs[i].setSequence(tmp.toString());\r
302 \r
303       if (newDSNeeded)\r
304       {\r
305         if (command.seqs[i].getDatasetSequence() != null)\r
306         {\r
307           Sequence ds = new Sequence(command.seqs[i].getName(),\r
308                                      jalview.analysis.AlignSeq.extractGaps(\r
309                                          jalview.util.Comparison.GapChars,\r
310                                          command.seqs[i].getSequenceAsString()\r
311                                      ),\r
312                                      command.seqs[i].getStart(),\r
313                                      command.seqs[i].getEnd());\r
314           ds.setDescription(command.seqs[i].getDescription());\r
315           command.seqs[i].setDatasetSequence(ds);\r
316         }\r
317 \r
318         adjustFeatures(command, i, start, end, true);\r
319       }\r
320     }\r
321 \r
322     adjustAnnotations(command, true);\r
323 \r
324     command.string = null;\r
325   }\r
326 \r
327   final void adjustAnnotations(Edit command, boolean insert)\r
328   {\r
329 \r
330     AlignmentAnnotation[] annotations = null;\r
331 \r
332     if (command.fullAlignmentHeight)\r
333     {\r
334       annotations = command.al.getAlignmentAnnotation();\r
335     }\r
336     else\r
337     {\r
338       int aSize = 0;\r
339       AlignmentAnnotation[] tmp;\r
340       for (int s = 0; s < command.seqs.length; s++)\r
341       {\r
342         if (command.seqs[s].getAnnotation() == null)\r
343         {\r
344           continue;\r
345         }\r
346 \r
347         if (aSize == 0)\r
348         {\r
349           annotations = command.seqs[s].getAnnotation();\r
350         }\r
351         else\r
352         {\r
353           tmp = new AlignmentAnnotation\r
354               [aSize + command.seqs[s].getAnnotation().length];\r
355 \r
356           System.arraycopy(annotations, 0, tmp, 0, aSize);\r
357 \r
358           System.arraycopy(command.seqs[s].getAnnotation(),\r
359                            0, tmp, aSize,\r
360                            command.seqs[s].getAnnotation().length);\r
361 \r
362           annotations = tmp;\r
363         }\r
364 \r
365         aSize = annotations.length;\r
366       }\r
367     }\r
368 \r
369     if (annotations == null)\r
370     {\r
371       return;\r
372     }\r
373 \r
374     if (!insert)\r
375     {\r
376       command.deletedAnnotations = new Hashtable();\r
377     }\r
378 \r
379     int aSize;\r
380     Annotation[] temp;\r
381     for (int a = 0; a < annotations.length; a++)\r
382     {\r
383       if (annotations[a].autoCalculated)\r
384       {\r
385         continue;\r
386       }\r
387 \r
388       int tSize = 0;\r
389 \r
390       aSize = annotations[a].annotations.length;\r
391       if (insert)\r
392       {\r
393         temp = new Annotation[aSize + command.number];\r
394       }\r
395       else\r
396       {\r
397         if (command.position < aSize)\r
398         {\r
399           if (command.position + command.number > aSize)\r
400           {\r
401             tSize = aSize;\r
402           }\r
403           else\r
404           {\r
405             tSize = aSize - command.number + command.position;\r
406           }\r
407         }\r
408         else\r
409         {\r
410           tSize = aSize;\r
411         }\r
412 \r
413         if (tSize < 0)\r
414         {\r
415           tSize = aSize;\r
416         }\r
417         temp = new Annotation[tSize];\r
418 \r
419       }\r
420 \r
421       if (insert)\r
422       {\r
423         if (command.position < annotations[a].annotations.length)\r
424         {\r
425           System.arraycopy(annotations[a].annotations,\r
426                            0, temp, 0, command.position);\r
427 \r
428           if (command.deletedAnnotations != null\r
429               &&\r
430               command.deletedAnnotations.containsKey(annotations[a].\r
431               annotationId))\r
432           {\r
433             Annotation[] restore = (Annotation[])\r
434                 command.deletedAnnotations.get(annotations[a].annotationId);\r
435 \r
436             System.arraycopy(restore,\r
437                              0,\r
438                              temp,\r
439                              command.position,\r
440                              command.number);\r
441 \r
442           }\r
443 \r
444           System.arraycopy(annotations[a].annotations,\r
445                            command.position, temp,\r
446                            command.position + command.number,\r
447                            aSize - command.position);\r
448         }\r
449         else\r
450         {\r
451           if (command.deletedAnnotations != null\r
452               &&\r
453               command.deletedAnnotations.containsKey(annotations[a].\r
454               annotationId))\r
455           {\r
456             Annotation[] restore = (Annotation[])\r
457                 command.deletedAnnotations.get(annotations[a].annotationId);\r
458 \r
459             temp = new Annotation[annotations[a].annotations.length +\r
460                 restore.length];\r
461             System.arraycopy(annotations[a].annotations,\r
462                              0, temp, 0,\r
463                              annotations[a].annotations.length);\r
464             System.arraycopy(restore, 0, temp,\r
465                              annotations[a].annotations.length, restore.length);\r
466           }\r
467           else\r
468           {\r
469             temp = annotations[a].annotations;\r
470           }\r
471         }\r
472       }\r
473       else\r
474       {\r
475         if (tSize != aSize || command.position < 2)\r
476         {\r
477           System.arraycopy(annotations[a].annotations,\r
478                            0, temp, 0, command.position);\r
479 \r
480           Annotation[] deleted = new Annotation[command.number];\r
481           System.arraycopy(annotations[a].annotations,\r
482                            command.position, deleted, 0, command.number);\r
483 \r
484           command.deletedAnnotations.put(annotations[a].annotationId,\r
485                                          deleted);\r
486 \r
487           System.arraycopy(annotations[a].annotations,\r
488                            command.position + command.number,\r
489                            temp, command.position,\r
490                            aSize - command.position - command.number);\r
491         }\r
492         else\r
493         {\r
494           int dSize = aSize - command.position;\r
495 \r
496           if (dSize > 0)\r
497           {\r
498             Annotation[] deleted = new Annotation[command.number];\r
499             System.arraycopy(annotations[a].annotations,\r
500                              command.position, deleted, 0, dSize);\r
501 \r
502             command.deletedAnnotations.put(annotations[a].annotationId,\r
503                                            deleted);\r
504 \r
505             tSize = Math.min(annotations[a].annotations.length,\r
506                              command.position);\r
507             temp = new Annotation[tSize];\r
508             System.arraycopy(annotations[a].annotations,\r
509                              0, temp, 0, tSize);\r
510           }\r
511           else\r
512           {\r
513             temp = annotations[a].annotations;\r
514           }\r
515         }\r
516       }\r
517 \r
518       annotations[a].annotations = temp;\r
519     }\r
520   }\r
521 \r
522   final void adjustFeatures(Edit command, int index, int i, int j,\r
523                             boolean insert)\r
524   {\r
525     SequenceI seq = command.seqs[index];\r
526     SequenceI sequence = seq.getDatasetSequence();\r
527     if (sequence == null)\r
528     {\r
529       sequence = seq;\r
530     }\r
531 \r
532     if (insert)\r
533     {\r
534       if (command.editedFeatures != null\r
535           && command.editedFeatures.containsKey(seq))\r
536       {\r
537         sequence.setSequenceFeatures(\r
538             (SequenceFeature[]) command.editedFeatures.get(seq)\r
539             );\r
540       }\r
541 \r
542       return;\r
543     }\r
544 \r
545     SequenceFeature[] sf = sequence.getSequenceFeatures();\r
546 \r
547     if (sf == null)\r
548     {\r
549       return;\r
550     }\r
551 \r
552     SequenceFeature[] oldsf = new SequenceFeature[sf.length];\r
553 \r
554     int cSize = j - i;\r
555 \r
556     for (int s = 0; s < sf.length; s++)\r
557     {\r
558       SequenceFeature copy = new SequenceFeature(sf[s]);\r
559 \r
560       oldsf[s] = copy;\r
561 \r
562       if (sf[s].getEnd() < i)\r
563       {\r
564         continue;\r
565       }\r
566 \r
567       if (sf[s].getBegin() > j)\r
568       {\r
569         sf[s].setBegin(copy.getBegin() - cSize);\r
570         sf[s].setEnd(copy.getEnd() - cSize);\r
571         continue;\r
572       }\r
573 \r
574       if (sf[s].getBegin() >= i)\r
575       {\r
576         sf[s].setBegin(i);\r
577       }\r
578 \r
579       if (sf[s].getEnd() < j)\r
580       {\r
581         sf[s].setEnd(j - 1);\r
582       }\r
583 \r
584       sf[s].setEnd(sf[s].getEnd() - (cSize));\r
585 \r
586       if (sf[s].getBegin() > sf[s].getEnd())\r
587       {\r
588         sequence.deleteFeature(sf[s]);\r
589       }\r
590     }\r
591 \r
592     if (command.editedFeatures == null)\r
593     {\r
594       command.editedFeatures = new Hashtable();\r
595     }\r
596 \r
597     command.editedFeatures.put(seq, oldsf);\r
598 \r
599   }\r
600 \r
601   class Edit\r
602   {\r
603     boolean fullAlignmentHeight = false;\r
604     Hashtable deletedAnnotations;\r
605     Hashtable editedFeatures;\r
606     AlignmentI al;\r
607     int command;\r
608     char[][] string;\r
609     SequenceI[] seqs;\r
610     int[] alIndex;\r
611     int position, number;\r
612     char gapChar;\r
613 \r
614     Edit(int command,\r
615          SequenceI[] seqs,\r
616          int position,\r
617          int number,\r
618          char gapChar)\r
619     {\r
620       this.command = command;\r
621       this.seqs = seqs;\r
622       this.position = position;\r
623       this.number = number;\r
624       this.gapChar = gapChar;\r
625     }\r
626 \r
627     Edit(int command,\r
628          SequenceI[] seqs,\r
629          int position,\r
630          int number,\r
631          AlignmentI al)\r
632     {\r
633       this.gapChar = al.getGapCharacter();\r
634       this.command = command;\r
635       this.seqs = seqs;\r
636       this.position = position;\r
637       this.number = number;\r
638       this.al = al;\r
639 \r
640       alIndex = new int[seqs.length];\r
641       for (int i = 0; i < seqs.length; i++)\r
642       {\r
643         alIndex[i] = al.findIndex(seqs[i]);\r
644       }\r
645 \r
646       fullAlignmentHeight = (al.getHeight() == seqs.length);\r
647     }\r
648   }\r
649 \r
650 }\r