Project

General

Profile

Download (31.9 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2014 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9
package eu.etaxonomy.taxeditor.molecular.editor.e4;
10

    
11
import java.io.File;
12
import java.io.IOException;
13
import java.io.InputStream;
14
import java.util.ArrayList;
15
import java.util.Collection;
16
import java.util.Collections;
17
import java.util.Iterator;
18
import java.util.List;
19
import java.util.Map;
20
import java.util.TreeMap;
21

    
22
import javax.annotation.PostConstruct;
23
import javax.annotation.PreDestroy;
24
import javax.inject.Inject;
25

    
26
import org.biojava.bio.chromatogram.ChromatogramFactory;
27
import org.biojava.bio.chromatogram.UnsupportedChromatogramFormatException;
28
import org.eclipse.core.runtime.IProgressMonitor;
29
import org.eclipse.e4.ui.di.Focus;
30
import org.eclipse.e4.ui.di.Persist;
31
import org.eclipse.e4.ui.model.application.ui.MDirtyable;
32
import org.eclipse.swt.SWT;
33
import org.eclipse.swt.dnd.Clipboard;
34
import org.eclipse.swt.widgets.Composite;
35
import org.eclipse.swt.widgets.Display;
36
import org.eclipse.ui.PartInitException;
37
import org.eclipse.ui.PlatformUI;
38
import org.eclipse.ui.commands.ICommandService;
39

    
40
import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
41
import eu.etaxonomy.cdm.api.service.molecular.ISequenceService;
42
import eu.etaxonomy.cdm.common.URI;
43
import eu.etaxonomy.cdm.model.media.MediaUtils;
44
import eu.etaxonomy.cdm.model.molecular.Sequence;
45
import eu.etaxonomy.cdm.model.molecular.SequenceString;
46
import eu.etaxonomy.cdm.model.molecular.SingleRead;
47
import eu.etaxonomy.cdm.model.molecular.SingleReadAlignment;
48
import eu.etaxonomy.taxeditor.model.MessagingUtils;
49
import eu.etaxonomy.taxeditor.molecular.TaxeditorMolecularPlugin;
50
import eu.etaxonomy.taxeditor.molecular.editor.AlignmentEditorActionUpdater;
51
import eu.etaxonomy.taxeditor.molecular.editor.AlignmentEditorInput;
52
import eu.etaxonomy.taxeditor.molecular.editor.PherogramMouseListener;
53
import eu.etaxonomy.taxeditor.molecular.editor.e4.handler.ToggleInsertOverwriteHandlerE4;
54
import eu.etaxonomy.taxeditor.molecular.editor.e4.handler.ToggleLeftRightInsertionHandlerE4;
55
import eu.etaxonomy.taxeditor.molecular.l10n.Messages;
56
import eu.etaxonomy.taxeditor.store.CdmStore;
57
import eu.etaxonomy.taxeditor.view.search.derivative.DerivateLabelProvider;
58
import info.bioinfweb.commons.swt.SWTUtils;
59
import info.bioinfweb.libralign.alignmentarea.AlignmentArea;
60
import info.bioinfweb.libralign.alignmentarea.selection.SelectionModel;
61
import info.bioinfweb.libralign.alignmentarea.tokenpainter.NucleotideTokenPainter;
62
import info.bioinfweb.libralign.dataarea.implementations.ConsensusSequenceArea;
63
import info.bioinfweb.libralign.dataarea.implementations.pherogram.PherogramArea;
64
import info.bioinfweb.libralign.dataarea.implementations.sequenceindex.SequenceIndexArea;
65
import info.bioinfweb.libralign.editsettings.EditSettingsChangeEvent;
66
import info.bioinfweb.libralign.editsettings.EditSettingsListener;
67
import info.bioinfweb.libralign.model.AlignmentModel;
68
import info.bioinfweb.libralign.model.AlignmentModelChangeListener;
69
import info.bioinfweb.libralign.model.adapters.StringAdapter;
70
import info.bioinfweb.libralign.model.events.SequenceChangeEvent;
71
import info.bioinfweb.libralign.model.events.SequenceRenamedEvent;
72
import info.bioinfweb.libralign.model.events.TokenChangeEvent;
73
import info.bioinfweb.libralign.model.implementations.PackedAlignmentModel;
74
import info.bioinfweb.libralign.model.tokenset.CharacterTokenSet;
75
import info.bioinfweb.libralign.model.tokenset.TokenSet;
76
import info.bioinfweb.libralign.model.utils.AlignmentModelUtils;
77
import info.bioinfweb.libralign.multiplealignments.AlignmentAreaList;
78
import info.bioinfweb.libralign.multiplealignments.MultipleAlignmentsContainer;
79
import info.bioinfweb.libralign.pherogram.model.PherogramAlignmentRelation;
80
import info.bioinfweb.libralign.pherogram.model.PherogramAreaModel;
81
import info.bioinfweb.libralign.pherogram.model.ShiftChange;
82
import info.bioinfweb.libralign.pherogram.provider.BioJavaPherogramProvider;
83
import info.bioinfweb.libralign.pherogram.provider.PherogramProvider;
84
import info.bioinfweb.libralign.pherogram.provider.ReverseComplementPherogramProvider;
85
import info.bioinfweb.tic.SWTComponentFactory;
86

    
87
/**
88
 * Editor component to edit a contig alignment used to combine different overlapping pherograms from Sanger sequencing to
89
 * a consensus sequence.
90
 * <p>
91
 * The contained GUI components used to edit the alignment come from <a href="http://bioinfweb.info/LibrAlign/">LibrAlign</a>.
92
 *
93
 * @author Ben Stöver
94
 * @author pplitzner
95
 * @date 04.08.2014
96
 */
97
public class AlignmentEditorE4 {
98
    public static final String ID = "eu.etaxonomy.taxeditor.molecular.AlignmentEditor"; //$NON-NLS-1$
99

    
100
	public static final int READS_AREA_INDEX = 1;
101
    public static final int EDITABLE_CONSENSUS_AREA_INDEX = READS_AREA_INDEX + 1;
102
    public static final int CONSENSUS_HINT_AREA_INDEX = EDITABLE_CONSENSUS_AREA_INDEX + 1;
103
	public static final int PHEROGRAM_AREA_INDEX = 0;
104
	public static final int CONSENSUS_DATA_AREA_INDEX = 0;
105
	public static final String DEFAULT_READ_NAME_PREFIX = "Read "; //$NON-NLS-1$
106
	public static final String CONSENSUS_NAME = "Consensus"; //$NON-NLS-1$
107

    
108

    
109
    private ConversationHolder conversationHolder;
110
	private final AlignmentModelChangeListener DIRTY_LISTENER = new AlignmentModelChangeListener() {
111
				@Override
112
				public <T> void afterTokenChange(TokenChangeEvent<T> e) {
113
					setDirty();
114
				}
115

    
116
				@Override
117
				public <T> void afterSequenceRenamed(SequenceRenamedEvent<T> e) {
118
					setDirty();
119
				}
120

    
121
				@Override
122
				public <T> void afterSequenceChange(SequenceChangeEvent<T> e) {
123
					setDirty();
124
				}
125

    
126
				@Override
127
				public <T, U> void afterProviderChanged(AlignmentModel<T> oldProvider,
128
						AlignmentModel<U> newProvider) {  // Not expected.
129

    
130
					setDirty();
131
				}
132
			};
133
	private final AlignmentEditorActionUpdater ACTION_UPDATER = new AlignmentEditorActionUpdater();
134
	public final Clipboard CLIPBOARD = new Clipboard(Display.getCurrent());  //TODO Move to global EDITor class.
135

    
136

    
137
    private MultipleAlignmentsContainer alignmentsContainer = null;
138
    private final Map<String, SingleReadAlignment> cdmMap = new TreeMap<>();  //TODO Move this to ContigSequenceDataProvider
139

    
140
    @Inject
141
    private MDirtyable dirty;
142

    
143
    private AlignmentEditorInput input;
144

    
145
    @Inject
146
    public AlignmentEditorE4() {
147
    }
148

    
149
    private void refreshToolbarElement(String id) {
150
		ICommandService commandService =
151
				PlatformUI.getWorkbench().getActiveWorkbenchWindow().getService(ICommandService.class);
152
		if (commandService != null) {
153
			commandService.refreshElements(id, Collections.EMPTY_MAP);
154
		}
155
    }
156

    
157
    private void registerEditSettingListener(MultipleAlignmentsContainer container) {
158
        container.getEditSettings().addListener(new EditSettingsListener() {
159
            @Override
160
            public void workingModeChanged(EditSettingsChangeEvent e) {}  // Currently nothing to do
161

    
162
            @Override
163
            public void insertLeftInDataAreaChanged(EditSettingsChangeEvent e) {
164
                updateStatusBar();
165
                refreshToolbarElement(ToggleLeftRightInsertionHandlerE4.COMMAND_ID);
166
            }
167

    
168
            @Override
169
            public void insertChanged(EditSettingsChangeEvent e) {
170
                updateStatusBar();
171
                refreshToolbarElement(ToggleInsertOverwriteHandlerE4.COMMAND_ID);
172
            }
173
        });
174
    }
175

    
176
    private AlignmentArea createIndexArea(MultipleAlignmentsContainer container, AlignmentArea labeledArea) {
177
		AlignmentArea result = new AlignmentArea(container);
178
		result.setAllowVerticalScrolling(false);
179
		result.getDataAreas().getTopAreas().add(new SequenceIndexArea(result.getContentArea(), labeledArea));
180
		return result;
181
    }
182

    
183
    private AlignmentArea createEditableAlignmentArea(MultipleAlignmentsContainer container, boolean allowVerticalScrolling) {
184
		AlignmentArea result = new AlignmentArea(container);
185
		result.setAllowVerticalScrolling(allowVerticalScrolling);
186

    
187
		CharacterTokenSet tokenSet = CharacterTokenSet.newDNAInstance();  //TODO Should NUCLEOTIDE be used instead?
188
		AlignmentModel<Character> model = new PackedAlignmentModel<Character>(tokenSet);
189
		result.setAlignmentModel(model, false);
190
		model.getChangeListeners().add(DIRTY_LISTENER);
191
		result.getPaintSettings().getTokenPainterList().set(0, new NucleotideTokenPainter());
192

    
193
		return result;
194
	}
195

    
196
    private AlignmentArea createConsensusHintArea(MultipleAlignmentsContainer container,
197
    		AlignmentArea labeledArea) {
198

    
199
		AlignmentArea result = new AlignmentArea(container);
200
		result.setAllowVerticalScrolling(false);
201
		result.getDataAreas().getBottomAreas().add(
202
				new ConsensusSequenceArea(result.getContentArea(), labeledArea));
203
		return result;
204
    }
205

    
206
    private MultipleAlignmentsContainer getAlignmentsContainer() {
207
    	if (alignmentsContainer == null) {
208
    		alignmentsContainer = new MultipleAlignmentsContainer();
209

    
210
    		AlignmentAreaList list = alignmentsContainer.getAlignmentAreas();
211
    		AlignmentArea readsArea = createEditableAlignmentArea(alignmentsContainer, true);
212
    		readsArea.getSelection().addSelectionListener(ACTION_UPDATER);
213
    	    list.add(createIndexArea(alignmentsContainer, readsArea));
214
    		list.add(readsArea);  // Make sure READS_AREA_INDEX is correct.
215
    		AlignmentArea editableConsensusArea = createEditableAlignmentArea(alignmentsContainer, false);
216
    		editableConsensusArea.getSelection().addSelectionListener(ACTION_UPDATER);
217
    		list.add(editableConsensusArea);  // Make sure COMSENSUS_AREA_INDEX is correct.
218
    		list.add(createConsensusHintArea(alignmentsContainer, readsArea));
219

    
220
    		registerEditSettingListener(alignmentsContainer);
221
       	}
222
		return alignmentsContainer;
223
	}
224

    
225

    
226
    public AlignmentArea getReadsArea() {
227
    	return getAlignmentsContainer().getAlignmentAreas().get(READS_AREA_INDEX);
228
    }
229

    
230

    
231
    public AlignmentArea getEditableConsensusArea() {
232
    	return getAlignmentsContainer().getAlignmentAreas().get(EDITABLE_CONSENSUS_AREA_INDEX);
233
    }
234

    
235

    
236
    /**
237
     * Checks whether {@link #getReadsArea()} or {@link #getEditableConsensusArea()} currently
238
     * have the user focus and returns the according component.
239
     *
240
     * @return either the reads or the consensus alignment area or {@code null} if none of these
241
     *         components is currently focused
242
     */
243
    public AlignmentArea getFocusedArea() {
244
    	AlignmentArea result = getReadsArea();
245
    	if (hasFocus(result)) {
246
    		return result;
247
    	}
248
    	else {
249
    		result = getEditableConsensusArea();
250
        	if (hasFocus(result)) {
251
        		return result;
252
        	}
253
        	else {
254
        		return null;
255
        	}
256
    	}
257
    }
258

    
259
    /**
260
     * Checks whether the specified alignment area or one of its subcomponents currently has the
261
     * focus.
262
     *
263
     * @param area the alignment area to be checked (Can only be {@link #getReadsArea()} or
264
     *        {@link #getEditableConsensusArea()}.)
265
     * @return {@code true} if the specified component is focused and is either equal to
266
     *         {@link #getReadsArea()} or {@link #getEditableConsensusArea()}or {@code false} otherwise
267
     */
268
    private boolean hasFocus(AlignmentArea area) {
269
    	return SWTUtils.childHasFocus((Composite)area.getToolkitComponent());
270
    }
271

    
272
    public boolean hasPherogram(String sequenceID) {
273
        return getReadsArea().getDataAreas().getSequenceAreas(sequenceID).size() > PHEROGRAM_AREA_INDEX;
274
    }
275

    
276
    public PherogramArea getPherogramArea(String sequenceID) {
277
        if (hasPherogram(sequenceID)) {
278
            return (PherogramArea)getReadsArea().getDataAreas().getSequenceAreas(sequenceID).get(PHEROGRAM_AREA_INDEX);
279
        }
280
        else {
281
            return null;
282
        }
283
    }
284

    
285
    private ConsensusSequenceArea getConsensusHintDataArea() {
286
        return (ConsensusSequenceArea)getAlignmentsContainer().getAlignmentAreas().
287
                get(CONSENSUS_HINT_AREA_INDEX).getDataAreas().getBottomAreas().
288
                get(CONSENSUS_DATA_AREA_INDEX);
289
    }
290

    
291
    @Deprecated  //TODO Remove as soon as testing period is over
292
    private void createTestContents() {
293
		// Just for testing:
294
		try {
295
			addRead(URI.fromFile(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")), false); //$NON-NLS-1$
296
            //addRead(URI.fromFile(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")), false);
297
            addRead(URI.fromFile(new File("D:/Users/BenStoever/ownCloud/Dokumente/Projekte/EDITor/Quelltexte/LibrAlign branch/Repository/eu.etaxonomy.taxeditor.editor/src/main/resources/AlignmentTestData/Test_qualityScore.scf")), false); //$NON-NLS-1$
298

    
299
			// Add test consensus sequence:
300
			AlignmentModel consensusModel = getEditableConsensusArea().getAlignmentModel();
301
			String id = consensusModel.addSequence(CONSENSUS_NAME);
302
			Collection<Object> tokens = new ArrayList<Object>();  // First save tokens in a collection to avoid GUI updated for each token.
303
			tokens.add(consensusModel.getTokenSet().tokenByRepresentation("A")); //$NON-NLS-1$
304
			tokens.add(consensusModel.getTokenSet().tokenByRepresentation("C")); //$NON-NLS-1$
305
			tokens.add(consensusModel.getTokenSet().tokenByRepresentation("G")); //$NON-NLS-1$
306
			tokens.add(consensusModel.getTokenSet().tokenByRepresentation("T")); //$NON-NLS-1$
307
			consensusModel.insertTokensAt(id, 0, tokens);
308
		}
309
		catch (Exception e) {
310
			throw new RuntimeException(e);
311
		}
312
    }
313

    
314
    private void readCdmData(Sequence sequenceNode) {
315
    	//TODO If called from somewhere else than createPartControl() the editorInput needs to be checked and previous contents need to be cleared (or updated).
316

    
317
		// Add reads:
318
		for (SingleReadAlignment singleReadAlignment : sequenceNode.getSingleReadAlignments()) {
319
			try {
320
				SingleRead pherogramInfo = singleReadAlignment.getSingleRead();
321
				String id = addRead(DerivateLabelProvider.getDerivateText(pherogramInfo, conversationHolder),
322
						getPherogramURI(pherogramInfo),
323
						singleReadAlignment.isReverseComplement(),
324
						singleReadAlignment.getEditedSequence(),
325
						singleReadAlignment.getFirstSeqPosition(),
326
						singleReadAlignment.getLeftCutPosition(),
327
						singleReadAlignment.getRightCutPosition(),
328
						singleReadAlignment.getShifts());
329
				cdmMap.put(id, singleReadAlignment);
330
			}
331
			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).
332
                MessagingUtils.errorDialog(Messages.AlignmentEditor_ERROR_SINGLE_READ, null, Messages.AlignmentEditor_ERROR_SINGLE_READ_MESSAGE +
333
                        e.getLocalizedMessage(), TaxeditorMolecularPlugin.PLUGIN_ID, e, false);
334
			}
335
		}
336

    
337
		// Set consensus sequence:
338
		AlignmentModel consensusModel = getEditableConsensusArea().getAlignmentModel();
339
		String id = consensusModel.addSequence(CONSENSUS_NAME);
340
		consensusModel.insertTokensAt(id, 0, AlignmentModelUtils.charSequenceToTokenList(
341
				sequenceNode.getConsensusSequence().getString(), consensusModel.getTokenSet()));
342
		//TODO Can the consensus sequence also be null? / Should it be created here, if nothing is in the DB?
343
    }
344

    
345
    @PostConstruct
346
    public void createPartControl(Composite parent) {
347
        if (CdmStore.isActive()){
348
            if(conversationHolder == null){
349
                conversationHolder = CdmStore.createConversation();
350
            }
351
        }
352
        else{
353
            return;
354
        }
355
        SWTComponentFactory.getInstance().getSWTComponent(getAlignmentsContainer(), parent, SWT.NONE);
356
        Display.getCurrent().addFilter(SWT.FocusIn, ACTION_UPDATER);
357
        Display.getCurrent().addFilter(SWT.FocusOut, ACTION_UPDATER);
358
	}
359

    
360

    
361
    @PreDestroy
362
    public void dispose() {
363
        Display.getCurrent().removeFilter(SWT.FocusIn, ACTION_UPDATER);
364
        Display.getCurrent().removeFilter(SWT.FocusOut, ACTION_UPDATER);
365
        CLIPBOARD.dispose();
366
        input.dispose();
367

    
368
        if(conversationHolder!=null){
369
            conversationHolder.close();
370
            conversationHolder = null;
371
        }
372
        if(input!=null){
373
            input.dispose();
374
        }
375
        dirty.setDirty(false);
376
    }
377

    
378

    
379
	private void updateStatusBar() {
380
	    //FIXME E4 migrate
381
//        IActionBars bars = getEditorSite().getActionBars();
382
//        bars.getStatusLineManager().setMessage(
383
//                Messages.AlignmentEditor_EDIT_MODE + (getReadsArea().getEditSettings().isInsert() ? Messages.AlignmentEditor_INSERT : Messages.AlignmentEditor_OVERWRITE) + "  " + //$NON-NLS-1$
384
//        		Messages.AlignmentEditor_INSERTION_PHEROGRAM +
385
//	       		(getReadsArea().getEditSettings().isInsertLeftInDataArea() ? Messages.AlignmentEditor_LEFT : Messages.AlignmentEditor_RIGHT));  //TODO multi language
386
    }
387

    
388

    
389
    private SingleReadAlignment.Shift[] convertToCDMShifts(PherogramAreaModel model) {
390
    	Iterator<ShiftChange> iterator = model.shiftChangeIterator();
391
    	List<SingleReadAlignment.Shift> shifts = new ArrayList<SingleReadAlignment.Shift>();
392
    	while (iterator.hasNext()) {
393
    		ShiftChange shiftChange = iterator.next();
394
    		shifts.add(new SingleReadAlignment.Shift(shiftChange.getBaseCallIndex(), shiftChange.getShiftChange()));
395
    	}
396
    	return shifts.toArray(new SingleReadAlignment.Shift[shifts.size()]);
397
    }
398

    
399

    
400
    @Persist
401
    public void doSave(IProgressMonitor monitor) {
402
        String taskName = Messages.AlignmentEditor_SAVING_ALIGNMENT;  //TODO multi language
403
        monitor.beginTask(taskName, 3);
404

    
405
        //re-loading sequence to avoid session conflicts
406
        Sequence sequenceNode = CdmStore.getService(ISequenceService.class).load(input.getSequenceNodeUuid());
407
        input.setSequenceNode(sequenceNode);
408
        StringAdapter stringProvider = new StringAdapter(getEditableConsensusArea().getAlignmentModel(), false);  // Throws an exception if a token has more than one character.
409

    
410
        // Write consensus sequence:
411
        SequenceString consensusSequenceObj = sequenceNode.getConsensusSequence();
412
        String newConsensusSequence = stringProvider.getSequence(
413
                getEditableConsensusArea().getAlignmentModel().sequenceIDByName(CONSENSUS_NAME));
414
        if (consensusSequenceObj == null) {
415
            sequenceNode.setConsensusSequence(SequenceString.NewInstance(newConsensusSequence));
416
        }
417
        else {
418
            consensusSequenceObj.setString(newConsensusSequence);
419
        }
420

    
421
        // Write single reads:
422
        stringProvider.setUnderlyingModel(getReadsArea().getAlignmentModel());
423
        sequenceNode.getSingleReadAlignments().retainAll(cdmMap.values());  // Remove all reads that are not in the alignment anymore.
424
        Iterator<String> iterator = getReadsArea().getAlignmentModel().sequenceIDIterator();
425
        while (iterator.hasNext()) {
426
            String id = iterator.next();
427
            SingleReadAlignment singleRead = cdmMap.get(id);
428
            if (singleRead == null) {
429
                throw new InternalError(Messages.AlignmentEditor_NEW_READ_FAILURE);  //TODO multi language
430
                //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?
431
                //singleRead = SingleReadAlignment.NewInstance(consensusSequence, singleRead, shifts, editedSequence);
432
            }
433

    
434
            singleRead.setEditedSequence(stringProvider.getSequence(id));
435

    
436
            PherogramArea pherogramArea = getPherogramArea(id);
437
            if (pherogramArea != null) {
438
                PherogramAreaModel model = pherogramArea.getModel();
439
                singleRead.setReverseComplement(model.getPherogramProvider() instanceof ReverseComplementPherogramProvider);  // Works only if ReverseComplementPherogramProvider instances are not nested.
440
                singleRead.setShifts(convertToCDMShifts(getPherogramArea(id).getModel()));
441
                singleRead.setFirstSeqPosition(model.getFirstSeqPos());
442
                singleRead.setLeftCutPosition(model.getLeftCutPosition());
443
                singleRead.setRightCutPosition(model.getRightCutPosition());
444
            }
445
        }
446

    
447
        if (!conversationHolder.isBound()) {
448
            conversationHolder.bind();
449
        }
450
        monitor.worked(1);
451

    
452
        input.merge();
453
        // Commit the conversation and start a new transaction immediately:
454
        conversationHolder.commit(true);
455
        monitor.worked(1);
456

    
457
        dirty.setDirty(false);
458
        monitor.worked(1);
459
        monitor.done();
460
    }
461

    
462

    
463
    public void init(AlignmentEditorInput input) throws PartInitException {
464
        this.input = input;
465

    
466
        updateStatusBar();
467

    
468
        if (input.getSequenceNodeUuid() != null) {
469
            Sequence sequenceNode = CdmStore.getService(ISequenceService.class).load(input.getSequenceNodeUuid());
470
            //re-load into the current session if it is already persisted in the DB
471
            if(sequenceNode!=null && sequenceNode.getId()!=0){
472
                sequenceNode = CdmStore.getService(ISequenceService.class).load(sequenceNode.getUuid());
473
            }
474
            readCdmData(sequenceNode);
475
        }
476
        else {
477
            createTestContents();  // This case will removed after the test phase and an exception should probably be thrown.
478
        }
479
    }
480

    
481

    
482
    public boolean isDirty() {
483
        return dirty.isDirty();
484
    }
485

    
486

    
487
    private void setDirty() {
488
    	dirty.setDirty(true);
489
    }
490

    
491

    
492
    @Focus
493
    public void setFocus() {
494
        if(conversationHolder != null){
495
            conversationHolder.bind();
496
        }
497
        if(input!=null){
498
            input.bind();
499
        }
500
    }
501

    
502
    public boolean isInsertMode() {
503
        return getAlignmentsContainer().getEditSettings().isInsert();
504
    }
505

    
506

    
507
    public boolean isInsertLeftInPherogram() {
508
        return getAlignmentsContainer().getEditSettings().isInsertLeftInDataArea();
509
    }
510

    
511

    
512
    public void toggleLeftRightInsertionInPherogram() {
513
    	getAlignmentsContainer().getEditSettings().toggleInsertLeftInDataArea();
514
    }
515

    
516

    
517
    public void toggleInsertOverwrite() {
518
    	getAlignmentsContainer().getEditSettings().toggleInsert();
519
    }
520

    
521

    
522
    private String cutPherogram(boolean left) {
523
        SelectionModel selection = getReadsArea().getSelection();
524
        if (selection.getCursorHeight() != 1) {
525
            return Messages.AlignmentEditor_CUTTING_FAILURE;  //TODO multi language
526
        }
527
        else {
528
            PherogramArea pherogramArea =
529
                    getPherogramArea(getReadsArea().getSequenceOrder().idByIndex(selection.getCursorRow()));
530
            if (pherogramArea == null) {
531
                return Messages.AlignmentEditor_NO_ATTACHED_PHEROGRAM;  //TODO multi language
532
            }
533
            else {
534
                if (left) {
535
                    if (pherogramArea.setLeftCutPositionBySelection()) {
536
                        return null;
537
                    }
538
                    else {
539
                        return Messages.AlignmentEditor_LEFT_END_OUTSIDE;  //TODO multi language
540
                    }
541
                }
542
                else {
543
                    if (pherogramArea.setRightCutPositionBySelection()) {
544
                        return null;
545
                    }
546
                    else {
547
                        return Messages.AlignmentEditor_RIGHT_END_OUTSIDE;  //TODO multi language
548
                    }
549
                }
550
            }
551
        }
552
    }
553

    
554

    
555
    public String cutPherogramLeft() {
556
        return cutPherogram(true);
557
    }
558

    
559

    
560
    public String cutPherogramRight() {
561
        return cutPherogram(false);
562
    }
563

    
564

    
565
    public void reverseComplementSelectedSequences() {
566
    	SelectionModel selection = getReadsArea().getSelection();
567
    	AlignmentModel<?> model = getReadsArea().getAlignmentModel();
568
    	for (int row = selection.getFirstRow(); row < selection.getFirstRow() + selection.getCursorHeight(); row++) {
569
    		String sequenceID = getReadsArea().getSequenceOrder().idByIndex(row);
570
			PherogramArea area = getPherogramArea(sequenceID);
571
			PherogramAreaModel pherogramAlignmentModel = area.getModel();
572

    
573
            PherogramAlignmentRelation rightRelation = pherogramAlignmentModel.editableIndexByBaseCallIndex(
574
                    pherogramAlignmentModel.getRightCutPosition());
575
            int rightBorder;
576
            if (rightRelation.getCorresponding() == PherogramAlignmentRelation.OUT_OF_RANGE) {
577
                rightBorder = rightRelation.getBeforeValidIndex() + 1;
578
            }
579
            else {
580
                rightBorder = rightRelation.getAfterValidIndex();
581
            }
582

    
583
			AlignmentModelUtils.reverseComplement(model, sequenceID,
584
			        pherogramAlignmentModel.editableIndexByBaseCallIndex(
585
			                pherogramAlignmentModel.getLeftCutPosition()).getBeforeValidIndex(),
586
			        rightBorder);
587
			pherogramAlignmentModel.reverseComplement();
588
		}
589
    }
590

    
591

    
592
    /**
593
     * Recreates the whole consensus sequence from all single read sequences. The previous consensus
594
     * sequence is overwritten.
595
     */
596
    @SuppressWarnings("unchecked")
597
    public <T> void createConsensusSequence() {
598
        ConsensusSequenceArea area = getConsensusHintDataArea();
599
        AlignmentModel<T> model = (AlignmentModel<T>)getEditableConsensusArea().getAlignmentModel();
600
        String sequenceID = model.sequenceIDIterator().next();  // There is always one sequence contained.
601
        int length = getReadsArea().getAlignmentModel().getMaxSequenceLength();
602

    
603
        Collection<T> tokens = new ArrayList<T>(length);
604
        for (int column = 0; column < length; column++) {
605
            tokens.add(model.getTokenSet().tokenByRepresentation(area.getConsensusToken(column)));
606
        }
607

    
608
        model.removeTokensAt(sequenceID, 0, model.getSequenceLength(sequenceID));
609
        model.insertTokensAt(sequenceID, 0, tokens);
610
    }
611

    
612

    
613
    /**
614
     * Updates the current consensus sequence by replacing gaps by the according consensus tokens
615
     * calculated from the single read sequences and extends the consensus sequence if necessary.
616
     */
617
    @SuppressWarnings("unchecked")
618
    public <T> void updateConsensusSequence() {
619
        ConsensusSequenceArea area = getConsensusHintDataArea();
620
        AlignmentModel<T> model = (AlignmentModel<T>)getEditableConsensusArea().getAlignmentModel();
621
        TokenSet<T> tokenSet = model.getTokenSet();
622
        String sequenceID = model.sequenceIDIterator().next();  // There is always one sequence contained.
623
        int currentConsensusLength = model.getSequenceLength(sequenceID);
624
        int overallLength = getReadsArea().getAlignmentModel().getMaxSequenceLength();
625

    
626
        // Replace gaps by new information:
627
        for (int column = 0; column < currentConsensusLength; column++) {
628
            if (tokenSet.isGapToken(model.getTokenAt(sequenceID, column))) {
629
                T newToken = tokenSet.tokenByRepresentation(area.getConsensusToken(column));
630
                if (!tokenSet.isGapToken(newToken)) {
631
                    model.setTokenAt(sequenceID, column, newToken);
632
                }
633
            }
634
        }
635

    
636
        // Append additional tokens:
637
        if (overallLength > currentConsensusLength) {
638
            Collection<T> tokens = new ArrayList<T>(overallLength);
639
            for (int column = currentConsensusLength; column < overallLength; column++) {
640
                tokens.add(tokenSet.tokenByRepresentation(area.getConsensusToken(column)));
641
            }
642
            model.appendTokens(sequenceID, tokens);
643
        }
644
    }
645

    
646

    
647
	public static PherogramProvider readPherogram(URI uri) throws IOException, UnsupportedChromatogramFormatException {
648
	    PherogramProvider result;
649
		InputStream stream = uri.toURL().openStream();
650
		try {
651
			result = new BioJavaPherogramProvider(ChromatogramFactory.create(stream));
652
		}
653
		finally {
654
			stream.close();
655
		}
656
		return result;
657
	}
658

    
659

    
660
	private String newReadName() {
661
		int index = 1;
662
		while (getReadsArea().getAlignmentModel().sequenceIDByName(DEFAULT_READ_NAME_PREFIX + index) != null) {
663
			index++;
664
		}
665
		return DEFAULT_READ_NAME_PREFIX + index;
666
	}
667

    
668

    
669
    public void addRead(URI pherogramURI, boolean reverseComplemented) throws IOException, UnsupportedChromatogramFormatException {
670
    	addRead(newReadName(), pherogramURI, reverseComplemented, null, null, null, null, null);
671
    }
672

    
673

    
674
    /**
675
     * Adds a new sequence with attached phergram data area to the reads alignment.
676
     * <p>
677
     * If {@code null} is specified as {@code editedSequence} the base call sequence from the pherogram will
678
     * be set as the edited sequence. If {@code null} is specified as {@code shifts} no shifts between the edited
679
     * and the base calls sequence are assumed.
680
     *
681
     * @param name the name of the new sequence
682
     * @param pherogramURI the URI where the associated pherogram file is located
683
     * @param reverseComplemented Specify {@code true} here, if the reverse complement of the pherogram data should
684
     *        be added, {@code false} otherwise.
685
     * @param editedSequence the edited version of the base call sequence (May be {@code null}.)
686
     * @param shifts the alignment information that links the edited and the base call sequence (May be {@code null}.)
687
     * @return the sequence ID of the added read
688
     * @throws IOException if an error occurred when trying to read the pherogram file
689
     * @throws UnsupportedChromatogramFormatException if the format of the pherogram file is not supported
690
     */
691
    public String addRead(String name, URI pherogramURI, boolean reverseComplemented, String editedSequence,
692
            Integer firstSeqPos, Integer leftCutPos, Integer rightCutPos, SingleReadAlignment.Shift[] shifts)
693
            throws IOException, UnsupportedChromatogramFormatException {
694

    
695
		AlignmentModel model = getReadsArea().getAlignmentModel();
696
		PherogramProvider pherogramProvider = null;
697
		if (pherogramURI != null) {
698
		    pherogramProvider = readPherogram(pherogramURI);  // Must happen before a sequence is added, because it might throw an exception.
699
            if (reverseComplemented) {
700
                pherogramProvider = new ReverseComplementPherogramProvider(pherogramProvider);
701
            }
702
		}
703

    
704
        // Create sequence:
705
		model.addSequence(name);
706
		String id = model.sequenceIDByName(name);
707

    
708
		// Set edited sequence:
709
		Collection<Object> tokens = null;  // First save tokens in a collection to avoid GUI updated for each token.
710
		if (editedSequence != null) {
711
			tokens = AlignmentModelUtils.charSequenceToTokenList(editedSequence, model.getTokenSet());
712
		}
713
		else if (pherogramProvider != null) {  // Copy base call sequence into alignment:
714
			tokens = new ArrayList<Object>();
715
			for (int i = 0; i < pherogramProvider.getSequenceLength(); i++) {
716
				tokens.add(model.getTokenSet().tokenByRepresentation(
717
					Character.toString(pherogramProvider.getBaseCall(i))));
718
			}
719
			setDirty();
720
		}
721

    
722
		if (tokens != null) {  // If either an edited sequence or a pherogram URI was provided.
723
		    model.insertTokensAt(id, 0, tokens);
724

    
725
		    if (pherogramProvider != null) {
726
		        // Create pherogram area:
727
		        PherogramArea pherogramArea = new PherogramArea(getReadsArea().getContentArea(),
728
		                new PherogramAreaModel(pherogramProvider));
729

    
730
		        // Set position properties and shifts:
731
		        PherogramAreaModel phergramModel = pherogramArea.getModel();
732
		        if ((firstSeqPos != null) && (leftCutPos != null)) {
733
		            phergramModel.setFirstSeqLeftCutPos(firstSeqPos, leftCutPos);
734
		        }
735
		        if (rightCutPos != null) {
736
		            phergramModel.setRightCutPosition(rightCutPos);
737
		        }
738
		        if ((shifts != null) && (shifts.length > 0)) {
739
		            for (int i = 0; i < shifts.length; i++) {
740
		                phergramModel.addShiftChange(shifts[i].position, shifts[i].shift);
741
		            }
742
		            setDirty();
743
		        }
744

    
745
		        // Add pherogram area to GUI:
746
		        pherogramArea.addMouseListener(new PherogramMouseListener(pherogramArea));
747
		        getReadsArea().getDataAreas().getSequenceAreas(id).add(pherogramArea);
748
		    }
749
		}
750
		return id;
751
	}
752

    
753

    
754
    public static URI getPherogramURI(SingleRead pherogramInfo) {
755
        if (pherogramInfo.getPherogram() != null) {
756
            return MediaUtils.getFirstMediaRepresentationPart(pherogramInfo.getPherogram()).getUri();
757
        }
758
        else {
759
            return null;
760
        }
761
    }
762
}
(1-1/2)