Version 1.01
[taxeditor.git] / eclipseprojects / eu.etaxonomy.taxeditor / src / eu / etaxonomy / taxeditor / model / CdmUtil.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.HashSet;
13 import java.util.List;
14 import java.util.Set;
15 import java.util.SortedSet;
16
17 import org.apache.log4j.Logger;
18 import org.eclipse.jface.dialogs.MessageDialog;
19 import org.springframework.transaction.TransactionStatus;
20
21 import eu.etaxonomy.cdm.api.service.IDescriptionService;
22 import eu.etaxonomy.cdm.api.service.INameService;
23 import eu.etaxonomy.cdm.api.service.IReferenceService;
24 import eu.etaxonomy.cdm.api.service.ITaxonService;
25 import eu.etaxonomy.cdm.model.common.OrderedTermVocabulary;
26 import eu.etaxonomy.cdm.model.common.TermVocabulary;
27 import eu.etaxonomy.cdm.model.common.TimePeriod;
28 import eu.etaxonomy.cdm.model.description.Feature;
29 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
30 import eu.etaxonomy.cdm.model.name.NameRelationship;
31 import eu.etaxonomy.cdm.model.name.NameRelationshipType;
32 import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
33 import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
34 import eu.etaxonomy.cdm.model.name.NonViralName;
35 import eu.etaxonomy.cdm.model.name.Rank;
36 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
37 import eu.etaxonomy.cdm.model.reference.ReferenceBase;
38 import eu.etaxonomy.cdm.model.taxon.Synonym;
39 import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
40 import eu.etaxonomy.cdm.model.taxon.Taxon;
41 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
42 import eu.etaxonomy.cdm.strategy.parser.INonViralNameParser;
43 import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
44 import eu.etaxonomy.taxeditor.TaxEditorPlugin;
45 import eu.etaxonomy.taxeditor.UiUtil;
46 import eu.etaxonomy.taxeditor.preference.PreferencesUtil;
47
48 /**
49 * @author p.ciardelli
50 * @created 17.09.2008
51 * @version 1.0
52 */
53 public class CdmUtil {
54 private static final Logger logger = Logger.getLogger(CdmUtil.class);
55
56 private static INonViralNameParser nonViralNameParser;
57 private static TermVocabulary<Feature> features;
58 private static SortedSet<Rank> ranks;
59 private static TermVocabulary<NomenclaturalStatusType> statii;
60
61 /**
62 * Clears all objects retrieved from <code>cdmlib</code>
63 * to force them to re-initialize the next time they
64 * are called.
65 */
66 public static void clearLibraryObjects() {
67 features = null;
68 ranks = null;
69 nonViralNameParser = null;
70
71 TaxEditorPlugin.getDefault().setNomStatusVocabulary(null);
72 }
73
74 /**
75 * Checks whether synonym's name is the basionym for ALL names
76 * in its group.
77 *
78 * @param synonym
79 *
80 * @return
81 */
82 public static boolean isSynonymGroupBasionym(Synonym synonym) {
83
84 TaxonNameBase synonymName = synonym.getName();
85 return isNameGroupBasionym(synonymName);
86 }
87
88 /**
89 * Checks whether name is the basionym for ALL names
90 * in its group.
91 * @param name
92 *
93 * @return
94 */
95 public static boolean isNameGroupBasionym(TaxonNameBase name) {
96 if (name == null) {
97 return false;
98 }
99
100 HomotypicalGroup homotypicalGroup = name.getHomotypicalGroup();
101 if (homotypicalGroup == null) {
102 return false;
103 }
104
105 Set<TaxonNameBase> typifiedNames = homotypicalGroup.getTypifiedNames();
106
107 // Check whether there are any other names in the group
108 if (typifiedNames.size() == 1) {
109 return false;
110 }
111
112 boolean isBasionymToAll = true;
113
114 for (TaxonNameBase taxonName : typifiedNames) {
115 if (!taxonName.equals(name)) {
116 if (!isNameBasionymOf(name, taxonName)) {
117 return false;
118 }
119 }
120 }
121 return true;
122 }
123
124 /**
125 * Checks whether a basionym relationship exists between fromName and toName.
126 *
127 * @param fromName
128 * @param toName
129 * @return
130 */
131 public static boolean isNameBasionymOf(TaxonNameBase fromName, TaxonNameBase toName) {
132 Set<NameRelationship> relations = toName.getRelationsToThisName();
133 for (NameRelationship relation : relations) {
134 if (relation.getType().equals(NameRelationshipType.BASIONYM()) &&
135 relation.getFromName().equals(fromName)) {
136 return true;
137 }
138 }
139 return false;
140 }
141
142 /**
143 * Creates a basionym relationship between basionymName and
144 * each name in its homotypic group.
145 *
146 * @param basionymName
147 */
148 public static void setGroupBasionym(TaxonNameBase basionymName) {
149 HomotypicalGroup homotypicalGroup = basionymName.getHomotypicalGroup();
150 if (homotypicalGroup == null) {
151 return;
152 }
153 for (TaxonNameBase name : homotypicalGroup.getTypifiedNames()) {
154 if (!name.equals(basionymName)) {
155
156 // First check whether the relationship already exists
157 if (!isNameBasionymOf(basionymName, name)) {
158
159 // Then create it
160 name.addRelationshipFromName(basionymName,
161 NameRelationshipType.BASIONYM(), null);
162 }
163 }
164 }
165 }
166
167 /**
168 * Removes all basionym relationships between basionymName and
169 * the names in its homotypic group.
170 *
171 * @param basionymName
172 */
173 public static void removeGroupBasionym(TaxonNameBase basionymName) {
174 HomotypicalGroup homotypicalGroup = basionymName.getHomotypicalGroup();
175 Set<NameRelationship> relations = basionymName.getRelationsFromThisName();
176 Set<NameRelationship> removeRelations = new HashSet<NameRelationship>();
177
178 for (NameRelationship relation : relations) {
179
180 // If this is a basionym relation, and toName is in the homotypical group,
181 // remove the relationship.
182 if (relation.getType().equals(NameRelationshipType.BASIONYM()) &&
183 relation.getToName().getHomotypicalGroup().equals(homotypicalGroup)) {
184 removeRelations.add(relation);
185 }
186 }
187
188 // Removing relations from a set through which we are iterating causes a
189 // ConcurrentModificationException. Therefore, we delete the targeted
190 // relations in a second step.
191 for (NameRelationship relation : removeRelations) {
192 basionymName.removeNameRelationship(relation);
193 }
194 }
195
196 /**
197 * Checks whether name belongs to the same homotypic group as taxon's name.
198 *
199 * @param name
200 * @param taxon
201 * @return
202 */
203 public static boolean isNameHomotypic(TaxonNameBase name, Taxon taxon) {
204 TaxonNameBase taxonName = taxon.getName();
205 if (taxonName == null || name == null) {
206 return false;
207 }
208 HomotypicalGroup homotypicGroup = taxonName.getHomotypicalGroup();
209 if (homotypicGroup == null) {
210 return false;
211 }
212 if (homotypicGroup.equals(name.getHomotypicalGroup())) {
213 return true;
214 }
215 return false;
216 }
217
218 /**
219 * Returns whatever is currently considered the display name for Taxon
220 * objects. Currently titleCache.
221 *
222 * @param taxon
223 * @return
224 */
225 public static String getDisplayName(TaxonBase taxonBase) {
226 TaxonNameBase name = taxonBase.getName();
227 return getDisplayName(name);
228 }
229
230 /**
231 * @param name
232 * @return
233 */
234 public static String getDisplayName(TaxonNameBase name) {
235 if (name != null) {
236 return name.getTitleCache();
237 }
238 return "";
239 }
240
241 public static String getDisplayNameAndRef(TaxonBase taxonBase) {
242 TaxonNameBase name = taxonBase.getName();
243 if (name != null) {
244 return name.getFullTitleCache();
245 }
246 return "";
247 }
248
249 /**
250 * @return
251 */
252 private static INonViralNameParser getNonViralNameParser() {
253 if (nonViralNameParser == null) {
254 nonViralNameParser = NonViralNameParserImpl.NewInstance();
255 }
256 return nonViralNameParser;
257 }
258
259 /**
260 * Takes a raw string, returns a brand spanking new name object.
261 * <p>
262 * If no nomenclatural code is specified, the code specified in user preferences
263 * is used, i.e. if the preferred code is ICBN, a <code>BotanicalName</code> is returned;
264 * if it's ICZN, a <code>ZoologicalName</code> is returned.
265 * </p>
266 *
267 * @param fullReference
268 * @param nomCode
269 * @param rank
270 * @return
271 */
272 public static TaxonNameBase parseFullReference(String fullReference,
273 NomenclaturalCode nomCode, Rank rank) {
274
275 if (nomCode == null) {
276 nomCode = PreferencesUtil.getPreferredNomenclaturalCode();
277 }
278
279 TaxonNameBase name = getNonViralNameParser().parseReferencedName(fullReference,
280 nomCode, rank);
281
282 if (name.hasProblem()) {
283 name.setFullTitleCache(fullReference);
284 }
285
286 return name;
287 }
288
289 /**
290 * Takes an existing name object and a string, returns the same name
291 * object with the fields parsed from the string.
292 *
293 * @param nameToBeFilled
294 * @param fullReference
295 * @param rank
296 * @param makeEmpty
297 */
298 public static void parseFullReference(NonViralName nameToBeFilled, String fullReference) {
299
300 Rank rank = null;
301 boolean makeEmpty = true;
302
303 // If the name already has a rank, make sure it is passed to the parser
304 if (nameToBeFilled.getRank() != null) {
305 rank = nameToBeFilled.getRank();
306 }
307
308 // Call get status to get status list into the transaction
309 // for (NomenclaturalStatusType status : nameToBeFilled.getStatus()) {
310 //
311 // }
312 // TaxEditorPlugin.getDefault().getCdmApp().getTermService().saveTerm(termBase)
313 // NomenclaturalStatusType term = NomenclaturalStatusType.INVALID();
314 //// .getRepresentation(Language.DEFAULT());
315 // TaxEditorPlugin.getDefaulty().getCdmApp().getTermService().saveTerm(term);
316 // logger.warn(term.getRepresentation(Language.DEFAULT()));
317
318 getNonViralNameParser().parseReferencedName(nameToBeFilled, fullReference, rank, makeEmpty);
319
320 if (nameToBeFilled.hasProblem()) {
321 nameToBeFilled.setFullTitleCache(fullReference);
322 }
323 }
324
325 /**
326 * @param nameToBeFilled
327 * @param fullNameString
328 * @param rank
329 * @param makeEmpty
330 */
331 public static void parseFullName(NonViralName nameToBeFilled,
332 String fullNameString, Rank rank, boolean makeEmpty) {
333 getNonViralNameParser().parseFullName(nameToBeFilled, fullNameString,
334 rank, makeEmpty);
335 }
336
337
338
339 /**
340 * @return
341 */
342 public static ReferenceBase getSessionDefaultSec() {
343 // return null;
344 return TaxEditorPlugin.getDefault().getSessionDefaultSec();
345 }
346
347 /**
348 * @param oldTaxon
349 * @param newAcceptedTaxon
350 * @param synonymType
351 * @param citation
352 * @param citationMicroReference
353 */
354 public static void makeTaxonSynonym(Taxon oldTaxon, Taxon newAcceptedTaxon,
355 SynonymRelationshipType synonymType, ReferenceBase citation,
356 String citationMicroReference) {
357 ITaxonService taxonService = getTaxonService();
358 taxonService.makeTaxonSynonym(oldTaxon, newAcceptedTaxon, synonymType,
359 citation, citationMicroReference);
360 }
361
362 /**
363 * @param searchText
364 * @return
365 */
366 public static Set<TaxonNameBase> getNameByName(String searchText) {
367 Set<TaxonNameBase> resultsSet = new HashSet<TaxonNameBase>();
368 resultsSet.addAll(getNameService()
369 .getNamesByName(searchText.replace("*", "%")));
370 return resultsSet;
371 }
372
373 public static INameService getNameService() {
374 return TaxEditorPlugin.getDefault().getNameService();
375 }
376
377 public static ITaxonService getTaxonService() {
378 return TaxEditorPlugin.getDefault().getTaxonService();
379 }
380
381 public static IDescriptionService getDescriptionService() {
382 return TaxEditorPlugin.getDefault().getDescriptionService();
383 }
384
385 public static IReferenceService getReferenceService() {
386 return TaxEditorPlugin.getDefault().getReferenceService();
387 }
388
389 public static TermVocabulary<Feature> getFeatures() {
390 if (features == null) {
391 features = CdmUtil.getDescriptionService().
392 getDefaultFeatureVocabulary();
393 }
394 return features;
395 }
396
397 public static SortedSet<Rank> getRanks() {
398 if (ranks == null) {
399 // TransactionStatus tx = startReadOnlyTransaction();
400 OrderedTermVocabulary<Rank> rankVocabulary = TaxEditorPlugin.getDefault().getRankVocabulary();
401 ranks = rankVocabulary.getOrderedTerms(null);
402 // commitTransaction(tx);
403 }
404 return ranks;
405 }
406
407 /**
408 * @param searchText
409 * @param taxon
410 * @return
411 */
412 public static Set<TaxonNameBase> getNameByNameForTaxonContext(String searchText, Taxon taxon) {
413 Set<TaxonNameBase> resultsSet = new HashSet<TaxonNameBase>();
414 resultsSet.addAll(getNameService().getNamesByName
415 (searchText.replace("*", "%"), taxon));
416 return resultsSet;
417 }
418
419 public static TransactionStatus startTransaction() {
420 return TaxEditorPlugin.getDefault().getCdmApp().startTransaction();
421 }
422
423 public static TransactionStatus startReadOnlyTransaction() {
424 return TaxEditorPlugin.getDefault().getCdmApp().startTransaction(true);
425 }
426
427 public static void commitTransaction(TransactionStatus tx) {
428 TaxEditorPlugin.getDefault().getCdmApp().commitTransaction(tx);
429 }
430
431 public static void getNomenclaturalReference(TaxonNameBase name) {
432 // INomenclaturalReference nomenclaturalReference = (INomenclaturalReference) name.getNomenclaturalReference();
433 // if (nomenclaturalReference == null) {
434 // return "";
435 // }
436 // String microReference = name.getNomenclaturalMicroReference();
437 // return CdmUtils.Nz(nomenclaturalReference.getNomenclaturalCitation(microReference));
438 }
439
440 /**
441 * Converts a <code>String</code> whose format is either a valid year
442 * or two valid years with the format "XXXX-XXXX" into a TimePeriod.
443 *
444 * @see #getValidYear(String yearStr)
445 * @param refYear
446 * @return
447 * @throws NumberFormatException
448 */
449 public static TimePeriod convertTimePeriod(String refYear) throws NumberFormatException {
450
451 if (refYear == null || ("").equals(refYear)){
452 return null;
453 }
454
455 TimePeriod datePublished = TimePeriod.NewInstance();
456
457 // In case format is "xxxx-xxxx"
458 String[] years = refYear.split("-");
459
460 // Unlikely case of "xxxx-xxxx-xxxx..."
461 if (years.length > 2) {
462 throw new NumberFormatException();
463 }
464
465 // Set startYear
466 datePublished.setStartYear(getValidYear(years[0]));
467
468 // Format is "xxxx-xxxx"
469 if (years.length == 2) {
470 datePublished.setEndYear(getValidYear(years[1]));
471 }
472
473 return datePublished;
474 }
475
476
477 /**
478 * Checks whether a <code>String</code> is a valid year between
479 * 1750 and 2030. Throws a <code>NumberFormatException</code> if not.
480 *
481 * @param yearStr
482 * @return
483 * @throws NumberFormatException
484 */
485 public static Integer getValidYear(String yearStr) throws NumberFormatException {
486
487 Integer yearInt = null;
488
489 // Try casting string - don't catch number format exception
490 try {
491 yearInt = new Integer(yearStr);
492 } catch (ClassCastException e) {
493 throw new NumberFormatException();
494 }
495
496 // Is year in valid range?
497 if (yearInt < 1750 || yearInt > 2030) {
498 throw new NumberFormatException();
499 }
500
501 return yearInt;
502 }
503
504 /**
505 * Searches for references by string. "%" is used as a wildcard.
506 *
507 * @param text
508 * @return
509 */
510 public static List getReferencesByTitle(String reference) {
511
512 reference = reference.replace("*", "%");
513 List resultsList = null;
514 try {
515 resultsList = getReferenceService().getReferencesByTitle(reference);
516 } catch (RuntimeException e) {
517 MessageDialog.openError(UiUtil.getShell(), "Search reference error",
518 "Reference search returned an error. This could be a Hibernate concurrency problem. " +
519 "Please try saving your work, then searching again.");
520 e.printStackTrace();
521 }
522 return resultsList;
523 }
524
525 /**
526 * Returns true if <code>taxon</code> belongs to the children, grandchildren,
527 * etc. of <code>checkTaxon</code>.
528 *
529 * @param taxon
530 * @param checkTaxon
531 * @return
532 */
533 public static boolean isTaxonChildOfTaxon(Taxon taxon, Taxon checkTaxon) {
534
535 // Iterate through all checkTaxon's children
536 for (Taxon childTaxon : checkTaxon.getTaxonomicChildren()) {
537
538 if (childTaxon.equals(taxon)) {
539 return true;
540 } else {
541
542 // Compare taxon with childTaxon's children
543 if (isTaxonChildOfTaxon(taxon, childTaxon)) {
544 return true;
545 }
546 }
547 }
548 return false;
549 }
550 }