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
.cdm
.model
.molecular
.SingleReadAlignment
.Shift
;
77 import eu
.etaxonomy
.taxeditor
.model
.MessagingUtils
;
78 import eu
.etaxonomy
.taxeditor
.molecular
.TaxeditorMolecularPlugin
;
79 import eu
.etaxonomy
.taxeditor
.molecular
.handler
.ToggleInsertOverwriteHandler
;
80 import eu
.etaxonomy
.taxeditor
.molecular
.handler
.ToggleLeftRightInsertionHandler
;
81 import eu
.etaxonomy
.taxeditor
.store
.CdmStore
;
82 import eu
.etaxonomy
.taxeditor
.view
.derivateSearch
.DerivateLabelProvider
;
87 * Editor component to edit a contig alignment used to combine different overlapping pherograms from Sanger sequencing to
88 * a consensus sequence.
90 * The contained GUI components used to edit the alignment come from <a href="http://bioinfweb.info/LibrAlign/">LibrAlign</a>.
96 public class AlignmentEditor
extends EditorPart
{
97 public static final String ID
= "eu.etaxonomy.taxeditor.molecular.AlignmentEditor";
99 public static final int READS_AREA_INDEX
= 1;
100 public static final int EDITABLE_CONSENSUS_AREA_INDEX
= READS_AREA_INDEX
+ 1;
101 public static final int CONSENSUS_HINT_AREA_INDEX
= EDITABLE_CONSENSUS_AREA_INDEX
+ 1;
102 public static final int PHEROGRAM_AREA_INDEX
= 0;
103 public static final int CONSENSUS_DATA_AREA_INDEX
= 0;
104 public static final String DEFAULT_READ_NAME_PREFIX
= "Read ";
105 public static final String CONSENSUS_NAME
= "Consensus";
108 private final ConversationHolder conversationHolder
;
109 private final AlignmentModelChangeListener DIRTY_LISTENER
= new AlignmentModelChangeListener() {
111 public <T
> void afterTokenChange(TokenChangeEvent
<T
> e
) {
116 public <T
> void afterSequenceRenamed(SequenceRenamedEvent
<T
> e
) {
121 public <T
> void afterSequenceChange(SequenceChangeEvent
<T
> e
) {
126 public <T
, U
> void afterProviderChanged(AlignmentModel
<T
> oldProvider
,
127 AlignmentModel
<U
> newProvider
) { // Not expected.
132 private final AlignmentEditorActionUpdater ACTION_UPDATER
= new AlignmentEditorActionUpdater();
133 public final Clipboard CLIPBOARD
= new Clipboard(Display
.getCurrent()); //TODO Move to global EDITor class.
136 private MultipleAlignmentsContainer alignmentsContainer
= null;
137 private final Map
<String
, SingleReadAlignment
> cdmMap
= new TreeMap
<String
, SingleReadAlignment
>(); //TODO Move this to ContigSequenceDataProvider
138 private boolean dirty
= false;
140 public AlignmentEditor()
143 conversationHolder
= CdmStore
.createConversation();
144 //conversationHolder = null;
148 private void refreshToolbarElement(String id
) {
149 ICommandService commandService
=
150 (ICommandService
)PlatformUI
.getWorkbench().getActiveWorkbenchWindow().getService(ICommandService
.class);
151 if (commandService
!= null) {
152 commandService
.refreshElements(id
, Collections
.EMPTY_MAP
);
157 private void registerEditSettingListener(MultipleAlignmentsContainer container
) {
158 container
.getEditSettings().addListener(new EditSettingsListener() {
160 public void workingModeChanged(EditSettingsChangeEvent e
) {} // Currently nothing to do
163 public void insertLeftInDataAreaChanged(EditSettingsChangeEvent e
) {
165 refreshToolbarElement(ToggleLeftRightInsertionHandler
.COMMAND_ID
);
169 public void insertChanged(EditSettingsChangeEvent e
) {
171 refreshToolbarElement(ToggleInsertOverwriteHandler
.COMMAND_ID
);
177 private AlignmentArea
createIndexArea(MultipleAlignmentsContainer container
, AlignmentArea labeledArea
) {
178 AlignmentArea result
= new AlignmentArea(container
);
179 result
.setAllowVerticalScrolling(false);
180 result
.getDataAreas().getTopAreas().add(new SequenceIndexArea(result
.getContentArea(), labeledArea
));
185 private AlignmentArea
createEditableAlignmentArea(MultipleAlignmentsContainer container
, boolean allowVerticalScrolling
) {
186 AlignmentArea result
= new AlignmentArea(container
);
187 result
.setAllowVerticalScrolling(allowVerticalScrolling
);
189 CharacterTokenSet tokenSet
= CharacterTokenSet
.newDNAInstance(); //TODO Should NUCLEOTIDE be used instead?
190 AlignmentModel
<Character
> provider
= new PackedAlignmentModel
<Character
>(tokenSet
);
191 result
.setAlignmentModel(provider
, false);
192 provider
.getChangeListeners().add(DIRTY_LISTENER
);
193 result
.getPaintSettings().getTokenPainterList().set(0, new NucleotideTokenPainter());
199 private AlignmentArea
createConsensusHintArea(MultipleAlignmentsContainer container
,
200 AlignmentArea labeledArea
) {
202 AlignmentArea result
= new AlignmentArea(container
);
203 result
.setAllowVerticalScrolling(false);
204 result
.getDataAreas().getBottomAreas().add(
205 new ConsensusSequenceArea(result
.getContentArea(), labeledArea
));
210 private MultipleAlignmentsContainer
getAlignmentsContainer() {
211 if (alignmentsContainer
== null) {
212 alignmentsContainer
= new MultipleAlignmentsContainer();
214 AlignmentAreaList list
= alignmentsContainer
.getAlignmentAreas();
215 AlignmentArea readsArea
= createEditableAlignmentArea(alignmentsContainer
, true);
216 readsArea
.getSelection().addSelectionListener(ACTION_UPDATER
);
217 list
.add(createIndexArea(alignmentsContainer
, readsArea
));
218 list
.add(readsArea
); // Make sure READS_AREA_INDEX is correct.
219 AlignmentArea editableConsensusArea
= createEditableAlignmentArea(alignmentsContainer
, false);
220 editableConsensusArea
.getSelection().addSelectionListener(ACTION_UPDATER
);
221 list
.add(editableConsensusArea
); // Make sure COMSENSUS_AREA_INDEX is correct.
222 list
.add(createConsensusHintArea(alignmentsContainer
, readsArea
));
224 registerEditSettingListener(alignmentsContainer
);
226 return alignmentsContainer
;
230 public AlignmentArea
getReadsArea() {
231 return getAlignmentsContainer().getAlignmentAreas().get(READS_AREA_INDEX
);
235 public AlignmentArea
getEditableConsensusArea() {
236 return getAlignmentsContainer().getAlignmentAreas().get(EDITABLE_CONSENSUS_AREA_INDEX
);
241 * Checks whether {@link #getReadsArea()} or {@link #getEditableConsensusArea()} currently
242 * have the user focus and returns the according component.
244 * @return either the reads or the consensus alignment area or {@code null} if none of these
245 * components is currently focused
247 public AlignmentArea
getFocusedArea() {
248 AlignmentArea result
= getReadsArea();
249 if (hasFocus(result
)) {
253 result
= getEditableConsensusArea();
254 if (hasFocus(result
)) {
265 * Checks whether the specified alignment area or one of its subcomponents currently has the
268 * @param area the alignment area to be checked (Can only be {@link #getReadsArea()} or
269 * {@link #getEditableConsensusArea()}.)
270 * @return {@code true} if the specified component is focused and is either equal to
271 * {@link #getReadsArea()} or {@link #getEditableConsensusArea()}or {@code false} otherwise
273 private boolean hasFocus(AlignmentArea area
) {
274 return SWTUtils
.childHasFocus((Composite
)area
.getToolkitComponent());
278 public boolean hasPherogram(String sequenceID
) {
279 return getReadsArea().getDataAreas().getSequenceAreas(sequenceID
).size() > PHEROGRAM_AREA_INDEX
;
283 public PherogramArea
getPherogramArea(String sequenceID
) {
284 if (hasPherogram(sequenceID
)) {
285 return (PherogramArea
)getReadsArea().getDataAreas().getSequenceAreas(sequenceID
).get(PHEROGRAM_AREA_INDEX
);
293 private ConsensusSequenceArea
getConsensusHintDataArea() {
294 return (ConsensusSequenceArea
)getAlignmentsContainer().getAlignmentAreas().
295 get(CONSENSUS_HINT_AREA_INDEX
).getDataAreas().getBottomAreas().
296 get(CONSENSUS_DATA_AREA_INDEX
);
300 @Deprecated //TODO Remove as soon as testing period is over
301 private void createTestContents() {
304 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);
305 //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);
306 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);
308 // Add test consensus sequence:
309 AlignmentModel consensusModel
= getEditableConsensusArea().getAlignmentModel();
310 String id
= consensusModel
.addSequence(CONSENSUS_NAME
);
311 Collection
<Object
> tokens
= new ArrayList
<Object
>(); // First save tokens in a collection to avoid GUI updated for each token.
312 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("A"));
313 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("C"));
314 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("G"));
315 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("T"));
316 consensusModel
.insertTokensAt(id
, 0, tokens
);
318 catch (Exception e
) {
319 throw new RuntimeException(e
);
324 private void readCDMData(Sequence sequenceNode
) {
325 //TODO If called from somewhere else than createPartControl() the editorInput needs to be checked and previous contents need to be cleared (or updated).
328 for (SingleReadAlignment singleReadAlignment
: sequenceNode
.getSingleReadAlignments()) {
330 SingleRead pherogramInfo
= singleReadAlignment
.getSingleRead();
332 if (pherogramInfo
.getPherogram() != null) {
333 uri
= MediaUtils
.getFirstMediaRepresentationPart(pherogramInfo
.getPherogram()).getUri();
335 String id
= addRead(DerivateLabelProvider
.getDerivateText(pherogramInfo
, conversationHolder
),
337 singleReadAlignment
.isReverseComplement(),
338 singleReadAlignment
.getEditedSequence(),
339 singleReadAlignment
.getFirstSeqPosition(),
340 singleReadAlignment
.getLeftCutPosition(),
341 singleReadAlignment
.getRightCutPosition(),
342 singleReadAlignment
.getShifts());
343 cdmMap
.put(id
, singleReadAlignment
);
345 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).
346 MessagingUtils
.errorDialog("Error", null, "A single read was skipped because of the following error:\n\n" +
347 e
.getLocalizedMessage(), TaxeditorMolecularPlugin
.PLUGIN_ID
, e
, false);
351 // Set consensus sequence:
352 AlignmentModel consensusProvider
= getEditableConsensusArea().getAlignmentModel();
353 String id
= consensusProvider
.addSequence(CONSENSUS_NAME
);
354 consensusProvider
.insertTokensAt(id
, 0, AlignmentModelUtils
.charSequenceToTokenList(
355 sequenceNode
.getConsensusSequence().getString(), consensusProvider
.getTokenSet()));
356 //TODO Can the consensus sequence also be null? / Should it be created here, if nothing is in the DB?
361 public void createPartControl(Composite parent
) {
362 SWTComponentFactory
.getInstance().getSWTComponent(getAlignmentsContainer(), parent
, SWT
.NONE
);
363 Display
.getCurrent().addFilter(SWT
.FocusIn
, ACTION_UPDATER
);
364 Display
.getCurrent().addFilter(SWT
.FocusOut
, ACTION_UPDATER
);
367 if (getEditorInput() instanceof AlignmentEditorInput
) {
368 if (((AlignmentEditorInput
)getEditorInput()).getSequenceNodeUuid() != null) {
369 Sequence sequenceNode
= CdmStore
.getService(ISequenceService
.class).load(((AlignmentEditorInput
)getEditorInput()).getSequenceNodeUuid());
370 //re-load into the current session if it is already persisted in the DB
371 if(sequenceNode
!=null && sequenceNode
.getId()!=0){
372 sequenceNode
= CdmStore
.getService(ISequenceService
.class).load(sequenceNode
.getUuid());
374 readCDMData(sequenceNode
);
377 createTestContents(); // This case will removed after the test phase and an exception should probably be thrown.
381 throw new IllegalArgumentException("The editor input must have the type " +
382 AlignmentEditorInput
.class.getCanonicalName()); //TODO What should be done here?
388 public void dispose() {
389 Display
.getCurrent().removeFilter(SWT
.FocusIn
, ACTION_UPDATER
);
390 Display
.getCurrent().removeFilter(SWT
.FocusOut
, ACTION_UPDATER
);
392 ((AlignmentEditorInput
)getEditorInput()).dispose();
397 private void updateStatusBar() {
398 IActionBars bars
= getEditorSite().getActionBars();
399 bars
.getStatusLineManager().setMessage("Edit mode: " +
400 (getReadsArea().getEditSettings().isInsert() ?
"Insert" : "Overwrite") + " " +
401 "Insertion in pherogram: " +
402 (getReadsArea().getEditSettings().isInsertLeftInDataArea() ?
"Left" : "Right"));
406 private SingleReadAlignment
.Shift
[] convertToCDMShifts(PherogramAreaModel model
) {
407 Iterator
<ShiftChange
> iterator
= model
.shiftChangeIterator();
408 List
<Shift
> shifts
= new ArrayList
<SingleReadAlignment
.Shift
>();
409 while (iterator
.hasNext()) {
410 ShiftChange shiftChange
= iterator
.next();
411 shifts
.add(new SingleReadAlignment
.Shift(shiftChange
.getBaseCallIndex(), shiftChange
.getShiftChange()));
413 return shifts
.toArray(new Shift
[]{});
418 public void doSave(IProgressMonitor monitor
) {
419 if (getEditorInput() instanceof AlignmentEditorInput
) {
420 String taskName
= "Saving alignment";
421 monitor
.beginTask(taskName
, 3);
423 //re-loading sequence to avoid session conflicts
424 Sequence sequenceNode
= CdmStore
.getService(ISequenceService
.class).load(((AlignmentEditorInput
)getEditorInput()).getSequenceNodeUuid());
425 ((AlignmentEditorInput
)getEditorInput()).setSequenceNode(sequenceNode
);
426 StringAdapter stringProvider
= new StringAdapter(getEditableConsensusArea().getAlignmentModel(), false); // Throws an exception if a token has more than one character.
428 // Write consensus sequence:
429 SequenceString consensusSequenceObj
= sequenceNode
.getConsensusSequence();
430 String newConsensusSequence
= stringProvider
.getSequence(
431 getEditableConsensusArea().getAlignmentModel().sequenceIDByName(CONSENSUS_NAME
));
432 if (consensusSequenceObj
== null) {
433 sequenceNode
.setConsensusSequence(SequenceString
.NewInstance(newConsensusSequence
));
436 consensusSequenceObj
.setString(newConsensusSequence
);
439 // Write single reads:
440 stringProvider
.setUnderlyingModel(getReadsArea().getAlignmentModel());
441 sequenceNode
.getSingleReadAlignments().retainAll(cdmMap
.values()); // Remove all reads that are not in the alignment anymore.
442 Iterator
<String
> iterator
= getReadsArea().getAlignmentModel().sequenceIDIterator();
443 while (iterator
.hasNext()) {
444 String id
= iterator
.next();
445 SingleReadAlignment singleRead
= cdmMap
.get(id
);
446 if (singleRead
== null) {
447 throw new InternalError("Creating new reads from AlignmentEditor not implemented.");
448 //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?
449 //singleRead = SingleReadAlignment.NewInstance(consensusSequence, singleRead, shifts, editedSequence);
452 singleRead
.setEditedSequence(stringProvider
.getSequence(id
));
454 PherogramArea pherogramArea
= getPherogramArea(id
);
455 if (pherogramArea
!= null) {
456 PherogramAreaModel model
= pherogramArea
.getModel();
457 singleRead
.setReverseComplement(model
.getPherogramProvider() instanceof ReverseComplementPherogramProvider
); // Works only if ReverseComplementPherogramProvider instances are not nested.
458 singleRead
.setShifts(convertToCDMShifts(getPherogramArea(id
).getModel()));
459 singleRead
.setFirstSeqPosition(model
.getFirstSeqPos());
460 singleRead
.setLeftCutPosition(model
.getLeftCutPosition());
461 singleRead
.setRightCutPosition(model
.getRightCutPosition());
465 if (!conversationHolder
.isBound()) {
466 conversationHolder
.bind();
470 ((AlignmentEditorInput
)getEditorInput()).merge();
471 // Commit the conversation and start a new transaction immediately:
472 conversationHolder
.commit(true);
478 firePropertyChange(PROP_DIRTY
);
481 //TODO Throw exception as soon as testing period which allows unlinked AlignmentEditor is over.
487 public void doSaveAs() {}
491 public void init(IEditorSite site
, IEditorInput input
) throws PartInitException
{
498 public boolean isDirty() {
503 private void setDirty() {
505 firePropertyChange(IEditorPart
.PROP_DIRTY
);
510 public boolean isSaveAsAllowed() {
511 return false; // "Save as" not allowed.
516 public void setFocus() {
517 if(conversationHolder
!= null){
518 conversationHolder
.bind();
520 ((AlignmentEditorInput
)getEditorInput()).bind();
523 public boolean isInsertMode() {
524 return getAlignmentsContainer().getEditSettings().isInsert();
528 public boolean isInsertLeftInPherogram() {
529 return getAlignmentsContainer().getEditSettings().isInsertLeftInDataArea();
533 public void toggleLeftRightInsertionInPherogram() {
534 getAlignmentsContainer().getEditSettings().toggleInsertLeftInDataArea();
538 public void toggleInsertOverwrite() {
539 getAlignmentsContainer().getEditSettings().toggleInsert();
543 private String
cutPherogram(boolean left
) {
544 SelectionModel selection
= getReadsArea().getSelection();
545 if (selection
.getCursorHeight() != 1) {
546 return "Cutting pherograms is only possible if exactly one row is selected.";
549 PherogramArea pherogramArea
=
550 getPherogramArea(getReadsArea().getSequenceOrder().idByIndex(selection
.getCursorRow()));
551 if (pherogramArea
== null) {
552 return "There is no pherogram attached to the current sequence.";
556 if (pherogramArea
.setLeftCutPositionBySelection()) {
560 return "The left end of the selection lies outside the pherogram attached to this sequence.";
564 if (pherogramArea
.setRightCutPositionBySelection()) {
568 return "The right end of the selection lies outside the pherogram attached to this sequence.";
576 public String
cutPherogramLeft() {
577 return cutPherogram(true);
581 public String
cutPherogramRight() {
582 return cutPherogram(false);
586 public void reverseComplementSelectedSequences() {
587 SelectionModel selection
= getReadsArea().getSelection();
588 AlignmentModel
<?
> model
= getReadsArea().getAlignmentModel();
589 for (int row
= selection
.getFirstRow(); row
< selection
.getFirstRow() + selection
.getCursorHeight(); row
++) {
590 String sequenceID
= getReadsArea().getSequenceOrder().idByIndex(row
);
591 PherogramArea area
= getPherogramArea(sequenceID
);
592 PherogramAreaModel pherogramAlignmentModel
= area
.getModel();
593 AlignmentModelUtils
.reverseComplement(model
, sequenceID
,
594 pherogramAlignmentModel
.editableIndexByBaseCallIndex(
595 pherogramAlignmentModel
.getLeftCutPosition()).getBeforeValidIndex(),
596 pherogramAlignmentModel
.editableIndexByBaseCallIndex(
597 pherogramAlignmentModel
.getRightCutPosition()).getAfterValidIndex());
598 pherogramAlignmentModel
.reverseComplement();
604 * Recreates the whole consensus sequence from all single read sequences. The previous consensus
605 * sequence is overwritte.
607 @SuppressWarnings("unchecked")
608 public <T
> void createConsensusSequence() {
609 ConsensusSequenceArea area
= getConsensusHintDataArea();
610 AlignmentModel
<T
> model
= (AlignmentModel
<T
>)getEditableConsensusArea().getAlignmentModel();
611 String sequenceID
= model
.sequenceIDIterator().next(); // There is always one sequence contained.
612 int length
= getReadsArea().getAlignmentModel().getMaxSequenceLength();
614 Collection
<T
> tokens
= new ArrayList
<T
>(length
);
615 for (int column
= 0; column
< length
; column
++) {
616 tokens
.add(model
.getTokenSet().tokenByRepresentation(area
.getConsensusToken(column
)));
619 model
.removeTokensAt(sequenceID
, 0, model
.getSequenceLength(sequenceID
));
620 model
.insertTokensAt(sequenceID
, 0, tokens
);
625 * Updates the current consensus sequence by replacing gaps by the according consensus tokens
626 * calculated from the single read sequences and extends the consensus sequence if necessary.
628 @SuppressWarnings("unchecked")
629 public <T
> void updateConsensusSequence() {
630 ConsensusSequenceArea area
= getConsensusHintDataArea();
631 AlignmentModel
<T
> model
= (AlignmentModel
<T
>)getEditableConsensusArea().getAlignmentModel();
632 TokenSet
<T
> tokenSet
= model
.getTokenSet();
633 String sequenceID
= model
.sequenceIDIterator().next(); // There is always one sequence contained.
634 int currentConsensusLength
= model
.getSequenceLength(sequenceID
);
635 int overallLength
= getReadsArea().getAlignmentModel().getMaxSequenceLength();
637 // Replace gaps by new information:
638 for (int column
= 0; column
< currentConsensusLength
; column
++) {
639 if (tokenSet
.isGapToken(model
.getTokenAt(sequenceID
, column
))) {
640 T newToken
= tokenSet
.tokenByRepresentation(area
.getConsensusToken(column
));
641 if (!tokenSet
.isGapToken(newToken
)) {
642 model
.setTokenAt(sequenceID
, column
, newToken
);
647 // Append additional tokens:
648 if (overallLength
> currentConsensusLength
) {
649 Collection
<T
> tokens
= new ArrayList
<T
>(overallLength
);
650 for (int column
= currentConsensusLength
; column
< overallLength
; column
++) {
651 tokens
.add(tokenSet
.tokenByRepresentation(area
.getConsensusToken(column
)));
653 model
.appendTokens(sequenceID
, tokens
);
658 public static PherogramProvider
readPherogram(URI uri
) throws IOException
, UnsupportedChromatogramFormatException
{
659 PherogramProvider result
;
660 InputStream stream
= uri
.toURL().openStream();
662 result
= new BioJavaPherogramProvider(ChromatogramFactory
.create(stream
));
671 private String
newReadName() {
673 while (getReadsArea().getAlignmentModel().sequenceIDByName(DEFAULT_READ_NAME_PREFIX
+ index
) != null) {
676 return DEFAULT_READ_NAME_PREFIX
+ index
;
680 public void addRead(URI pherogramURI
, boolean reverseComplemented
) throws IOException
, UnsupportedChromatogramFormatException
{
681 addRead(newReadName(), pherogramURI
, reverseComplemented
, null, null, null, null, null);
686 * Adds a new sequence with attached phergram data area to the reads alignment.
688 * If {@code null} is specified as {@code editedSequence} the base call sequence from the pherogram will
689 * be set as the edited sequence. If {@code null} is specified as {@code shifts} no shifts between the edited
690 * and the base calls sequence are assumed.
692 * @param name the name of the new sequence
693 * @param pherogramURI the URI where the associated pherogram file is located
694 * @param reverseComplemented Specify {@code true} here, if the reverse complement of the pherogram data should
695 * be added, {@code false} otherwise.
696 * @param editedSequence the edited version of the base call sequence (May be {@code null}.)
697 * @param shifts the alignment information that links the edited and the base call sequence (May be {@code null}.)
698 * @return the sequence ID of the added read
699 * @throws IOException if an error occurred when trying to read the pherogram file
700 * @throws UnsupportedChromatogramFormatException if the format of the pherogram file is not supported
702 public String
addRead(String name
, URI pherogramURI
, boolean reverseComplemented
, String editedSequence
,
703 Integer firstSeqPos
, Integer leftCutPos
, Integer rightCutPos
, SingleReadAlignment
.Shift
[] shifts
)
704 throws IOException
, UnsupportedChromatogramFormatException
{
706 AlignmentModel provider
= getReadsArea().getAlignmentModel();
707 PherogramProvider pherogramProvider
= null;
708 if (pherogramURI
!= null) {
709 pherogramProvider
= readPherogram(pherogramURI
); // Must happen before a sequence is added, because it might throw an exception.
710 if (reverseComplemented
) {
711 pherogramProvider
= new ReverseComplementPherogramProvider(pherogramProvider
);
716 provider
.addSequence(name
);
717 String id
= provider
.sequenceIDByName(name
);
719 // Set edited sequence:
720 Collection
<Object
> tokens
= null; // First save tokens in a collection to avoid GUI updated for each token.
721 if (editedSequence
!= null) {
722 tokens
= AlignmentModelUtils
.charSequenceToTokenList(editedSequence
, provider
.getTokenSet());
724 else if (pherogramProvider
!= null) { // Copy base call sequence into alignment:
725 tokens
= new ArrayList
<Object
>();
726 for (int i
= 0; i
< pherogramProvider
.getSequenceLength(); i
++) {
727 tokens
.add(provider
.getTokenSet().tokenByRepresentation(
728 Character
.toString(pherogramProvider
.getBaseCall(i
))));
733 if (tokens
!= null) { // If either an edited sequence or a pherogram URI was provided.
734 provider
.insertTokensAt(id
, 0, tokens
);
736 if (pherogramProvider
!= null) {
737 // Create pherogram area:
738 PherogramArea pherogramArea
= new PherogramArea(getReadsArea().getContentArea(),
739 new PherogramAreaModel(pherogramProvider
));
741 // Set position properties and shifts:
742 PherogramAreaModel model
= pherogramArea
.getModel();
743 if ((firstSeqPos
!= null) && (leftCutPos
!= null)) {
744 model
.setFirstSeqLeftCutPos(firstSeqPos
, leftCutPos
);
746 if (rightCutPos
!= null) {
747 model
.setRightCutPosition(rightCutPos
);
749 if ((shifts
!= null) && (shifts
.length
> 0)) {
750 for (int i
= 0; i
< shifts
.length
; i
++) {
751 model
.addShiftChange(shifts
[i
].position
, shifts
[i
].shift
);
756 // Add pherogram area to GUI:
757 pherogramArea
.addMouseListener(new PherogramMouseListener(pherogramArea
));
758 getReadsArea().getDataAreas().getSequenceAreas(id
).add(pherogramArea
);