2 * Copyright (C) 2014 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
9 package eu
.etaxonomy
.taxeditor
.molecular
.editor
;
12 import info
.bioinfweb
.commons
.swt
.SWTUtils
;
13 import info
.bioinfweb
.libralign
.alignmentarea
.AlignmentArea
;
14 import info
.bioinfweb
.libralign
.alignmentarea
.selection
.SelectionModel
;
15 import info
.bioinfweb
.libralign
.alignmentarea
.tokenpainter
.NucleotideTokenPainter
;
16 import info
.bioinfweb
.libralign
.dataarea
.implementations
.ConsensusSequenceArea
;
17 import info
.bioinfweb
.libralign
.dataarea
.implementations
.pherogram
.PherogramArea
;
18 import info
.bioinfweb
.libralign
.dataarea
.implementations
.sequenceindex
.SequenceIndexArea
;
19 import info
.bioinfweb
.libralign
.editsettings
.EditSettingsChangeEvent
;
20 import info
.bioinfweb
.libralign
.editsettings
.EditSettingsListener
;
21 import info
.bioinfweb
.libralign
.model
.AlignmentModel
;
22 import info
.bioinfweb
.libralign
.model
.AlignmentModelChangeListener
;
23 import info
.bioinfweb
.libralign
.model
.adapters
.StringAdapter
;
24 import info
.bioinfweb
.libralign
.model
.events
.SequenceChangeEvent
;
25 import info
.bioinfweb
.libralign
.model
.events
.SequenceRenamedEvent
;
26 import info
.bioinfweb
.libralign
.model
.events
.TokenChangeEvent
;
27 import info
.bioinfweb
.libralign
.model
.implementations
.PackedAlignmentModel
;
28 import info
.bioinfweb
.libralign
.model
.tokenset
.CharacterTokenSet
;
29 import info
.bioinfweb
.libralign
.model
.tokenset
.TokenSet
;
30 import info
.bioinfweb
.libralign
.model
.utils
.AlignmentModelUtils
;
31 import info
.bioinfweb
.libralign
.multiplealignments
.AlignmentAreaList
;
32 import info
.bioinfweb
.libralign
.multiplealignments
.MultipleAlignmentsContainer
;
33 import info
.bioinfweb
.libralign
.pherogram
.model
.PherogramAreaModel
;
34 import info
.bioinfweb
.libralign
.pherogram
.model
.ShiftChange
;
35 import info
.bioinfweb
.libralign
.pherogram
.provider
.BioJavaPherogramProvider
;
36 import info
.bioinfweb
.libralign
.pherogram
.provider
.PherogramProvider
;
37 import info
.bioinfweb
.libralign
.pherogram
.provider
.ReverseComplementPherogramProvider
;
38 import info
.bioinfweb
.tic
.SWTComponentFactory
;
41 import java
.io
.IOException
;
42 import java
.io
.InputStream
;
44 import java
.util
.ArrayList
;
45 import java
.util
.Collection
;
46 import java
.util
.Collections
;
47 import java
.util
.Iterator
;
48 import java
.util
.List
;
50 import java
.util
.TreeMap
;
52 import org
.biojava
.bio
.chromatogram
.ChromatogramFactory
;
53 import org
.biojava
.bio
.chromatogram
.UnsupportedChromatogramFormatException
;
54 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
55 import org
.eclipse
.swt
.SWT
;
56 import org
.eclipse
.swt
.dnd
.Clipboard
;
57 import org
.eclipse
.swt
.widgets
.Composite
;
58 import org
.eclipse
.swt
.widgets
.Display
;
59 import org
.eclipse
.ui
.IActionBars
;
60 import org
.eclipse
.ui
.IEditorInput
;
61 import org
.eclipse
.ui
.IEditorPart
;
62 import org
.eclipse
.ui
.IEditorSite
;
63 import org
.eclipse
.ui
.PartInitException
;
64 import org
.eclipse
.ui
.PlatformUI
;
65 import org
.eclipse
.ui
.commands
.ICommandService
;
66 import org
.eclipse
.ui
.part
.EditorPart
;
68 import eu
.etaxonomy
.cdm
.api
.conversation
.ConversationHolder
;
69 import eu
.etaxonomy
.cdm
.api
.service
.molecular
.ISequenceService
;
70 import eu
.etaxonomy
.cdm
.model
.media
.MediaUtils
;
71 import eu
.etaxonomy
.cdm
.model
.molecular
.Sequence
;
72 import eu
.etaxonomy
.cdm
.model
.molecular
.SequenceString
;
73 import eu
.etaxonomy
.cdm
.model
.molecular
.SingleRead
;
74 import eu
.etaxonomy
.cdm
.model
.molecular
.SingleReadAlignment
;
75 import eu
.etaxonomy
.taxeditor
.model
.MessagingUtils
;
76 import eu
.etaxonomy
.taxeditor
.molecular
.TaxeditorMolecularPlugin
;
77 import eu
.etaxonomy
.taxeditor
.molecular
.handler
.ToggleInsertOverwriteHandler
;
78 import eu
.etaxonomy
.taxeditor
.molecular
.handler
.ToggleLeftRightInsertionHandler
;
79 import eu
.etaxonomy
.taxeditor
.store
.CdmStore
;
80 import eu
.etaxonomy
.taxeditor
.view
.derivateSearch
.DerivateLabelProvider
;
85 * Editor component to edit a contig alignment used to combine different overlapping pherograms from Sanger sequencing to
86 * a consensus sequence.
88 * The contained GUI components used to edit the alignment come from <a href="http://bioinfweb.info/LibrAlign/">LibrAlign</a>.
94 public class AlignmentEditor
extends EditorPart
{
95 public static final String ID
= "eu.etaxonomy.taxeditor.molecular.AlignmentEditor";
97 public static final int READS_AREA_INDEX
= 1;
98 public static final int EDITABLE_CONSENSUS_AREA_INDEX
= READS_AREA_INDEX
+ 1;
99 public static final int CONSENSUS_HINT_AREA_INDEX
= EDITABLE_CONSENSUS_AREA_INDEX
+ 1;
100 public static final int PHEROGRAM_AREA_INDEX
= 0;
101 public static final int CONSENSUS_DATA_AREA_INDEX
= 0;
102 public static final String DEFAULT_READ_NAME_PREFIX
= "Read ";
103 public static final String CONSENSUS_NAME
= "Consensus";
106 private final ConversationHolder conversationHolder
;
107 private final AlignmentModelChangeListener DIRTY_LISTENER
= new AlignmentModelChangeListener() {
109 public <T
> void afterTokenChange(TokenChangeEvent
<T
> e
) {
114 public <T
> void afterSequenceRenamed(SequenceRenamedEvent
<T
> e
) {
119 public <T
> void afterSequenceChange(SequenceChangeEvent
<T
> e
) {
124 public <T
, U
> void afterProviderChanged(AlignmentModel
<T
> oldProvider
,
125 AlignmentModel
<U
> newProvider
) { // Not expected.
130 private final AlignmentEditorActionUpdater ACTION_UPDATER
= new AlignmentEditorActionUpdater();
131 public final Clipboard CLIPBOARD
= new Clipboard(Display
.getCurrent()); //TODO Move to global EDITor class.
134 private MultipleAlignmentsContainer alignmentsContainer
= null;
135 private final Map
<String
, SingleReadAlignment
> cdmMap
= new TreeMap
<String
, SingleReadAlignment
>(); //TODO Move this to ContigSequenceDataProvider
136 private boolean dirty
= false;
139 public AlignmentEditor() {
141 conversationHolder
= CdmStore
.createConversation();
142 //conversationHolder = null;
146 private void refreshToolbarElement(String id
) {
147 ICommandService commandService
=
148 (ICommandService
)PlatformUI
.getWorkbench().getActiveWorkbenchWindow().getService(ICommandService
.class);
149 if (commandService
!= null) {
150 commandService
.refreshElements(id
, Collections
.EMPTY_MAP
);
155 private void registerEditSettingListener(MultipleAlignmentsContainer container
) {
156 container
.getEditSettings().addListener(new EditSettingsListener() {
158 public void workingModeChanged(EditSettingsChangeEvent e
) {} // Currently nothing to do
161 public void insertLeftInDataAreaChanged(EditSettingsChangeEvent e
) {
163 refreshToolbarElement(ToggleLeftRightInsertionHandler
.COMMAND_ID
);
167 public void insertChanged(EditSettingsChangeEvent e
) {
169 refreshToolbarElement(ToggleInsertOverwriteHandler
.COMMAND_ID
);
175 private AlignmentArea
createIndexArea(MultipleAlignmentsContainer container
, AlignmentArea labeledArea
) {
176 AlignmentArea result
= new AlignmentArea(container
);
177 result
.setAllowVerticalScrolling(false);
178 result
.getDataAreas().getTopAreas().add(new SequenceIndexArea(result
.getContentArea(), labeledArea
));
183 private AlignmentArea
createEditableAlignmentArea(MultipleAlignmentsContainer container
, boolean allowVerticalScrolling
) {
184 AlignmentArea result
= new AlignmentArea(container
);
185 result
.setAllowVerticalScrolling(allowVerticalScrolling
);
187 CharacterTokenSet tokenSet
= CharacterTokenSet
.newDNAInstance(); //TODO Should NUCLEOTIDE be used instead?
188 AlignmentModel
<Character
> model
= new PackedAlignmentModel
<Character
>(tokenSet
);
189 result
.setAlignmentModel(model
, false);
190 model
.getChangeListeners().add(DIRTY_LISTENER
);
191 result
.getPaintSettings().getTokenPainterList().set(0, new NucleotideTokenPainter());
197 private AlignmentArea
createConsensusHintArea(MultipleAlignmentsContainer container
,
198 AlignmentArea labeledArea
) {
200 AlignmentArea result
= new AlignmentArea(container
);
201 result
.setAllowVerticalScrolling(false);
202 result
.getDataAreas().getBottomAreas().add(
203 new ConsensusSequenceArea(result
.getContentArea(), labeledArea
));
208 private MultipleAlignmentsContainer
getAlignmentsContainer() {
209 if (alignmentsContainer
== null) {
210 alignmentsContainer
= new MultipleAlignmentsContainer();
212 AlignmentAreaList list
= alignmentsContainer
.getAlignmentAreas();
213 AlignmentArea readsArea
= createEditableAlignmentArea(alignmentsContainer
, true);
214 readsArea
.getSelection().addSelectionListener(ACTION_UPDATER
);
215 list
.add(createIndexArea(alignmentsContainer
, readsArea
));
216 list
.add(readsArea
); // Make sure READS_AREA_INDEX is correct.
217 AlignmentArea editableConsensusArea
= createEditableAlignmentArea(alignmentsContainer
, false);
218 editableConsensusArea
.getSelection().addSelectionListener(ACTION_UPDATER
);
219 list
.add(editableConsensusArea
); // Make sure COMSENSUS_AREA_INDEX is correct.
220 list
.add(createConsensusHintArea(alignmentsContainer
, readsArea
));
222 registerEditSettingListener(alignmentsContainer
);
224 return alignmentsContainer
;
228 public AlignmentArea
getReadsArea() {
229 return getAlignmentsContainer().getAlignmentAreas().get(READS_AREA_INDEX
);
233 public AlignmentArea
getEditableConsensusArea() {
234 return getAlignmentsContainer().getAlignmentAreas().get(EDITABLE_CONSENSUS_AREA_INDEX
);
239 * Checks whether {@link #getReadsArea()} or {@link #getEditableConsensusArea()} currently
240 * have the user focus and returns the according component.
242 * @return either the reads or the consensus alignment area or {@code null} if none of these
243 * components is currently focused
245 public AlignmentArea
getFocusedArea() {
246 AlignmentArea result
= getReadsArea();
247 if (hasFocus(result
)) {
251 result
= getEditableConsensusArea();
252 if (hasFocus(result
)) {
263 * Checks whether the specified alignment area or one of its subcomponents currently has the
266 * @param area the alignment area to be checked (Can only be {@link #getReadsArea()} or
267 * {@link #getEditableConsensusArea()}.)
268 * @return {@code true} if the specified component is focused and is either equal to
269 * {@link #getReadsArea()} or {@link #getEditableConsensusArea()}or {@code false} otherwise
271 private boolean hasFocus(AlignmentArea area
) {
272 return SWTUtils
.childHasFocus((Composite
)area
.getToolkitComponent());
276 public boolean hasPherogram(String sequenceID
) {
277 return getReadsArea().getDataAreas().getSequenceAreas(sequenceID
).size() > PHEROGRAM_AREA_INDEX
;
281 public PherogramArea
getPherogramArea(String sequenceID
) {
282 if (hasPherogram(sequenceID
)) {
283 return (PherogramArea
)getReadsArea().getDataAreas().getSequenceAreas(sequenceID
).get(PHEROGRAM_AREA_INDEX
);
291 private ConsensusSequenceArea
getConsensusHintDataArea() {
292 return (ConsensusSequenceArea
)getAlignmentsContainer().getAlignmentAreas().
293 get(CONSENSUS_HINT_AREA_INDEX
).getDataAreas().getBottomAreas().
294 get(CONSENSUS_DATA_AREA_INDEX
);
298 @Deprecated //TODO Remove as soon as testing period is over
299 private void createTestContents() {
302 addRead(new File("D:/Users/BenStoever/ownCloud/Dokumente/Projekte/EDITor/Quelltexte/LibrAlign branch/Repository/eu.etaxonomy.taxeditor.editor/src/main/resources/AlignmentTestData/JR430_JR-P01.ab1").toURI(), false);
303 //addRead(new File("D:/Users/BenStoever/ownCloud/Dokumente/Projekte/EDITor/Quelltexte/LibrAlign branch/Repository/eu.etaxonomy.taxeditor.editor/src/main/resources/AlignmentTestData/JR444_JR-P05.ab1").toURI(), false);
304 addRead(new File("D:/Users/BenStoever/ownCloud/Dokumente/Projekte/EDITor/Quelltexte/LibrAlign branch/Repository/eu.etaxonomy.taxeditor.editor/src/main/resources/AlignmentTestData/Test_qualityScore.scf").toURI(), false);
306 // Add test consensus sequence:
307 AlignmentModel consensusModel
= getEditableConsensusArea().getAlignmentModel();
308 String id
= consensusModel
.addSequence(CONSENSUS_NAME
);
309 Collection
<Object
> tokens
= new ArrayList
<Object
>(); // First save tokens in a collection to avoid GUI updated for each token.
310 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("A"));
311 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("C"));
312 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("G"));
313 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("T"));
314 consensusModel
.insertTokensAt(id
, 0, tokens
);
316 catch (Exception e
) {
317 throw new RuntimeException(e
);
322 private void readCDMData(Sequence sequenceNode
) {
323 //TODO If called from somewhere else than createPartControl() the editorInput needs to be checked and previous contents need to be cleared (or updated).
326 for (SingleReadAlignment singleReadAlignment
: sequenceNode
.getSingleReadAlignments()) {
328 SingleRead pherogramInfo
= singleReadAlignment
.getSingleRead();
329 String id
= addRead(DerivateLabelProvider
.getDerivateText(pherogramInfo
, conversationHolder
),
330 getPherogramURI(pherogramInfo
),
331 singleReadAlignment
.isReverseComplement(),
332 singleReadAlignment
.getEditedSequence(),
333 singleReadAlignment
.getFirstSeqPosition(),
334 singleReadAlignment
.getLeftCutPosition(),
335 singleReadAlignment
.getRightCutPosition(),
336 singleReadAlignment
.getShifts());
337 cdmMap
.put(id
, singleReadAlignment
);
339 catch (Exception e
) { // Usually due to an error while trying to read the pherogram (e.g. due to an unsupported format or an invalid URI).
340 MessagingUtils
.errorDialog("Error", null, "A single read was skipped because of the following error:\n\n" +
341 e
.getLocalizedMessage(), TaxeditorMolecularPlugin
.PLUGIN_ID
, e
, false);
345 // Set consensus sequence:
346 AlignmentModel consensusModel
= getEditableConsensusArea().getAlignmentModel();
347 String id
= consensusModel
.addSequence(CONSENSUS_NAME
);
348 consensusModel
.insertTokensAt(id
, 0, AlignmentModelUtils
.charSequenceToTokenList(
349 sequenceNode
.getConsensusSequence().getString(), consensusModel
.getTokenSet()));
350 //TODO Can the consensus sequence also be null? / Should it be created here, if nothing is in the DB?
355 public void createPartControl(Composite parent
) {
356 SWTComponentFactory
.getInstance().getSWTComponent(getAlignmentsContainer(), parent
, SWT
.NONE
);
357 Display
.getCurrent().addFilter(SWT
.FocusIn
, ACTION_UPDATER
);
358 Display
.getCurrent().addFilter(SWT
.FocusOut
, ACTION_UPDATER
);
361 if (getEditorInput() instanceof AlignmentEditorInput
) {
362 if (((AlignmentEditorInput
)getEditorInput()).getSequenceNodeUuid() != null) {
363 Sequence sequenceNode
= CdmStore
.getService(ISequenceService
.class).load(((AlignmentEditorInput
)getEditorInput()).getSequenceNodeUuid());
364 //re-load into the current session if it is already persisted in the DB
365 if(sequenceNode
!=null && sequenceNode
.getId()!=0){
366 sequenceNode
= CdmStore
.getService(ISequenceService
.class).load(sequenceNode
.getUuid());
368 readCDMData(sequenceNode
);
371 createTestContents(); // This case will removed after the test phase and an exception should probably be thrown.
375 throw new IllegalArgumentException("The editor input must have the type " +
376 AlignmentEditorInput
.class.getCanonicalName()); //TODO What should be done here?
382 public void dispose() {
383 Display
.getCurrent().removeFilter(SWT
.FocusIn
, ACTION_UPDATER
);
384 Display
.getCurrent().removeFilter(SWT
.FocusOut
, ACTION_UPDATER
);
386 ((AlignmentEditorInput
)getEditorInput()).dispose();
391 private void updateStatusBar() {
392 IActionBars bars
= getEditorSite().getActionBars();
393 bars
.getStatusLineManager().setMessage("Edit mode: " +
394 (getReadsArea().getEditSettings().isInsert() ?
"Insert" : "Overwrite") + " " +
395 "Insertion in pherogram: " +
396 (getReadsArea().getEditSettings().isInsertLeftInDataArea() ?
"Left" : "Right")); //TODO multi language
400 private SingleReadAlignment
.Shift
[] convertToCDMShifts(PherogramAreaModel model
) {
401 Iterator
<ShiftChange
> iterator
= model
.shiftChangeIterator();
402 List
<SingleReadAlignment
.Shift
> shifts
= new ArrayList
<SingleReadAlignment
.Shift
>();
403 while (iterator
.hasNext()) {
404 ShiftChange shiftChange
= iterator
.next();
405 shifts
.add(new SingleReadAlignment
.Shift(shiftChange
.getBaseCallIndex(), shiftChange
.getShiftChange()));
407 return shifts
.toArray(new SingleReadAlignment
.Shift
[shifts
.size()]);
412 public void doSave(IProgressMonitor monitor
) {
413 if (getEditorInput() instanceof AlignmentEditorInput
) {
414 String taskName
= "Saving alignment"; //TODO multi language
415 monitor
.beginTask(taskName
, 3);
417 //re-loading sequence to avoid session conflicts
418 Sequence sequenceNode
= CdmStore
.getService(ISequenceService
.class).load(((AlignmentEditorInput
)getEditorInput()).getSequenceNodeUuid());
419 ((AlignmentEditorInput
)getEditorInput()).setSequenceNode(sequenceNode
);
420 StringAdapter stringProvider
= new StringAdapter(getEditableConsensusArea().getAlignmentModel(), false); // Throws an exception if a token has more than one character.
422 // Write consensus sequence:
423 SequenceString consensusSequenceObj
= sequenceNode
.getConsensusSequence();
424 String newConsensusSequence
= stringProvider
.getSequence(
425 getEditableConsensusArea().getAlignmentModel().sequenceIDByName(CONSENSUS_NAME
));
426 if (consensusSequenceObj
== null) {
427 sequenceNode
.setConsensusSequence(SequenceString
.NewInstance(newConsensusSequence
));
430 consensusSequenceObj
.setString(newConsensusSequence
);
433 // Write single reads:
434 stringProvider
.setUnderlyingModel(getReadsArea().getAlignmentModel());
435 sequenceNode
.getSingleReadAlignments().retainAll(cdmMap
.values()); // Remove all reads that are not in the alignment anymore.
436 Iterator
<String
> iterator
= getReadsArea().getAlignmentModel().sequenceIDIterator();
437 while (iterator
.hasNext()) {
438 String id
= iterator
.next();
439 SingleReadAlignment singleRead
= cdmMap
.get(id
);
440 if (singleRead
== null) {
441 throw new InternalError("Creating new reads from AlignmentEditor not implemented."); //TODO multi language
442 //TODO Create new read object. => Shall it be allowed to add reads in the alignment editor which are not represented in the CDM tree before the alignment editor is saved?
443 //singleRead = SingleReadAlignment.NewInstance(consensusSequence, singleRead, shifts, editedSequence);
446 singleRead
.setEditedSequence(stringProvider
.getSequence(id
));
448 PherogramArea pherogramArea
= getPherogramArea(id
);
449 if (pherogramArea
!= null) {
450 PherogramAreaModel model
= pherogramArea
.getModel();
451 singleRead
.setReverseComplement(model
.getPherogramProvider() instanceof ReverseComplementPherogramProvider
); // Works only if ReverseComplementPherogramProvider instances are not nested.
452 singleRead
.setShifts(convertToCDMShifts(getPherogramArea(id
).getModel()));
453 singleRead
.setFirstSeqPosition(model
.getFirstSeqPos());
454 singleRead
.setLeftCutPosition(model
.getLeftCutPosition());
455 singleRead
.setRightCutPosition(model
.getRightCutPosition());
459 if (!conversationHolder
.isBound()) {
460 conversationHolder
.bind();
464 ((AlignmentEditorInput
)getEditorInput()).merge();
465 // Commit the conversation and start a new transaction immediately:
466 conversationHolder
.commit(true);
472 firePropertyChange(PROP_DIRTY
);
475 //TODO Throw exception as soon as testing period which allows unlinked AlignmentEditor is over.
481 public void doSaveAs() {}
485 public void init(IEditorSite site
, IEditorInput input
) throws PartInitException
{
492 public boolean isDirty() {
497 private void setDirty() {
499 firePropertyChange(IEditorPart
.PROP_DIRTY
);
504 public boolean isSaveAsAllowed() {
505 return false; // "Save as" not allowed.
510 public void setFocus() {
511 if(conversationHolder
!= null){
512 conversationHolder
.bind();
514 ((AlignmentEditorInput
)getEditorInput()).bind();
517 public boolean isInsertMode() {
518 return getAlignmentsContainer().getEditSettings().isInsert();
522 public boolean isInsertLeftInPherogram() {
523 return getAlignmentsContainer().getEditSettings().isInsertLeftInDataArea();
527 public void toggleLeftRightInsertionInPherogram() {
528 getAlignmentsContainer().getEditSettings().toggleInsertLeftInDataArea();
532 public void toggleInsertOverwrite() {
533 getAlignmentsContainer().getEditSettings().toggleInsert();
537 private String
cutPherogram(boolean left
) {
538 SelectionModel selection
= getReadsArea().getSelection();
539 if (selection
.getCursorHeight() != 1) {
540 return "Cutting pherograms is only possible if exactly one row is selected."; //TODO multi language
543 PherogramArea pherogramArea
=
544 getPherogramArea(getReadsArea().getSequenceOrder().idByIndex(selection
.getCursorRow()));
545 if (pherogramArea
== null) {
546 return "There is no pherogram attached to the current sequence."; //TODO multi language
550 if (pherogramArea
.setLeftCutPositionBySelection()) {
554 return "The left end of the selection lies outside the pherogram attached to this sequence."; //TODO multi language
558 if (pherogramArea
.setRightCutPositionBySelection()) {
562 return "The right end of the selection lies outside the pherogram attached to this sequence."; //TODO multi language
570 public String
cutPherogramLeft() {
571 return cutPherogram(true);
575 public String
cutPherogramRight() {
576 return cutPherogram(false);
580 public void reverseComplementSelectedSequences() {
581 SelectionModel selection
= getReadsArea().getSelection();
582 AlignmentModel
<?
> model
= getReadsArea().getAlignmentModel();
583 for (int row
= selection
.getFirstRow(); row
< selection
.getFirstRow() + selection
.getCursorHeight(); row
++) {
584 String sequenceID
= getReadsArea().getSequenceOrder().idByIndex(row
);
585 PherogramArea area
= getPherogramArea(sequenceID
);
586 PherogramAreaModel pherogramAlignmentModel
= area
.getModel();
587 AlignmentModelUtils
.reverseComplement(model
, sequenceID
,
588 pherogramAlignmentModel
.editableIndexByBaseCallIndex(
589 pherogramAlignmentModel
.getLeftCutPosition()).getBeforeValidIndex(),
590 pherogramAlignmentModel
.editableIndexByBaseCallIndex(
591 pherogramAlignmentModel
.getRightCutPosition()).getAfterValidIndex());
592 pherogramAlignmentModel
.reverseComplement();
598 * Recreates the whole consensus sequence from all single read sequences. The previous consensus
599 * sequence is overwritte.
601 @SuppressWarnings("unchecked")
602 public <T
> void createConsensusSequence() {
603 ConsensusSequenceArea area
= getConsensusHintDataArea();
604 AlignmentModel
<T
> model
= (AlignmentModel
<T
>)getEditableConsensusArea().getAlignmentModel();
605 String sequenceID
= model
.sequenceIDIterator().next(); // There is always one sequence contained.
606 int length
= getReadsArea().getAlignmentModel().getMaxSequenceLength();
608 Collection
<T
> tokens
= new ArrayList
<T
>(length
);
609 for (int column
= 0; column
< length
; column
++) {
610 tokens
.add(model
.getTokenSet().tokenByRepresentation(area
.getConsensusToken(column
)));
613 model
.removeTokensAt(sequenceID
, 0, model
.getSequenceLength(sequenceID
));
614 model
.insertTokensAt(sequenceID
, 0, tokens
);
619 * Updates the current consensus sequence by replacing gaps by the according consensus tokens
620 * calculated from the single read sequences and extends the consensus sequence if necessary.
622 @SuppressWarnings("unchecked")
623 public <T
> void updateConsensusSequence() {
624 ConsensusSequenceArea area
= getConsensusHintDataArea();
625 AlignmentModel
<T
> model
= (AlignmentModel
<T
>)getEditableConsensusArea().getAlignmentModel();
626 TokenSet
<T
> tokenSet
= model
.getTokenSet();
627 String sequenceID
= model
.sequenceIDIterator().next(); // There is always one sequence contained.
628 int currentConsensusLength
= model
.getSequenceLength(sequenceID
);
629 int overallLength
= getReadsArea().getAlignmentModel().getMaxSequenceLength();
631 // Replace gaps by new information:
632 for (int column
= 0; column
< currentConsensusLength
; column
++) {
633 if (tokenSet
.isGapToken(model
.getTokenAt(sequenceID
, column
))) {
634 T newToken
= tokenSet
.tokenByRepresentation(area
.getConsensusToken(column
));
635 if (!tokenSet
.isGapToken(newToken
)) {
636 model
.setTokenAt(sequenceID
, column
, newToken
);
641 // Append additional tokens:
642 if (overallLength
> currentConsensusLength
) {
643 Collection
<T
> tokens
= new ArrayList
<T
>(overallLength
);
644 for (int column
= currentConsensusLength
; column
< overallLength
; column
++) {
645 tokens
.add(tokenSet
.tokenByRepresentation(area
.getConsensusToken(column
)));
647 model
.appendTokens(sequenceID
, tokens
);
652 public static PherogramProvider
readPherogram(URI uri
) throws IOException
, UnsupportedChromatogramFormatException
{
653 PherogramProvider result
;
654 InputStream stream
= uri
.toURL().openStream();
656 result
= new BioJavaPherogramProvider(ChromatogramFactory
.create(stream
));
665 private String
newReadName() {
667 while (getReadsArea().getAlignmentModel().sequenceIDByName(DEFAULT_READ_NAME_PREFIX
+ index
) != null) {
670 return DEFAULT_READ_NAME_PREFIX
+ index
;
674 public void addRead(URI pherogramURI
, boolean reverseComplemented
) throws IOException
, UnsupportedChromatogramFormatException
{
675 addRead(newReadName(), pherogramURI
, reverseComplemented
, null, null, null, null, null);
680 * Adds a new sequence with attached phergram data area to the reads alignment.
682 * If {@code null} is specified as {@code editedSequence} the base call sequence from the pherogram will
683 * be set as the edited sequence. If {@code null} is specified as {@code shifts} no shifts between the edited
684 * and the base calls sequence are assumed.
686 * @param name the name of the new sequence
687 * @param pherogramURI the URI where the associated pherogram file is located
688 * @param reverseComplemented Specify {@code true} here, if the reverse complement of the pherogram data should
689 * be added, {@code false} otherwise.
690 * @param editedSequence the edited version of the base call sequence (May be {@code null}.)
691 * @param shifts the alignment information that links the edited and the base call sequence (May be {@code null}.)
692 * @return the sequence ID of the added read
693 * @throws IOException if an error occurred when trying to read the pherogram file
694 * @throws UnsupportedChromatogramFormatException if the format of the pherogram file is not supported
696 public String
addRead(String name
, URI pherogramURI
, boolean reverseComplemented
, String editedSequence
,
697 Integer firstSeqPos
, Integer leftCutPos
, Integer rightCutPos
, SingleReadAlignment
.Shift
[] shifts
)
698 throws IOException
, UnsupportedChromatogramFormatException
{
700 AlignmentModel model
= getReadsArea().getAlignmentModel();
701 PherogramProvider pherogramProvider
= null;
702 if (pherogramURI
!= null) {
703 pherogramProvider
= readPherogram(pherogramURI
); // Must happen before a sequence is added, because it might throw an exception.
704 if (reverseComplemented
) {
705 pherogramProvider
= new ReverseComplementPherogramProvider(pherogramProvider
);
710 model
.addSequence(name
);
711 String id
= model
.sequenceIDByName(name
);
713 // Set edited sequence:
714 Collection
<Object
> tokens
= null; // First save tokens in a collection to avoid GUI updated for each token.
715 if (editedSequence
!= null) {
716 tokens
= AlignmentModelUtils
.charSequenceToTokenList(editedSequence
, model
.getTokenSet());
718 else if (pherogramProvider
!= null) { // Copy base call sequence into alignment:
719 tokens
= new ArrayList
<Object
>();
720 for (int i
= 0; i
< pherogramProvider
.getSequenceLength(); i
++) {
721 tokens
.add(model
.getTokenSet().tokenByRepresentation(
722 Character
.toString(pherogramProvider
.getBaseCall(i
))));
727 if (tokens
!= null) { // If either an edited sequence or a pherogram URI was provided.
728 model
.insertTokensAt(id
, 0, tokens
);
730 if (pherogramProvider
!= null) {
731 // Create pherogram area:
732 PherogramArea pherogramArea
= new PherogramArea(getReadsArea().getContentArea(),
733 new PherogramAreaModel(pherogramProvider
));
735 // Set position properties and shifts:
736 PherogramAreaModel phergramModel
= pherogramArea
.getModel();
737 if ((firstSeqPos
!= null) && (leftCutPos
!= null)) {
738 phergramModel
.setFirstSeqLeftCutPos(firstSeqPos
, leftCutPos
);
740 if (rightCutPos
!= null) {
741 phergramModel
.setRightCutPosition(rightCutPos
);
743 if ((shifts
!= null) && (shifts
.length
> 0)) {
744 for (int i
= 0; i
< shifts
.length
; i
++) {
745 phergramModel
.addShiftChange(shifts
[i
].position
, shifts
[i
].shift
);
750 // Add pherogram area to GUI:
751 pherogramArea
.addMouseListener(new PherogramMouseListener(pherogramArea
));
752 getReadsArea().getDataAreas().getSequenceAreas(id
).add(pherogramArea
);
759 public static URI
getPherogramURI(SingleRead pherogramInfo
) {
760 if (pherogramInfo
.getPherogram() != null) {
761 return MediaUtils
.getFirstMediaRepresentationPart(pherogramInfo
.getPherogram()).getUri();