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
.ui
.IActionBars
;
58 import org
.eclipse
.ui
.IEditorInput
;
59 import org
.eclipse
.ui
.IEditorPart
;
60 import org
.eclipse
.ui
.IEditorSite
;
61 import org
.eclipse
.ui
.PartInitException
;
62 import org
.eclipse
.ui
.PlatformUI
;
63 import org
.eclipse
.ui
.commands
.ICommandService
;
64 import org
.eclipse
.ui
.part
.EditorPart
;
66 import eu
.etaxonomy
.cdm
.api
.conversation
.ConversationHolder
;
67 import eu
.etaxonomy
.cdm
.api
.service
.molecular
.ISequenceService
;
68 import eu
.etaxonomy
.cdm
.model
.media
.MediaUtils
;
69 import eu
.etaxonomy
.cdm
.model
.molecular
.Sequence
;
70 import eu
.etaxonomy
.cdm
.model
.molecular
.SequenceString
;
71 import eu
.etaxonomy
.cdm
.model
.molecular
.SingleRead
;
72 import eu
.etaxonomy
.cdm
.model
.molecular
.SingleReadAlignment
;
73 import eu
.etaxonomy
.cdm
.model
.molecular
.SingleReadAlignment
.Shift
;
74 import eu
.etaxonomy
.taxeditor
.model
.MessagingUtils
;
75 import eu
.etaxonomy
.taxeditor
.molecular
.TaxeditorMolecularPlugin
;
76 import eu
.etaxonomy
.taxeditor
.molecular
.handler
.ToggleInsertOverwriteHandler
;
77 import eu
.etaxonomy
.taxeditor
.molecular
.handler
.ToggleLeftRightInsertionHandler
;
78 import eu
.etaxonomy
.taxeditor
.store
.CdmStore
;
79 import eu
.etaxonomy
.taxeditor
.view
.derivateSearch
.DerivateLabelProvider
;
84 * Editor component to edit a contig alignment used to combine different overlapping pherograms from Sanger sequencing to
85 * a consensus sequence.
87 * The contained GUI components used to edit the alignment come from <a href="http://bioinfweb.info/LibrAlign/">LibrAlign</a>.
93 public class AlignmentEditor
extends EditorPart
{
94 public static final String ID
= "eu.etaxonomy.taxeditor.molecular.AlignmentEditor";
96 public static final int READS_AREA_INDEX
= 1;
97 public static final int EDITABLE_CONSENSUS_AREA_INDEX
= READS_AREA_INDEX
+ 1;
98 public static final int CONSENSUS_HINT_AREA_INDEX
= EDITABLE_CONSENSUS_AREA_INDEX
+ 1;
99 public static final int PHEROGRAM_AREA_INDEX
= 0;
100 public static final int CONSENSUS_DATA_AREA_INDEX
= 0;
101 public static final String DEFAULT_READ_NAME_PREFIX
= "Read ";
102 public static final String CONSENSUS_NAME
= "Consensus";
105 private final ConversationHolder conversationHolder
;
106 private final AlignmentModelChangeListener DIRTY_LISTENER
= new AlignmentModelChangeListener() {
108 public <T
> void afterTokenChange(TokenChangeEvent
<T
> e
) {
113 public <T
> void afterSequenceRenamed(SequenceRenamedEvent
<T
> e
) {
118 public <T
> void afterSequenceChange(SequenceChangeEvent
<T
> e
) {
123 public <T
, U
> void afterProviderChanged(AlignmentModel
<T
> oldProvider
,
124 AlignmentModel
<U
> newProvider
) { // Not expected.
130 private MultipleAlignmentsContainer alignmentsContainer
= null;
131 private final Map
<Integer
, SingleReadAlignment
> cdmMap
= new TreeMap
<Integer
, SingleReadAlignment
>(); //TODO Move this to ContigSequenceDataProvider
132 private boolean dirty
= false;
135 public AlignmentEditor() {
137 conversationHolder
= CdmStore
.createConversation();
138 //conversationHolder = null;
142 private void refreshToolbarElement(String id
) {
143 ICommandService commandService
=
144 (ICommandService
)PlatformUI
.getWorkbench().getActiveWorkbenchWindow().getService(ICommandService
.class);
145 if (commandService
!= null) {
146 commandService
.refreshElements(id
, Collections
.EMPTY_MAP
);
151 private void registerEditSettingListener(MultipleAlignmentsContainer container
) {
152 container
.getEditSettings().addListener(new EditSettingsListener() {
154 public void workingModeChanged(EditSettingsChangeEvent e
) {} // Currently nothing to do
157 public void insertLeftInDataAreaChanged(EditSettingsChangeEvent e
) {
159 refreshToolbarElement(ToggleLeftRightInsertionHandler
.COMMAND_ID
);
163 public void insertChanged(EditSettingsChangeEvent e
) {
165 refreshToolbarElement(ToggleInsertOverwriteHandler
.COMMAND_ID
);
171 private AlignmentArea
createIndexArea(MultipleAlignmentsContainer container
, AlignmentArea labeledArea
) {
172 AlignmentArea result
= new AlignmentArea(container
);
173 result
.setAllowVerticalScrolling(false);
174 result
.getDataAreas().getTopAreas().add(new SequenceIndexArea(result
.getContentArea(), labeledArea
));
179 private AlignmentArea
createEditableAlignmentArea(MultipleAlignmentsContainer container
, boolean allowVerticalScrolling
) {
180 AlignmentArea result
= new AlignmentArea(container
);
181 result
.setAllowVerticalScrolling(allowVerticalScrolling
);
183 CharacterTokenSet tokenSet
= CharacterTokenSet
.newDNAInstance(); //TODO Should NUCLEOTIDE be used instead?
184 AlignmentModel
<Character
> provider
= new PackedAlignmentModel
<Character
>(tokenSet
);
185 result
.setAlignmentModel(provider
, false);
186 provider
.getChangeListeners().add(DIRTY_LISTENER
);
187 result
.getPaintSettings().getTokenPainterList().set(0, new NucleotideTokenPainter());
193 private AlignmentArea
createConsensusHintArea(MultipleAlignmentsContainer container
,
194 AlignmentArea labeledArea
) {
196 AlignmentArea result
= new AlignmentArea(container
);
197 result
.setAllowVerticalScrolling(false);
198 result
.getDataAreas().getBottomAreas().add(
199 new ConsensusSequenceArea(result
.getContentArea(), labeledArea
));
204 private MultipleAlignmentsContainer
getAlignmentsContainer() {
205 if (alignmentsContainer
== null) {
206 alignmentsContainer
= new MultipleAlignmentsContainer();
208 AlignmentAreaList list
= alignmentsContainer
.getAlignmentAreas();
209 AlignmentArea readsArea
= createEditableAlignmentArea(alignmentsContainer
, true);
210 list
.add(createIndexArea(alignmentsContainer
, readsArea
));
211 list
.add(readsArea
); // Make sure READS_AREA_INDEX is correct.
212 list
.add(createEditableAlignmentArea(alignmentsContainer
, false)); // Make sure COMSENSUS_AREA_INDEX is correct.
213 list
.add(createConsensusHintArea(alignmentsContainer
, readsArea
));
215 registerEditSettingListener(alignmentsContainer
);
217 return alignmentsContainer
;
221 public AlignmentArea
getReadsArea() {
222 return getAlignmentsContainer().getAlignmentAreas().get(READS_AREA_INDEX
);
226 private AlignmentArea
getEditableConsensusArea() {
227 return getAlignmentsContainer().getAlignmentAreas().get(EDITABLE_CONSENSUS_AREA_INDEX
);
231 public boolean hasPherogram(int sequenceID
) {
232 return getReadsArea().getDataAreas().getSequenceAreas(sequenceID
).size() > PHEROGRAM_AREA_INDEX
;
236 public PherogramArea
getPherogramArea(int sequenceID
) {
237 if (hasPherogram(sequenceID
)) {
238 return (PherogramArea
)getReadsArea().getDataAreas().getSequenceAreas(sequenceID
).get(PHEROGRAM_AREA_INDEX
);
246 private ConsensusSequenceArea
getConsensusHintDataArea() {
247 return (ConsensusSequenceArea
)getAlignmentsContainer().getAlignmentAreas().
248 get(CONSENSUS_HINT_AREA_INDEX
).getDataAreas().getBottomAreas().
249 get(CONSENSUS_DATA_AREA_INDEX
);
253 @Deprecated //TODO Remove as soon as testing period is over
254 private void createTestContents() {
257 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);
258 //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);
259 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);
261 // Add test consensus sequence:
262 AlignmentModel consensusModel
= getEditableConsensusArea().getAlignmentModel();
263 int id
= consensusModel
.addSequence(CONSENSUS_NAME
);
264 Collection
<Object
> tokens
= new ArrayList
<Object
>(); // First save tokens in a collection to avoid GUI updated for each token.
265 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("A"));
266 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("C"));
267 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("G"));
268 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("T"));
269 consensusModel
.insertTokensAt(id
, 0, tokens
);
271 catch (Exception e
) {
272 throw new RuntimeException(e
);
277 private void readCDMData(Sequence sequenceNode
) {
278 //TODO If called from somewhere else than createPartControl() the editorInput needs to be checked and previous contents need to be cleared (or updated).
281 for (SingleReadAlignment singleReadAlignment
: sequenceNode
.getSingleReadAlignments()) {
283 SingleRead pherogramInfo
= singleReadAlignment
.getSingleRead();
285 if (pherogramInfo
.getPherogram() != null) {
286 uri
= MediaUtils
.getFirstMediaRepresentationPart(pherogramInfo
.getPherogram()).getUri();
288 int id
= addRead(DerivateLabelProvider
.getDerivateText(pherogramInfo
, conversationHolder
),
290 singleReadAlignment
.isReverseComplement(),
291 singleReadAlignment
.getEditedSequence(),
292 singleReadAlignment
.getFirstSeqPosition(),
293 singleReadAlignment
.getLeftCutPosition(),
294 singleReadAlignment
.getRightCutPosition(),
295 singleReadAlignment
.getShifts());
296 cdmMap
.put(id
, singleReadAlignment
);
298 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).
299 MessagingUtils
.errorDialog("Error", null, "A single read was skipped because of the following error:\n\n" +
300 e
.getLocalizedMessage(), TaxeditorMolecularPlugin
.PLUGIN_ID
, e
, false);
304 // Set consensus sequence:
305 AlignmentModel consensusProvider
= getEditableConsensusArea().getAlignmentModel();
306 int id
= consensusProvider
.addSequence(CONSENSUS_NAME
);
307 consensusProvider
.insertTokensAt(id
, 0, AlignmentModelUtils
.charSequenceToTokenList(
308 sequenceNode
.getConsensusSequence().getString(), consensusProvider
.getTokenSet()));
309 //TODO Can the consensus sequence also be null? / Should it be created here, if nothing is in the DB?
314 * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
317 public void createPartControl(Composite parent
) {
318 SWTComponentFactory
.getInstance().getSWTComponent(getAlignmentsContainer(), parent
, SWT
.NONE
);
321 if (getEditorInput() instanceof AlignmentEditorInput
) {
322 if (((AlignmentEditorInput
)getEditorInput()).getSequenceNodeUuid() != null) {
323 Sequence sequenceNode
= CdmStore
.getService(ISequenceService
.class).load(((AlignmentEditorInput
)getEditorInput()).getSequenceNodeUuid());
324 //re-load into the current session if it is already persisted in the DB
325 if(sequenceNode
!=null && sequenceNode
.getId()!=0){
326 sequenceNode
= CdmStore
.getService(ISequenceService
.class).load(sequenceNode
.getUuid());
328 readCDMData(sequenceNode
);
331 createTestContents(); // This case will removed after the test phase and an exception should probably be thrown.
335 throw new IllegalArgumentException("The editor input must have the type " +
336 AlignmentEditorInput
.class.getCanonicalName()); //TODO What should be done here?
341 private void updateStatusBar() {
342 IActionBars bars
= getEditorSite().getActionBars();
343 bars
.getStatusLineManager().setMessage("Edit mode: " +
344 (getReadsArea().getEditSettings().isInsert() ?
"Insert" : "Overwrite") + " " +
345 "Insertion in pherogram: " +
346 (getReadsArea().getEditSettings().isInsertLeftInDataArea() ?
"Left" : "Right"));
350 private SingleReadAlignment
.Shift
[] convertToCDMShifts(PherogramAreaModel model
) {
351 Iterator
<ShiftChange
> iterator
= model
.shiftChangeIterator();
352 List
<Shift
> shifts
= new ArrayList
<SingleReadAlignment
.Shift
>();
353 while (iterator
.hasNext()) {
354 ShiftChange shiftChange
= iterator
.next();
355 shifts
.add(new SingleReadAlignment
.Shift(shiftChange
.getBaseCallIndex(), shiftChange
.getShiftChange()));
357 return shifts
.toArray(new Shift
[]{});
362 * @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor)
365 public void doSave(IProgressMonitor monitor
) {
366 if (getEditorInput() instanceof AlignmentEditorInput
) {
367 String taskName
= "Saving alignment";
368 monitor
.beginTask(taskName
, 3);
370 //re-loading sequence to avoid session conflicts
371 Sequence sequenceNode
= CdmStore
.getService(ISequenceService
.class).load(((AlignmentEditorInput
)getEditorInput()).getSequenceNodeUuid());
372 StringAdapter stringProvider
= new StringAdapter(getEditableConsensusArea().getAlignmentModel(), false); // Throws an exception if a token has more than one character.
374 // Write consensus sequence:
375 SequenceString consensusSequenceObj
= sequenceNode
.getConsensusSequence();
376 String newConsensusSequence
= stringProvider
.getSequence(
377 getEditableConsensusArea().getAlignmentModel().sequenceIDByName(CONSENSUS_NAME
));
378 if (consensusSequenceObj
== null) {
379 sequenceNode
.setConsensusSequence(SequenceString
.NewInstance(newConsensusSequence
));
382 consensusSequenceObj
.setString(newConsensusSequence
);
385 // Write single reads:
386 stringProvider
.setUnderlyingProvider(getReadsArea().getAlignmentModel());
387 sequenceNode
.getSingleReadAlignments().retainAll(cdmMap
.values()); // Remove all reads that are not in the alignment anymore.
388 Iterator
<Integer
> iterator
= getReadsArea().getAlignmentModel().sequenceIDIterator();
389 while (iterator
.hasNext()) {
390 int id
= iterator
.next();
391 SingleReadAlignment singleRead
= cdmMap
.get(id
);
392 if (singleRead
== null) {
393 throw new InternalError("Creating new reads from AlignmentEditor not implemented.");
394 //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?
395 //singleRead = SingleReadAlignment.NewInstance(consensusSequence, singleRead, shifts, editedSequence);
398 singleRead
.setEditedSequence(stringProvider
.getSequence(id
));
400 PherogramArea pherogramArea
= getPherogramArea(id
);
401 if (pherogramArea
!= null) {
402 PherogramAreaModel model
= pherogramArea
.getModel();
403 singleRead
.setReverseComplement(model
.getPherogramProvider() instanceof ReverseComplementPherogramProvider
); // Works only if ReverseComplementPherogramProvider instances are not nested.
404 singleRead
.setShifts(convertToCDMShifts(getPherogramArea(id
).getModel()));
405 singleRead
.setFirstSeqPosition(model
.getFirstSeqPos());
406 singleRead
.setLeftCutPosition(model
.getLeftCutPosition());
407 singleRead
.setRightCutPosition(model
.getRightCutPosition());
411 if (!conversationHolder
.isBound()) {
412 conversationHolder
.bind();
416 // Commit the conversation and start a new transaction immediately:
417 conversationHolder
.commit(true);
423 firePropertyChange(PROP_DIRTY
);
426 //TODO Throw exception as soon as testing period which allows unlinked AlignmentEditor is over.
432 * @see org.eclipse.ui.part.EditorPart#doSaveAs()
435 public void doSaveAs() {}
439 * @see org.eclipse.ui.part.EditorPart#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
442 public void init(IEditorSite site
, IEditorInput input
) throws PartInitException
{
449 * @see org.eclipse.ui.part.EditorPart#isDirty()
452 public boolean isDirty() {
457 private void setDirty() {
459 firePropertyChange(IEditorPart
.PROP_DIRTY
);
464 * @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed()
467 public boolean isSaveAsAllowed() {
468 return false; // "Save as" not allowed.
473 public void setFocus() {
474 if(conversationHolder
!=null){
475 conversationHolder
.bind();
480 public boolean isInsertMode() {
481 return getAlignmentsContainer().getEditSettings().isInsert();
485 public boolean isInsertLeftInPherogram() {
486 return getAlignmentsContainer().getEditSettings().isInsertLeftInDataArea();
490 public void toggleLeftRightInsertionInPherogram() {
491 getAlignmentsContainer().getEditSettings().toggleInsertLeftInDataArea();
495 public void toggleInsertOverwrite() {
496 getAlignmentsContainer().getEditSettings().toggleInsert();
500 private String
cutPherogram(boolean left
) {
501 SelectionModel selection
= getReadsArea().getSelection();
502 if (selection
.getCursorHeight() != 1) {
503 return "Cutting pherograms is only possible if exactly one row is selected.";
506 PherogramArea pherogramArea
=
507 getPherogramArea(getReadsArea().getSequenceOrder().idByIndex(selection
.getCursorRow()));
508 if (pherogramArea
== null) {
509 return "There is no pherogram attached to the current sequence.";
513 if (pherogramArea
.setLeftCutPositionBySelection()) {
517 return "The left end of the selection lies outside the pherogram attached to this sequence.";
521 if (pherogramArea
.setRightCutPositionBySelection()) {
525 return "The right end of the selection lies outside the pherogram attached to this sequence.";
533 public String
cutPherogramLeft() {
534 return cutPherogram(true);
538 public String
cutPherogramRight() {
539 return cutPherogram(false);
543 public void reverseComplementSelectedSequences() {
544 SelectionModel selection
= getReadsArea().getSelection();
545 AlignmentModel
<?
> model
= getReadsArea().getAlignmentModel();
546 for (int row
= selection
.getFirstRow(); row
< selection
.getFirstRow() + selection
.getCursorHeight(); row
++) {
547 int sequenceID
= getReadsArea().getSequenceOrder().idByIndex(row
);
548 PherogramArea area
= getPherogramArea(sequenceID
);
549 PherogramAreaModel pherogramAlignmentModel
= area
.getModel();
550 AlignmentModelUtils
.reverseComplement(model
, sequenceID
,
551 pherogramAlignmentModel
.editableIndexByBaseCallIndex(
552 pherogramAlignmentModel
.getLeftCutPosition()).getBeforeValidIndex(),
553 pherogramAlignmentModel
.editableIndexByBaseCallIndex(
554 pherogramAlignmentModel
.getRightCutPosition()).getAfterValidIndex());
555 pherogramAlignmentModel
.reverseComplement();
561 * Recreates the whole consensus sequence from all single read sequences. The previous consensus
562 * sequence is overwritte.
564 @SuppressWarnings("unchecked")
565 public <T
> void createConsensusSequence() {
566 ConsensusSequenceArea area
= getConsensusHintDataArea();
567 AlignmentModel
<T
> model
= (AlignmentModel
<T
>)getEditableConsensusArea().getAlignmentModel();
568 int sequenceID
= model
.sequenceIDIterator().next(); // There is always one sequence contained.
569 int length
= getReadsArea().getAlignmentModel().getMaxSequenceLength();
571 Collection
<T
> tokens
= new ArrayList
<T
>(length
);
572 for (int column
= 0; column
< length
; column
++) {
573 tokens
.add(model
.getTokenSet().tokenByRepresentation(area
.getConsensusToken(column
)));
576 model
.removeTokensAt(sequenceID
, 0, model
.getSequenceLength(sequenceID
));
577 model
.insertTokensAt(sequenceID
, 0, tokens
);
582 * Updates the current consensus sequence by replacing gaps by the according consensus tokens
583 * calculated from the single read sequences and extends the consensus sequence if necessary.
585 @SuppressWarnings("unchecked")
586 public <T
> void updateConsensusSequence() {
587 ConsensusSequenceArea area
= getConsensusHintDataArea();
588 AlignmentModel
<T
> model
= (AlignmentModel
<T
>)getEditableConsensusArea().getAlignmentModel();
589 TokenSet
<T
> tokenSet
= model
.getTokenSet();
590 int sequenceID
= model
.sequenceIDIterator().next(); // There is always one sequence contained.
591 int currentConsensusLength
= model
.getSequenceLength(sequenceID
);
592 int overallLength
= getReadsArea().getAlignmentModel().getMaxSequenceLength();
594 // Replace gaps by new information:
595 for (int column
= 0; column
< currentConsensusLength
; column
++) {
596 if (tokenSet
.isGapToken(model
.getTokenAt(sequenceID
, column
))) {
597 T newToken
= tokenSet
.tokenByRepresentation(area
.getConsensusToken(column
));
598 if (!tokenSet
.isGapToken(newToken
)) {
599 model
.setTokenAt(sequenceID
, column
, newToken
);
604 // Append additional tokens:
605 if (overallLength
> currentConsensusLength
) {
606 Collection
<T
> tokens
= new ArrayList
<T
>(overallLength
);
607 for (int column
= currentConsensusLength
; column
< overallLength
; column
++) {
608 tokens
.add(tokenSet
.tokenByRepresentation(area
.getConsensusToken(column
)));
610 model
.appendTokens(sequenceID
, tokens
);
615 public static PherogramProvider
readPherogram(URI uri
) throws IOException
, UnsupportedChromatogramFormatException
{
616 PherogramProvider result
;
617 InputStream stream
= uri
.toURL().openStream();
619 result
= new BioJavaPherogramProvider(ChromatogramFactory
.create(stream
));
628 private String
newReadName() {
630 while (getReadsArea().getAlignmentModel().sequenceIDByName(DEFAULT_READ_NAME_PREFIX
+ index
)
631 != AlignmentModel
.NO_SEQUENCE_FOUND
) {
635 return DEFAULT_READ_NAME_PREFIX
+ index
;
639 public void addRead(URI pherogramURI
, boolean reverseComplemented
) throws IOException
, UnsupportedChromatogramFormatException
{
640 addRead(newReadName(), pherogramURI
, reverseComplemented
, null, null, null, null, null);
645 * Adds a new sequence with attached phergram data area to the reads alignment.
647 * If {@code null} is specified as {@code editedSequence} the base call sequence from the pherogram will
648 * be set as the edited sequence. If {@code null} is specified as {@code shifts} no shifts between the edited
649 * and the base calls sequence are assumed.
651 * @param name the name of the new sequence
652 * @param pherogramURI the URI where the associated pherogram file is located
653 * @param reverseComplemented Specify {@code true} here, if the reverse complement of the pherogram data should
654 * be added, {@code false} otherwise.
655 * @param editedSequence the edited version of the base call sequence (May be {@code null}.)
656 * @param shifts the alignment information that links the edited and the base call sequence (May be {@code null}.)
657 * @return the sequence ID of the added read
658 * @throws IOException if an error occurred when trying to read the pherogram file
659 * @throws UnsupportedChromatogramFormatException if the format of the pherogram file is not supported
661 public int addRead(String name
, URI pherogramURI
, boolean reverseComplemented
, String editedSequence
,
662 Integer firstSeqPos
, Integer leftCutPos
, Integer rightCutPos
, SingleReadAlignment
.Shift
[] shifts
)
663 throws IOException
, UnsupportedChromatogramFormatException
{
665 AlignmentModel provider
= getReadsArea().getAlignmentModel();
666 PherogramProvider pherogramProvider
= null;
667 if (pherogramURI
!= null) {
668 pherogramProvider
= readPherogram(pherogramURI
); // Must happen before a sequence is added, because it might throw an exception.
669 if (reverseComplemented
) {
670 pherogramProvider
= new ReverseComplementPherogramProvider(pherogramProvider
);
675 provider
.addSequence(name
);
676 int id
= provider
.sequenceIDByName(name
);
678 // Set edited sequence:
679 Collection
<Object
> tokens
= null; // First save tokens in a collection to avoid GUI updated for each token.
680 if (editedSequence
!= null) {
681 tokens
= AlignmentModelUtils
.charSequenceToTokenList(editedSequence
, provider
.getTokenSet());
683 else if (pherogramProvider
!= null) { // Copy base call sequence into alignment:
684 tokens
= new ArrayList
<Object
>();
685 for (int i
= 0; i
< pherogramProvider
.getSequenceLength(); i
++) {
686 tokens
.add(provider
.getTokenSet().tokenByRepresentation(
687 Character
.toString(pherogramProvider
.getBaseCall(i
))));
692 if (tokens
!= null) { // If either an edited sequence or a pherogram URI was provided.
693 provider
.insertTokensAt(id
, 0, tokens
);
695 if (pherogramProvider
!= null) {
696 // Create pherogram area:
697 PherogramArea pherogramArea
= new PherogramArea(getReadsArea().getContentArea(),
698 new PherogramAreaModel(pherogramProvider
));
700 // Set position properties and shifts:
701 PherogramAreaModel model
= pherogramArea
.getModel();
702 if ((firstSeqPos
!= null) && (leftCutPos
!= null)) {
703 model
.setFirstSeqLeftCutPos(firstSeqPos
, leftCutPos
);
705 if (rightCutPos
!= null) {
706 model
.setRightCutPosition(rightCutPos
);
708 if ((shifts
!= null) && (shifts
.length
> 0)) {
709 for (int i
= 0; i
< shifts
.length
; i
++) {
710 model
.addShiftChange(shifts
[i
].position
, shifts
[i
].shift
);
715 // Add pherogram area to GUI:
716 pherogramArea
.addMouseListener(new PherogramMouseListener(pherogramArea
));
717 getReadsArea().getDataAreas().getSequenceAreas(id
).add(pherogramArea
);