5bfc0de3f10740ef1214f24bd615abdd365d34e2
[taxeditor.git] / eclipseprojects / eu.etaxonomy.taxeditor / src / eu / etaxonomy / taxeditor / model / CdmSessionDataRepository.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.model;
11
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.HashMap;
15 import java.util.HashSet;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Set;
19 import java.util.SortedSet;
20 import java.util.UUID;
21
22 import org.apache.log4j.Logger;
23 import org.eclipse.core.databinding.observable.Realm;
24 import org.eclipse.core.databinding.observable.set.IObservableSet;
25 import org.eclipse.core.databinding.observable.set.ISetChangeListener;
26 import org.eclipse.core.databinding.observable.set.SetChangeEvent;
27 import org.eclipse.core.databinding.observable.set.WritableSet;
28 import org.eclipse.jface.databinding.swt.SWTObservables;
29 import org.eclipse.swt.widgets.Display;
30
31 import eu.etaxonomy.cdm.api.application.CdmApplicationController;
32 import eu.etaxonomy.cdm.api.service.ITermService;
33 import eu.etaxonomy.cdm.model.common.Language;
34 import eu.etaxonomy.cdm.model.common.OrderedTermVocabulary;
35 import eu.etaxonomy.cdm.model.common.TermVocabulary;
36 import eu.etaxonomy.cdm.model.description.Feature;
37 import eu.etaxonomy.cdm.model.name.NameRelationshipType;
38 import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
39 import eu.etaxonomy.cdm.model.name.Rank;
40 import eu.etaxonomy.cdm.model.reference.ReferenceBase;
41 import eu.etaxonomy.cdm.model.taxon.Taxon;
42 import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
43 import eu.etaxonomy.taxeditor.datasource.CdmTransactionController;
44 import eu.etaxonomy.taxeditor.editor.name.CdmParserController;
45 import eu.etaxonomy.taxeditor.navigation.QuickNameTaxon;
46 import eu.etaxonomy.taxeditor.navigation.RecentNamesView;
47
48 /**
49 * @author p.ciardelli
50 * @created 17.12.2008
51 * @version 1.0
52 */
53 public class CdmSessionDataRepository implements ICdmSessionDataRepository {
54 private static final Logger logger = Logger
55 .getLogger(CdmSessionDataRepository.class);
56
57 private static ICdmSessionDataRepository repository;
58
59 /**
60 * This map of taxa to their taxonomic children is used by
61 * NameTreeContentProvider's getChildren method.
62 *
63 * key = taxon, value = key's child taxa Note that a map can have a key ==
64 * null, i.e. no parent taxon
65 */
66 private HashMap<Taxon, Set<Taxon>> taxonomicChildrenMap;
67
68 private CdmApplicationController applicationController;
69
70 private IObservableSet observableTaxonSet;
71
72 private List<ICdmTaxonSetListener> listeners;
73
74 private TermVocabulary<Feature> features;
75
76 private SortedSet<Rank> ranks;
77
78 private TermVocabulary<NomenclaturalStatusType> nomStatus;
79
80 private SortedSet<NameRelationshipType> nameRelationshipTypes;
81
82 private OrderedTermVocabulary<TaxonRelationshipType> taxonRelationshipTypes;
83
84 private Set<TaxonRelationshipType> conceptRelationshipTypes;
85
86 public static ICdmSessionDataRepository getDefault() {
87
88 if (repository == null) {
89 repository = new CdmSessionDataRepository();
90 }
91
92
93 return repository;
94 }
95
96 private CdmSessionDataRepository() {
97 getObservableTaxa().addSetChangeListener(new observableTaxaListener());
98 }
99
100 @Override
101 public void clearAllData() {
102 clearTaxonData();
103 clearNonTaxonData();
104 }
105
106 @Override
107 public void clearTaxonData() {
108 removeAllTaxa();
109
110 // Clear "recent names" list
111 RecentNamesView.clearRecentNames();
112 }
113
114 @Override
115 public void clearNonTaxonData() {
116 features = null;
117 ranks = null;
118 nomStatus = null;
119 taxonRelationshipTypes = null;
120 nameRelationshipTypes = null;
121 conceptRelationshipTypes = null;
122
123 // Parser is also transaction-, and therefore session-, dependent
124 CdmParserController.clearNonViralNameParser();
125 }
126
127 @Override
128 public void setApplicationController(CdmApplicationController applicationController) {
129 this.applicationController = applicationController;
130 }
131
132 private Map<Taxon, Set<Taxon>> getTaxonomicChildrenMap() {
133 if (taxonomicChildrenMap == null) {
134 taxonomicChildrenMap = new HashMap<Taxon, Set<Taxon>>();
135 }
136 return taxonomicChildrenMap;
137 }
138
139 private CdmApplicationController getApplicationController() {
140 // Make sure the app controller has been set
141 if (applicationController == null) {
142 throw new IllegalStateException("CdmApplicationController not yet set.");
143 }
144 return applicationController;
145 }
146
147 @Override
148 public ReferenceBase getDefaultSec() {
149 // TODO why is this returning null? and of course, replace w the real deal
150 return getApplicationController().getReferenceService().getReferenceByUuid(
151 UUID.fromString("f3593c18-a8d2-4e51-bdad-0befbf8fb2d1"));
152 }
153
154 @Override
155 public Set<Taxon> getRootTaxa() {
156 return getTaxonomicChildren(null);
157 }
158
159 @Override
160 public Set<Taxon> getTaxonomicChildren(Taxon parentTaxon) {
161
162 // Check whether the map contains children for parentTaxon
163 if (!getTaxonomicChildrenMap().containsKey(parentTaxon)) {
164
165 // Make a new child taxa set to ensure deleted taxa
166 // don't re-appear as NULL children
167 Set<Taxon> childTaxa = new HashSet<Taxon>();
168
169 // Root taxa have parentTaxon = NULL
170 if (parentTaxon == null) {
171
172 // Get root taxa from data source
173 boolean onlyWithChildren = false;
174 boolean withMisapplications = true;
175 childTaxa.addAll(getApplicationController().getTaxonService().getRootTaxa(
176 getDefaultSec(), onlyWithChildren, withMisapplications));
177
178 } else {
179
180 // Get children from taxon
181 childTaxa.addAll(parentTaxon.getTaxonomicChildren());
182 }
183
184 // Iterate through all child taxa, throwing out NULLs and saving the rest to internal collections
185 Set<Taxon> nullTaxa = new HashSet<Taxon>();
186 for (Taxon taxon : childTaxa) {
187
188 // Some imports resulted in NULL child taxa
189 if (taxon == null) {
190 if (parentTaxon == null) {
191 logger.warn("NULL taxon in root taxa.");
192 } else {
193 logger.warn("Taxon " + parentTaxon.toString() + " has a NULL child taxon.");
194 }
195 nullTaxa.add(taxon);
196 } else {
197
198 // Add the taxon to internal collections
199 addTaxonIntern(taxon);
200 }
201 }
202 childTaxa.removeAll(nullTaxa);
203
204 // Put the new set in the parent-children map
205 getTaxonomicChildrenMap().put(parentTaxon, childTaxa);
206 }
207 return getTaxonomicChildrenMap().get(parentTaxon);
208 }
209
210 private void addTaxonIntern(Taxon taxon) {
211
212 // Add it to the transaction if it's a persistent taxon
213 if (!(taxon instanceof QuickNameTaxon)) {
214 CdmTransactionController.addTaxonToTransaction(taxon);
215 }
216
217 // Remove then re-add session taxon
218 getObservableTaxa().add(taxon);
219 }
220
221 @Override
222 public void addTaxon(Taxon taxon) {
223
224 // Add taxon to its parent's child map
225 Taxon parentTaxon = taxon.getTaxonomicParent();
226 getTaxonomicChildren(parentTaxon).add(taxon);
227
228 // Add taxon to internal collections
229 addTaxonIntern(taxon);
230 }
231
232 @Override
233 public IObservableSet getObservableTaxa() {
234 if (observableTaxonSet == null) {
235 Realm realm = SWTObservables.getRealm(Display.getDefault());
236 observableTaxonSet = new WritableSet(realm);
237 }
238 return observableTaxonSet;
239 }
240
241 @Override
242 public boolean deleteTaxon(Taxon taxon) {
243
244 // Remove taxon from repository collections
245 removeTaxon(taxon);
246
247 // If taxon has been saved before, delete it from the data source
248 if (isInPersistentDataSource(taxon)) {
249 getApplicationController().getTaxonService().removeTaxon(taxon);
250 }
251
252 // If parent taxon has been saved before, save it to reflect loss of child
253 Taxon parentTaxon = taxon.getTaxonomicParent();
254 if (parentTaxon != null && isInPersistentDataSource(parentTaxon)) {
255 getApplicationController().getTaxonService().saveTaxon(parentTaxon);
256 }
257
258 return true;
259 }
260
261 private boolean isInPersistentDataSource(Taxon taxon) {
262 UUID taxonUuid = taxon.getUuid();
263 if (getApplicationController().getTaxonService()
264 .getTaxonByUuid(taxonUuid) != null) {
265 return true;
266 } else {
267 return false;
268 }
269 }
270
271 @Override
272 public void removeTaxon(Taxon taxon) {
273
274 // Recursively remove all children, children's children, etc.
275 clearTaxonomicChildren(taxon);
276
277 // Remove from parent and from parent's child map
278 Taxon parentTaxon = taxon.getTaxonomicParent();
279 if (parentTaxon != null) {
280 removeTaxonomicChild(taxon, parentTaxon);
281 }
282
283 // Remove from session taxa
284 getObservableTaxa().remove(taxon);
285
286 // Remove from recent names list
287 RecentNamesView.removeRecentName(taxon);
288 }
289
290 private void removeTaxonomicChild(Taxon taxon, Taxon parentTaxon) {
291 parentTaxon.removeTaxonomicChild(taxon);
292 getTaxonomicChildren(parentTaxon).remove(taxon);
293 }
294
295 private void clearTaxonomicChildren(Taxon taxon) {
296 Set<Taxon> children = getTaxonomicChildren(taxon);
297 if (children != null) {
298 for (Taxon child : children) {
299 clearTaxonomicChildren(child);
300 }
301 getTaxonomicChildrenMap().remove(taxon);
302 getObservableTaxa().removeAll(children);
303 }
304 }
305
306 @Override
307 public Collection<Taxon> getAllTaxa() {
308 return getObservableTaxa();
309 }
310
311 @Override
312 public void removeAllTaxa() {
313 taxonomicChildrenMap.clear();
314 observableTaxonSet.clear();
315 }
316
317
318 @Override
319 public boolean saveTaxon(Taxon taxon) {
320
321 // If this is not in the list of observable taxa, add
322 if (!isInRepository(taxon)) {
323 addTaxon(taxon);
324 }
325
326 // Save taxon to CDM layer
327 CdmUtil.getTaxonService().saveTaxon(taxon);
328
329 // Notify taxon listeners in case name has been updated -
330 // updates editor view tab and recent names
331 taxon.firePropertyChange("name", null, null);
332
333 return true;
334 }
335
336 private boolean isInRepository(Taxon taxon) {
337 return observableTaxonSet.contains(taxon);
338 }
339
340 @Override
341 public void setTaxonomicParent(Taxon taxon, Taxon newParentTaxon) {
342
343 // Get old taxonomic parent
344 Taxon oldParentTaxon = taxon.getTaxonomicParent();
345
346 // Set new taxonomic parent
347 taxon.setTaxonomicParent(newParentTaxon, null, null);
348
349 // Update child taxa collection
350 getTaxonomicChildren(oldParentTaxon).remove(taxon);
351 getTaxonomicChildren(newParentTaxon).add(taxon);
352
353 // Notify listeners that taxon has moved
354 if (listeners != null) {
355 for (ICdmTaxonSetListener listener : listeners) {
356 listener.taxonMoved(taxon, newParentTaxon);
357 }
358 }
359 }
360
361 @Override
362 public TermVocabulary<Feature> getFeatures() {
363 if (features == null) {
364 features = getApplicationController().getDescriptionService().
365 getDefaultFeatureVocabulary();
366 }
367 return features;
368 }
369
370 @Override
371 public SortedSet<Rank> getRanks() {
372 if (ranks == null) {
373
374 OrderedTermVocabulary<Rank> rankVocabulary =
375 getApplicationController().getNameService().getRankVocabulary();
376 ranks = rankVocabulary.getOrderedTerms(null);
377 }
378 return ranks;
379 }
380
381 @Override
382 public TermVocabulary<NomenclaturalStatusType> getNomStatus() {
383 if (nomStatus == null) {
384 nomStatus = getApplicationController().getNameService()
385 .getStatusTypeVocabulary();
386 }
387 return nomStatus;
388 }
389
390 @Override
391 public SortedSet<NameRelationshipType> getNameRelationshipTypes() {
392 if (nameRelationshipTypes == null) {
393 TermVocabulary<NameRelationshipType> nameRelationshipTypesUnsorted =
394 getApplicationController().getNameService()
395 .getNameRelationshipTypeVocabulary();
396
397 // Add all terms manually to transaction
398 ITermService termService = getApplicationController().getTermService();
399 for (NameRelationshipType type : nameRelationshipTypesUnsorted) {
400 termService.saveTerm(type);
401 }
402
403 // Get sorted list of types
404 nameRelationshipTypes = nameRelationshipTypesUnsorted.
405 getTermsOrderedByLabels(Language.DEFAULT());
406 }
407 return nameRelationshipTypes;
408 }
409
410 @Override
411 public void addTaxonSetListener(ICdmTaxonSetListener listener) {
412
413 if (listeners == null) {
414 listeners = new ArrayList<ICdmTaxonSetListener>();
415 }
416 listeners.add(listener);
417 }
418
419 class observableTaxaListener implements ISetChangeListener {
420 @Override
421 public void handleSetChange(SetChangeEvent event) {
422 if (event.diff != null) {
423 Set<Taxon> additions = event.diff.getAdditions();
424 Set<Taxon> removals = event.diff.getRemovals();
425
426 if (listeners != null) {
427 for (ICdmTaxonSetListener listener : listeners) {
428
429 if (additions.size() > 0) {
430 listener.taxaAdded(additions);
431 }
432
433 if (removals.size() > 0) {
434 listener.taxaRemoved(removals);
435 }
436 }
437 }
438 }
439 }
440 }
441
442 @Override
443 public OrderedTermVocabulary<TaxonRelationshipType> getTaxonRelationshipTypes() {
444 if (taxonRelationshipTypes == null) {
445 taxonRelationshipTypes = getApplicationController().getTaxonService().
446 getTaxonRelationshipTypeVocabulary();
447 }
448 return taxonRelationshipTypes;
449 }
450
451 @Override
452 public Set<TaxonRelationshipType> getConceptRelationshipTypes() {
453 if (conceptRelationshipTypes == null) {
454 conceptRelationshipTypes = getTaxonRelationshipTypes().getTerms();
455 conceptRelationshipTypes.remove(TaxonRelationshipType.MISAPPLIED_NAME_FOR());
456 conceptRelationshipTypes.remove(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN());
457 }
458 return conceptRelationshipTypes;
459 }
460 }