Refactor character matrix
[taxeditor.git] / eu.etaxonomy.taxeditor.editor / src / main / java / eu / etaxonomy / taxeditor / editor / descriptiveDataSet / DescriptiveDataSetEditor.java
1 /**
2 * Copyright (C) 2017 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.editor.descriptiveDataSet;
10
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.Collections;
14 import java.util.HashSet;
15 import java.util.Iterator;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Set;
19 import java.util.UUID;
20
21 import javax.annotation.PostConstruct;
22 import javax.annotation.PreDestroy;
23 import javax.inject.Inject;
24 import javax.inject.Named;
25
26 import org.eclipse.core.runtime.IProgressMonitor;
27 import org.eclipse.e4.core.contexts.ContextInjectionFactory;
28 import org.eclipse.e4.core.contexts.IEclipseContext;
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.e4.ui.services.EMenuService;
33 import org.eclipse.e4.ui.services.IServiceConstants;
34 import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
35 import org.eclipse.jface.util.LocalSelectionTransfer;
36 import org.eclipse.jface.viewers.IStructuredSelection;
37 import org.eclipse.jface.viewers.Viewer;
38 import org.eclipse.swt.SWT;
39 import org.eclipse.swt.dnd.DND;
40 import org.eclipse.swt.dnd.Transfer;
41 import org.eclipse.swt.events.KeyAdapter;
42 import org.eclipse.swt.events.KeyEvent;
43 import org.eclipse.swt.events.ModifyEvent;
44 import org.eclipse.swt.events.ModifyListener;
45 import org.eclipse.swt.events.SelectionAdapter;
46 import org.eclipse.swt.widgets.Composite;
47 import org.eclipse.swt.widgets.Display;
48 import org.eclipse.swt.widgets.Shell;
49
50 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
51 import eu.etaxonomy.cdm.api.conversation.IConversationEnabled;
52 import eu.etaxonomy.cdm.api.service.IDescriptiveDataSetService;
53 import eu.etaxonomy.cdm.api.service.ITaxonNodeService;
54 import eu.etaxonomy.cdm.api.service.ITermService;
55 import eu.etaxonomy.cdm.model.common.DefinedTermBase;
56 import eu.etaxonomy.cdm.model.description.DescriptiveDataSet;
57 import eu.etaxonomy.cdm.model.description.FeatureTree;
58 import eu.etaxonomy.cdm.model.location.NamedArea;
59 import eu.etaxonomy.cdm.model.name.Rank;
60 import eu.etaxonomy.cdm.model.taxon.ITaxonTreeNode;
61 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
62 import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
63 import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
64 import eu.etaxonomy.taxeditor.editor.l10n.Messages;
65 import eu.etaxonomy.taxeditor.model.IDirtyMarkable;
66 import eu.etaxonomy.taxeditor.model.IPartContentHasDetails;
67 import eu.etaxonomy.taxeditor.model.MessagingUtils;
68 import eu.etaxonomy.taxeditor.session.ICdmEntitySession;
69 import eu.etaxonomy.taxeditor.session.ICdmEntitySessionEnabled;
70 import eu.etaxonomy.taxeditor.store.CdmStore;
71 import eu.etaxonomy.taxeditor.ui.dialog.selection.NamedAreaSelectionDialog;
72 import eu.etaxonomy.taxeditor.ui.element.CdmFormFactory;
73 import eu.etaxonomy.taxeditor.workbench.part.IE4SavablePart;
74
75 /**
76 * Editor for configuring DescriptiveDataSets
77 * @author pplitzner
78 * @since Nov 21, 2017
79 *
80 */
81 public class DescriptiveDataSetEditor implements IE4SavablePart, IConversationEnabled, ICdmEntitySessionEnabled,
82 IPartContentHasDetails, IDirtyMarkable {
83
84 private DescriptiveDataSetComposite composite;
85
86 private ConversationHolder conversation;
87
88 private ICdmEntitySession cdmEntitySession;
89
90 private DescriptiveDataSet descriptiveDataSet;
91
92 private final int dndOperations = DND.DROP_MOVE;
93
94 @Inject
95 private MDirtyable dirty;
96
97 @Inject
98 private ESelectionService selectionService;
99
100 private ModifyListener labelModifyListener;
101
102 @PostConstruct
103 public void create(Composite parent, IEclipseContext context, @Named(IServiceConstants.ACTIVE_SHELL)Shell shell,
104 EMenuService menuService){
105 if (CdmStore.isActive()){
106 if(conversation == null){
107 conversation = CdmStore.createConversation();
108 }
109 if(cdmEntitySession == null){
110 cdmEntitySession = CdmStore.getCurrentSessionManager().newSession(this, true);
111 }
112 }
113 else{
114 return;
115 }
116 CdmFormFactory cdmFormFactory = new CdmFormFactory(Display.getCurrent());
117 ContextInjectionFactory.inject(cdmFormFactory, context);
118
119 composite = new DescriptiveDataSetComposite(parent, SWT.NONE);
120
121 labelModifyListener = new ModifyListener() {
122 @Override
123 public void modifyText(ModifyEvent e) {
124 descriptiveDataSet.setLabel(composite.getTxt_label().getText());
125 dirty.setDirty(true);
126 }
127 };
128 composite.getRankMin().addSelectionChangedListener(event->dirty.setDirty(true));
129 composite.getRankMin().setText("Select min rank...");
130 composite.getRankMax().addSelectionChangedListener(event->dirty.setDirty(true));
131 composite.getRankMin().setText("Select max rank...");
132 composite.getBtnRemoveRankMin().addSelectionListener(new SelectionAdapter() {
133 @Override
134 public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {
135 composite.getRankMin().setElement(null);
136 dirty.setDirty(true);
137 }
138 });
139 composite.getBtnRemoveRankMax().addSelectionListener(new SelectionAdapter() {
140 @Override
141 public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {
142 composite.getRankMax().setElement(null);
143 dirty.setDirty(true);
144 }
145 });
146
147 composite.getBtnChooseArea().addSelectionListener(new SelectionAdapter() {
148
149 @Override
150 public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {
151 NamedArea area = NamedAreaSelectionDialog.select(shell, null, null);
152 if(area!=null){
153 composite.setArea(area);
154 }
155 dirty.setDirty(true);
156 }
157 });
158 composite.getBtnRemoveArea().addSelectionListener(new SelectionAdapter() {
159
160 @Override
161 public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {
162 composite.removeArea();
163 dirty.setDirty(true);
164 }
165 });
166
167 composite.getFeatureTreeEditorComposite().init(null, null, e->selectionService.setSelection(e.getSelection()), this, null);
168
169 //add drag'n'drop support
170 Transfer[] transfers = new Transfer[] {LocalSelectionTransfer.getTransfer()};
171 composite.getTaxonNodeTree().addDropSupport(dndOperations, transfers, new TaxonNodeDropAdapter(this));
172
173 composite.getTaxonNodeTree().getTree().addKeyListener(new KeyAdapter() {
174 @Override
175 public void keyPressed(KeyEvent e) {
176 if(e.character==SWT.DEL){
177 removeSelectedTaxonNodes();
178 }
179 }
180 });
181
182
183 //create context menu
184 menuService.registerContextMenu(composite.getTaxonNodeTree().getControl(), "eu.etaxonomy.taxeditor.editor.descriptiveDataSet.DescriptiveDataSetEditor.popupmenu.specimeneditor");
185
186 }
187
188 public void init(UUID descriptiveDataSetUuid) {
189 this.descriptiveDataSet = cdmEntitySession.remoteLoad(CdmStore.getService(IDescriptiveDataSetService.class), descriptiveDataSetUuid);
190 if(descriptiveDataSet.getLabel()!=null){
191 composite.getTxt_label().setText(descriptiveDataSet.getLabel());
192 }
193 if(descriptiveDataSet.getDescriptiveSystem()!=null){
194 composite.setCharacters(descriptiveDataSet.getDescriptiveSystem());
195 }
196 Rank maxRank = descriptiveDataSet.getMaxRank();
197 if(maxRank!=null){
198 composite.setRankMax(maxRank);
199 }
200 Rank minRank = descriptiveDataSet.getMinRank();
201 if(minRank!=null){
202 composite.setRankMin(minRank);
203 }
204 Set<NamedArea> geoFilter = descriptiveDataSet.getGeoFilter();
205 if(geoFilter!=null && !geoFilter.isEmpty()){
206 composite.setArea(geoFilter.iterator().next());
207 }
208 Set<TaxonNode> taxonSubtreeFilter = descriptiveDataSet.getTaxonSubtreeFilter();
209 if(taxonSubtreeFilter!=null){
210 composite.getTaxonNodeTree().setInput(taxonSubtreeFilter);
211 }
212
213 composite.getTxt_label().addModifyListener(labelModifyListener);
214 }
215
216 public void addTaxonNode(TaxonNodeDto taxonNodeDto){
217 TaxonNode taxonNode = cdmEntitySession.remoteLoad(CdmStore.getService(ITaxonNodeService.class), taxonNodeDto.getUuid());
218 //check if node belongs to same classification
219 Set<TaxonNode> taxonSubtreeFilter = descriptiveDataSet.getTaxonSubtreeFilter();
220 if(taxonSubtreeFilter!=null && !taxonSubtreeFilter.isEmpty()){
221 if(!taxonSubtreeFilter.iterator().next().getClassification().equals(taxonNode.getClassification())){
222 MessagingUtils.warningDialog(Messages.TaxonNodeDropAdapter_CLASSIFICATIONS_NO_MATCH, this.getClass(),
223 Messages.TaxonNodeDropAdapter_CLASSIFICATIONS_NO_MATCH_MESSAGE);
224 return;
225 }
226 }
227 Viewer taxonTreeViewer = getTaxonTreeViewer();
228 Object input = taxonTreeViewer.getInput();
229 Collection<TaxonNode> treeNodes;
230 if(input==null){
231 treeNodes = new ArrayList<>();
232 }
233 else{
234 treeNodes = (Collection<TaxonNode>) input;
235 }
236 treeNodes.add(taxonNode);
237 taxonTreeViewer.setInput(treeNodes);
238 dirty.setDirty(true);
239 }
240
241 public void removeSelectedTaxonNodes(){
242 IStructuredSelection selection = (IStructuredSelection) composite.getTaxonNodeTree().getSelection();
243 if(selection.toList().stream().anyMatch(object->!(object instanceof TaxonNode))){
244 MessagingUtils.warningDialog(Messages.DescriptiveDataSetEditor_DELETE_FAIL_TITLE, this.getClass(), Messages.DescriptiveDataSetEditor_DELETE_FAIL_MESSAGE);
245 return;
246 }
247 Iterator<Object> iterator = selection.iterator();
248 Viewer taxonTreeViewer = getTaxonTreeViewer();
249 Collection<TaxonNode> input = (Collection<TaxonNode>) taxonTreeViewer.getInput();
250 while(iterator.hasNext()){
251 Object next = iterator.next();
252 TaxonNode taxonNode = (TaxonNode) next;
253 input.remove(taxonNode);
254 descriptiveDataSet.removeTaxonSubtree(taxonNode);
255 }
256 taxonTreeViewer.setInput(input);
257 dirty.setDirty(true);
258 }
259
260 @Persist
261 @Override
262 public void save(IProgressMonitor monitor) {
263 NamedArea area = composite.getArea();
264 Set<NamedArea> areas = new HashSet<>();
265 if(area!=null){
266 areas.add(area);
267 }
268 Object input = composite.getTaxonNodeTree().getInput();
269 if(input!=null){
270 descriptiveDataSet.setTaxonSubtreeFilter(new HashSet<>());//clear existing filter
271 Collection<ITaxonTreeNode> taxonNodes = (Collection<ITaxonTreeNode>) input;
272 for (ITaxonTreeNode taxonTreeNode : taxonNodes) {
273 if(taxonTreeNode instanceof TaxonNode){
274 TaxonNode taxonNode = (TaxonNode)taxonTreeNode;
275 descriptiveDataSet.addTaxonSubtree(taxonNode);
276 }
277 }
278 }
279 FeatureTree characters = composite.getCharacters();
280 if(characters!=null){
281 //save characters because they can be modified in this editor
282 characters.getDistinctFeatures().forEach(character->CdmStore.getService(ITermService.class).merge(character,true));
283 }
284
285
286 DefinedTermBase rankMaxSelection = composite.getRankMax().getSelection();
287 Rank rankMax = null;
288 if(rankMaxSelection instanceof Rank){
289 rankMax = (Rank) rankMaxSelection;
290 }
291 DefinedTermBase rankMinSelection = composite.getRankMin().getSelection();
292 Rank rankMin = null;
293 if(rankMinSelection instanceof Rank){
294 rankMin = (Rank) rankMinSelection;
295 }
296
297 descriptiveDataSet.setMaxRank(rankMax);
298 descriptiveDataSet.setMinRank(rankMin);
299 descriptiveDataSet.setDescriptiveSystem(characters);
300 descriptiveDataSet.setGeoFilter(areas);
301
302 conversation.commit();
303 CdmStore.getService(IDescriptiveDataSetService.class).merge(descriptiveDataSet, true);
304
305 dirty.setDirty(false);
306 }
307
308 @PreDestroy
309 public void dispose() {
310 if (conversation != null) {
311 conversation.close();
312 conversation = null;
313 }
314 if(cdmEntitySession != null) {
315 cdmEntitySession.dispose();
316 cdmEntitySession = null;
317 }
318 dirty.setDirty(false);
319 }
320
321 @Focus
322 public void setFocus() {
323 if(composite!=null){
324 composite.setFocus();
325 }
326 if (getConversationHolder() != null) {
327 getConversationHolder().bind();
328 }
329 if(cdmEntitySession != null) {
330 cdmEntitySession.bind();
331 }
332 }
333
334 public Viewer getTaxonTreeViewer() {
335 return composite.getTaxonNodeTree();
336 }
337
338 public DescriptiveDataSet getDescriptiveDataSet() {
339 return descriptiveDataSet;
340 }
341
342 @Override
343 public void update(CdmDataChangeMap arg0) {
344 }
345
346 @Override
347 public ICdmEntitySession getCdmEntitySession() {
348 return cdmEntitySession;
349 }
350
351 @Override
352 public Collection<DescriptiveDataSet> getRootEntities() {
353 return Collections.singleton(descriptiveDataSet);
354 }
355
356 @Override
357 public Map<Object, List<String>> getPropertyPathsMap() {
358 return null;
359 }
360
361 @Override
362 public ConversationHolder getConversationHolder() {
363 return conversation;
364 }
365
366 @Override
367 public void changed(Object element) {
368 dirty.setDirty(true);
369 }
370
371 @Override
372 public void forceDirty() {
373 dirty.setDirty(true);
374 }
375
376 }