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
.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
;
49 import java
.util
.TreeMap
;
51 import org
.biojava
.bio
.chromatogram
.ChromatogramFactory
;
52 import org
.biojava
.bio
.chromatogram
.UnsupportedChromatogramFormatException
;
53 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
54 import org
.eclipse
.swt
.SWT
;
55 import org
.eclipse
.swt
.widgets
.Composite
;
56 import org
.eclipse
.ui
.IActionBars
;
57 import org
.eclipse
.ui
.IEditorInput
;
58 import org
.eclipse
.ui
.IEditorPart
;
59 import org
.eclipse
.ui
.IEditorSite
;
60 import org
.eclipse
.ui
.PartInitException
;
61 import org
.eclipse
.ui
.PlatformUI
;
62 import org
.eclipse
.ui
.commands
.ICommandService
;
63 import org
.eclipse
.ui
.part
.EditorPart
;
65 import eu
.etaxonomy
.cdm
.api
.conversation
.ConversationHolder
;
66 import eu
.etaxonomy
.cdm
.api
.service
.molecular
.ISequenceService
;
67 import eu
.etaxonomy
.cdm
.model
.media
.MediaUtils
;
68 import eu
.etaxonomy
.cdm
.model
.molecular
.Sequence
;
69 import eu
.etaxonomy
.cdm
.model
.molecular
.SequenceString
;
70 import eu
.etaxonomy
.cdm
.model
.molecular
.SingleRead
;
71 import eu
.etaxonomy
.cdm
.model
.molecular
.SingleReadAlignment
;
72 import eu
.etaxonomy
.taxeditor
.editor
.handler
.alignmenteditor
.ToggleInsertOverwriteHandler
;
73 import eu
.etaxonomy
.taxeditor
.editor
.handler
.alignmenteditor
.ToggleLeftRightInsertionHandler
;
74 import eu
.etaxonomy
.taxeditor
.model
.MessagingUtils
;
75 import eu
.etaxonomy
.taxeditor
.store
.CdmStore
;
76 import eu
.etaxonomy
.taxeditor
.view
.derivateSearch
.DerivateLabelProvider
;
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 CharacterTokenSet tokenSet
= CharacterTokenSet
.newDNAInstance(); //TODO Should NUCLEOTIDE be used instead?
182 AlignmentModel
<Character
> provider
= new PackedAlignmentModel
<Character
>(tokenSet
);
183 result
.setAlignmentModel(provider
, false);
184 provider
.getChangeListeners().add(DIRTY_LISTENER
);
185 result
.getPaintSettings().getTokenPainterList().set(0, new NucleotideTokenPainter());
191 private AlignmentArea
createConsensusHintArea(MultipleAlignmentsContainer container
,
192 AlignmentArea labeledArea
) {
194 AlignmentArea result
= new AlignmentArea(container
);
195 result
.setAllowVerticalScrolling(false);
196 result
.getDataAreas().getBottomAreas().add(
197 new ConsensusSequenceArea(result
.getContentArea(), labeledArea
));
202 private MultipleAlignmentsContainer
getAlignmentsContainer() {
203 if (alignmentsContainer
== null) {
204 alignmentsContainer
= new MultipleAlignmentsContainer();
206 AlignmentAreaList list
= alignmentsContainer
.getAlignmentAreas();
207 AlignmentArea readsArea
= createEditableAlignmentArea(alignmentsContainer
, true);
208 list
.add(createIndexArea(alignmentsContainer
, readsArea
));
209 list
.add(readsArea
); // Make sure READS_AREA_INDEX is correct.
210 list
.add(createEditableAlignmentArea(alignmentsContainer
, false)); // Make sure COMSENSUS_AREA_INDEX is correct.
211 list
.add(createConsensusHintArea(alignmentsContainer
, readsArea
));
213 registerEditSettingListener(alignmentsContainer
);
215 return alignmentsContainer
;
219 public AlignmentArea
getReadsArea() {
220 return getAlignmentsContainer().getAlignmentAreas().get(READS_AREA_INDEX
);
224 private AlignmentArea
getEditableConsensusArea() {
225 return getAlignmentsContainer().getAlignmentAreas().get(EDITABLE_CONSENSUS_AREA_INDEX
);
229 public boolean hasPherogram(int sequenceID
) {
230 return getReadsArea().getDataAreas().getSequenceAreas(sequenceID
).size() > PHEROGRAM_AREA_INDEX
;
234 public PherogramArea
getPherogramArea(int sequenceID
) {
235 if (hasPherogram(sequenceID
)) {
236 return (PherogramArea
)getReadsArea().getDataAreas().getSequenceAreas(sequenceID
).get(PHEROGRAM_AREA_INDEX
);
244 private ConsensusSequenceArea
getConsensusHintDataArea() {
245 return (ConsensusSequenceArea
)getAlignmentsContainer().getAlignmentAreas().
246 get(CONSENSUS_HINT_AREA_INDEX
).getDataAreas().getBottomAreas().
247 get(CONSENSUS_DATA_AREA_INDEX
);
251 @Deprecated //TODO Remove as soon as testing period is over
252 private void createTestContents() {
255 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);
256 //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);
257 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);
259 // Add test consensus sequence:
260 AlignmentModel consensusModel
= getEditableConsensusArea().getAlignmentModel();
261 int id
= consensusModel
.addSequence(CONSENSUS_NAME
);
262 Collection
<Object
> tokens
= new ArrayList
<Object
>(); // First save tokens in a collection to avoid GUI updated for each token.
263 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("A"));
264 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("C"));
265 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("G"));
266 tokens
.add(consensusModel
.getTokenSet().tokenByRepresentation("T"));
267 consensusModel
.insertTokensAt(id
, 0, tokens
);
269 catch (Exception e
) {
270 throw new RuntimeException(e
);
275 private void readCDMData(Sequence sequenceNode
) {
276 //TODO If called from somewhere else than createPartControl() the editorInput needs to be checked and previous contents need to be cleared (or updated).
279 for (SingleReadAlignment singleReadAlignment
: sequenceNode
.getSingleReadAlignments()) {
281 SingleRead pherogramInfo
= singleReadAlignment
.getSingleRead();
283 if (pherogramInfo
.getPherogram() != null) {
284 uri
= MediaUtils
.getFirstMediaRepresentationPart(pherogramInfo
.getPherogram()).getUri();
286 int id
= addRead(DerivateLabelProvider
.getDerivateText(pherogramInfo
, conversationHolder
),
288 singleReadAlignment
.isReverseComplement(),
289 singleReadAlignment
.getEditedSequence(),
290 singleReadAlignment
.getFirstSeqPosition(),
291 singleReadAlignment
.getLeftCutPosition(),
292 singleReadAlignment
.getRightCutPosition(),
293 singleReadAlignment
.getShifts());
294 cdmMap
.put(id
, singleReadAlignment
);
296 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).
297 MessagingUtils
.errorDialog("Error", null, "A single read was skipped because of the following error:\n\n" +
298 e
.getLocalizedMessage(), "eu.etaxonomy.taxeditor.editor", e
, false);
302 // Set consensus sequence:
303 AlignmentModel consensusProvider
= getEditableConsensusArea().getAlignmentModel();
304 int id
= consensusProvider
.addSequence(CONSENSUS_NAME
);
305 consensusProvider
.insertTokensAt(id
, 0, AlignmentModelUtils
.charSequenceToTokenList(
306 sequenceNode
.getConsensusSequence().getString(), consensusProvider
.getTokenSet()));
307 //TODO Can the consensus sequence also be null? / Should it be created here, if nothing is in the DB?
312 * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
315 public void createPartControl(Composite parent
) {
316 SWTComponentFactory
.getInstance().getSWTComponent(getAlignmentsContainer(), parent
, SWT
.NONE
);
319 if (getEditorInput() instanceof AlignmentEditorInput
) {
320 if (((AlignmentEditorInput
)getEditorInput()).getSequenceNode() != null) {
321 Sequence sequenceNode
= ((AlignmentEditorInput
)getEditorInput()).getSequenceNode();
322 //re-load into the current session if it is already persisted in the DB
323 if(sequenceNode
!=null && sequenceNode
.getId()!=0){
324 sequenceNode
= CdmStore
.getService(ISequenceService
.class).load(sequenceNode
.getUuid());
326 readCDMData(sequenceNode
);
329 createTestContents(); // This case will removed after the test phase and an exception should probably be thrown.
333 throw new IllegalArgumentException("The editor input must have the type " +
334 AlignmentEditorInput
.class.getCanonicalName()); //TODO What should be done here?
339 private void updateStatusBar() {
340 IActionBars bars
= getEditorSite().getActionBars();
341 bars
.getStatusLineManager().setMessage("Edit mode: " +
342 (getReadsArea().getEditSettings().isInsert() ?
"Insert" : "Overwrite") + " " +
343 "Insertion in pherogram: " +
344 (getReadsArea().getEditSettings().isInsertLeftInDataArea() ?
"Left" : "Right"));
348 private SingleReadAlignment
.Shift
[] convertToCDMShifts(PherogramAreaModel model
) {
349 SingleReadAlignment
.Shift
[] result
= new SingleReadAlignment
.Shift
[model
.getShiftChangeCount()];
350 Iterator
<ShiftChange
> iterator
= model
.shiftChangeIterator();
352 while (iterator
.hasNext()) {
353 ShiftChange shiftChange
= iterator
.next();
354 result
[pos
] = new SingleReadAlignment
.Shift(shiftChange
.getBaseCallIndex(), shiftChange
.getShiftChange());
361 * @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor)
364 public void doSave(IProgressMonitor monitor
) {
365 if (getEditorInput() instanceof AlignmentEditorInput
) {
366 String taskName
= "Saving alignment";
367 monitor
.beginTask(taskName
, 3);
369 Sequence sequenceNode
= ((AlignmentEditorInput
)getEditorInput()).getSequenceNode();
370 StringAdapter stringProvider
= new StringAdapter(getEditableConsensusArea().getAlignmentModel(), false); // Throws an exception if a token has more than one character.
372 // Write consensus sequence:
373 SequenceString consensusSequenceObj
= sequenceNode
.getConsensusSequence();
374 String newConsensusSequence
= stringProvider
.getSequence(
375 getEditableConsensusArea().getAlignmentModel().sequenceIDByName(CONSENSUS_NAME
));
376 if (consensusSequenceObj
== null) {
377 sequenceNode
.setConsensusSequence(SequenceString
.NewInstance(newConsensusSequence
));
380 consensusSequenceObj
.setString(newConsensusSequence
);
383 // Write single reads:
384 stringProvider
.setUnderlyingProvider(getReadsArea().getAlignmentModel());
385 sequenceNode
.getSingleReadAlignments().retainAll(cdmMap
.values()); // Remove all reads that are not in the alignment anymore.
386 Iterator
<Integer
> iterator
= getReadsArea().getAlignmentModel().sequenceIDIterator();
387 while (iterator
.hasNext()) {
388 int id
= iterator
.next();
389 SingleReadAlignment singleRead
= cdmMap
.get(id
);
390 if (singleRead
== null) {
391 throw new InternalError("Creating new reads from AlignmentEditor not implemented.");
392 //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?
393 //singleRead = SingleReadAlignment.NewInstance(consensusSequence, singleRead, shifts, editedSequence);
396 singleRead
.setEditedSequence(stringProvider
.getSequence(id
));
398 PherogramAreaModel model
= getPherogramArea(id
).getModel();
399 singleRead
.setReverseComplement(model
.getPherogramProvider() instanceof ReverseComplementPherogramProvider
); // Works only if ReverseComplementPherogramProvider instances are not nested.
400 singleRead
.setShifts(convertToCDMShifts(getPherogramArea(id
).getModel()));
401 singleRead
.setFirstSeqPosition(model
.getFirstSeqPos());
402 singleRead
.setLeftCutPosition(model
.getLeftCutPosition());
403 singleRead
.setRightCutPosition(model
.getRightCutPosition());
406 if (!conversationHolder
.isBound()) {
407 conversationHolder
.bind();
411 // Commit the conversation and start a new transaction immediately:
412 conversationHolder
.commit(true);
418 firePropertyChange(PROP_DIRTY
);
421 //TODO Throw exception as soon as testing period which allows unlinked AlignmentEditor is over.
427 * @see org.eclipse.ui.part.EditorPart#doSaveAs()
430 public void doSaveAs() {}
434 * @see org.eclipse.ui.part.EditorPart#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
437 public void init(IEditorSite site
, IEditorInput input
) throws PartInitException
{
444 * @see org.eclipse.ui.part.EditorPart#isDirty()
447 public boolean isDirty() {
452 private void setDirty() {
454 firePropertyChange(IEditorPart
.PROP_DIRTY
);
459 * @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed()
462 public boolean isSaveAsAllowed() {
463 return false; // "Save as" not allowed.
468 public void setFocus() {
469 if(conversationHolder
!=null){
470 conversationHolder
.bind();
475 public boolean isInsertMode() {
476 return getAlignmentsContainer().getEditSettings().isInsert();
480 public boolean isInsertLeftInPherogram() {
481 return getAlignmentsContainer().getEditSettings().isInsertLeftInDataArea();
485 public void toggleLeftRightInsertionInPherogram() {
486 getAlignmentsContainer().getEditSettings().toggleInsertLeftInDataArea();
490 public void toggleInsertOverwrite() {
491 getAlignmentsContainer().getEditSettings().toggleInsert();
495 private String
cutPherogram(boolean left
) {
496 SelectionModel selection
= getReadsArea().getSelection();
497 if (selection
.getCursorHeight() != 1) {
498 return "Cutting pherograms is only possible if exactly one row is selected.";
501 PherogramArea pherogramArea
=
502 getPherogramArea(getReadsArea().getSequenceOrder().idByIndex(selection
.getCursorRow()));
503 if (pherogramArea
== null) {
504 return "There is no pherogram attached to the current sequence.";
508 if (pherogramArea
.setLeftCutPositionBySelection()) {
512 return "The left end of the selection lies outside the pherogram attached to this sequence.";
516 if (pherogramArea
.setRightCutPositionBySelection()) {
520 return "The right end of the selection lies outside the pherogram attached to this sequence.";
528 public String
cutPherogramLeft() {
529 return cutPherogram(true);
533 public String
cutPherogramRight() {
534 return cutPherogram(false);
538 public void reverseComplementSelectedSequences() {
539 SelectionModel selection
= getReadsArea().getSelection();
540 AlignmentModel
<?
> model
= getReadsArea().getAlignmentModel();
541 for (int row
= selection
.getFirstRow(); row
< selection
.getFirstRow() + selection
.getCursorHeight(); row
++) {
542 int sequenceID
= getReadsArea().getSequenceOrder().idByIndex(row
);
543 PherogramArea area
= getPherogramArea(sequenceID
);
544 PherogramAreaModel pherogramAlignmentModel
= area
.getModel();
545 AlignmentModelUtils
.reverseComplement(model
, sequenceID
,
546 pherogramAlignmentModel
.editableIndexByBaseCallIndex(
547 pherogramAlignmentModel
.getLeftCutPosition()).getBeforeValidIndex(),
548 pherogramAlignmentModel
.editableIndexByBaseCallIndex(
549 pherogramAlignmentModel
.getRightCutPosition()).getAfterValidIndex());
550 pherogramAlignmentModel
.reverseComplement();
556 * Recreates the whole consensus sequence from all single read sequences. The previous consensus
557 * sequence is overwritte.
559 @SuppressWarnings("unchecked")
560 public <T
> void createConsensusSequence() {
561 ConsensusSequenceArea area
= getConsensusHintDataArea();
562 AlignmentModel
<T
> model
= (AlignmentModel
<T
>)getEditableConsensusArea().getAlignmentModel();
563 int sequenceID
= model
.sequenceIDIterator().next(); // There is always one sequence contained.
564 int length
= getReadsArea().getAlignmentModel().getMaxSequenceLength();
566 Collection
<T
> tokens
= new ArrayList
<T
>(length
);
567 for (int column
= 0; column
< length
; column
++) {
568 tokens
.add(model
.getTokenSet().tokenByRepresentation(area
.getConsensusToken(column
)));
571 model
.removeTokensAt(sequenceID
, 0, model
.getSequenceLength(sequenceID
));
572 model
.insertTokensAt(sequenceID
, 0, tokens
);
577 * Updates the current consensus sequence by replacing gaps by the according consensus tokens
578 * calculated from the single read sequences and extends the consensus sequence if necessary.
580 @SuppressWarnings("unchecked")
581 public <T
> void updateConsensusSequence() {
582 ConsensusSequenceArea area
= getConsensusHintDataArea();
583 AlignmentModel
<T
> model
= (AlignmentModel
<T
>)getEditableConsensusArea().getAlignmentModel();
584 TokenSet
<T
> tokenSet
= model
.getTokenSet();
585 int sequenceID
= model
.sequenceIDIterator().next(); // There is always one sequence contained.
586 int currentConsensusLength
= model
.getSequenceLength(sequenceID
);
587 int overallLength
= getReadsArea().getAlignmentModel().getMaxSequenceLength();
589 // Replace gaps by new information:
590 for (int column
= 0; column
< currentConsensusLength
; column
++) {
591 if (tokenSet
.isGapToken(model
.getTokenAt(sequenceID
, column
))) {
592 T newToken
= tokenSet
.tokenByRepresentation(area
.getConsensusToken(column
));
593 if (!tokenSet
.isGapToken(newToken
)) {
594 model
.setTokenAt(sequenceID
, column
, newToken
);
599 // Append additional tokens:
600 if (overallLength
> currentConsensusLength
) {
601 Collection
<T
> tokens
= new ArrayList
<T
>(overallLength
);
602 for (int column
= currentConsensusLength
; column
< overallLength
; column
++) {
603 tokens
.add(tokenSet
.tokenByRepresentation(area
.getConsensusToken(column
)));
605 model
.appendTokens(sequenceID
, tokens
);
610 public static PherogramProvider
readPherogram(URI uri
) throws IOException
, UnsupportedChromatogramFormatException
{
611 PherogramProvider result
;
612 InputStream stream
= uri
.toURL().openStream();
614 result
= new BioJavaPherogramProvider(ChromatogramFactory
.create(stream
));
623 private String
newReadName() {
625 while (getReadsArea().getAlignmentModel().sequenceIDByName(DEFAULT_READ_NAME_PREFIX
+ index
)
626 != AlignmentModel
.NO_SEQUENCE_FOUND
) {
630 return DEFAULT_READ_NAME_PREFIX
+ index
;
634 public void addRead(URI pherogramURI
, boolean reverseComplemented
) throws IOException
, UnsupportedChromatogramFormatException
{
635 addRead(newReadName(), pherogramURI
, reverseComplemented
, null, null, null, null, null);
640 * Adds a new sequence with attached phergram data area to the reads alignment.
642 * If {@code null} is specified as {@code editedSequence} the base call sequence from the pherogram will
643 * be set as the edited sequence. If {@code null} is specified as {@code shifts} no shifts between the edited
644 * and the base calls sequence are assumed.
646 * @param name the name of the new sequence
647 * @param pherogramURI the URI where the associated pherogram file is located
648 * @param reverseComplemented Specify {@code true} here, if the reverse complement of the pherogram data should
649 * be added, {@code false} otherwise.
650 * @param editedSequence the edited version of the base call sequence (May be {@code null}.)
651 * @param shifts the alignment information that links the edited and the base call sequence (May be {@code null}.)
652 * @return the sequence ID of the added read
653 * @throws IOException if an error occurred when trying to read the pherogram file
654 * @throws UnsupportedChromatogramFormatException if the format of the pherogram file is not supported
656 public int addRead(String name
, URI pherogramURI
, boolean reverseComplemented
, String editedSequence
,
657 Integer firstSeqPos
, Integer leftCutPos
, Integer rightCutPos
, SingleReadAlignment
.Shift
[] shifts
)
658 throws IOException
, UnsupportedChromatogramFormatException
{
660 AlignmentModel provider
= getReadsArea().getAlignmentModel();
661 PherogramProvider pherogramProvider
= null;
662 if (pherogramURI
!= null) {
663 pherogramProvider
= readPherogram(pherogramURI
); // Must happen before a sequence is added, because it might throw an exception.
664 if (reverseComplemented
) {
665 pherogramProvider
= new ReverseComplementPherogramProvider(pherogramProvider
);
670 provider
.addSequence(name
);
671 int id
= provider
.sequenceIDByName(name
);
673 // Set edited sequence:
674 Collection
<Object
> tokens
= null; // First save tokens in a collection to avoid GUI updated for each token.
675 if (editedSequence
!= null) {
676 tokens
= AlignmentModelUtils
.charSequenceToTokenList(editedSequence
, provider
.getTokenSet());
678 else if (pherogramProvider
!= null) { // Copy base call sequence into alignment:
679 tokens
= new ArrayList
<Object
>();
680 for (int i
= 0; i
< pherogramProvider
.getSequenceLength(); i
++) {
681 tokens
.add(provider
.getTokenSet().tokenByRepresentation(
682 Character
.toString(pherogramProvider
.getBaseCall(i
))));
687 if (tokens
!= null) { // If either an edited sequence or a pherogram URI was provided.
688 provider
.insertTokensAt(id
, 0, tokens
);
689 // Create pherogram area:
690 PherogramArea pherogramArea
= new PherogramArea(getReadsArea().getContentArea(),
691 new PherogramAreaModel(pherogramProvider
));
693 // Set position properties and shifts:
694 PherogramAreaModel model
= pherogramArea
.getModel();
695 if ((firstSeqPos
!= null) && (leftCutPos
!= null)) {
696 model
.setFirstSeqLeftCutPos(firstSeqPos
, leftCutPos
);
698 if (rightCutPos
!= null) {
699 model
.setRightCutPosition(rightCutPos
);
701 if ((shifts
!= null) && (shifts
.length
> 0)) {
702 for (int i
= 0; i
< shifts
.length
; i
++) {
703 model
.addShiftChange(shifts
[i
].position
, shifts
[i
].shift
);
708 // Add pherogram area to GUI:
709 pherogramArea
.addMouseListener(new PherogramMouseListener(pherogramArea
));
710 getReadsArea().getDataAreas().getSequenceAreas(id
).add(pherogramArea
);