a2adc2ee82c93f29a5480fd47e9d1ea1e8d24b1e
[taxeditor.git] / eu.etaxonomy.taxeditor.editor / src / main / java / eu / etaxonomy / taxeditor / editor / molecular / AlignmentEditor.java
1 // $Id$
2 /**
3 * Copyright (C) 2014 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
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.
9 */
10 package eu.etaxonomy.taxeditor.editor.molecular;
11
12
13 import info.bioinfweb.libralign.alignmentarea.AlignmentArea;
14 import info.bioinfweb.libralign.alignmentarea.content.AlignmentContentArea;
15 import info.bioinfweb.libralign.dataarea.implementations.ConsensusSequenceArea;
16 import info.bioinfweb.libralign.dataarea.implementations.SequenceIndexArea;
17 import info.bioinfweb.libralign.dataarea.implementations.pherogram.PherogramArea;
18 import info.bioinfweb.libralign.editsettings.EditSettingsChangeEvent;
19 import info.bioinfweb.libralign.editsettings.EditSettingsListener;
20 import info.bioinfweb.libralign.multiplealignments.AlignmentAreaList;
21 import info.bioinfweb.libralign.multiplealignments.MultipleAlignmentsContainer;
22 import info.bioinfweb.libralign.pherogram.provider.BioJavaPherogramProvider;
23 import info.bioinfweb.libralign.pherogram.provider.PherogramProvider;
24 import info.bioinfweb.libralign.sequenceprovider.SequenceDataProvider;
25 import info.bioinfweb.libralign.sequenceprovider.implementations.PackedSequenceDataProvider;
26 import info.bioinfweb.libralign.sequenceprovider.tokenset.BioJavaTokenSet;
27 import info.bioinfweb.libralign.sequenceprovider.tokenset.TokenSet;
28
29 import java.io.File;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.net.URI;
33 import java.util.ArrayList;
34 import java.util.Collection;
35 import java.util.Collections;
36 import java.util.Map;
37 import java.util.TreeMap;
38
39 import org.biojava.bio.chromatogram.ChromatogramFactory;
40 import org.biojava.bio.chromatogram.UnsupportedChromatogramFormatException;
41 import org.biojava3.core.sequence.compound.DNACompoundSet;
42 import org.biojava3.core.sequence.compound.NucleotideCompound;
43 import org.eclipse.core.runtime.IProgressMonitor;
44 import org.eclipse.swt.SWT;
45 import org.eclipse.swt.widgets.Composite;
46 import org.eclipse.ui.IActionBars;
47 import org.eclipse.ui.IEditorInput;
48 import org.eclipse.ui.IEditorSite;
49 import org.eclipse.ui.PartInitException;
50 import org.eclipse.ui.PlatformUI;
51 import org.eclipse.ui.commands.ICommandService;
52 import org.eclipse.ui.part.EditorPart;
53
54 import eu.etaxonomy.taxeditor.editor.handler.ToggleInsertOverwriteHandler;
55 import eu.etaxonomy.taxeditor.editor.handler.ToggleLeftRightInsertionHandler;
56
57
58
59 /**
60 * Editor component to edit a contig alignment used to combine different overlapping pherograms from Sanger sequencing to
61 * a consensus sequence.
62 * <p>
63 * The contained GUI components used to edit the alignment come from <a href="http://bioinfweb.info/LibrAlign/">LibrAlign</a>.
64 *
65 * @author Ben Stöver
66 * @author pplitzner
67 * @date 04.08.2014
68 */
69 public class AlignmentEditor extends EditorPart {
70 public static final String ID = "eu.etaxonomy.taxeditor.editor.molecular.AlignmentEditor";
71 public static final int READS_AREA_INDEX = 1;
72 public static final int CONSENSUS_AREA_INDEX = READS_AREA_INDEX + 1;
73 public static final String DEFAULT_READ_NAME_PREFIX = "Read ";
74
75
76 private MultipleAlignmentsContainer alignmentsContainer = null;
77 private Map<Integer, URI> uriMap = new TreeMap<Integer, URI>(); //TODO Move this to ContigSequenceDataProvider
78
79
80 private void refreshToolbarElement(String id) {
81 ICommandService commandService =
82 (ICommandService)PlatformUI.getWorkbench().getActiveWorkbenchWindow().getService(ICommandService.class);
83 if (commandService != null) {
84 commandService.refreshElements(id, Collections.EMPTY_MAP);
85 }
86 }
87
88
89 private void registerEditSettingListener(MultipleAlignmentsContainer container) {
90 container.getEditSettings().addListener(new EditSettingsListener() {
91 @Override
92 public void workingModeChanged(EditSettingsChangeEvent e) {} // Currently nothing to do
93
94 @Override
95 public void insertLeftInDataAreaChanged(EditSettingsChangeEvent e) {
96 updateStatusBar();
97 refreshToolbarElement(ToggleLeftRightInsertionHandler.COMMAND_ID);
98 }
99
100 @Override
101 public void insertChanged(EditSettingsChangeEvent e) {
102 updateStatusBar();
103 refreshToolbarElement(ToggleInsertOverwriteHandler.COMMAND_ID);
104 }
105 });
106 }
107
108
109 private AlignmentArea createIndexArea(MultipleAlignmentsContainer container) {
110 AlignmentArea result = new AlignmentArea(container);
111 result.setAllowVerticalScrolling(false);
112 result.getDataAreas().getTopAreas().add(new SequenceIndexArea(result.getContentArea()));
113 return result;
114 }
115
116
117 private AlignmentArea createEditableAlignmentArea(MultipleAlignmentsContainer container, boolean allowVerticalScrolling) {
118 AlignmentArea result = new AlignmentArea(container);
119 result.setAllowVerticalScrolling(allowVerticalScrolling);
120
121 TokenSet<NucleotideCompound> tokenSet = new BioJavaTokenSet<NucleotideCompound>(new DNACompoundSet(), true);
122 SequenceDataProvider<NucleotideCompound> provider = new PackedSequenceDataProvider<NucleotideCompound>(tokenSet);
123 result.setSequenceProvider(provider, false);
124
125 return result;
126 }
127
128
129 private AlignmentArea createConsensusHintArea(MultipleAlignmentsContainer container,
130 SequenceDataProvider<?> sequenceProvider) {
131
132 AlignmentArea result = new AlignmentArea(container);
133 result.setAllowVerticalScrolling(false);
134 result.getDataAreas().getBottomAreas().add(
135 new ConsensusSequenceArea(result.getContentArea(), sequenceProvider));
136 return result;
137 }
138
139
140 private MultipleAlignmentsContainer getAlignmentsContainer() {
141 if (alignmentsContainer == null) {
142 alignmentsContainer = new MultipleAlignmentsContainer();
143
144 AlignmentAreaList list = alignmentsContainer.getAlignmentAreas();
145 list.add(createIndexArea(alignmentsContainer));
146 AlignmentArea readsArea = createEditableAlignmentArea(alignmentsContainer, true);
147 list.add(readsArea); // Make sure READS_AREA_INDEX is correct.
148 list.add(createEditableAlignmentArea(alignmentsContainer, false)); // Make sure COMSENSUS_AREA_INDEX is correct.
149 list.add(createConsensusHintArea(alignmentsContainer,
150 readsArea.getSequenceProvider()));
151
152 registerEditSettingListener(alignmentsContainer);
153 }
154 return alignmentsContainer;
155 }
156
157
158 private AlignmentArea getReadsArea() {
159 return getAlignmentsContainer().getAlignmentAreas().get(READS_AREA_INDEX);
160 }
161
162
163 private AlignmentArea getConsensusArea() {
164 return getAlignmentsContainer().getAlignmentAreas().get(CONSENSUS_AREA_INDEX);
165 }
166
167
168 /* (non-Javadoc)
169 * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
170 */
171 @Override
172 public void createPartControl(Composite parent) {
173 getAlignmentsContainer().createSWTWidget(parent, SWT.NONE);
174 updateStatusBar();
175
176 // Just for testing:
177 try {
178 addRead(new File("D:/Users/BenStoever/Documents/Studium/Projekte/Promotion/EDITor/Quelltexte/LibrAlign branch/Repository/eu.etaxonomy.taxeditor.editor/src/main/resources/AlignmentTestData/JR430_JR-P01.ab1").toURI());
179 addRead(new File("D:/Users/BenStoever/Documents/Studium/Projekte/Promotion/EDITor/Quelltexte/LibrAlign branch/Repository/eu.etaxonomy.taxeditor.editor/src/main/resources/AlignmentTestData/JR444_JR-P05.ab1").toURI());
180
181 // Add test consensus sequence:
182 SequenceDataProvider consensusProvider = getConsensusArea().getSequenceProvider();
183 int id = consensusProvider.addSequence("Consensus");
184 Collection<Object> tokens = new ArrayList<Object>(); // First save tokens in a collection to avoid GUI updated for each token.
185 tokens.add(consensusProvider.getTokenSet().tokenByKeyChar('A'));
186 tokens.add(consensusProvider.getTokenSet().tokenByKeyChar('C'));
187 tokens.add(consensusProvider.getTokenSet().tokenByKeyChar('G'));
188 tokens.add(consensusProvider.getTokenSet().tokenByKeyChar('T'));
189 consensusProvider.insertTokensAt(id, 0, tokens);
190 }
191 catch (Exception e) {
192 throw new RuntimeException(e);
193 }
194 }
195
196
197 private void updateStatusBar() {
198 IActionBars bars = getEditorSite().getActionBars();
199 bars.getStatusLineManager().setMessage("Edit mode: " +
200 (getReadsArea().getEditSettings().isInsert() ? "Insert" : "Overwrite") + " " +
201 "Insertion in pherogram: " +
202 (getReadsArea().getEditSettings().isInsertLeftInDataArea() ? "Left" : "Right"));
203 }
204
205
206 /* (non-Javadoc)
207 * @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor)
208 */
209 @Override
210 public void doSave(IProgressMonitor monitor) {
211 // TODO Auto-generated method stub
212
213 }
214
215
216 /* (non-Javadoc)
217 * @see org.eclipse.ui.part.EditorPart#doSaveAs()
218 */
219 @Override
220 public void doSaveAs() {
221 // TODO Auto-generated method stub
222
223 }
224
225
226 /* (non-Javadoc)
227 * @see org.eclipse.ui.part.EditorPart#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
228 */
229 @Override
230 public void init(IEditorSite site, IEditorInput input) throws PartInitException {
231 setSite(site);
232 setInput(input);
233 }
234
235
236 /* (non-Javadoc)
237 * @see org.eclipse.ui.part.EditorPart#isDirty()
238 */
239 @Override
240 public boolean isDirty() {
241 // TODO Auto-generated method stub
242 return false;
243 }
244
245
246 /* (non-Javadoc)
247 * @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed()
248 */
249 @Override
250 public boolean isSaveAsAllowed() {
251 // TODO Auto-generated method stub
252 return false;
253 }
254
255
256 @Override
257 public void setFocus() {}
258
259
260 public boolean isInsertMode() {
261 return getAlignmentsContainer().getEditSettings().isInsert();
262 }
263
264
265 public boolean isInsertLeftInPherogram() {
266 return getAlignmentsContainer().getEditSettings().isInsertLeftInDataArea();
267 }
268
269
270 public void toggleLeftRightInsertionInPherogram() {
271 getAlignmentsContainer().getEditSettings().toggleInsertLeftInDataArea();
272 }
273
274
275 public void toggleInsertOverwrite() {
276 getAlignmentsContainer().getEditSettings().toggleInsert();
277 }
278
279
280 public static PherogramProvider readPherogram(URI uri) throws IOException, UnsupportedChromatogramFormatException {
281 PherogramProvider result;
282 InputStream stream = uri.toURL().openStream();
283 try {
284 result = new BioJavaPherogramProvider(ChromatogramFactory.create(stream));
285 }
286 finally {
287 stream.close();
288 }
289 return result;
290 }
291
292
293 private String newReadName() {
294 int index = 1;
295 while (getReadsArea().getSequenceProvider().sequenceIDByName(DEFAULT_READ_NAME_PREFIX + index)
296 != SequenceDataProvider.NO_SEQUENCE_FOUND) {
297
298 index++;
299 }
300 return DEFAULT_READ_NAME_PREFIX + index;
301 }
302
303
304 public void addRead(URI pherogramURI) throws IOException, UnsupportedChromatogramFormatException {
305 addRead(newReadName(), pherogramURI);
306 }
307
308
309 public void addRead(String name, URI pherogramURI) throws IOException, UnsupportedChromatogramFormatException {
310 SequenceDataProvider provider = getReadsArea().getSequenceProvider();
311 PherogramProvider pherogramProvider = readPherogram(pherogramURI); // Must happen before a sequence is added, because it might throw an exception.
312
313 // Create sequence:
314 provider.addSequence(name);
315 int id = provider.sequenceIDByName(name);
316
317 // Copy base call sequence into alignment:
318 Collection<Object> tokens = new ArrayList<Object>(); // First save tokens in a collection to avoid GUI updated for each token.
319 for (int i = 0; i < pherogramProvider.getSequenceLength(); i++) {
320 tokens.add(provider.getTokenSet().tokenByKeyChar(
321 pherogramProvider.getBaseCall(i).getUpperedBase().charAt(0)));
322 }
323 provider.insertTokensAt(id, 0, tokens);
324
325 // Add data area:
326 PherogramArea pherogramArea = new PherogramArea(getReadsArea().getContentArea(), pherogramProvider);
327 pherogramArea.addMouseListener(new PherogramMouseListener(pherogramURI));
328 getReadsArea().getDataAreas().getSequenceAreas(id).add(pherogramArea);
329
330 // Save source URI:
331 uriMap.put(id, pherogramURI);
332 }
333 }