fix #8642 Add tree editor for rank, NamedArea and PresenceAbsence
[taxeditor.git] / eu.etaxonomy.taxeditor.store / src / main / java / eu / etaxonomy / taxeditor / featuretree / e4 / AbstractTermTreeEditor.java
1 /**
2 * Copyright (C) 2007 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
10 package eu.etaxonomy.taxeditor.featuretree.e4;
11
12 import java.util.Arrays;
13 import java.util.HashMap;
14 import java.util.List;
15 import java.util.Map;
16
17 import javax.annotation.PostConstruct;
18 import javax.annotation.PreDestroy;
19 import javax.inject.Inject;
20
21 import org.eclipse.core.runtime.IProgressMonitor;
22 import org.eclipse.e4.ui.di.Focus;
23 import org.eclipse.e4.ui.di.Persist;
24 import org.eclipse.e4.ui.di.UISynchronize;
25 import org.eclipse.e4.ui.model.application.ui.MDirtyable;
26 import org.eclipse.e4.ui.model.application.ui.basic.MPart;
27 import org.eclipse.e4.ui.services.EMenuService;
28 import org.eclipse.e4.ui.workbench.modeling.EPartService;
29 import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
30 import org.eclipse.jface.util.LocalSelectionTransfer;
31 import org.eclipse.jface.viewers.ISelection;
32 import org.eclipse.jface.viewers.ISelectionChangedListener;
33 import org.eclipse.jface.viewers.IStructuredSelection;
34 import org.eclipse.jface.viewers.SelectionChangedEvent;
35 import org.eclipse.jface.viewers.StructuredSelection;
36 import org.eclipse.jface.viewers.TreeViewer;
37 import org.eclipse.swt.SWT;
38 import org.eclipse.swt.dnd.DND;
39 import org.eclipse.swt.dnd.Transfer;
40 import org.eclipse.swt.events.KeyAdapter;
41 import org.eclipse.swt.events.KeyEvent;
42 import org.eclipse.swt.layout.FillLayout;
43 import org.eclipse.swt.widgets.Composite;
44 import org.eclipse.ui.IMemento;
45
46 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
47 import eu.etaxonomy.cdm.api.conversation.IConversationEnabled;
48 import eu.etaxonomy.cdm.api.service.ITermService;
49 import eu.etaxonomy.cdm.api.service.ITermTreeService;
50 import eu.etaxonomy.cdm.model.description.Character;
51 import eu.etaxonomy.cdm.model.term.TermNode;
52 import eu.etaxonomy.cdm.model.term.TermTree;
53 import eu.etaxonomy.cdm.model.term.TermType;
54 import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
55 import eu.etaxonomy.taxeditor.editor.definedterm.FeatureTreeViewerComparator;
56 import eu.etaxonomy.taxeditor.editor.definedterm.TermTransfer;
57 import eu.etaxonomy.taxeditor.featuretree.FeatureNodeTransfer;
58 import eu.etaxonomy.taxeditor.featuretree.FeatureTreeContentProvider;
59 import eu.etaxonomy.taxeditor.featuretree.FeatureTreeLabelProvider;
60 import eu.etaxonomy.taxeditor.featuretree.e4.operation.AddFeatureOperation;
61 import eu.etaxonomy.taxeditor.model.AbstractUtility;
62 import eu.etaxonomy.taxeditor.model.IContextListener;
63 import eu.etaxonomy.taxeditor.model.IDirtyMarkable;
64 import eu.etaxonomy.taxeditor.model.IPartContentHasDetails;
65 import eu.etaxonomy.taxeditor.model.IPartContentHasSupplementalData;
66 import eu.etaxonomy.taxeditor.session.ICdmEntitySession;
67 import eu.etaxonomy.taxeditor.store.AppModelId;
68 import eu.etaxonomy.taxeditor.store.CdmStore;
69 import eu.etaxonomy.taxeditor.store.StoreUtil;
70 import eu.etaxonomy.taxeditor.workbench.part.IE4ViewerPart;
71
72 /**
73 *
74 * @author pplitzner
75 * @date 06.06.2017
76 *
77 */
78 public abstract class AbstractTermTreeEditor implements IFeatureTreeEditor, ISelectionChangedListener,
79 IE4ViewerPart, IPartContentHasDetails, IPartContentHasSupplementalData,
80 IContextListener, IConversationEnabled, IDirtyMarkable {
81
82 protected static final List<String> TREE_PROPERTY_PATH = Arrays.asList(new String[] {
83 "root", //$NON-NLS-1$
84 "root.children", //$NON-NLS-1$
85 "root.children.inapplicableIf", //$NON-NLS-1$
86 "root.children.inapplicableIf.feature", //$NON-NLS-1$
87 "root.children.inapplicableIf.state", //$NON-NLS-1$
88 "root.children.onlyApplicableIf", //$NON-NLS-1$
89 "root.children.onlyApplicableIf.feature", //$NON-NLS-1$
90 "root.children.onlyApplicableIf.state", //$NON-NLS-1$
91 });
92
93 private ConversationHolder conversation;
94
95 private ICdmEntitySession cdmEntitySession;
96
97 @Inject
98 private ESelectionService selService;
99
100 @Inject
101 private MDirtyable dirty;
102
103 @Inject
104 private UISynchronize sync;
105
106 @Inject
107 private MPart thisPart;
108
109 private TreeViewer viewer;
110
111 @Inject
112 public AbstractTermTreeEditor() {
113 CdmStore.getContextManager().addContextListener(this);
114 }
115
116 @PostConstruct
117 public void createControl(Composite parent, EMenuService menuService){
118 if (CdmStore.isActive()){
119 initSession();
120 }
121 else{
122 return;
123 }
124 parent.setLayout(new FillLayout());
125 viewer = new TreeViewer(parent);
126 viewer.setContentProvider(new FeatureTreeContentProvider());
127 viewer.setLabelProvider(new FeatureTreeLabelProvider());
128
129 int ops = DND.DROP_COPY | DND.DROP_MOVE;
130 Transfer[] transfers = new Transfer[] {
131 FeatureNodeTransfer.getInstance(),
132 TermTransfer.getInstance(),
133 LocalSelectionTransfer.getTransfer()};
134 viewer.addDragSupport(ops, transfers, new FeatureNodeDragListener(viewer));
135 viewer.addDropSupport(ops, transfers, new FeatureTreeDropAdapter(this, viewer, sync));
136 viewer.addSelectionChangedListener(this);
137 viewer.getTree().addKeyListener(new KeyAdapter() {
138 @Override
139 public void keyPressed(KeyEvent e) {
140 if(e.stateMask == SWT.MOD1 && e.keyCode == 'c'){
141 copy(viewer.getStructuredSelection());
142 }
143 else if(e.stateMask == SWT.MOD1 && e.keyCode == 'v'){
144 paste(viewer.getStructuredSelection());
145 }
146 }
147 });
148
149 List<TermTree> trees = getTrees();
150 viewer.setComparator(new FeatureTreeViewerComparator());
151 viewer.setInput(trees);
152
153 //create context menu
154 menuService.registerContextMenu(viewer.getControl(), AppModelId.POPUPMENU_EU_ETAXONOMY_TAXEDITOR_STORE_POPUPMENU_FEATURETREEEDITOR);
155 }
156
157 protected abstract List<TermTree> getTrees();
158
159 public void paste(IStructuredSelection selection) {
160 if (StoreUtil.promptCheckIsDirty(this)) {
161 return;
162 }
163
164 ISelection clipBoardSelection = LocalSelectionTransfer.getTransfer().getSelection();
165 Object firstElement = selection.getFirstElement();
166 TermNode parentNode = null;
167 if(firstElement instanceof TermNode){
168 parentNode = (TermNode) firstElement;
169 }
170 else if(firstElement instanceof TermTree){
171 parentNode = ((TermTree)firstElement).getRoot();
172 }
173 if(parentNode!=null){
174 TermNode copiedNode = (TermNode) ((IStructuredSelection)clipBoardSelection).getFirstElement();
175
176 AddFeatureOperation operation = new AddFeatureOperation(copiedNode.getTerm().getUuid(), parentNode, this, this);
177 AbstractUtility.executeOperation(operation, sync);
178 }
179
180 }
181
182 public void copy(IStructuredSelection selection) {
183 LocalSelectionTransfer.getTransfer().setSelection(selection);
184 }
185
186 private void initSession(){
187 if(conversation == null){
188 conversation = CdmStore.createConversation();
189 }
190 if(cdmEntitySession==null){
191 cdmEntitySession = CdmStore.getCurrentSessionManager().newSession(this, true);
192 }
193 }
194
195 private void clearSession() {
196 if(conversation!=null){
197 conversation.close();
198 conversation = null;
199 }
200 if(cdmEntitySession != null) {
201 cdmEntitySession.dispose();
202 cdmEntitySession = null;
203 }
204 dirty.setDirty(false);
205 }
206
207 public void setDirty(boolean isDirty){
208 this.dirty.setDirty(isDirty);
209 }
210
211 @Override
212 public boolean isDirty(){
213 return dirty.isDirty();
214 }
215
216 @Override
217 public void selectionChanged(SelectionChangedEvent event) {
218 //propagate selection
219 selService.setSelection(event.getSelection());
220 }
221
222 @Focus
223 public void focus(){
224 if(viewer!=null){
225 viewer.getControl().setFocus();
226 }
227 if(conversation!=null && !conversation.isBound()){
228 conversation.bind();
229 }
230 if(cdmEntitySession != null) {
231 cdmEntitySession.bind();
232 }
233 }
234
235 @Override
236 public void refresh(){
237 viewer.refresh();
238 }
239
240 public TreeViewer getViewer(){
241 return viewer;
242 }
243
244 @Override
245 public IStructuredSelection getSelection() {
246 return (IStructuredSelection) viewer.getSelection();
247 }
248
249 @Override
250 public ConversationHolder getConversationHolder() {
251 return conversation;
252 }
253
254 @Override
255 @Persist
256 public void save(IProgressMonitor monitor){
257 if (!conversation.isBound()) {
258 conversation.bind();
259 }
260
261 // commit the conversation and start a new transaction immediately
262 conversation.commit(true);
263
264 CdmStore.getService(ITermTreeService.class).saveOrUpdate(getRootEntities());
265
266 List<TermTree> rootEntities = getRootEntities();
267 for (TermTree featureTree : rootEntities) {
268 if(featureTree.getTermType().equals(TermType.Character)){
269 TermTree<Character> characterTree = featureTree;
270 //save characters because they can be modified in this editor
271 characterTree.getDistinctTerms().forEach(character->CdmStore.getService(ITermService.class).merge(character,true));
272 }
273 }
274
275 initializeTrees();
276
277 this.setDirty(false);
278 }
279
280 private void initializeTrees() {
281 Object[] expandedElements = viewer.getExpandedElements();
282 viewer.getTree().removeAll();
283 List<TermTree> trees = getTrees();
284 viewer.setInput(trees);
285 viewer.setExpandedElements(expandedElements);
286 }
287
288 @PreDestroy
289 public void dispose(){
290 selService.setSelection(null);
291 clearSession();
292 }
293
294 @Override
295 public ICdmEntitySession getCdmEntitySession() {
296 return cdmEntitySession;
297 }
298
299 @Override
300 public Map<Object, List<String>> getPropertyPathsMap() {
301 List<String> propertyPaths = Arrays.asList(new String[] {
302 "children", //$NON-NLS-1$
303 "feature", //$NON-NLS-1$
304 "featureTree", //$NON-NLS-1$
305 });
306 Map<Object, List<String>> propertyPathMap =
307 new HashMap<Object, List<String>>();
308 propertyPathMap.put(TermNode.class,propertyPaths);
309 return propertyPathMap;
310 }
311
312 @Override
313 public List<TermTree> getRootEntities() {
314 return (List<TermTree>) viewer.getInput();
315 }
316
317 @Override
318 public void contextAboutToStop(IMemento memento, IProgressMonitor monitor) {
319 }
320
321 @Override
322 public void contextStop(IMemento memento, IProgressMonitor monitor) {
323 //close view when workbench closes
324 try{
325 thisPart.getContext().get(EPartService.class).hidePart(thisPart);
326 }
327 catch(Exception e){
328 //nothing
329 }
330 }
331
332 @Override
333 public void contextStart(IMemento memento, IProgressMonitor monitor) {
334 }
335
336 @Override
337 public void contextRefresh(IProgressMonitor monitor) {
338 }
339
340 @Override
341 public void workbenchShutdown(IMemento memento, IProgressMonitor monitor) {
342 }
343
344 @Override
345 public void update(CdmDataChangeMap arg0) {
346 }
347
348 @Override
349 public void changed(Object element) {
350 dirty.setDirty(true);
351 viewer.refresh();
352 }
353
354 @Override
355 public void forceDirty() {
356 dirty.setDirty(true);
357 }
358
359 @Override
360 public boolean postOperation(Object objectAffectedByOperation) {
361 initializeTrees();
362 viewer.refresh();
363 if(objectAffectedByOperation instanceof TermNode){
364 TermNode node = (TermNode)objectAffectedByOperation;
365 viewer.expandToLevel(node.getGraph(), 1);
366 }
367 if(objectAffectedByOperation!=null){
368 StructuredSelection selection = new StructuredSelection(objectAffectedByOperation);
369 viewer.setSelection(selection);
370 }
371 return true;
372 }
373
374 @Override
375 public boolean onComplete() {
376 return false;
377 }
378
379 }