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
;
107 private final AlignmentModelChangeListener DIRTY_LISTENER
= new AlignmentModelChangeListener() {
109 public <T
> void afterTokenChange(TokenChangeEvent
<T
> e
) {
114 public <T
> void afterSequenceRenamed(SequenceRenamedEvent
<T
> e
) {
119 public <T
> void afterSequenceChange(SequenceChangeEvent
<T
> e
) {
124 public <T
, U
> void afterProviderChanged(AlignmentModel
<T
> oldProvider
,
125 AlignmentModel
<U
> newProvider
) { // Not expected.
131 private MultipleAlignmentsContainer alignmentsContainer
= null;
132 private final Map
<Integer
, SingleReadAlignment
> cdmMap
= new TreeMap
<Integer
, SingleReadAlignment
>(); //TODO Move this to ContigSequenceDataProvider
133 private boolean dirty
= false;
136 public AlignmentEditor() {
138 conversationHolder
= CdmStore
.createConversation();
139 //conversationHolder = null;
143 private void refreshToolbarElement(String id
) {
144 ICommandService commandService
=
145 (ICommandService
)PlatformUI
.getWorkbench().getActiveWorkbenchWindow().getService(ICommandService
.class);
146 if (commandService
!= null) {
147 commandService
.refreshElements(id
, Collections
.EMPTY_MAP
);
152 private void registerEditSettingListener(MultipleAlignmentsContainer container
) {
153 container
.getEditSettings().addListener(new EditSettingsListener() {
155 public void workingModeChanged(EditSettingsChangeEvent e
) {} // Currently nothing to do
158 public void insertLeftInDataAreaChanged(EditSettingsChangeEvent e
) {
160 refreshToolbarElement(ToggleLeftRightInsertionHandler
.COMMAND_ID
);
164 public void insertChanged(EditSettingsChangeEvent e
) {
166 refreshToolbarElement(ToggleInsertOverwriteHandler
.COMMAND_ID
);
172 private AlignmentArea
createIndexArea(MultipleAlignmentsContainer container
, AlignmentArea labeledArea
) {
173 AlignmentArea result
= new AlignmentArea(container
);
174 result
.setAllowVerticalScrolling(false);
175 result
.getDataAreas().getTopAreas().add(new SequenceIndexArea(result
.getContentArea(), labeledArea
));
180 private AlignmentArea
createEditableAlignmentArea(MultipleAlignmentsContainer container
, boolean allowVerticalScrolling
) {
181 AlignmentArea result
= new AlignmentArea(container
);
182 result
.setAllowVerticalScrolling(allowVerticalScrolling
);
184 CharacterTokenSet tokenSet
= CharacterTokenSet
.newDNAInstance(); //TODO Should NUCLEOTIDE be used instead?
185 AlignmentModel
<Character
> provider
= new PackedAlignmentModel
<Character
>(tokenSet
);
186 result
.setAlignmentModel(provider
, false);
187 provider
.getChangeListeners().add(DIRTY_LISTENER
);
188 result
.getPaintSettings().getTokenPainterList().set(0, new NucleotideTokenPainter());
194 private AlignmentArea
createConsensusHintArea(MultipleAlignmentsContainer container
,
195 AlignmentArea labeledArea
) {
197 AlignmentArea result
= new AlignmentArea(container
);
198 result
.setAllowVerticalScrolling(false);
199 result
.getDataAreas().getBottomAreas().add(
200 new ConsensusSequenceArea(result
.getContentArea(), labeledArea
));
205 private MultipleAlignmentsContainer
getAlignmentsContainer() {
206 if (alignmentsContainer
== null) {
207 alignmentsContainer
= new MultipleAlignmentsContainer();
209 AlignmentAreaList list
= alignmentsContainer
.getAlignmentAreas();
210 AlignmentArea readsArea
= createEditableAlignmentArea(alignmentsContainer
, true);
211 list
.add(createIndexArea(alignmentsContainer
, readsArea
));
212 list
.add(readsArea
); // Make sure READS_AREA_INDEX is correct.
213 list
.add(createEditableAlignmentArea(alignmentsContainer
, false)); // Make sure COMSENSUS_AREA_INDEX is correct.
214 list
.add(createConsensusHintArea(alignmentsContainer
, readsArea
));
216 registerEditSettingListener(alignmentsContainer
);
218 return alignmentsContainer
;
222 public AlignmentArea
getReadsArea() {
223 return getAlignmentsContainer().getAlignmentAreas().get(READS_AREA_INDEX
);
227 private AlignmentArea
getEditableConsensusArea() {
228 return getAlignmentsContainer().getAlignmentAreas().get(EDITABLE_CONSENSUS_AREA_INDEX
);
232 public boolean hasPherogram(int sequenceID
) {
233 return getReadsArea().getDataAreas().getSequenceAreas(sequenceID
).size() > PHEROGRAM_AREA_INDEX
;
237 public PherogramArea
getPherogramArea(int sequenceID
) {
238 if (hasPherogram(sequenceID
)) {
239 return (PherogramArea
)getReadsArea().getDataAreas().getSequenceAreas(sequenceID
).get(PHEROGRAM_AREA_INDEX
);
247 private ConsensusSequenceArea
getConsensusHintDataArea() {
248 return (ConsensusSequenceArea
)getAlignmentsContainer().getAlignmentAreas().
249 get(CONSENSUS_HINT_AREA_INDEX
).getDataAreas().getBottomAreas().
250 get(CONSENSUS_DATA_AREA_INDEX
);
254 @Deprecated //TODO Remove as soon as testing period is over
255 private void createTestContents() {
258 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);
259 //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);
260 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);
262 // Add test consensus sequence:
263 AlignmentModel consensusModel
= getEditableConsensusArea().getAlignmentModel();
264 int id
= consensusModel
.addSequence(CONSENSUS_NAME
);
265 Collection
<Object
> tokens
= new ArrayList
<Object
>(); // First save tokens in a collection to avoid GUI updated for each token.
266 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("A"));
267 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("C"));
268 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("G"));
269 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("T"));
270 consensusModel
.insertTokensAt(id
, 0, tokens
);
272 catch (Exception e
) {
273 throw new RuntimeException(e
);
278 private void readCDMData(Sequence sequenceNode
) {
279 //TODO If called from somewhere else than createPartControl() the editorInput needs to be checked and previous contents need to be cleared (or updated).
282 for (SingleReadAlignment singleReadAlignment
: sequenceNode
.getSingleReadAlignments()) {
284 SingleRead pherogramInfo
= singleReadAlignment
.getSingleRead();
286 if (pherogramInfo
.getPherogram() != null) {
287 uri
= MediaUtils
.getFirstMediaRepresentationPart(pherogramInfo
.getPherogram()).getUri();
289 int id
= addRead(DerivateLabelProvider
.getDerivateText(pherogramInfo
, conversationHolder
),
291 singleReadAlignment
.isReverseComplement(),
292 singleReadAlignment
.getEditedSequence(),
293 singleReadAlignment
.getFirstSeqPosition(),
294 singleReadAlignment
.getLeftCutPosition(),
295 singleReadAlignment
.getRightCutPosition(),
296 singleReadAlignment
.getShifts());
297 cdmMap
.put(id
, singleReadAlignment
);
299 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).
300 MessagingUtils
.errorDialog("Error", null, "A single read was skipped because of the following error:\n\n" +
301 e
.getLocalizedMessage(), TaxeditorMolecularPlugin
.PLUGIN_ID
, e
, false);
305 // Set consensus sequence:
306 AlignmentModel consensusProvider
= getEditableConsensusArea().getAlignmentModel();
307 int id
= consensusProvider
.addSequence(CONSENSUS_NAME
);
308 consensusProvider
.insertTokensAt(id
, 0, AlignmentModelUtils
.charSequenceToTokenList(
309 sequenceNode
.getConsensusSequence().getString(), consensusProvider
.getTokenSet()));
310 //TODO Can the consensus sequence also be null? / Should it be created here, if nothing is in the DB?
315 * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
318 public void createPartControl(Composite parent
) {
319 SWTComponentFactory
.getInstance().getSWTComponent(getAlignmentsContainer(), parent
, SWT
.NONE
);
322 if (getEditorInput() instanceof AlignmentEditorInput
) {
323 if (((AlignmentEditorInput
)getEditorInput()).getSequenceNodeUuid() != null) {
324 Sequence sequenceNode
= CdmStore
.getService(ISequenceService
.class).load(((AlignmentEditorInput
)getEditorInput()).getSequenceNodeUuid());
325 //re-load into the current session if it is already persisted in the DB
326 if(sequenceNode
!=null && sequenceNode
.getId()!=0){
327 sequenceNode
= CdmStore
.getService(ISequenceService
.class).load(sequenceNode
.getUuid());
329 readCDMData(sequenceNode
);
332 createTestContents(); // This case will removed after the test phase and an exception should probably be thrown.
336 throw new IllegalArgumentException("The editor input must have the type " +
337 AlignmentEditorInput
.class.getCanonicalName()); //TODO What should be done here?
342 private void updateStatusBar() {
343 IActionBars bars
= getEditorSite().getActionBars();
344 bars
.getStatusLineManager().setMessage("Edit mode: " +
345 (getReadsArea().getEditSettings().isInsert() ?
"Insert" : "Overwrite") + " " +
346 "Insertion in pherogram: " +
347 (getReadsArea().getEditSettings().isInsertLeftInDataArea() ?
"Left" : "Right"));
351 private SingleReadAlignment
.Shift
[] convertToCDMShifts(PherogramAreaModel model
) {
352 Iterator
<ShiftChange
> iterator
= model
.shiftChangeIterator();
353 List
<Shift
> shifts
= new ArrayList
<SingleReadAlignment
.Shift
>();
354 while (iterator
.hasNext()) {
355 ShiftChange shiftChange
= iterator
.next();
356 shifts
.add(new SingleReadAlignment
.Shift(shiftChange
.getBaseCallIndex(), shiftChange
.getShiftChange()));
358 return shifts
.toArray(new Shift
[]{});
363 * @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor)
366 public void doSave(IProgressMonitor monitor
) {
367 if (getEditorInput() instanceof AlignmentEditorInput
) {
368 String taskName
= "Saving alignment";
369 monitor
.beginTask(taskName
, 3);
371 //re-loading sequence to avoid session conflicts
372 Sequence sequenceNode
= CdmStore
.getService(ISequenceService
.class).load(((AlignmentEditorInput
)getEditorInput()).getSequenceNodeUuid());
373 StringAdapter stringProvider
= new StringAdapter(getEditableConsensusArea().getAlignmentModel(), false); // Throws an exception if a token has more than one character.
375 // Write consensus sequence:
376 SequenceString consensusSequenceObj
= sequenceNode
.getConsensusSequence();
377 String newConsensusSequence
= stringProvider
.getSequence(
378 getEditableConsensusArea().getAlignmentModel().sequenceIDByName(CONSENSUS_NAME
));
379 if (consensusSequenceObj
== null) {
380 sequenceNode
.setConsensusSequence(SequenceString
.NewInstance(newConsensusSequence
));
383 consensusSequenceObj
.setString(newConsensusSequence
);
386 // Write single reads:
387 stringProvider
.setUnderlyingProvider(getReadsArea().getAlignmentModel());
388 sequenceNode
.getSingleReadAlignments().retainAll(cdmMap
.values()); // Remove all reads that are not in the alignment anymore.
389 Iterator
<Integer
> iterator
= getReadsArea().getAlignmentModel().sequenceIDIterator();
390 while (iterator
.hasNext()) {
391 int id
= iterator
.next();
392 SingleReadAlignment singleRead
= cdmMap
.get(id
);
393 if (singleRead
== null) {
394 throw new InternalError("Creating new reads from AlignmentEditor not implemented.");
395 //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?
396 //singleRead = SingleReadAlignment.NewInstance(consensusSequence, singleRead, shifts, editedSequence);
399 singleRead
.setEditedSequence(stringProvider
.getSequence(id
));
401 PherogramArea pherogramArea
= getPherogramArea(id
);
402 if (pherogramArea
!= null) {
403 PherogramAreaModel model
= pherogramArea
.getModel();
404 singleRead
.setReverseComplement(model
.getPherogramProvider() instanceof ReverseComplementPherogramProvider
); // Works only if ReverseComplementPherogramProvider instances are not nested.
405 singleRead
.setShifts(convertToCDMShifts(getPherogramArea(id
).getModel()));
406 singleRead
.setFirstSeqPosition(model
.getFirstSeqPos());
407 singleRead
.setLeftCutPosition(model
.getLeftCutPosition());
408 singleRead
.setRightCutPosition(model
.getRightCutPosition());
412 if (!conversationHolder
.isBound()) {
413 conversationHolder
.bind();
417 // Commit the conversation and start a new transaction immediately:
418 conversationHolder
.commit(true);
424 firePropertyChange(PROP_DIRTY
);
427 //TODO Throw exception as soon as testing period which allows unlinked AlignmentEditor is over.
433 * @see org.eclipse.ui.part.EditorPart#doSaveAs()
436 public void doSaveAs() {}
440 * @see org.eclipse.ui.part.EditorPart#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
443 public void init(IEditorSite site
, IEditorInput input
) throws PartInitException
{
450 * @see org.eclipse.ui.part.EditorPart#isDirty()
453 public boolean isDirty() {
458 private void setDirty() {
460 firePropertyChange(IEditorPart
.PROP_DIRTY
);
465 * @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed()
468 public boolean isSaveAsAllowed() {
469 return false; // "Save as" not allowed.
474 public void setFocus() {
475 if(conversationHolder
!=null){
476 conversationHolder
.bind();
481 public boolean isInsertMode() {
482 return getAlignmentsContainer().getEditSettings().isInsert();
486 public boolean isInsertLeftInPherogram() {
487 return getAlignmentsContainer().getEditSettings().isInsertLeftInDataArea();
491 public void toggleLeftRightInsertionInPherogram() {
492 getAlignmentsContainer().getEditSettings().toggleInsertLeftInDataArea();
496 public void toggleInsertOverwrite() {
497 getAlignmentsContainer().getEditSettings().toggleInsert();
501 private String
cutPherogram(boolean left
) {
502 SelectionModel selection
= getReadsArea().getSelection();
503 if (selection
.getCursorHeight() != 1) {
504 return "Cutting pherograms is only possible if exactly one row is selected.";
507 PherogramArea pherogramArea
=
508 getPherogramArea(getReadsArea().getSequenceOrder().idByIndex(selection
.getCursorRow()));
509 if (pherogramArea
== null) {
510 return "There is no pherogram attached to the current sequence.";
514 if (pherogramArea
.setLeftCutPositionBySelection()) {
518 return "The left end of the selection lies outside the pherogram attached to this sequence.";
522 if (pherogramArea
.setRightCutPositionBySelection()) {
526 return "The right end of the selection lies outside the pherogram attached to this sequence.";
534 public String
cutPherogramLeft() {
535 return cutPherogram(true);
539 public String
cutPherogramRight() {
540 return cutPherogram(false);
544 public void reverseComplementSelectedSequences() {
545 SelectionModel selection
= getReadsArea().getSelection();
546 AlignmentModel
<?
> model
= getReadsArea().getAlignmentModel();
547 for (int row
= selection
.getFirstRow(); row
< selection
.getFirstRow() + selection
.getCursorHeight(); row
++) {
548 int sequenceID
= getReadsArea().getSequenceOrder().idByIndex(row
);
549 PherogramArea area
= getPherogramArea(sequenceID
);
550 PherogramAreaModel pherogramAlignmentModel
= area
.getModel();
551 AlignmentModelUtils
.reverseComplement(model
, sequenceID
,
552 pherogramAlignmentModel
.editableIndexByBaseCallIndex(
553 pherogramAlignmentModel
.getLeftCutPosition()).getBeforeValidIndex(),
554 pherogramAlignmentModel
.editableIndexByBaseCallIndex(
555 pherogramAlignmentModel
.getRightCutPosition()).getAfterValidIndex());
556 pherogramAlignmentModel
.reverseComplement();
562 * Recreates the whole consensus sequence from all single read sequences. The previous consensus
563 * sequence is overwritte.
565 @SuppressWarnings("unchecked")
566 public <T
> void createConsensusSequence() {
567 ConsensusSequenceArea area
= getConsensusHintDataArea();
568 AlignmentModel
<T
> model
= (AlignmentModel
<T
>)getEditableConsensusArea().getAlignmentModel();
569 int sequenceID
= model
.sequenceIDIterator().next(); // There is always one sequence contained.
570 int length
= getReadsArea().getAlignmentModel().getMaxSequenceLength();
572 Collection
<T
> tokens
= new ArrayList
<T
>(length
);
573 for (int column
= 0; column
< length
; column
++) {
574 tokens
.add(model
.getTokenSet().tokenByRepresentation(area
.getConsensusToken(column
)));
577 model
.removeTokensAt(sequenceID
, 0, model
.getSequenceLength(sequenceID
));
578 model
.insertTokensAt(sequenceID
, 0, tokens
);
583 * Updates the current consensus sequence by replacing gaps by the according consensus tokens
584 * calculated from the single read sequences and extends the consensus sequence if necessary.
586 @SuppressWarnings("unchecked")
587 public <T
> void updateConsensusSequence() {
588 ConsensusSequenceArea area
= getConsensusHintDataArea();
589 AlignmentModel
<T
> model
= (AlignmentModel
<T
>)getEditableConsensusArea().getAlignmentModel();
590 TokenSet
<T
> tokenSet
= model
.getTokenSet();
591 int sequenceID
= model
.sequenceIDIterator().next(); // There is always one sequence contained.
592 int currentConsensusLength
= model
.getSequenceLength(sequenceID
);
593 int overallLength
= getReadsArea().getAlignmentModel().getMaxSequenceLength();
595 // Replace gaps by new information:
596 for (int column
= 0; column
< currentConsensusLength
; column
++) {
597 if (tokenSet
.isGapToken(model
.getTokenAt(sequenceID
, column
))) {
598 T newToken
= tokenSet
.tokenByRepresentation(area
.getConsensusToken(column
));
599 if (!tokenSet
.isGapToken(newToken
)) {
600 model
.setTokenAt(sequenceID
, column
, newToken
);
605 // Append additional tokens:
606 if (overallLength
> currentConsensusLength
) {
607 Collection
<T
> tokens
= new ArrayList
<T
>(overallLength
);
608 for (int column
= currentConsensusLength
; column
< overallLength
; column
++) {
609 tokens
.add(tokenSet
.tokenByRepresentation(area
.getConsensusToken(column
)));
611 model
.appendTokens(sequenceID
, tokens
);
616 public static PherogramProvider
readPherogram(URI uri
) throws IOException
, UnsupportedChromatogramFormatException
{
617 PherogramProvider result
;
618 InputStream stream
= uri
.toURL().openStream();
620 result
= new BioJavaPherogramProvider(ChromatogramFactory
.create(stream
));
629 private String
newReadName() {
631 while (getReadsArea().getAlignmentModel().sequenceIDByName(DEFAULT_READ_NAME_PREFIX
+ index
)
632 != AlignmentModel
.NO_SEQUENCE_FOUND
) {
636 return DEFAULT_READ_NAME_PREFIX
+ index
;
640 public void addRead(URI pherogramURI
, boolean reverseComplemented
) throws IOException
, UnsupportedChromatogramFormatException
{
641 addRead(newReadName(), pherogramURI
, reverseComplemented
, null, null, null, null, null);
646 * Adds a new sequence with attached phergram data area to the reads alignment.
648 * If {@code null} is specified as {@code editedSequence} the base call sequence from the pherogram will
649 * be set as the edited sequence. If {@code null} is specified as {@code shifts} no shifts between the edited
650 * and the base calls sequence are assumed.
652 * @param name the name of the new sequence
653 * @param pherogramURI the URI where the associated pherogram file is located
654 * @param reverseComplemented Specify {@code true} here, if the reverse complement of the pherogram data should
655 * be added, {@code false} otherwise.
656 * @param editedSequence the edited version of the base call sequence (May be {@code null}.)
657 * @param shifts the alignment information that links the edited and the base call sequence (May be {@code null}.)
658 * @return the sequence ID of the added read
659 * @throws IOException if an error occurred when trying to read the pherogram file
660 * @throws UnsupportedChromatogramFormatException if the format of the pherogram file is not supported
662 public int addRead(String name
, URI pherogramURI
, boolean reverseComplemented
, String editedSequence
,
663 Integer firstSeqPos
, Integer leftCutPos
, Integer rightCutPos
, SingleReadAlignment
.Shift
[] shifts
)
664 throws IOException
, UnsupportedChromatogramFormatException
{
666 AlignmentModel provider
= getReadsArea().getAlignmentModel();
667 PherogramProvider pherogramProvider
= null;
668 if (pherogramURI
!= null) {
669 pherogramProvider
= readPherogram(pherogramURI
); // Must happen before a sequence is added, because it might throw an exception.
670 if (reverseComplemented
) {
671 pherogramProvider
= new ReverseComplementPherogramProvider(pherogramProvider
);
676 provider
.addSequence(name
);
677 int id
= provider
.sequenceIDByName(name
);
679 // Set edited sequence:
680 Collection
<Object
> tokens
= null; // First save tokens in a collection to avoid GUI updated for each token.
681 if (editedSequence
!= null) {
682 tokens
= AlignmentModelUtils
.charSequenceToTokenList(editedSequence
, provider
.getTokenSet());
684 else if (pherogramProvider
!= null) { // Copy base call sequence into alignment:
685 tokens
= new ArrayList
<Object
>();
686 for (int i
= 0; i
< pherogramProvider
.getSequenceLength(); i
++) {
687 tokens
.add(provider
.getTokenSet().tokenByRepresentation(
688 Character
.toString(pherogramProvider
.getBaseCall(i
))));
693 if (tokens
!= null) { // If either an edited sequence or a pherogram URI was provided.
694 provider
.insertTokensAt(id
, 0, tokens
);
695 // Create pherogram area:
696 PherogramArea pherogramArea
= new PherogramArea(getReadsArea().getContentArea(),
697 new PherogramAreaModel(pherogramProvider
));
699 // Set position properties and shifts:
700 PherogramAreaModel model
= pherogramArea
.getModel();
701 if ((firstSeqPos
!= null) && (leftCutPos
!= null)) {
702 model
.setFirstSeqLeftCutPos(firstSeqPos
, leftCutPos
);
704 if (rightCutPos
!= null) {
705 model
.setRightCutPosition(rightCutPos
);
707 if ((shifts
!= null) && (shifts
.length
> 0)) {
708 for (int i
= 0; i
< shifts
.length
; i
++) {
709 model
.addShiftChange(shifts
[i
].position
, shifts
[i
].shift
);
714 // Add pherogram area to GUI:
715 pherogramArea
.addMouseListener(new PherogramMouseListener(pherogramArea
));
716 getReadsArea().getDataAreas().getSequenceAreas(id
).add(pherogramArea
);