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
.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
.SequenceIndexArea
;
18 import info
.bioinfweb
.libralign
.dataarea
.implementations
.pherogram
.PherogramArea
;
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
.AlignmentModelUtils
;
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
.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
.widgets
.Composite
;
57 import org
.eclipse
.swt
.widgets
.Control
;
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
.cdm
.model
.molecular
.SingleReadAlignment
.Shift
;
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();
134 private MultipleAlignmentsContainer alignmentsContainer
= null;
135 private final Map
<Integer
, SingleReadAlignment
> cdmMap
= new TreeMap
<Integer
, SingleReadAlignment
>(); //TODO Move this to ContigSequenceDataProvider
136 private boolean dirty
= false;
138 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
> provider
= new PackedAlignmentModel
<Character
>(tokenSet
);
189 result
.setAlignmentModel(provider
, false);
190 provider
.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 childHasFocus((Composite
)area
.getToolkitComponent());
276 public static boolean childHasFocus(Composite parent
) { //TODO Move to bioinfweb.commons.swt
277 return isChildComponent(parent
, Display
.getCurrent().getFocusControl());
281 public static boolean isChildComponent(Composite parent
, Control child
) { //TODO Move to bioinfweb.commons.swt
282 while ((child
!= parent
) && (child
!= null)) {
283 child
= child
.getParent();
285 return (child
== parent
);
289 public boolean hasPherogram(int sequenceID
) {
290 return getReadsArea().getDataAreas().getSequenceAreas(sequenceID
).size() > PHEROGRAM_AREA_INDEX
;
294 public PherogramArea
getPherogramArea(int sequenceID
) {
295 if (hasPherogram(sequenceID
)) {
296 return (PherogramArea
)getReadsArea().getDataAreas().getSequenceAreas(sequenceID
).get(PHEROGRAM_AREA_INDEX
);
304 private ConsensusSequenceArea
getConsensusHintDataArea() {
305 return (ConsensusSequenceArea
)getAlignmentsContainer().getAlignmentAreas().
306 get(CONSENSUS_HINT_AREA_INDEX
).getDataAreas().getBottomAreas().
307 get(CONSENSUS_DATA_AREA_INDEX
);
311 @Deprecated //TODO Remove as soon as testing period is over
312 private void createTestContents() {
315 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);
316 //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);
317 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);
319 // Add test consensus sequence:
320 AlignmentModel consensusModel
= getEditableConsensusArea().getAlignmentModel();
321 int id
= consensusModel
.addSequence(CONSENSUS_NAME
);
322 Collection
<Object
> tokens
= new ArrayList
<Object
>(); // First save tokens in a collection to avoid GUI updated for each token.
323 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("A"));
324 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("C"));
325 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("G"));
326 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("T"));
327 consensusModel
.insertTokensAt(id
, 0, tokens
);
329 catch (Exception e
) {
330 throw new RuntimeException(e
);
335 private void readCDMData(Sequence sequenceNode
) {
336 //TODO If called from somewhere else than createPartControl() the editorInput needs to be checked and previous contents need to be cleared (or updated).
339 for (SingleReadAlignment singleReadAlignment
: sequenceNode
.getSingleReadAlignments()) {
341 SingleRead pherogramInfo
= singleReadAlignment
.getSingleRead();
343 if (pherogramInfo
.getPherogram() != null) {
344 uri
= MediaUtils
.getFirstMediaRepresentationPart(pherogramInfo
.getPherogram()).getUri();
346 int id
= addRead(DerivateLabelProvider
.getDerivateText(pherogramInfo
, conversationHolder
),
348 singleReadAlignment
.isReverseComplement(),
349 singleReadAlignment
.getEditedSequence(),
350 singleReadAlignment
.getFirstSeqPosition(),
351 singleReadAlignment
.getLeftCutPosition(),
352 singleReadAlignment
.getRightCutPosition(),
353 singleReadAlignment
.getShifts());
354 cdmMap
.put(id
, singleReadAlignment
);
356 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).
357 MessagingUtils
.errorDialog("Error", null, "A single read was skipped because of the following error:\n\n" +
358 e
.getLocalizedMessage(), TaxeditorMolecularPlugin
.PLUGIN_ID
, e
, false);
362 // Set consensus sequence:
363 AlignmentModel consensusProvider
= getEditableConsensusArea().getAlignmentModel();
364 int id
= consensusProvider
.addSequence(CONSENSUS_NAME
);
365 consensusProvider
.insertTokensAt(id
, 0, AlignmentModelUtils
.charSequenceToTokenList(
366 sequenceNode
.getConsensusSequence().getString(), consensusProvider
.getTokenSet()));
367 //TODO Can the consensus sequence also be null? / Should it be created here, if nothing is in the DB?
372 public void createPartControl(Composite parent
) {
373 SWTComponentFactory
.getInstance().getSWTComponent(getAlignmentsContainer(), parent
, SWT
.NONE
);
374 Display
.getCurrent().addFilter(SWT
.FocusIn
, ACTION_UPDATER
);
375 Display
.getCurrent().addFilter(SWT
.FocusOut
, ACTION_UPDATER
);
378 if (getEditorInput() instanceof AlignmentEditorInput
) {
379 if (((AlignmentEditorInput
)getEditorInput()).getSequenceNodeUuid() != null) {
380 Sequence sequenceNode
= CdmStore
.getService(ISequenceService
.class).load(((AlignmentEditorInput
)getEditorInput()).getSequenceNodeUuid());
381 //re-load into the current session if it is already persisted in the DB
382 if(sequenceNode
!=null && sequenceNode
.getId()!=0){
383 sequenceNode
= CdmStore
.getService(ISequenceService
.class).load(sequenceNode
.getUuid());
385 readCDMData(sequenceNode
);
388 createTestContents(); // This case will removed after the test phase and an exception should probably be thrown.
392 throw new IllegalArgumentException("The editor input must have the type " +
393 AlignmentEditorInput
.class.getCanonicalName()); //TODO What should be done here?
399 public void dispose() {
400 Display
.getCurrent().removeFilter(SWT
.FocusIn
, ACTION_UPDATER
);
401 Display
.getCurrent().removeFilter(SWT
.FocusOut
, ACTION_UPDATER
);
406 private void updateStatusBar() {
407 IActionBars bars
= getEditorSite().getActionBars();
408 bars
.getStatusLineManager().setMessage("Edit mode: " +
409 (getReadsArea().getEditSettings().isInsert() ?
"Insert" : "Overwrite") + " " +
410 "Insertion in pherogram: " +
411 (getReadsArea().getEditSettings().isInsertLeftInDataArea() ?
"Left" : "Right"));
415 private SingleReadAlignment
.Shift
[] convertToCDMShifts(PherogramAreaModel model
) {
416 Iterator
<ShiftChange
> iterator
= model
.shiftChangeIterator();
417 List
<Shift
> shifts
= new ArrayList
<SingleReadAlignment
.Shift
>();
418 while (iterator
.hasNext()) {
419 ShiftChange shiftChange
= iterator
.next();
420 shifts
.add(new SingleReadAlignment
.Shift(shiftChange
.getBaseCallIndex(), shiftChange
.getShiftChange()));
422 return shifts
.toArray(new Shift
[]{});
427 public void doSave(IProgressMonitor monitor
) {
428 if (getEditorInput() instanceof AlignmentEditorInput
) {
429 String taskName
= "Saving alignment";
430 monitor
.beginTask(taskName
, 3);
432 //re-loading sequence to avoid session conflicts
433 Sequence sequenceNode
= CdmStore
.getService(ISequenceService
.class).load(((AlignmentEditorInput
)getEditorInput()).getSequenceNodeUuid());
434 StringAdapter stringProvider
= new StringAdapter(getEditableConsensusArea().getAlignmentModel(), false); // Throws an exception if a token has more than one character.
436 // Write consensus sequence:
437 SequenceString consensusSequenceObj
= sequenceNode
.getConsensusSequence();
438 String newConsensusSequence
= stringProvider
.getSequence(
439 getEditableConsensusArea().getAlignmentModel().sequenceIDByName(CONSENSUS_NAME
));
440 if (consensusSequenceObj
== null) {
441 sequenceNode
.setConsensusSequence(SequenceString
.NewInstance(newConsensusSequence
));
444 consensusSequenceObj
.setString(newConsensusSequence
);
447 // Write single reads:
448 stringProvider
.setUnderlyingProvider(getReadsArea().getAlignmentModel());
449 sequenceNode
.getSingleReadAlignments().retainAll(cdmMap
.values()); // Remove all reads that are not in the alignment anymore.
450 Iterator
<Integer
> iterator
= getReadsArea().getAlignmentModel().sequenceIDIterator();
451 while (iterator
.hasNext()) {
452 int id
= iterator
.next();
453 SingleReadAlignment singleRead
= cdmMap
.get(id
);
454 if (singleRead
== null) {
455 throw new InternalError("Creating new reads from AlignmentEditor not implemented.");
456 //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?
457 //singleRead = SingleReadAlignment.NewInstance(consensusSequence, singleRead, shifts, editedSequence);
460 singleRead
.setEditedSequence(stringProvider
.getSequence(id
));
462 PherogramArea pherogramArea
= getPherogramArea(id
);
463 if (pherogramArea
!= null) {
464 PherogramAreaModel model
= pherogramArea
.getModel();
465 singleRead
.setReverseComplement(model
.getPherogramProvider() instanceof ReverseComplementPherogramProvider
); // Works only if ReverseComplementPherogramProvider instances are not nested.
466 singleRead
.setShifts(convertToCDMShifts(getPherogramArea(id
).getModel()));
467 singleRead
.setFirstSeqPosition(model
.getFirstSeqPos());
468 singleRead
.setLeftCutPosition(model
.getLeftCutPosition());
469 singleRead
.setRightCutPosition(model
.getRightCutPosition());
473 if (!conversationHolder
.isBound()) {
474 conversationHolder
.bind();
478 // Commit the conversation and start a new transaction immediately:
479 conversationHolder
.commit(true);
485 firePropertyChange(PROP_DIRTY
);
488 //TODO Throw exception as soon as testing period which allows unlinked AlignmentEditor is over.
494 public void doSaveAs() {}
498 public void init(IEditorSite site
, IEditorInput input
) throws PartInitException
{
505 public boolean isDirty() {
510 private void setDirty() {
512 firePropertyChange(IEditorPart
.PROP_DIRTY
);
517 public boolean isSaveAsAllowed() {
518 return false; // "Save as" not allowed.
523 public void setFocus() {
524 if(conversationHolder
!= null){
525 conversationHolder
.bind();
530 public boolean isInsertMode() {
531 return getAlignmentsContainer().getEditSettings().isInsert();
535 public boolean isInsertLeftInPherogram() {
536 return getAlignmentsContainer().getEditSettings().isInsertLeftInDataArea();
540 public void toggleLeftRightInsertionInPherogram() {
541 getAlignmentsContainer().getEditSettings().toggleInsertLeftInDataArea();
545 public void toggleInsertOverwrite() {
546 getAlignmentsContainer().getEditSettings().toggleInsert();
550 private String
cutPherogram(boolean left
) {
551 SelectionModel selection
= getReadsArea().getSelection();
552 if (selection
.getCursorHeight() != 1) {
553 return "Cutting pherograms is only possible if exactly one row is selected.";
556 PherogramArea pherogramArea
=
557 getPherogramArea(getReadsArea().getSequenceOrder().idByIndex(selection
.getCursorRow()));
558 if (pherogramArea
== null) {
559 return "There is no pherogram attached to the current sequence.";
563 if (pherogramArea
.setLeftCutPositionBySelection()) {
567 return "The left end of the selection lies outside the pherogram attached to this sequence.";
571 if (pherogramArea
.setRightCutPositionBySelection()) {
575 return "The right end of the selection lies outside the pherogram attached to this sequence.";
583 public String
cutPherogramLeft() {
584 return cutPherogram(true);
588 public String
cutPherogramRight() {
589 return cutPherogram(false);
593 public void reverseComplementSelectedSequences() {
594 SelectionModel selection
= getReadsArea().getSelection();
595 AlignmentModel
<?
> model
= getReadsArea().getAlignmentModel();
596 for (int row
= selection
.getFirstRow(); row
< selection
.getFirstRow() + selection
.getCursorHeight(); row
++) {
597 int sequenceID
= getReadsArea().getSequenceOrder().idByIndex(row
);
598 PherogramArea area
= getPherogramArea(sequenceID
);
599 PherogramAreaModel pherogramAlignmentModel
= area
.getModel();
600 AlignmentModelUtils
.reverseComplement(model
, sequenceID
,
601 pherogramAlignmentModel
.editableIndexByBaseCallIndex(
602 pherogramAlignmentModel
.getLeftCutPosition()).getBeforeValidIndex(),
603 pherogramAlignmentModel
.editableIndexByBaseCallIndex(
604 pherogramAlignmentModel
.getRightCutPosition()).getAfterValidIndex());
605 pherogramAlignmentModel
.reverseComplement();
611 * Recreates the whole consensus sequence from all single read sequences. The previous consensus
612 * sequence is overwritte.
614 @SuppressWarnings("unchecked")
615 public <T
> void createConsensusSequence() {
616 ConsensusSequenceArea area
= getConsensusHintDataArea();
617 AlignmentModel
<T
> model
= (AlignmentModel
<T
>)getEditableConsensusArea().getAlignmentModel();
618 int sequenceID
= model
.sequenceIDIterator().next(); // There is always one sequence contained.
619 int length
= getReadsArea().getAlignmentModel().getMaxSequenceLength();
621 Collection
<T
> tokens
= new ArrayList
<T
>(length
);
622 for (int column
= 0; column
< length
; column
++) {
623 tokens
.add(model
.getTokenSet().tokenByRepresentation(area
.getConsensusToken(column
)));
626 model
.removeTokensAt(sequenceID
, 0, model
.getSequenceLength(sequenceID
));
627 model
.insertTokensAt(sequenceID
, 0, tokens
);
632 * Updates the current consensus sequence by replacing gaps by the according consensus tokens
633 * calculated from the single read sequences and extends the consensus sequence if necessary.
635 @SuppressWarnings("unchecked")
636 public <T
> void updateConsensusSequence() {
637 ConsensusSequenceArea area
= getConsensusHintDataArea();
638 AlignmentModel
<T
> model
= (AlignmentModel
<T
>)getEditableConsensusArea().getAlignmentModel();
639 TokenSet
<T
> tokenSet
= model
.getTokenSet();
640 int sequenceID
= model
.sequenceIDIterator().next(); // There is always one sequence contained.
641 int currentConsensusLength
= model
.getSequenceLength(sequenceID
);
642 int overallLength
= getReadsArea().getAlignmentModel().getMaxSequenceLength();
644 // Replace gaps by new information:
645 for (int column
= 0; column
< currentConsensusLength
; column
++) {
646 if (tokenSet
.isGapToken(model
.getTokenAt(sequenceID
, column
))) {
647 T newToken
= tokenSet
.tokenByRepresentation(area
.getConsensusToken(column
));
648 if (!tokenSet
.isGapToken(newToken
)) {
649 model
.setTokenAt(sequenceID
, column
, newToken
);
654 // Append additional tokens:
655 if (overallLength
> currentConsensusLength
) {
656 Collection
<T
> tokens
= new ArrayList
<T
>(overallLength
);
657 for (int column
= currentConsensusLength
; column
< overallLength
; column
++) {
658 tokens
.add(tokenSet
.tokenByRepresentation(area
.getConsensusToken(column
)));
660 model
.appendTokens(sequenceID
, tokens
);
665 public static PherogramProvider
readPherogram(URI uri
) throws IOException
, UnsupportedChromatogramFormatException
{
666 PherogramProvider result
;
667 InputStream stream
= uri
.toURL().openStream();
669 result
= new BioJavaPherogramProvider(ChromatogramFactory
.create(stream
));
678 private String
newReadName() {
680 while (getReadsArea().getAlignmentModel().sequenceIDByName(DEFAULT_READ_NAME_PREFIX
+ index
)
681 != AlignmentModel
.NO_SEQUENCE_FOUND
) {
685 return DEFAULT_READ_NAME_PREFIX
+ index
;
689 public void addRead(URI pherogramURI
, boolean reverseComplemented
) throws IOException
, UnsupportedChromatogramFormatException
{
690 addRead(newReadName(), pherogramURI
, reverseComplemented
, null, null, null, null, null);
695 * Adds a new sequence with attached phergram data area to the reads alignment.
697 * If {@code null} is specified as {@code editedSequence} the base call sequence from the pherogram will
698 * be set as the edited sequence. If {@code null} is specified as {@code shifts} no shifts between the edited
699 * and the base calls sequence are assumed.
701 * @param name the name of the new sequence
702 * @param pherogramURI the URI where the associated pherogram file is located
703 * @param reverseComplemented Specify {@code true} here, if the reverse complement of the pherogram data should
704 * be added, {@code false} otherwise.
705 * @param editedSequence the edited version of the base call sequence (May be {@code null}.)
706 * @param shifts the alignment information that links the edited and the base call sequence (May be {@code null}.)
707 * @return the sequence ID of the added read
708 * @throws IOException if an error occurred when trying to read the pherogram file
709 * @throws UnsupportedChromatogramFormatException if the format of the pherogram file is not supported
711 public int addRead(String name
, URI pherogramURI
, boolean reverseComplemented
, String editedSequence
,
712 Integer firstSeqPos
, Integer leftCutPos
, Integer rightCutPos
, SingleReadAlignment
.Shift
[] shifts
)
713 throws IOException
, UnsupportedChromatogramFormatException
{
715 AlignmentModel provider
= getReadsArea().getAlignmentModel();
716 PherogramProvider pherogramProvider
= null;
717 if (pherogramURI
!= null) {
718 pherogramProvider
= readPherogram(pherogramURI
); // Must happen before a sequence is added, because it might throw an exception.
719 if (reverseComplemented
) {
720 pherogramProvider
= new ReverseComplementPherogramProvider(pherogramProvider
);
725 provider
.addSequence(name
);
726 int id
= provider
.sequenceIDByName(name
);
728 // Set edited sequence:
729 Collection
<Object
> tokens
= null; // First save tokens in a collection to avoid GUI updated for each token.
730 if (editedSequence
!= null) {
731 tokens
= AlignmentModelUtils
.charSequenceToTokenList(editedSequence
, provider
.getTokenSet());
733 else if (pherogramProvider
!= null) { // Copy base call sequence into alignment:
734 tokens
= new ArrayList
<Object
>();
735 for (int i
= 0; i
< pherogramProvider
.getSequenceLength(); i
++) {
736 tokens
.add(provider
.getTokenSet().tokenByRepresentation(
737 Character
.toString(pherogramProvider
.getBaseCall(i
))));
742 if (tokens
!= null) { // If either an edited sequence or a pherogram URI was provided.
743 provider
.insertTokensAt(id
, 0, tokens
);
745 if (pherogramProvider
!= null) {
746 // Create pherogram area:
747 PherogramArea pherogramArea
= new PherogramArea(getReadsArea().getContentArea(),
748 new PherogramAreaModel(pherogramProvider
));
750 // Set position properties and shifts:
751 PherogramAreaModel model
= pherogramArea
.getModel();
752 if ((firstSeqPos
!= null) && (leftCutPos
!= null)) {
753 model
.setFirstSeqLeftCutPos(firstSeqPos
, leftCutPos
);
755 if (rightCutPos
!= null) {
756 model
.setRightCutPosition(rightCutPos
);
758 if ((shifts
!= null) && (shifts
.length
> 0)) {
759 for (int i
= 0; i
< shifts
.length
; i
++) {
760 model
.addShiftChange(shifts
[i
].position
, shifts
[i
].shift
);
765 // Add pherogram area to GUI:
766 pherogramArea
.addMouseListener(new PherogramMouseListener(pherogramArea
));
767 getReadsArea().getDataAreas().getSequenceAreas(id
).add(pherogramArea
);