3 * Copyright (C) 2014 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
10 package eu
.etaxonomy
.taxeditor
.molecular
.editor
;
13 import info
.bioinfweb
.commons
.swt
.SWTUtils
;
14 import info
.bioinfweb
.libralign
.alignmentarea
.AlignmentArea
;
15 import info
.bioinfweb
.libralign
.alignmentarea
.selection
.SelectionModel
;
16 import info
.bioinfweb
.libralign
.alignmentarea
.tokenpainter
.NucleotideTokenPainter
;
17 import info
.bioinfweb
.libralign
.dataarea
.implementations
.ConsensusSequenceArea
;
18 import info
.bioinfweb
.libralign
.dataarea
.implementations
.pherogram
.PherogramArea
;
19 import info
.bioinfweb
.libralign
.dataarea
.implementations
.sequenceindex
.SequenceIndexArea
;
20 import info
.bioinfweb
.libralign
.editsettings
.EditSettingsChangeEvent
;
21 import info
.bioinfweb
.libralign
.editsettings
.EditSettingsListener
;
22 import info
.bioinfweb
.libralign
.model
.AlignmentModel
;
23 import info
.bioinfweb
.libralign
.model
.AlignmentModelChangeListener
;
24 import info
.bioinfweb
.libralign
.model
.adapters
.StringAdapter
;
25 import info
.bioinfweb
.libralign
.model
.events
.SequenceChangeEvent
;
26 import info
.bioinfweb
.libralign
.model
.events
.SequenceRenamedEvent
;
27 import info
.bioinfweb
.libralign
.model
.events
.TokenChangeEvent
;
28 import info
.bioinfweb
.libralign
.model
.implementations
.PackedAlignmentModel
;
29 import info
.bioinfweb
.libralign
.model
.tokenset
.CharacterTokenSet
;
30 import info
.bioinfweb
.libralign
.model
.tokenset
.TokenSet
;
31 import info
.bioinfweb
.libralign
.model
.utils
.AlignmentModelUtils
;
32 import info
.bioinfweb
.libralign
.multiplealignments
.AlignmentAreaList
;
33 import info
.bioinfweb
.libralign
.multiplealignments
.MultipleAlignmentsContainer
;
34 import info
.bioinfweb
.libralign
.pherogram
.model
.PherogramAreaModel
;
35 import info
.bioinfweb
.libralign
.pherogram
.model
.ShiftChange
;
36 import info
.bioinfweb
.libralign
.pherogram
.provider
.BioJavaPherogramProvider
;
37 import info
.bioinfweb
.libralign
.pherogram
.provider
.PherogramProvider
;
38 import info
.bioinfweb
.libralign
.pherogram
.provider
.ReverseComplementPherogramProvider
;
39 import info
.bioinfweb
.tic
.SWTComponentFactory
;
42 import java
.io
.IOException
;
43 import java
.io
.InputStream
;
45 import java
.util
.ArrayList
;
46 import java
.util
.Collection
;
47 import java
.util
.Collections
;
48 import java
.util
.Iterator
;
49 import java
.util
.List
;
51 import java
.util
.TreeMap
;
53 import org
.biojava
.bio
.chromatogram
.ChromatogramFactory
;
54 import org
.biojava
.bio
.chromatogram
.UnsupportedChromatogramFormatException
;
55 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
56 import org
.eclipse
.swt
.SWT
;
57 import org
.eclipse
.swt
.dnd
.Clipboard
;
58 import org
.eclipse
.swt
.widgets
.Composite
;
59 import org
.eclipse
.swt
.widgets
.Display
;
60 import org
.eclipse
.ui
.IActionBars
;
61 import org
.eclipse
.ui
.IEditorInput
;
62 import org
.eclipse
.ui
.IEditorPart
;
63 import org
.eclipse
.ui
.IEditorSite
;
64 import org
.eclipse
.ui
.PartInitException
;
65 import org
.eclipse
.ui
.PlatformUI
;
66 import org
.eclipse
.ui
.commands
.ICommandService
;
67 import org
.eclipse
.ui
.part
.EditorPart
;
69 import eu
.etaxonomy
.cdm
.api
.conversation
.ConversationHolder
;
70 import eu
.etaxonomy
.cdm
.api
.service
.molecular
.ISequenceService
;
71 import eu
.etaxonomy
.cdm
.model
.media
.MediaUtils
;
72 import eu
.etaxonomy
.cdm
.model
.molecular
.Sequence
;
73 import eu
.etaxonomy
.cdm
.model
.molecular
.SequenceString
;
74 import eu
.etaxonomy
.cdm
.model
.molecular
.SingleRead
;
75 import eu
.etaxonomy
.cdm
.model
.molecular
.SingleReadAlignment
;
76 import eu
.etaxonomy
.taxeditor
.model
.MessagingUtils
;
77 import eu
.etaxonomy
.taxeditor
.molecular
.TaxeditorMolecularPlugin
;
78 import eu
.etaxonomy
.taxeditor
.molecular
.handler
.ToggleInsertOverwriteHandler
;
79 import eu
.etaxonomy
.taxeditor
.molecular
.handler
.ToggleLeftRightInsertionHandler
;
80 import eu
.etaxonomy
.taxeditor
.store
.CdmStore
;
81 import eu
.etaxonomy
.taxeditor
.view
.derivateSearch
.DerivateLabelProvider
;
86 * Editor component to edit a contig alignment used to combine different overlapping pherograms from Sanger sequencing to
87 * a consensus sequence.
89 * The contained GUI components used to edit the alignment come from <a href="http://bioinfweb.info/LibrAlign/">LibrAlign</a>.
95 public class AlignmentEditor
extends EditorPart
{
96 public static final String ID
= "eu.etaxonomy.taxeditor.molecular.AlignmentEditor";
98 public static final int READS_AREA_INDEX
= 1;
99 public static final int EDITABLE_CONSENSUS_AREA_INDEX
= READS_AREA_INDEX
+ 1;
100 public static final int CONSENSUS_HINT_AREA_INDEX
= EDITABLE_CONSENSUS_AREA_INDEX
+ 1;
101 public static final int PHEROGRAM_AREA_INDEX
= 0;
102 public static final int CONSENSUS_DATA_AREA_INDEX
= 0;
103 public static final String DEFAULT_READ_NAME_PREFIX
= "Read ";
104 public static final String CONSENSUS_NAME
= "Consensus";
107 private final ConversationHolder conversationHolder
;
108 private final AlignmentModelChangeListener DIRTY_LISTENER
= new AlignmentModelChangeListener() {
110 public <T
> void afterTokenChange(TokenChangeEvent
<T
> e
) {
115 public <T
> void afterSequenceRenamed(SequenceRenamedEvent
<T
> e
) {
120 public <T
> void afterSequenceChange(SequenceChangeEvent
<T
> e
) {
125 public <T
, U
> void afterProviderChanged(AlignmentModel
<T
> oldProvider
,
126 AlignmentModel
<U
> newProvider
) { // Not expected.
131 private final AlignmentEditorActionUpdater ACTION_UPDATER
= new AlignmentEditorActionUpdater();
132 public final Clipboard CLIPBOARD
= new Clipboard(Display
.getCurrent()); //TODO Move to global EDITor class.
135 private MultipleAlignmentsContainer alignmentsContainer
= null;
136 private final Map
<String
, SingleReadAlignment
> cdmMap
= new TreeMap
<String
, SingleReadAlignment
>(); //TODO Move this to ContigSequenceDataProvider
137 private boolean dirty
= false;
140 public AlignmentEditor() {
142 conversationHolder
= CdmStore
.createConversation();
143 //conversationHolder = null;
147 private void refreshToolbarElement(String id
) {
148 ICommandService commandService
=
149 (ICommandService
)PlatformUI
.getWorkbench().getActiveWorkbenchWindow().getService(ICommandService
.class);
150 if (commandService
!= null) {
151 commandService
.refreshElements(id
, Collections
.EMPTY_MAP
);
156 private void registerEditSettingListener(MultipleAlignmentsContainer container
) {
157 container
.getEditSettings().addListener(new EditSettingsListener() {
159 public void workingModeChanged(EditSettingsChangeEvent e
) {} // Currently nothing to do
162 public void insertLeftInDataAreaChanged(EditSettingsChangeEvent e
) {
164 refreshToolbarElement(ToggleLeftRightInsertionHandler
.COMMAND_ID
);
168 public void insertChanged(EditSettingsChangeEvent e
) {
170 refreshToolbarElement(ToggleInsertOverwriteHandler
.COMMAND_ID
);
176 private AlignmentArea
createIndexArea(MultipleAlignmentsContainer container
, AlignmentArea labeledArea
) {
177 AlignmentArea result
= new AlignmentArea(container
);
178 result
.setAllowVerticalScrolling(false);
179 result
.getDataAreas().getTopAreas().add(new SequenceIndexArea(result
.getContentArea(), labeledArea
));
184 private AlignmentArea
createEditableAlignmentArea(MultipleAlignmentsContainer container
, boolean allowVerticalScrolling
) {
185 AlignmentArea result
= new AlignmentArea(container
);
186 result
.setAllowVerticalScrolling(allowVerticalScrolling
);
188 CharacterTokenSet tokenSet
= CharacterTokenSet
.newDNAInstance(); //TODO Should NUCLEOTIDE be used instead?
189 AlignmentModel
<Character
> model
= new PackedAlignmentModel
<Character
>(tokenSet
);
190 result
.setAlignmentModel(model
, false);
191 model
.getChangeListeners().add(DIRTY_LISTENER
);
192 result
.getPaintSettings().getTokenPainterList().set(0, new NucleotideTokenPainter());
198 private AlignmentArea
createConsensusHintArea(MultipleAlignmentsContainer container
,
199 AlignmentArea labeledArea
) {
201 AlignmentArea result
= new AlignmentArea(container
);
202 result
.setAllowVerticalScrolling(false);
203 result
.getDataAreas().getBottomAreas().add(
204 new ConsensusSequenceArea(result
.getContentArea(), labeledArea
));
209 private MultipleAlignmentsContainer
getAlignmentsContainer() {
210 if (alignmentsContainer
== null) {
211 alignmentsContainer
= new MultipleAlignmentsContainer();
213 AlignmentAreaList list
= alignmentsContainer
.getAlignmentAreas();
214 AlignmentArea readsArea
= createEditableAlignmentArea(alignmentsContainer
, true);
215 readsArea
.getSelection().addSelectionListener(ACTION_UPDATER
);
216 list
.add(createIndexArea(alignmentsContainer
, readsArea
));
217 list
.add(readsArea
); // Make sure READS_AREA_INDEX is correct.
218 AlignmentArea editableConsensusArea
= createEditableAlignmentArea(alignmentsContainer
, false);
219 editableConsensusArea
.getSelection().addSelectionListener(ACTION_UPDATER
);
220 list
.add(editableConsensusArea
); // Make sure COMSENSUS_AREA_INDEX is correct.
221 list
.add(createConsensusHintArea(alignmentsContainer
, readsArea
));
223 registerEditSettingListener(alignmentsContainer
);
225 return alignmentsContainer
;
229 public AlignmentArea
getReadsArea() {
230 return getAlignmentsContainer().getAlignmentAreas().get(READS_AREA_INDEX
);
234 public AlignmentArea
getEditableConsensusArea() {
235 return getAlignmentsContainer().getAlignmentAreas().get(EDITABLE_CONSENSUS_AREA_INDEX
);
240 * Checks whether {@link #getReadsArea()} or {@link #getEditableConsensusArea()} currently
241 * have the user focus and returns the according component.
243 * @return either the reads or the consensus alignment area or {@code null} if none of these
244 * components is currently focused
246 public AlignmentArea
getFocusedArea() {
247 AlignmentArea result
= getReadsArea();
248 if (hasFocus(result
)) {
252 result
= getEditableConsensusArea();
253 if (hasFocus(result
)) {
264 * Checks whether the specified alignment area or one of its subcomponents currently has the
267 * @param area the alignment area to be checked (Can only be {@link #getReadsArea()} or
268 * {@link #getEditableConsensusArea()}.)
269 * @return {@code true} if the specified component is focused and is either equal to
270 * {@link #getReadsArea()} or {@link #getEditableConsensusArea()}or {@code false} otherwise
272 private boolean hasFocus(AlignmentArea area
) {
273 return SWTUtils
.childHasFocus((Composite
)area
.getToolkitComponent());
277 public boolean hasPherogram(String sequenceID
) {
278 return getReadsArea().getDataAreas().getSequenceAreas(sequenceID
).size() > PHEROGRAM_AREA_INDEX
;
282 public PherogramArea
getPherogramArea(String sequenceID
) {
283 if (hasPherogram(sequenceID
)) {
284 return (PherogramArea
)getReadsArea().getDataAreas().getSequenceAreas(sequenceID
).get(PHEROGRAM_AREA_INDEX
);
292 private ConsensusSequenceArea
getConsensusHintDataArea() {
293 return (ConsensusSequenceArea
)getAlignmentsContainer().getAlignmentAreas().
294 get(CONSENSUS_HINT_AREA_INDEX
).getDataAreas().getBottomAreas().
295 get(CONSENSUS_DATA_AREA_INDEX
);
299 @Deprecated //TODO Remove as soon as testing period is over
300 private void createTestContents() {
303 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);
304 //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);
305 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);
307 // Add test consensus sequence:
308 AlignmentModel consensusModel
= getEditableConsensusArea().getAlignmentModel();
309 String id
= consensusModel
.addSequence(CONSENSUS_NAME
);
310 Collection
<Object
> tokens
= new ArrayList
<Object
>(); // First save tokens in a collection to avoid GUI updated for each token.
311 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("A"));
312 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("C"));
313 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("G"));
314 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("T"));
315 consensusModel
.insertTokensAt(id
, 0, tokens
);
317 catch (Exception e
) {
318 throw new RuntimeException(e
);
323 private void readCDMData(Sequence sequenceNode
) {
324 //TODO If called from somewhere else than createPartControl() the editorInput needs to be checked and previous contents need to be cleared (or updated).
327 for (SingleReadAlignment singleReadAlignment
: sequenceNode
.getSingleReadAlignments()) {
329 SingleRead pherogramInfo
= singleReadAlignment
.getSingleRead();
330 String id
= addRead(DerivateLabelProvider
.getDerivateText(pherogramInfo
, conversationHolder
),
331 getPherogramURI(pherogramInfo
),
332 singleReadAlignment
.isReverseComplement(),
333 singleReadAlignment
.getEditedSequence(),
334 singleReadAlignment
.getFirstSeqPosition(),
335 singleReadAlignment
.getLeftCutPosition(),
336 singleReadAlignment
.getRightCutPosition(),
337 singleReadAlignment
.getShifts());
338 cdmMap
.put(id
, singleReadAlignment
);
340 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).
341 MessagingUtils
.errorDialog("Error", null, "A single read was skipped because of the following error:\n\n" +
342 e
.getLocalizedMessage(), TaxeditorMolecularPlugin
.PLUGIN_ID
, e
, false);
346 // Set consensus sequence:
347 AlignmentModel consensusModel
= getEditableConsensusArea().getAlignmentModel();
348 String id
= consensusModel
.addSequence(CONSENSUS_NAME
);
349 consensusModel
.insertTokensAt(id
, 0, AlignmentModelUtils
.charSequenceToTokenList(
350 sequenceNode
.getConsensusSequence().getString(), consensusModel
.getTokenSet()));
351 //TODO Can the consensus sequence also be null? / Should it be created here, if nothing is in the DB?
356 public void createPartControl(Composite parent
) {
357 SWTComponentFactory
.getInstance().getSWTComponent(getAlignmentsContainer(), parent
, SWT
.NONE
);
358 Display
.getCurrent().addFilter(SWT
.FocusIn
, ACTION_UPDATER
);
359 Display
.getCurrent().addFilter(SWT
.FocusOut
, ACTION_UPDATER
);
362 if (getEditorInput() instanceof AlignmentEditorInput
) {
363 if (((AlignmentEditorInput
)getEditorInput()).getSequenceNodeUuid() != null) {
364 Sequence sequenceNode
= CdmStore
.getService(ISequenceService
.class).load(((AlignmentEditorInput
)getEditorInput()).getSequenceNodeUuid());
365 //re-load into the current session if it is already persisted in the DB
366 if(sequenceNode
!=null && sequenceNode
.getId()!=0){
367 sequenceNode
= CdmStore
.getService(ISequenceService
.class).load(sequenceNode
.getUuid());
369 readCDMData(sequenceNode
);
372 createTestContents(); // This case will removed after the test phase and an exception should probably be thrown.
376 throw new IllegalArgumentException("The editor input must have the type " +
377 AlignmentEditorInput
.class.getCanonicalName()); //TODO What should be done here?
383 public void dispose() {
384 Display
.getCurrent().removeFilter(SWT
.FocusIn
, ACTION_UPDATER
);
385 Display
.getCurrent().removeFilter(SWT
.FocusOut
, ACTION_UPDATER
);
387 ((AlignmentEditorInput
)getEditorInput()).dispose();
392 private void updateStatusBar() {
393 IActionBars bars
= getEditorSite().getActionBars();
394 bars
.getStatusLineManager().setMessage("Edit mode: " +
395 (getReadsArea().getEditSettings().isInsert() ?
"Insert" : "Overwrite") + " " +
396 "Insertion in pherogram: " +
397 (getReadsArea().getEditSettings().isInsertLeftInDataArea() ?
"Left" : "Right")); //TODO multi language
401 private SingleReadAlignment
.Shift
[] convertToCDMShifts(PherogramAreaModel model
) {
402 Iterator
<ShiftChange
> iterator
= model
.shiftChangeIterator();
403 List
<SingleReadAlignment
.Shift
> shifts
= new ArrayList
<SingleReadAlignment
.Shift
>();
404 while (iterator
.hasNext()) {
405 ShiftChange shiftChange
= iterator
.next();
406 shifts
.add(new SingleReadAlignment
.Shift(shiftChange
.getBaseCallIndex(), shiftChange
.getShiftChange()));
408 return shifts
.toArray(new SingleReadAlignment
.Shift
[shifts
.size()]);
413 public void doSave(IProgressMonitor monitor
) {
414 if (getEditorInput() instanceof AlignmentEditorInput
) {
415 String taskName
= "Saving alignment"; //TODO multi language
416 monitor
.beginTask(taskName
, 3);
418 //re-loading sequence to avoid session conflicts
419 Sequence sequenceNode
= CdmStore
.getService(ISequenceService
.class).load(((AlignmentEditorInput
)getEditorInput()).getSequenceNodeUuid());
420 ((AlignmentEditorInput
)getEditorInput()).setSequenceNode(sequenceNode
);
421 StringAdapter stringProvider
= new StringAdapter(getEditableConsensusArea().getAlignmentModel(), false); // Throws an exception if a token has more than one character.
423 // Write consensus sequence:
424 SequenceString consensusSequenceObj
= sequenceNode
.getConsensusSequence();
425 String newConsensusSequence
= stringProvider
.getSequence(
426 getEditableConsensusArea().getAlignmentModel().sequenceIDByName(CONSENSUS_NAME
));
427 if (consensusSequenceObj
== null) {
428 sequenceNode
.setConsensusSequence(SequenceString
.NewInstance(newConsensusSequence
));
431 consensusSequenceObj
.setString(newConsensusSequence
);
434 // Write single reads:
435 stringProvider
.setUnderlyingModel(getReadsArea().getAlignmentModel());
436 sequenceNode
.getSingleReadAlignments().retainAll(cdmMap
.values()); // Remove all reads that are not in the alignment anymore.
437 Iterator
<String
> iterator
= getReadsArea().getAlignmentModel().sequenceIDIterator();
438 while (iterator
.hasNext()) {
439 String id
= iterator
.next();
440 SingleReadAlignment singleRead
= cdmMap
.get(id
);
441 if (singleRead
== null) {
442 throw new InternalError("Creating new reads from AlignmentEditor not implemented."); //TODO multi language
443 //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?
444 //singleRead = SingleReadAlignment.NewInstance(consensusSequence, singleRead, shifts, editedSequence);
447 singleRead
.setEditedSequence(stringProvider
.getSequence(id
));
449 PherogramArea pherogramArea
= getPherogramArea(id
);
450 if (pherogramArea
!= null) {
451 PherogramAreaModel model
= pherogramArea
.getModel();
452 singleRead
.setReverseComplement(model
.getPherogramProvider() instanceof ReverseComplementPherogramProvider
); // Works only if ReverseComplementPherogramProvider instances are not nested.
453 singleRead
.setShifts(convertToCDMShifts(getPherogramArea(id
).getModel()));
454 singleRead
.setFirstSeqPosition(model
.getFirstSeqPos());
455 singleRead
.setLeftCutPosition(model
.getLeftCutPosition());
456 singleRead
.setRightCutPosition(model
.getRightCutPosition());
460 if (!conversationHolder
.isBound()) {
461 conversationHolder
.bind();
465 ((AlignmentEditorInput
)getEditorInput()).merge();
466 // Commit the conversation and start a new transaction immediately:
467 conversationHolder
.commit(true);
473 firePropertyChange(PROP_DIRTY
);
476 //TODO Throw exception as soon as testing period which allows unlinked AlignmentEditor is over.
482 public void doSaveAs() {}
486 public void init(IEditorSite site
, IEditorInput input
) throws PartInitException
{
493 public boolean isDirty() {
498 private void setDirty() {
500 firePropertyChange(IEditorPart
.PROP_DIRTY
);
505 public boolean isSaveAsAllowed() {
506 return false; // "Save as" not allowed.
511 public void setFocus() {
512 if(conversationHolder
!= null){
513 conversationHolder
.bind();
515 ((AlignmentEditorInput
)getEditorInput()).bind();
518 public boolean isInsertMode() {
519 return getAlignmentsContainer().getEditSettings().isInsert();
523 public boolean isInsertLeftInPherogram() {
524 return getAlignmentsContainer().getEditSettings().isInsertLeftInDataArea();
528 public void toggleLeftRightInsertionInPherogram() {
529 getAlignmentsContainer().getEditSettings().toggleInsertLeftInDataArea();
533 public void toggleInsertOverwrite() {
534 getAlignmentsContainer().getEditSettings().toggleInsert();
538 private String
cutPherogram(boolean left
) {
539 SelectionModel selection
= getReadsArea().getSelection();
540 if (selection
.getCursorHeight() != 1) {
541 return "Cutting pherograms is only possible if exactly one row is selected."; //TODO multi language
544 PherogramArea pherogramArea
=
545 getPherogramArea(getReadsArea().getSequenceOrder().idByIndex(selection
.getCursorRow()));
546 if (pherogramArea
== null) {
547 return "There is no pherogram attached to the current sequence."; //TODO multi language
551 if (pherogramArea
.setLeftCutPositionBySelection()) {
555 return "The left end of the selection lies outside the pherogram attached to this sequence."; //TODO multi language
559 if (pherogramArea
.setRightCutPositionBySelection()) {
563 return "The right end of the selection lies outside the pherogram attached to this sequence."; //TODO multi language
571 public String
cutPherogramLeft() {
572 return cutPherogram(true);
576 public String
cutPherogramRight() {
577 return cutPherogram(false);
581 public void reverseComplementSelectedSequences() {
582 SelectionModel selection
= getReadsArea().getSelection();
583 AlignmentModel
<?
> model
= getReadsArea().getAlignmentModel();
584 for (int row
= selection
.getFirstRow(); row
< selection
.getFirstRow() + selection
.getCursorHeight(); row
++) {
585 String sequenceID
= getReadsArea().getSequenceOrder().idByIndex(row
);
586 PherogramArea area
= getPherogramArea(sequenceID
);
587 PherogramAreaModel pherogramAlignmentModel
= area
.getModel();
588 AlignmentModelUtils
.reverseComplement(model
, sequenceID
,
589 pherogramAlignmentModel
.editableIndexByBaseCallIndex(
590 pherogramAlignmentModel
.getLeftCutPosition()).getBeforeValidIndex(),
591 pherogramAlignmentModel
.editableIndexByBaseCallIndex(
592 pherogramAlignmentModel
.getRightCutPosition()).getAfterValidIndex());
593 pherogramAlignmentModel
.reverseComplement();
599 * Recreates the whole consensus sequence from all single read sequences. The previous consensus
600 * sequence is overwritte.
602 @SuppressWarnings("unchecked")
603 public <T
> void createConsensusSequence() {
604 ConsensusSequenceArea area
= getConsensusHintDataArea();
605 AlignmentModel
<T
> model
= (AlignmentModel
<T
>)getEditableConsensusArea().getAlignmentModel();
606 String sequenceID
= model
.sequenceIDIterator().next(); // There is always one sequence contained.
607 int length
= getReadsArea().getAlignmentModel().getMaxSequenceLength();
609 Collection
<T
> tokens
= new ArrayList
<T
>(length
);
610 for (int column
= 0; column
< length
; column
++) {
611 tokens
.add(model
.getTokenSet().tokenByRepresentation(area
.getConsensusToken(column
)));
614 model
.removeTokensAt(sequenceID
, 0, model
.getSequenceLength(sequenceID
));
615 model
.insertTokensAt(sequenceID
, 0, tokens
);
620 * Updates the current consensus sequence by replacing gaps by the according consensus tokens
621 * calculated from the single read sequences and extends the consensus sequence if necessary.
623 @SuppressWarnings("unchecked")
624 public <T
> void updateConsensusSequence() {
625 ConsensusSequenceArea area
= getConsensusHintDataArea();
626 AlignmentModel
<T
> model
= (AlignmentModel
<T
>)getEditableConsensusArea().getAlignmentModel();
627 TokenSet
<T
> tokenSet
= model
.getTokenSet();
628 String sequenceID
= model
.sequenceIDIterator().next(); // There is always one sequence contained.
629 int currentConsensusLength
= model
.getSequenceLength(sequenceID
);
630 int overallLength
= getReadsArea().getAlignmentModel().getMaxSequenceLength();
632 // Replace gaps by new information:
633 for (int column
= 0; column
< currentConsensusLength
; column
++) {
634 if (tokenSet
.isGapToken(model
.getTokenAt(sequenceID
, column
))) {
635 T newToken
= tokenSet
.tokenByRepresentation(area
.getConsensusToken(column
));
636 if (!tokenSet
.isGapToken(newToken
)) {
637 model
.setTokenAt(sequenceID
, column
, newToken
);
642 // Append additional tokens:
643 if (overallLength
> currentConsensusLength
) {
644 Collection
<T
> tokens
= new ArrayList
<T
>(overallLength
);
645 for (int column
= currentConsensusLength
; column
< overallLength
; column
++) {
646 tokens
.add(tokenSet
.tokenByRepresentation(area
.getConsensusToken(column
)));
648 model
.appendTokens(sequenceID
, tokens
);
653 public static PherogramProvider
readPherogram(URI uri
) throws IOException
, UnsupportedChromatogramFormatException
{
654 PherogramProvider result
;
655 InputStream stream
= uri
.toURL().openStream();
657 result
= new BioJavaPherogramProvider(ChromatogramFactory
.create(stream
));
666 private String
newReadName() {
668 while (getReadsArea().getAlignmentModel().sequenceIDByName(DEFAULT_READ_NAME_PREFIX
+ index
) != null) {
671 return DEFAULT_READ_NAME_PREFIX
+ index
;
675 public void addRead(URI pherogramURI
, boolean reverseComplemented
) throws IOException
, UnsupportedChromatogramFormatException
{
676 addRead(newReadName(), pherogramURI
, reverseComplemented
, null, null, null, null, null);
681 * Adds a new sequence with attached phergram data area to the reads alignment.
683 * If {@code null} is specified as {@code editedSequence} the base call sequence from the pherogram will
684 * be set as the edited sequence. If {@code null} is specified as {@code shifts} no shifts between the edited
685 * and the base calls sequence are assumed.
687 * @param name the name of the new sequence
688 * @param pherogramURI the URI where the associated pherogram file is located
689 * @param reverseComplemented Specify {@code true} here, if the reverse complement of the pherogram data should
690 * be added, {@code false} otherwise.
691 * @param editedSequence the edited version of the base call sequence (May be {@code null}.)
692 * @param shifts the alignment information that links the edited and the base call sequence (May be {@code null}.)
693 * @return the sequence ID of the added read
694 * @throws IOException if an error occurred when trying to read the pherogram file
695 * @throws UnsupportedChromatogramFormatException if the format of the pherogram file is not supported
697 public String
addRead(String name
, URI pherogramURI
, boolean reverseComplemented
, String editedSequence
,
698 Integer firstSeqPos
, Integer leftCutPos
, Integer rightCutPos
, SingleReadAlignment
.Shift
[] shifts
)
699 throws IOException
, UnsupportedChromatogramFormatException
{
701 AlignmentModel model
= getReadsArea().getAlignmentModel();
702 PherogramProvider pherogramProvider
= null;
703 if (pherogramURI
!= null) {
704 pherogramProvider
= readPherogram(pherogramURI
); // Must happen before a sequence is added, because it might throw an exception.
705 if (reverseComplemented
) {
706 pherogramProvider
= new ReverseComplementPherogramProvider(pherogramProvider
);
711 model
.addSequence(name
);
712 String id
= model
.sequenceIDByName(name
);
714 // Set edited sequence:
715 Collection
<Object
> tokens
= null; // First save tokens in a collection to avoid GUI updated for each token.
716 if (editedSequence
!= null) {
717 tokens
= AlignmentModelUtils
.charSequenceToTokenList(editedSequence
, model
.getTokenSet());
719 else if (pherogramProvider
!= null) { // Copy base call sequence into alignment:
720 tokens
= new ArrayList
<Object
>();
721 for (int i
= 0; i
< pherogramProvider
.getSequenceLength(); i
++) {
722 tokens
.add(model
.getTokenSet().tokenByRepresentation(
723 Character
.toString(pherogramProvider
.getBaseCall(i
))));
728 if (tokens
!= null) { // If either an edited sequence or a pherogram URI was provided.
729 model
.insertTokensAt(id
, 0, tokens
);
731 if (pherogramProvider
!= null) {
732 // Create pherogram area:
733 PherogramArea pherogramArea
= new PherogramArea(getReadsArea().getContentArea(),
734 new PherogramAreaModel(pherogramProvider
));
736 // Set position properties and shifts:
737 PherogramAreaModel phergramModel
= pherogramArea
.getModel();
738 if ((firstSeqPos
!= null) && (leftCutPos
!= null)) {
739 phergramModel
.setFirstSeqLeftCutPos(firstSeqPos
, leftCutPos
);
741 if (rightCutPos
!= null) {
742 phergramModel
.setRightCutPosition(rightCutPos
);
744 if ((shifts
!= null) && (shifts
.length
> 0)) {
745 for (int i
= 0; i
< shifts
.length
; i
++) {
746 phergramModel
.addShiftChange(shifts
[i
].position
, shifts
[i
].shift
);
751 // Add pherogram area to GUI:
752 pherogramArea
.addMouseListener(new PherogramMouseListener(pherogramArea
));
753 getReadsArea().getDataAreas().getSequenceAreas(id
).add(pherogramArea
);
760 public static URI
getPherogramURI(SingleRead pherogramInfo
) {
761 if (pherogramInfo
.getPherogram() != null) {
762 return MediaUtils
.getFirstMediaRepresentationPart(pherogramInfo
.getPherogram()).getUri();