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
.editor
.molecular
;
13 import info
.bioinfweb
.jphyloio
.events
.TokenSetType
;
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
.SequenceIndexArea
;
19 import info
.bioinfweb
.libralign
.dataarea
.implementations
.pherogram
.PherogramArea
;
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
.AlignmentModelUtils
;
25 import info
.bioinfweb
.libralign
.model
.adapters
.StringAdapter
;
26 import info
.bioinfweb
.libralign
.model
.events
.SequenceChangeEvent
;
27 import info
.bioinfweb
.libralign
.model
.events
.SequenceRenamedEvent
;
28 import info
.bioinfweb
.libralign
.model
.events
.TokenChangeEvent
;
29 import info
.bioinfweb
.libralign
.model
.implementations
.PackedAlignmentModel
;
30 import info
.bioinfweb
.libralign
.model
.tokenset
.BioJavaTokenSet
;
31 import info
.bioinfweb
.libralign
.model
.tokenset
.TokenSet
;
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
;
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
;
49 import java
.util
.TreeMap
;
51 import org
.biojava
.bio
.chromatogram
.ChromatogramFactory
;
52 import org
.biojava
.bio
.chromatogram
.UnsupportedChromatogramFormatException
;
53 import org
.biojava3
.core
.sequence
.compound
.DNACompoundSet
;
54 import org
.biojava3
.core
.sequence
.compound
.NucleotideCompound
;
55 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
56 import org
.eclipse
.swt
.SWT
;
57 import org
.eclipse
.swt
.widgets
.Composite
;
58 import org
.eclipse
.ui
.IActionBars
;
59 import org
.eclipse
.ui
.IEditorInput
;
60 import org
.eclipse
.ui
.IEditorPart
;
61 import org
.eclipse
.ui
.IEditorSite
;
62 import org
.eclipse
.ui
.PartInitException
;
63 import org
.eclipse
.ui
.PlatformUI
;
64 import org
.eclipse
.ui
.commands
.ICommandService
;
65 import org
.eclipse
.ui
.part
.EditorPart
;
67 import eu
.etaxonomy
.cdm
.api
.conversation
.ConversationHolder
;
68 import eu
.etaxonomy
.cdm
.api
.service
.molecular
.ISequenceService
;
69 import eu
.etaxonomy
.cdm
.model
.media
.MediaUtils
;
70 import eu
.etaxonomy
.cdm
.model
.molecular
.Sequence
;
71 import eu
.etaxonomy
.cdm
.model
.molecular
.SequenceString
;
72 import eu
.etaxonomy
.cdm
.model
.molecular
.SingleRead
;
73 import eu
.etaxonomy
.cdm
.model
.molecular
.SingleReadAlignment
;
74 import eu
.etaxonomy
.taxeditor
.editor
.handler
.alignmenteditor
.ToggleInsertOverwriteHandler
;
75 import eu
.etaxonomy
.taxeditor
.editor
.handler
.alignmenteditor
.ToggleLeftRightInsertionHandler
;
76 import eu
.etaxonomy
.taxeditor
.store
.CdmStore
;
81 * Editor component to edit a contig alignment used to combine different overlapping pherograms from Sanger sequencing to
82 * a consensus sequence.
84 * The contained GUI components used to edit the alignment come from <a href="http://bioinfweb.info/LibrAlign/">LibrAlign</a>.
90 public class AlignmentEditor
extends EditorPart
{
91 public static final String ID
= "eu.etaxonomy.taxeditor.editor.molecular.AlignmentEditor";
93 public static final int READS_AREA_INDEX
= 1;
94 public static final int EDITABLE_CONSENSUS_AREA_INDEX
= READS_AREA_INDEX
+ 1;
95 public static final int CONSENSUS_HINT_AREA_INDEX
= EDITABLE_CONSENSUS_AREA_INDEX
+ 1;
96 public static final int PHEROGRAM_AREA_INDEX
= 0;
97 public static final int CONSENSUS_DATA_AREA_INDEX
= 0;
98 public static final String DEFAULT_READ_NAME_PREFIX
= "Read ";
99 public static final String CONSENSUS_NAME
= "Consensus";
102 private final ConversationHolder conversationHolder
;
104 private final AlignmentModelChangeListener DIRTY_LISTENER
= new AlignmentModelChangeListener() {
106 public <T
> void afterTokenChange(TokenChangeEvent
<T
> e
) {
111 public <T
> void afterSequenceRenamed(SequenceRenamedEvent
<T
> e
) {
116 public <T
> void afterSequenceChange(SequenceChangeEvent
<T
> e
) {
121 public <T
, U
> void afterProviderChanged(AlignmentModel
<T
> oldProvider
,
122 AlignmentModel
<U
> newProvider
) { // Not expected.
128 private MultipleAlignmentsContainer alignmentsContainer
= null;
129 private final Map
<Integer
, SingleReadAlignment
> cdmMap
= new TreeMap
<Integer
, SingleReadAlignment
>(); //TODO Move this to ContigSequenceDataProvider
130 private boolean dirty
= false;
133 public AlignmentEditor() {
135 //conversationHolder = CdmStore.createConversation();
136 conversationHolder
= null;
140 private void refreshToolbarElement(String id
) {
141 ICommandService commandService
=
142 (ICommandService
)PlatformUI
.getWorkbench().getActiveWorkbenchWindow().getService(ICommandService
.class);
143 if (commandService
!= null) {
144 commandService
.refreshElements(id
, Collections
.EMPTY_MAP
);
149 private void registerEditSettingListener(MultipleAlignmentsContainer container
) {
150 container
.getEditSettings().addListener(new EditSettingsListener() {
152 public void workingModeChanged(EditSettingsChangeEvent e
) {} // Currently nothing to do
155 public void insertLeftInDataAreaChanged(EditSettingsChangeEvent e
) {
157 refreshToolbarElement(ToggleLeftRightInsertionHandler
.COMMAND_ID
);
161 public void insertChanged(EditSettingsChangeEvent e
) {
163 refreshToolbarElement(ToggleInsertOverwriteHandler
.COMMAND_ID
);
169 private AlignmentArea
createIndexArea(MultipleAlignmentsContainer container
, AlignmentArea labeledArea
) {
170 AlignmentArea result
= new AlignmentArea(container
);
171 result
.setAllowVerticalScrolling(false);
172 result
.getDataAreas().getTopAreas().add(new SequenceIndexArea(result
.getContentArea(), labeledArea
));
177 private AlignmentArea
createEditableAlignmentArea(MultipleAlignmentsContainer container
, boolean allowVerticalScrolling
) {
178 AlignmentArea result
= new AlignmentArea(container
);
179 result
.setAllowVerticalScrolling(allowVerticalScrolling
);
181 TokenSet
<NucleotideCompound
> tokenSet
= new BioJavaTokenSet
<NucleotideCompound
>(
182 TokenSetType
.DNA
, new DNACompoundSet(), true); //TODO Should NUCLEOTIDE be used instead?
183 AlignmentModel
<NucleotideCompound
> provider
= new PackedAlignmentModel
<NucleotideCompound
>(tokenSet
);
184 result
.setAlignmentModel(provider
, false);
185 provider
.getChangeListeners().add(DIRTY_LISTENER
);
186 result
.getPaintSettings().getTokenPainterList().set(0, new NucleotideTokenPainter());
192 private AlignmentArea
createConsensusHintArea(MultipleAlignmentsContainer container
,
193 AlignmentArea labeledArea
) {
195 AlignmentArea result
= new AlignmentArea(container
);
196 result
.setAllowVerticalScrolling(false);
197 result
.getDataAreas().getBottomAreas().add(
198 new ConsensusSequenceArea(result
.getContentArea(), labeledArea
));
203 private MultipleAlignmentsContainer
getAlignmentsContainer() {
204 if (alignmentsContainer
== null) {
205 alignmentsContainer
= new MultipleAlignmentsContainer();
207 AlignmentAreaList list
= alignmentsContainer
.getAlignmentAreas();
208 AlignmentArea readsArea
= createEditableAlignmentArea(alignmentsContainer
, true);
209 list
.add(createIndexArea(alignmentsContainer
, readsArea
));
210 list
.add(readsArea
); // Make sure READS_AREA_INDEX is correct.
211 list
.add(createEditableAlignmentArea(alignmentsContainer
, false)); // Make sure COMSENSUS_AREA_INDEX is correct.
212 list
.add(createConsensusHintArea(alignmentsContainer
, readsArea
));
214 registerEditSettingListener(alignmentsContainer
);
216 return alignmentsContainer
;
220 public AlignmentArea
getReadsArea() {
221 return getAlignmentsContainer().getAlignmentAreas().get(READS_AREA_INDEX
);
225 private AlignmentArea
getEditableConsensusArea() {
226 return getAlignmentsContainer().getAlignmentAreas().get(EDITABLE_CONSENSUS_AREA_INDEX
);
230 public PherogramArea
getPherogramArea(int sequenceID
) {
231 return (PherogramArea
)getReadsArea().getDataAreas().getSequenceAreas(sequenceID
).get(
232 PHEROGRAM_AREA_INDEX
);
236 private ConsensusSequenceArea
getConsensusHintDataArea() {
237 return (ConsensusSequenceArea
)getAlignmentsContainer().getAlignmentAreas().
238 get(CONSENSUS_HINT_AREA_INDEX
).getDataAreas().getBottomAreas().
239 get(CONSENSUS_DATA_AREA_INDEX
);
243 private void createTestContents() {
246 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);
247 //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);
248 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);
250 // Add test consensus sequence:
251 AlignmentModel consensusModel
= getEditableConsensusArea().getAlignmentModel();
252 int id
= consensusModel
.addSequence(CONSENSUS_NAME
);
253 Collection
<Object
> tokens
= new ArrayList
<Object
>(); // First save tokens in a collection to avoid GUI updated for each token.
254 tokens
.add(consensusModel
.getTokenSet().tokenByKeyChar('A'));
255 tokens
.add(consensusModel
.getTokenSet().tokenByKeyChar('C'));
256 tokens
.add(consensusModel
.getTokenSet().tokenByKeyChar('G'));
257 tokens
.add(consensusModel
.getTokenSet().tokenByKeyChar('T'));
258 consensusModel
.insertTokensAt(id
, 0, tokens
);
260 catch (Exception e
) {
261 throw new RuntimeException(e
);
266 private void readCDMData(Sequence sequenceNode
) {
267 //TODO If called from somewhere else than createPartControl() the editorInput needs to be checked and previous contents need to be cleared (or updated).
270 for (SingleReadAlignment singleReadAlignment
: sequenceNode
.getSingleReadAlignments()) {
272 SingleRead pherogramInfo
= singleReadAlignment
.getSingleRead();
273 int id
= addRead(pherogramInfo
.getPrimer().getLabel(), //TODO Should the sequence name contain other/additional/alternative data? Can the same string as in the derivative tree be used here?
274 MediaUtils
.getFirstMediaRepresentationPart(pherogramInfo
.getPherogram()).getUri(),
275 singleReadAlignment
.isReverseComplement(),
276 singleReadAlignment
.getEditedSequence(),
277 singleReadAlignment
.getFirstSeqPosition()!=null?singleReadAlignment
.getFirstSeqPosition():0,
278 singleReadAlignment
.getLeftCutPosition()!=null?singleReadAlignment
.getLeftCutPosition():0,
279 singleReadAlignment
.getRightCutPosition()!=null?singleReadAlignment
.getRightCutPosition():0,
280 singleReadAlignment
.getShifts());
281 cdmMap
.put(id
, singleReadAlignment
);
283 catch (IOException e
) {
284 e
.printStackTrace(); //TODO Output to user (Possibly collect for all pherograms and display in the end.)
286 catch (UnsupportedChromatogramFormatException e
) {
287 e
.printStackTrace(); //TODO Output to user (Possibly collect for all pherograms and display in the end.)
291 // Set consensus sequence:
292 AlignmentModel consensusProvider
= getEditableConsensusArea().getAlignmentModel();
293 int id
= consensusProvider
.addSequence(CONSENSUS_NAME
);
294 consensusProvider
.insertTokensAt(id
, 0, AlignmentModelUtils
.charSequenceToTokenList(
295 sequenceNode
.getConsensusSequence().getString(), consensusProvider
.getTokenSet()));
296 //TODO Can the consensus sequence also be null? / Should it be created here, if nothing is in the DB?
301 * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
304 public void createPartControl(Composite parent
) {
305 getAlignmentsContainer().createSWTWidget(parent
, SWT
.NONE
);
308 if (getEditorInput() instanceof AlignmentEditorInput
) {
309 if (((AlignmentEditorInput
)getEditorInput()).getSequenceNode() != null) {
310 Sequence sequenceNode
= ((AlignmentEditorInput
)getEditorInput()).getSequenceNode();
311 //re-load into the current session if it is already persisted in the DB
312 if(sequenceNode
!=null && sequenceNode
.getId()!=0){
313 sequenceNode
= CdmStore
.getService(ISequenceService
.class).load(sequenceNode
.getUuid());
315 readCDMData(sequenceNode
);
318 createTestContents(); // This case will removed after the test phase and an exception should probably be thrown.
322 throw new IllegalArgumentException("The editor input must have the type " +
323 AlignmentEditorInput
.class.getCanonicalName()); //TODO What should be done here?
328 private void updateStatusBar() {
329 IActionBars bars
= getEditorSite().getActionBars();
330 bars
.getStatusLineManager().setMessage("Edit mode: " +
331 (getReadsArea().getEditSettings().isInsert() ?
"Insert" : "Overwrite") + " " +
332 "Insertion in pherogram: " +
333 (getReadsArea().getEditSettings().isInsertLeftInDataArea() ?
"Left" : "Right"));
337 private SingleReadAlignment
.Shift
[] convertToCDMShifts(PherogramAreaModel model
) {
338 SingleReadAlignment
.Shift
[] result
= new SingleReadAlignment
.Shift
[model
.getShiftChangeCount()];
339 Iterator
<ShiftChange
> iterator
= model
.shiftChangeIterator();
341 while (iterator
.hasNext()) {
342 ShiftChange shiftChange
= iterator
.next();
343 result
[pos
] = new SingleReadAlignment
.Shift(shiftChange
.getBaseCallIndex(), shiftChange
.getShiftChange());
350 * @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor)
353 public void doSave(IProgressMonitor monitor
) {
354 if (getEditorInput() instanceof AlignmentEditorInput
) {
355 String taskName
= "Saving alignment";
356 monitor
.beginTask(taskName
, 3);
358 Sequence sequenceNode
= ((AlignmentEditorInput
)getEditorInput()).getSequenceNode();
359 StringAdapter stringProvider
= new StringAdapter(getEditableConsensusArea().getAlignmentModel(), false); // Throws an exception if a token has more than one character.
361 // Write consensus sequence:
362 SequenceString consensusSequenceObj
= sequenceNode
.getConsensusSequence();
363 String newConsensusSequence
= stringProvider
.getSequence(
364 getEditableConsensusArea().getAlignmentModel().sequenceIDByName(CONSENSUS_NAME
));
365 if (consensusSequenceObj
== null) {
366 sequenceNode
.setConsensusSequence(SequenceString
.NewInstance(newConsensusSequence
));
369 consensusSequenceObj
.setString(newConsensusSequence
);
372 // Write single reads:
373 stringProvider
.setUnderlyingProvider(getReadsArea().getAlignmentModel());
374 sequenceNode
.getSingleReadAlignments().retainAll(cdmMap
.values()); // Remove all reads that are not in the alignment anymore.
375 Iterator
<Integer
> iterator
= getReadsArea().getAlignmentModel().sequenceIDIterator();
376 while (iterator
.hasNext()) {
377 int id
= iterator
.next();
378 SingleReadAlignment singleRead
= cdmMap
.get(id
);
379 if (singleRead
== null) {
380 //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?
381 //singleRead = SingleReadAlignment.NewInstance(consensusSequence, singleRead, shifts, editedSequence);
384 singleRead
.setEditedSequence(stringProvider
.getSequence(id
));
386 PherogramAreaModel model
= getPherogramArea(id
).getModel();
387 singleRead
.setReverseComplement(model
.getPherogramProvider() instanceof ReverseComplementPherogramProvider
); // Works only if ReverseComplementPherogramProvider instances are not nested.
388 singleRead
.setShifts(convertToCDMShifts(getPherogramArea(id
).getModel()));
389 singleRead
.setFirstSeqPosition(model
.getFirstSeqPos());
390 singleRead
.setLeftCutPosition(model
.getLeftCutPosition());
391 singleRead
.setRightCutPosition(model
.getRightCutPosition());
394 if (!conversationHolder
.isBound()) {
395 conversationHolder
.bind();
399 // Commit the conversation and start a new transaction immediately:
400 conversationHolder
.commit(true);
406 firePropertyChange(PROP_DIRTY
);
409 //TODO Throw exception as soon as testing period which allows unlinked AlignmentEditor is over.
415 * @see org.eclipse.ui.part.EditorPart#doSaveAs()
418 public void doSaveAs() {}
422 * @see org.eclipse.ui.part.EditorPart#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
425 public void init(IEditorSite site
, IEditorInput input
) throws PartInitException
{
432 * @see org.eclipse.ui.part.EditorPart#isDirty()
435 public boolean isDirty() {
440 private void setDirty() {
442 firePropertyChange(IEditorPart
.PROP_DIRTY
);
447 * @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed()
450 public boolean isSaveAsAllowed() {
451 return false; // "Save as" not allowed.
456 public void setFocus() {
457 if(conversationHolder
!=null){
458 conversationHolder
.bind();
463 public boolean isInsertMode() {
464 return getAlignmentsContainer().getEditSettings().isInsert();
468 public boolean isInsertLeftInPherogram() {
469 return getAlignmentsContainer().getEditSettings().isInsertLeftInDataArea();
473 public void toggleLeftRightInsertionInPherogram() {
474 getAlignmentsContainer().getEditSettings().toggleInsertLeftInDataArea();
478 public void toggleInsertOverwrite() {
479 getAlignmentsContainer().getEditSettings().toggleInsert();
483 private String
cutPherogram(boolean left
) {
484 SelectionModel selection
= getReadsArea().getSelection();
485 if (selection
.getCursorHeight() != 1) {
486 return "Cutting pherograms is only possible if exactly one row is selected.";
489 PherogramArea pherogramArea
=
490 getPherogramArea(getReadsArea().getSequenceOrder().idByIndex(selection
.getCursorRow()));
491 if (pherogramArea
== null) {
492 return "There is no pherogram attached to the current sequence.";
496 if (pherogramArea
.setLeftCutPositionBySelection()) {
500 return "The left end of the selection lies outside the pherogram attached to this sequence.";
504 if (pherogramArea
.setRightCutPositionBySelection()) {
508 return "The right end of the selection lies outside the pherogram attached to this sequence.";
516 public String
cutPherogramLeft() {
517 return cutPherogram(true);
521 public String
cutPherogramRight() {
522 return cutPherogram(false);
526 public void reverseComplementSelectedSequences() {
527 SelectionModel selection
= getReadsArea().getSelection();
528 AlignmentModel
<?
> model
= getReadsArea().getAlignmentModel();
529 for (int row
= selection
.getFirstRow(); row
< selection
.getFirstRow() + selection
.getCursorHeight(); row
++) {
530 int sequenceID
= getReadsArea().getSequenceOrder().idByIndex(row
);
531 PherogramArea area
= getPherogramArea(sequenceID
);
532 PherogramAreaModel pherogramAlignmentModel
= area
.getModel();
533 AlignmentModelUtils
.reverseComplement(model
, sequenceID
,
534 pherogramAlignmentModel
.editableIndexByBaseCallIndex(
535 pherogramAlignmentModel
.getLeftCutPosition()).getBeforeValidIndex(),
536 pherogramAlignmentModel
.editableIndexByBaseCallIndex(
537 pherogramAlignmentModel
.getRightCutPosition()).getAfterValidIndex());
538 pherogramAlignmentModel
.reverseComplement();
544 * Recreates the whole consensus sequence from all single read sequences. The previous consensus
545 * sequence is overwritte.
547 @SuppressWarnings("unchecked")
548 public <T
> void createConsensusSequence() {
549 ConsensusSequenceArea area
= getConsensusHintDataArea();
550 AlignmentModel
<T
> model
= (AlignmentModel
<T
>)getEditableConsensusArea().getAlignmentModel();
551 int sequenceID
= model
.sequenceIDIterator().next(); // There is always one sequence contained.
552 int length
= getReadsArea().getAlignmentModel().getMaxSequenceLength();
554 Collection
<T
> tokens
= new ArrayList
<T
>(length
);
555 for (int column
= 0; column
< length
; column
++) {
556 tokens
.add(model
.getTokenSet().tokenByRepresentation(area
.getConsensusToken(column
)));
559 model
.removeTokensAt(sequenceID
, 0, model
.getSequenceLength(sequenceID
));
560 model
.insertTokensAt(sequenceID
, 0, tokens
);
565 * Updates the current consensus sequence by replacing gaps by the according consensus tokens
566 * calculated from the single read sequences and extends the consensus sequence if necessary.
568 @SuppressWarnings("unchecked")
569 public <T
> void updateConsensusSequence() {
570 ConsensusSequenceArea area
= getConsensusHintDataArea();
571 AlignmentModel
<T
> model
= (AlignmentModel
<T
>)getEditableConsensusArea().getAlignmentModel();
572 TokenSet
<T
> tokenSet
= model
.getTokenSet();
573 int sequenceID
= model
.sequenceIDIterator().next(); // There is always one sequence contained.
574 int currentConsensusLength
= model
.getSequenceLength(sequenceID
);
575 int overallLength
= getReadsArea().getAlignmentModel().getMaxSequenceLength();
577 // Replace gaps by new information:
578 for (int column
= 0; column
< currentConsensusLength
; column
++) {
579 if (tokenSet
.isGapToken(model
.getTokenAt(sequenceID
, column
))) {
580 T newToken
= tokenSet
.tokenByRepresentation(area
.getConsensusToken(column
));
581 if (!tokenSet
.isGapToken(newToken
)) {
582 model
.setTokenAt(sequenceID
, column
, newToken
);
587 // Append additional tokens:
588 if (overallLength
> currentConsensusLength
) {
589 Collection
<T
> tokens
= new ArrayList
<T
>(overallLength
);
590 for (int column
= currentConsensusLength
; column
< overallLength
; column
++) {
591 tokens
.add(tokenSet
.tokenByRepresentation(area
.getConsensusToken(column
)));
593 model
.appendTokens(sequenceID
, tokens
);
598 public static PherogramProvider
readPherogram(URI uri
) throws IOException
, UnsupportedChromatogramFormatException
{
599 PherogramProvider result
;
600 InputStream stream
= uri
.toURL().openStream();
602 result
= new BioJavaPherogramProvider(ChromatogramFactory
.create(stream
));
611 private String
newReadName() {
613 while (getReadsArea().getAlignmentModel().sequenceIDByName(DEFAULT_READ_NAME_PREFIX
+ index
)
614 != AlignmentModel
.NO_SEQUENCE_FOUND
) {
618 return DEFAULT_READ_NAME_PREFIX
+ index
;
622 public void addRead(URI pherogramURI
, boolean reverseComplemented
) throws IOException
, UnsupportedChromatogramFormatException
{
623 addRead(newReadName(), pherogramURI
, reverseComplemented
, null, 0, 0, 0, null); // Position values will be ignored, if shifts == null.
628 * Adds a new sequence with attached phergram data area to the reads alignment.
630 * If {@code null} is specified as {@code editedSequence} the base call sequence from the pherogram will
631 * be set as the edited sequence. If {@code null} is specified as {@code shifts} no shifts between the edited
632 * and the base calls sequence are assumed.
634 * @param name the name of the new sequence
635 * @param pherogramURI the URI where the associated pherogram file is located
636 * @param reverseComplemented Specify {@code true} here, if the reverse complement of the pherogram data should
637 * be added, {@code false} otherwise.
638 * @param editedSequence the edited version of the base call sequence (May be {@code null}.)
639 * @param shifts the alignment information that links the edited and the base call sequence (May be {@code null}.)
640 * @return the sequence ID of the added read
641 * @throws IOException if an error occurred when trying to read the pherogram file
642 * @throws UnsupportedChromatogramFormatException if the format of the pherogram file is not supported
644 public int addRead(String name
, URI pherogramURI
, boolean reverseComplemented
, String editedSequence
,
645 int firstSeqPos
, int leftCutPos
, int rightCutPos
, SingleReadAlignment
.Shift
[] shifts
)
646 throws IOException
, UnsupportedChromatogramFormatException
{
648 AlignmentModel provider
= getReadsArea().getAlignmentModel();
649 PherogramProvider pherogramProvider
= readPherogram(pherogramURI
); // Must happen before a sequence is added, because it might throw an exception.
650 if (reverseComplemented
) {
651 pherogramProvider
= new ReverseComplementPherogramProvider(pherogramProvider
);
655 provider
.addSequence(name
);
656 int id
= provider
.sequenceIDByName(name
);
658 // Set edited sequence:
659 Collection
<Object
> tokens
; // First save tokens in a collection to avoid GUI updated for each token.
660 if (editedSequence
!= null) {
661 tokens
= AlignmentModelUtils
.charSequenceToTokenList(editedSequence
, provider
.getTokenSet());
663 else { // Copy base call sequence into alignment:
664 tokens
= new ArrayList
<Object
>();
665 for (int i
= 0; i
< pherogramProvider
.getSequenceLength(); i
++) {
666 tokens
.add(provider
.getTokenSet().tokenByKeyChar(
667 pherogramProvider
.getBaseCall(i
).getUpperedBase().charAt(0)));
671 provider
.insertTokensAt(id
, 0, tokens
);
673 // Create pherogram area:
674 PherogramArea pherogramArea
= new PherogramArea(getReadsArea().getContentArea(),
675 new PherogramAreaModel(pherogramProvider
));
677 // Set position properties and shifts:
678 if ((shifts
!= null) && (shifts
.length
> 0)) {
679 PherogramAreaModel model
= pherogramArea
.getModel();
680 model
.setFirstSeqLeftCutPos(firstSeqPos
, leftCutPos
);
681 model
.setRightCutPosition(rightCutPos
);
683 for (int i
= 0; i
< shifts
.length
; i
++) {
684 model
.addShiftChange(shifts
[i
].position
, shifts
[i
].shift
);
689 // Add pherogram area to GUI:
690 pherogramArea
.addMouseListener(new PherogramMouseListener(pherogramArea
));
691 getReadsArea().getDataAreas().getSequenceAreas(id
).add(pherogramArea
);