cleanup
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / NameServiceImpl.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.cdm.api.service;
11
12 import java.io.IOException;
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.Collection;
16 import java.util.HashMap;
17 import java.util.HashSet;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Optional;
21 import java.util.Set;
22 import java.util.UUID;
23 import java.util.stream.Collectors;
24
25 import org.apache.log4j.Logger;
26 import org.apache.lucene.index.Term;
27 import org.apache.lucene.sandbox.queries.FuzzyLikeThisQuery;
28 import org.apache.lucene.search.BooleanClause.Occur;
29 import org.apache.lucene.search.BooleanQuery;
30 import org.apache.lucene.search.BooleanQuery.Builder;
31 import org.apache.lucene.search.TopDocs;
32 import org.apache.lucene.search.WildcardQuery;
33 import org.hibernate.criterion.Criterion;
34 import org.springframework.beans.factory.annotation.Autowired;
35 import org.springframework.beans.factory.annotation.Qualifier;
36 import org.springframework.stereotype.Service;
37 import org.springframework.transaction.annotation.Transactional;
38
39 import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase;
40 import eu.etaxonomy.cdm.api.service.config.NameDeletionConfigurator;
41 import eu.etaxonomy.cdm.api.service.dto.TypeDesignationStatusFilter;
42 import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
43 import eu.etaxonomy.cdm.api.service.pager.Pager;
44 import eu.etaxonomy.cdm.api.service.pager.impl.AbstractPagerImpl;
45 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
46 import eu.etaxonomy.cdm.api.service.search.DocumentSearchResult;
47 import eu.etaxonomy.cdm.api.service.search.ILuceneIndexToolProvider;
48 import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;
49 import eu.etaxonomy.cdm.api.service.search.LuceneParseException;
50 import eu.etaxonomy.cdm.api.service.search.LuceneSearch;
51 import eu.etaxonomy.cdm.api.service.search.QueryFactory;
52 import eu.etaxonomy.cdm.api.service.search.SearchResult;
53 import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;
54 import eu.etaxonomy.cdm.api.util.TaxonNamePartsFilter;
55 import eu.etaxonomy.cdm.common.URI;
56 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
57 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
58 import eu.etaxonomy.cdm.model.CdmBaseType;
59 import eu.etaxonomy.cdm.model.agent.Person;
60 import eu.etaxonomy.cdm.model.agent.Team;
61 import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
62 import eu.etaxonomy.cdm.model.common.CdmBase;
63 import eu.etaxonomy.cdm.model.common.Language;
64 import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
65 import eu.etaxonomy.cdm.model.common.SourcedEntityBase;
66 import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
67 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
68 import eu.etaxonomy.cdm.model.name.HybridRelationship;
69 import eu.etaxonomy.cdm.model.name.HybridRelationshipType;
70 import eu.etaxonomy.cdm.model.name.INonViralName;
71 import eu.etaxonomy.cdm.model.name.NameRelationship;
72 import eu.etaxonomy.cdm.model.name.NameRelationshipType;
73 import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
74 import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
75 import eu.etaxonomy.cdm.model.name.NomenclaturalSource;
76 import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
77 import eu.etaxonomy.cdm.model.name.Rank;
78 import eu.etaxonomy.cdm.model.name.Registration;
79 import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
80 import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
81 import eu.etaxonomy.cdm.model.name.TaxonName;
82 import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
83 import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
84 import eu.etaxonomy.cdm.model.name.TypeDesignationStatusBase;
85 import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;
86 import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
87 import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
88 import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
89 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
90 import eu.etaxonomy.cdm.model.reference.Reference;
91 import eu.etaxonomy.cdm.model.taxon.Taxon;
92 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
93 import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;
94 import eu.etaxonomy.cdm.persistence.dao.common.ISourcedEntityDao;
95 import eu.etaxonomy.cdm.persistence.dao.common.Restriction;
96 import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;
97 import eu.etaxonomy.cdm.persistence.dao.name.IHomotypicalGroupDao;
98 import eu.etaxonomy.cdm.persistence.dao.name.INomenclaturalStatusDao;
99 import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;
100 import eu.etaxonomy.cdm.persistence.dao.name.ITypeDesignationDao;
101 import eu.etaxonomy.cdm.persistence.dao.reference.IOriginalSourceDao;
102 import eu.etaxonomy.cdm.persistence.dto.TaxonNameParts;
103 import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
104 import eu.etaxonomy.cdm.persistence.query.MatchMode;
105 import eu.etaxonomy.cdm.persistence.query.OrderHint;
106 import eu.etaxonomy.cdm.strategy.cache.TaggedText;
107 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
108 import eu.etaxonomy.cdm.strategy.match.IMatchStrategy;
109 import eu.etaxonomy.cdm.strategy.match.IMatchable;
110 import eu.etaxonomy.cdm.strategy.match.IParsedMatchStrategy;
111 import eu.etaxonomy.cdm.strategy.match.MatchException;
112 import eu.etaxonomy.cdm.strategy.match.MatchStrategyFactory;
113 import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
114
115
116 @Service
117 @Transactional(readOnly = true)
118 public class NameServiceImpl
119 extends IdentifiableServiceBase<TaxonName,ITaxonNameDao>
120 implements INameService {
121
122 static private final Logger logger = Logger.getLogger(NameServiceImpl.class);
123
124 @Autowired
125 private IOccurrenceService occurrenceService;
126 @Autowired
127 private ICollectionService collectionService;
128 @Autowired
129 private ITaxonService taxonService;
130 @Autowired
131 private ICommonService commonService;
132 @Autowired
133 @Qualifier("sourcedEntityDao")
134 private ISourcedEntityDao<SourcedEntityBase<?>> sourcedEntityDao;
135 @Autowired
136 private INomenclaturalStatusDao nomStatusDao;
137 @Autowired
138 private ITypeDesignationDao typeDesignationDao;
139 @Autowired
140 private IHomotypicalGroupDao homotypicalGroupDao;
141 @Autowired
142 private ICdmGenericDao genericDao;
143 @Autowired
144 private ILuceneIndexToolProvider luceneIndexToolProvider;
145 @Autowired
146 private IOriginalSourceDao sourcedDao;
147
148 @Autowired
149 // @Qualifier("defaultBeanInitializer")
150 protected IBeanInitializer defaultBeanInitializer;
151
152 //***************************** CONSTRUCTOR **********************************/
153
154 /**
155 * Constructor
156 */
157 public NameServiceImpl(){}
158
159 //********************* METHODS ***********************************************//
160
161 @Override
162 @Transactional(readOnly = false)
163 public DeleteResult delete(UUID nameUUID){
164 NameDeletionConfigurator config = new NameDeletionConfigurator();
165 DeleteResult result = delete(nameUUID, config);
166 return result;
167 }
168
169 @Override
170 public DeleteResult delete(TaxonName name){
171 return delete(name.getUuid());
172 }
173
174 @Override
175 @Transactional(readOnly = false)
176 public DeleteResult delete(TaxonName name, NameDeletionConfigurator config) {
177 DeleteResult result = new DeleteResult();
178
179 if (name == null){
180 result.setAbort();
181 return result;
182 }
183
184 try{
185 result = this.isDeletable(name, config, null);
186 }catch(Exception e){
187 result.addException(e);
188 result.setError();
189 return result;
190 }
191 if (result.isOk()){
192 //remove references to this name
193 removeNameRelationshipsByDeleteConfig(name, config);
194
195 //remove name from homotypical group
196 HomotypicalGroup homotypicalGroup = name.getHomotypicalGroup();
197 if (homotypicalGroup != null){
198 homotypicalGroup.removeTypifiedName(name, false);
199 }
200
201 //all type designation relationships are removed as they belong to the name
202 deleteTypeDesignation(name, null);
203 //if original spellings should be deleted, remove it from the nomenclatural source
204 Set<TaxonName> namesToUpdate = new HashSet<>();
205 for (Object o: result.getRelatedObjects()){
206 if (o instanceof NomenclaturalSource && ((NomenclaturalSource)o).getNameUsedInSource() != null && ((NomenclaturalSource)o).getNameUsedInSource().equals(name)){
207 NomenclaturalSource nomSource = (NomenclaturalSource)o;
208 nomSource.setNameUsedInSource(null);
209 namesToUpdate.add(nomSource.getSourcedName());
210 }
211 }
212
213 try{
214 if (!namesToUpdate.isEmpty()){
215 Map<UUID, TaxonName> updatedNames = dao.saveOrUpdateAll(namesToUpdate);
216 Set<TaxonName> names = new HashSet<>(updatedNames.values());
217 result.addUpdatedObjects(names);
218 }
219 dao.delete(name);
220 result.addDeletedObject(name);
221
222 }catch(Exception e){
223 result.addException(e);
224 result.setError();
225 }
226 return result;
227 }
228
229 return result;
230 }
231
232 @Override
233 @Transactional(readOnly = false)
234 public DeleteResult delete(UUID nameUUID, NameDeletionConfigurator config) {
235
236 TaxonName name = dao.load(nameUUID);
237 return delete(name, config);
238 }
239
240 @Override
241 @Transactional(readOnly = false)
242 public UpdateResult cloneTypeDesignation(UUID nameUuid, SpecimenTypeDesignation baseDesignation,
243 String accessionNumber, String barcode, String catalogNumber,
244 UUID collectionUuid, SpecimenTypeDesignationStatus typeStatus, URI preferredStableUri){
245 UpdateResult result = new UpdateResult();
246
247 DerivedUnit baseSpecimen = HibernateProxyHelper.deproxy(occurrenceService.load(baseDesignation.getTypeSpecimen().getUuid(), Arrays.asList("collection")), DerivedUnit.class);
248 DerivedUnit duplicate = DerivedUnit.NewInstance(baseSpecimen.getRecordBasis());
249 DerivationEvent derivedFrom = baseSpecimen.getDerivedFrom();
250 Collection<FieldUnit> fieldUnits = occurrenceService.findFieldUnits(baseSpecimen.getUuid(), null);
251 if(fieldUnits.size()!=1){
252 result.addException(new Exception("More than one or no field unit found for specimen"));
253 result.setError();
254 return result;
255 }
256 for (SpecimenOrObservationBase<?> original : derivedFrom.getOriginals()) {
257 DerivationEvent.NewSimpleInstance(original, duplicate, derivedFrom.getType());
258 }
259 duplicate.setAccessionNumber(accessionNumber);
260 duplicate.setBarcode(barcode);
261 duplicate.setCatalogNumber(catalogNumber);
262 duplicate.setCollection(collectionService.load(collectionUuid));
263 SpecimenTypeDesignation typeDesignation = SpecimenTypeDesignation.NewInstance();
264 typeDesignation.setTypeSpecimen(duplicate);
265 typeDesignation.setTypeStatus(typeStatus);
266 typeDesignation.getTypeSpecimen().setPreferredStableUri(preferredStableUri);
267
268 TaxonName name = load(nameUuid);
269 name.getTypeDesignations().add(typeDesignation);
270
271 result.setCdmEntity(typeDesignation);
272 result.addUpdatedObject(name);
273 return result;
274 }
275
276 @Override
277 @Transactional
278 public DeleteResult deleteTypeDesignation(TaxonName name, TypeDesignationBase<?> typeDesignation){
279 if(typeDesignation != null && typeDesignation .isPersited()){
280 typeDesignation = HibernateProxyHelper.deproxy(sourcedEntityDao.load(typeDesignation.getUuid()), TypeDesignationBase.class);
281 }
282
283 DeleteResult result = new DeleteResult();
284 if (name == null && typeDesignation == null){
285 result.setError();
286 return result;
287 }else if (name != null && typeDesignation != null){
288 removeSingleDesignation(name, typeDesignation);
289 }else if (name != null){
290 @SuppressWarnings("rawtypes")
291 Set<TypeDesignationBase> designationSet = new HashSet<>(name.getTypeDesignations());
292 for (TypeDesignationBase<?> desig : designationSet){
293 desig = CdmBase.deproxy(desig);
294 removeSingleDesignation(name, desig);
295 }
296 }else if (typeDesignation != null){
297 Set<TaxonName> nameSet = new HashSet<>(typeDesignation.getTypifiedNames());
298 for (TaxonName singleName : nameSet){
299 singleName = CdmBase.deproxy(singleName);
300 removeSingleDesignation(singleName, typeDesignation);
301 }
302 }
303 result.addDeletedObject(typeDesignation);
304 result.addUpdatedObject(name);
305 return result;
306 }
307
308
309 @Override
310 @Transactional(readOnly = false)
311 public DeleteResult deleteTypeDesignation(UUID nameUuid, UUID typeDesignationUuid){
312 TaxonName nameBase = load(nameUuid);
313 TypeDesignationBase<?> typeDesignation = HibernateProxyHelper.deproxy(sourcedEntityDao.load(typeDesignationUuid), TypeDesignationBase.class);
314 return deleteTypeDesignation(nameBase, typeDesignation);
315 }
316
317 @Transactional
318 private void removeSingleDesignation(TaxonName name, TypeDesignationBase<?> typeDesignation) {
319
320 name.removeTypeDesignation(typeDesignation);
321 if (typeDesignation.getTypifiedNames().isEmpty()){
322 typeDesignation.removeType();
323 if (!typeDesignation.getRegistrations().isEmpty()){
324 for(Object reg: typeDesignation.getRegistrations()){
325 if (reg instanceof Registration){
326 ((Registration)reg).removeTypeDesignation(typeDesignation);
327 }
328 }
329 }
330
331 typeDesignationDao.delete(typeDesignation);
332
333 }
334 }
335
336
337
338 /**
339 * @param name
340 * @param config
341 */
342 private void removeNameRelationshipsByDeleteConfig(TaxonName name, NameDeletionConfigurator config) {
343 try {
344 if (config.isRemoveAllNameRelationships()){
345 Set<NameRelationship> rels = getModifiableSet(name.getNameRelations());
346 for (NameRelationship rel : rels){
347 name.removeNameRelationship(rel);
348 }
349 }else{
350 //relations to this name
351 Set<NameRelationship> rels = getModifiableSet(name.getRelationsToThisName());
352 for (NameRelationship rel : rels){
353 if (config.isIgnoreHasBasionym() && NameRelationshipType.BASIONYM().equals(rel.getType() )){
354 name.removeNameRelationship(rel);
355 }else if (config.isIgnoreHasReplacedSynonym() && NameRelationshipType.REPLACED_SYNONYM().equals(rel.getType())){
356 name.removeNameRelationship(rel);
357 }
358 }
359 //relations from this name
360 rels = getModifiableSet(name.getRelationsFromThisName());
361 for (NameRelationship rel : rels){
362 if (config.isIgnoreIsBasionymFor() && NameRelationshipType.BASIONYM().equals(rel.getType()) ){
363 name.removeNameRelationship(rel);
364 }else if (config.isIgnoreIsReplacedSynonymFor() && NameRelationshipType.REPLACED_SYNONYM().equals(rel.getType())){
365 name.removeNameRelationship(rel);
366 }
367 }
368 }
369 } catch (Exception e) {
370 throw new RuntimeException(e);
371 }
372 }
373
374 private Set<NameRelationship> getModifiableSet(Set<NameRelationship> relations) {
375 Set<NameRelationship> rels = new HashSet<NameRelationship>();
376 for (NameRelationship rel : relations){
377 rels.add(rel);
378 }
379 return rels;
380 }
381
382 //********************* METHODS ****************************************************************//
383
384 /**
385 * TODO candidate for harmonization
386 * new name findByName
387 */
388 @Override
389 @Deprecated
390 public List<TaxonName> getNamesByNameCache(String nameCache){
391 boolean includeAuthors = false;
392 List<TaxonName> result = dao.findByName(includeAuthors, nameCache, MatchMode.EXACT, null, null, null, null);
393 return result;
394 }
395
396 /**
397 * TODO candidate for harmonization
398 * new name saveHomotypicalGroups
399 *
400 * findByTitle
401 */
402 @Override
403 @Deprecated
404 public List<TaxonName> findNamesByTitleCache(String titleCache, MatchMode matchMode, List<String> propertyPaths){
405 List<TaxonName> result = dao.findByTitle(titleCache, matchMode, null, null, null, propertyPaths);
406 return result;
407 }
408
409 /**
410 * TODO candidate for harmonization
411 * new name saveHomotypicalGroups
412 *
413 * findByTitle
414 */
415 @Override
416 @Deprecated
417 public List<TaxonName> findNamesByNameCache(String nameCache, MatchMode matchMode, List<String> propertyPaths){
418 List<TaxonName> result = dao.findByName(false, nameCache, matchMode, null, null, null , propertyPaths);
419 return result;
420 }
421
422 @Override
423 public Pager<TaxonNameParts> findTaxonNameParts(Optional<String> genusOrUninomial,
424 Optional<String> infraGenericEpithet, Optional<String> specificEpithet,
425 Optional<String> infraSpecificEpithet, Rank rank, Set<UUID> excludedNamesUuids,
426 Integer pageSize, Integer pageIndex, List<OrderHint> orderHints) {
427
428
429 long count = dao.countTaxonNameParts(genusOrUninomial, infraGenericEpithet, specificEpithet, infraGenericEpithet, rank, excludedNamesUuids);
430
431 List<TaxonNameParts> results;
432 if(AbstractPagerImpl.hasResultsInRange(count, pageIndex, pageSize)){
433 results = dao.findTaxonNameParts(genusOrUninomial, infraGenericEpithet, specificEpithet, infraSpecificEpithet,
434 rank, excludedNamesUuids,
435 pageSize, pageIndex, orderHints);
436 } else {
437 results = new ArrayList<>();
438 }
439
440 return new DefaultPagerImpl<TaxonNameParts>(pageIndex, count, pageSize, results);
441 }
442
443 @Override
444 public Pager<TaxonNameParts> findTaxonNameParts(TaxonNamePartsFilter filter, String namePartQueryString,
445 Integer pageSize, Integer pageIndex, List<OrderHint> orderHints) {
446
447 return findTaxonNameParts(
448 filter.uninomialQueryString(namePartQueryString),
449 filter.infraGenericEpithet(namePartQueryString),
450 filter.specificEpithet(namePartQueryString),
451 filter.infraspecificEpithet(namePartQueryString),
452 filter.getRank(),
453 filter.getExludedNamesUuids(),
454 pageSize, pageIndex, orderHints);
455 }
456
457 /**
458 * TODO candidate for harmonization
459 * new name saveHomotypicalGroups
460 */
461 @Override
462 @Transactional(readOnly = false)
463 public Map<UUID, HomotypicalGroup> saveAllHomotypicalGroups(Collection<HomotypicalGroup> homotypicalGroups){
464 return homotypicalGroupDao.saveAll(homotypicalGroups);
465 }
466
467 /**
468 * TODO candidate for harmonization
469 * new name saveTypeDesignations
470 */
471 @Override
472 @Transactional(readOnly = false)
473 public Map<UUID, TypeDesignationBase<?>> saveTypeDesignationAll(Collection<TypeDesignationBase<?>> typeDesignationCollection){
474 return typeDesignationDao.saveAll(typeDesignationCollection);
475 }
476
477 /**
478 * TODO candidate for harmonization
479 * new name getNomenclaturalStatus
480 */
481 @Override
482 public List<NomenclaturalStatus> getAllNomenclaturalStatus(int limit, int start){
483 return nomStatusDao.list(limit, start);
484 }
485
486 @Override
487 public NomenclaturalStatus loadNomenclaturalStatus(UUID uuid, List<String> propertyPaths){
488 return nomStatusDao.load(uuid, propertyPaths);
489 }
490
491 /**
492 * TODO candidate for harmonization
493 * new name getTypeDesignations
494 */
495 @Override
496 public List<TypeDesignationBase<?>> getAllTypeDesignations(int limit, int start){
497 return typeDesignationDao.getAllTypeDesignations(limit, start);
498 }
499
500 @Override
501 public TypeDesignationBase<?> loadTypeDesignation(int id, List<String> propertyPaths){
502 return typeDesignationDao.load(id, propertyPaths);
503 }
504
505 @Override
506 public TypeDesignationBase<?> loadTypeDesignation(UUID uuid, List<String> propertyPaths){
507 return typeDesignationDao.load(uuid, propertyPaths);
508 }
509
510 @Override
511 public List<TypeDesignationBase<?>> loadTypeDesignations(List<UUID> uuids, List<String> propertyPaths){
512 if(uuids == null) {
513 return null;
514 }
515
516 List<TypeDesignationBase<?>> entities = new ArrayList<>();
517 for(UUID uuid : uuids) {
518 entities.add(uuid == null ? null : typeDesignationDao.load(uuid, propertyPaths));
519 }
520 return entities;
521 }
522
523 /**
524 * FIXME Candidate for harmonization
525 * homotypicalGroupService.list
526 */
527 @Override
528 public List<HomotypicalGroup> getAllHomotypicalGroups(int limit, int start){
529 return homotypicalGroupDao.list(limit, start);
530 }
531
532
533 @Override
534 public List<NomenclaturalSource> listOriginalSpellings(Integer pageSize, Integer pageNumber,
535 List<OrderHint> orderHints, List<String> propertyPaths) {
536
537 Long numberOfResults = sourcedDao.countWithNameUsedInSource(NomenclaturalSource.class);
538 List<NomenclaturalSource> results = new ArrayList<>();
539 if(numberOfResults > 0) {
540 results = sourcedDao.listWithNameUsedInSource(NomenclaturalSource.class, pageSize, pageNumber, orderHints, propertyPaths);
541 }
542 return results;
543 }
544
545 @Override
546 public List<NameRelationship> listNameRelationships(Set<NameRelationshipType> types,
547 Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
548
549 Long numberOfResults = dao.countNameRelationships(types);
550 List<NameRelationship> results = new ArrayList<>();
551 if(numberOfResults > 0) {
552 results = dao.getNameRelationships(types, pageSize, pageNumber, orderHints, propertyPaths);
553 }
554 return results;
555 }
556
557 @Override
558 public List<HybridRelationship> listHybridRelationships(Set<HybridRelationshipType> types,
559 Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
560
561 Long numberOfResults = dao.countHybridRelationships(types);
562 List<HybridRelationship> results = new ArrayList<>();
563 if(numberOfResults > 0) {
564 results = dao.getHybridRelationships(types, pageSize, pageNumber, orderHints, propertyPaths);
565 }
566 return results;
567 }
568
569
570 @Override
571 @Autowired
572 protected void setDao(ITaxonNameDao dao) {
573 this.dao = dao;
574 }
575
576 @Override
577 public Pager<HybridRelationship> getHybridNames(INonViralName name, HybridRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
578 Integer numberOfResults = dao.countHybridNames(name, type);
579
580 List<HybridRelationship> results = new ArrayList<HybridRelationship>();
581 if(AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) { // no point checking again
582 results = dao.getHybridNames(name, type, pageSize, pageNumber,orderHints,propertyPaths);
583 }
584
585 return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
586 }
587
588 @Override
589 public List<NameRelationship> listNameRelationships(TaxonName name, Direction direction, NameRelationshipType type, Integer pageSize,
590 Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
591
592 Integer numberOfResults = dao.countNameRelationships(name, direction, type);
593
594 List<NameRelationship> results = new ArrayList<NameRelationship>();
595 if (AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) { // no point checking again
596 results = dao.getNameRelationships(name, direction, type, pageSize, pageNumber, orderHints, propertyPaths);
597 }
598 return results;
599 }
600
601
602 protected LuceneSearch prepareFindByFuzzyNameSearch(Class<? extends CdmBase> clazz,
603 INonViralName nvn,
604 float accuracy,
605 int maxNoOfResults,
606 List<Language> languages,
607 boolean highlightFragments) {
608
609 String similarity = Float.toString(accuracy);
610 String searchSuffix = "~" + similarity;
611
612 Builder finalQueryBuilder = new Builder();
613 finalQueryBuilder.setDisableCoord(false);
614 Builder textQueryBuilder = new Builder();
615 textQueryBuilder.setDisableCoord(false);
616
617 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
618 QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
619
620 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
621 // luceneSearch.setSortFields(sortFields);
622
623 // ---- search criteria
624 luceneSearch.setCdmTypRestriction(clazz);
625
626 FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());
627 if(nvn.getGenusOrUninomial() != null && !nvn.getGenusOrUninomial().equals("")) {
628 fltq.addTerms(nvn.getGenusOrUninomial().toLowerCase(), "genusOrUninomial", accuracy, 3);
629 } else {
630 //textQuery.add(new RegexQuery (new Term ("genusOrUninomial", "^[a-zA-Z]*")), Occur.MUST_NOT);
631 textQueryBuilder.add(queryFactory.newTermQuery("genusOrUninomial", "_null_", false), Occur.MUST);
632 }
633
634 if(nvn.getInfraGenericEpithet() != null && !nvn.getInfraGenericEpithet().equals("")){
635 fltq.addTerms(nvn.getInfraGenericEpithet().toLowerCase(), "infraGenericEpithet", accuracy, 3);
636 } else {
637 //textQuery.add(new RegexQuery (new Term ("infraGenericEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
638 textQueryBuilder.add(queryFactory.newTermQuery("infraGenericEpithet", "_null_", false), Occur.MUST);
639 }
640
641 if(nvn.getSpecificEpithet() != null && !nvn.getSpecificEpithet().equals("")){
642 fltq.addTerms(nvn.getSpecificEpithet().toLowerCase(), "specificEpithet", accuracy, 3);
643 } else {
644 //textQuery.add(new RegexQuery (new Term ("specificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
645 textQueryBuilder.add(queryFactory.newTermQuery("specificEpithet", "_null_", false), Occur.MUST);
646 }
647
648 if(nvn.getInfraSpecificEpithet() != null && !nvn.getInfraSpecificEpithet().equals("")){
649 fltq.addTerms(nvn.getInfraSpecificEpithet().toLowerCase(), "infraSpecificEpithet", accuracy, 3);
650 } else {
651 //textQuery.add(new RegexQuery (new Term ("infraSpecificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
652 textQueryBuilder.add(queryFactory.newTermQuery("infraSpecificEpithet", "_null_", false), Occur.MUST);
653 }
654
655 if(nvn.getAuthorshipCache() != null && !nvn.getAuthorshipCache().equals("")){
656 fltq.addTerms(nvn.getAuthorshipCache().toLowerCase(), "authorshipCache", accuracy, 3);
657 } else {
658 //textQuery.add(new RegexQuery (new Term ("authorshipCache", "^[a-zA-Z]*")), Occur.MUST_NOT);
659 }
660
661 textQueryBuilder.add(fltq, Occur.MUST);
662
663 BooleanQuery textQuery = textQueryBuilder.build();
664 finalQueryBuilder.add(textQuery, Occur.MUST);
665
666 luceneSearch.setQuery(finalQueryBuilder.build());
667
668 if(highlightFragments){
669 luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
670 }
671 return luceneSearch;
672 }
673
674 protected LuceneSearch prepareFindByFuzzyNameCacheSearch(Class<? extends CdmBase> clazz,
675 String name,
676 float accuracy,
677 int maxNoOfResults,
678 List<Language> languages,
679 boolean highlightFragments) {
680
681 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
682 QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
683
684 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
685 // luceneSearch.setSortFields(sortFields);
686
687 // ---- search criteria
688 luceneSearch.setCdmTypRestriction(clazz);
689 FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());
690
691 fltq.addTerms(name, "nameCache", accuracy, 3);
692
693 BooleanQuery finalQuery = new BooleanQuery(false);
694
695 finalQuery.add(fltq, Occur.MUST);
696
697 luceneSearch.setQuery(finalQuery);
698
699 if(highlightFragments){
700 luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
701 }
702 return luceneSearch;
703 }
704
705 protected LuceneSearch prepareFindByExactNameSearch(Class<? extends CdmBase> clazz,
706 String name,
707 boolean wildcard,
708 List<Language> languages,
709 boolean highlightFragments) {
710 Builder textQueryBuilder = new Builder();
711
712 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
713 QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
714
715 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
716 // luceneSearch.setSortFields(sortFields);
717
718 // ---- search criteria
719 luceneSearch.setCdmTypRestriction(clazz);
720
721 if(name != null && !name.equals("")) {
722 if(wildcard) {
723 textQueryBuilder.add(new WildcardQuery(new Term("nameCache", name + "*")), Occur.MUST);
724 } else {
725 textQueryBuilder.add(queryFactory.newTermQuery("nameCache", name, false), Occur.MUST);
726 }
727 }
728
729 luceneSearch.setQuery(textQueryBuilder.build());
730
731 if(highlightFragments){
732 luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
733 }
734 return luceneSearch;
735 }
736
737 @Override
738 public List<SearchResult<TaxonName>> findByNameFuzzySearch(
739 String name,
740 float accuracy,
741 List<Language> languages,
742 boolean highlightFragments,
743 List<String> propertyPaths,
744 int maxNoOfResults) throws IOException, LuceneParseException {
745
746 logger.info("Name to fuzzy search for : " + name);
747 // parse the input name
748 NonViralNameParserImpl parser = new NonViralNameParserImpl();
749 INonViralName nvn = parser.parseFullName(name);
750 if(name != null && !name.equals("") && nvn == null) {
751 throw new LuceneParseException("Could not parse name " + name);
752 }
753 LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);
754
755 // --- execute search
756 TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
757
758
759 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
760 idFieldMap.put(CdmBaseType.TAXON_NAME, "id");
761
762 // --- initialize taxa, highlight matches ....
763 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
764
765 List<SearchResult<TaxonName>> searchResults = searchResultBuilder.createResultSet(
766 topDocs, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
767
768 return searchResults;
769
770 }
771
772 @Override
773 public List<DocumentSearchResult> findByNameFuzzySearch(
774 String name,
775 float accuracy,
776 List<Language> languages,
777 boolean highlightFragments,
778 int maxNoOfResults) throws IOException, LuceneParseException {
779
780 logger.info("Name to fuzzy search for : " + name);
781 // parse the input name
782 NonViralNameParserImpl parser = new NonViralNameParserImpl();
783 INonViralName nvn = parser.parseFullName(name);
784 if(name != null && !name.equals("") && nvn == null) {
785 throw new LuceneParseException("Could not parse name " + name);
786 }
787 LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);
788
789 // --- execute search
790 TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
791
792 // --- initialize taxa, highlight matches ....
793 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
794
795 List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
796
797 return searchResults;
798 }
799
800 @Override
801 public List<DocumentSearchResult> findByFuzzyNameCacheSearch(
802 String name,
803 float accuracy,
804 List<Language> languages,
805 boolean highlightFragments,
806 int maxNoOfResults) throws IOException, LuceneParseException {
807
808 logger.info("Name to fuzzy search for : " + name);
809
810 LuceneSearch luceneSearch = prepareFindByFuzzyNameCacheSearch(null, name, accuracy, maxNoOfResults, languages, highlightFragments);
811
812 // --- execute search
813 TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
814
815 // --- initialize taxa, highlight matches ....
816 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
817
818 List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
819
820 return searchResults;
821 }
822
823 @Override
824 public List<DocumentSearchResult> findByNameExactSearch(
825 String name,
826 boolean wildcard,
827 List<Language> languages,
828 boolean highlightFragments,
829 int maxNoOfResults) throws IOException, LuceneParseException {
830
831 logger.info("Name to exact search for : " + name);
832
833 LuceneSearch luceneSearch = prepareFindByExactNameSearch(null, name, wildcard, languages, highlightFragments);
834
835 // --- execute search
836
837 TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
838
839 // --- initialize taxa, highlight matches ....
840 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
841
842 List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
843
844 return searchResults;
845 }
846
847 @Override
848 public Pager<NameRelationship> pageNameRelationships(TaxonName name, Direction direction, NameRelationshipType type, Integer pageSize,
849 Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
850 List<NameRelationship> results = listNameRelationships(name, direction, type, pageSize, pageNumber, orderHints, propertyPaths);
851 return new DefaultPagerImpl<>(pageNumber, results.size(), pageSize, results);
852 }
853
854 @Override
855 public List<NameRelationship> listFromNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
856 return listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);
857 }
858
859 @Override
860 public Pager<NameRelationship> pageFromNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
861 List<NameRelationship> results = listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);
862 return new DefaultPagerImpl<>(pageNumber, results.size(), pageSize, results);
863 }
864
865 @Override
866 public List<NameRelationship> listToNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
867 return listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
868 }
869
870 @Override
871 public Pager<NameRelationship> pageToNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
872 List<NameRelationship> results = listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
873 return new DefaultPagerImpl<>(pageNumber, results.size(), pageSize, results);
874 }
875
876 @Override
877 public Pager<TypeDesignationBase> getTypeDesignations(TaxonName name, SpecimenTypeDesignationStatus status,
878 Integer pageSize, Integer pageNumber) {
879 return getTypeDesignations(name, status, pageSize, pageNumber, null);
880 }
881
882 @Override
883 public Pager<TypeDesignationBase> getTypeDesignations(TaxonName name, SpecimenTypeDesignationStatus status,
884 Integer pageSize, Integer pageNumber, List<String> propertyPaths){
885 long numberOfResults = dao.countTypeDesignations(name, status);
886
887 List<TypeDesignationBase> results = new ArrayList<>();
888 if(AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)) {
889 results = dao.getTypeDesignations(name, null, status, pageSize, pageNumber, propertyPaths);
890 }
891 return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
892 }
893
894 @Override
895 public List<TypeDesignationBase> getTypeDesignationsInHomotypicalGroup(UUID nameUuid, Integer pageSize,
896 Integer pageNumber, List<String> propertyPaths){
897 TaxonName name = load(nameUuid, Arrays.asList("nomenclaturalSource.citation.authorship"));
898 Set<TypeDesignationBase<?>> typeDesignations = name.getHomotypicalGroup().getTypeDesignations();
899 List<TypeDesignationBase> result = defaultBeanInitializer.initializeAll(new ArrayList(typeDesignations), propertyPaths);
900 return result;
901 }
902
903 /**
904 * FIXME Candidate for harmonization
905 * rename search
906 */
907 @Override
908 public Pager<TaxonName> searchNames(String uninomial,String infraGenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
909 List<String> propertyPaths) {
910 long numberOfResults = dao.countNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank);
911
912 List<TaxonName> results = new ArrayList<>();
913 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
914 results = dao.searchNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber, orderHints, propertyPaths);
915 }
916
917 return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
918 }
919
920 @Override
921 public List<UuidAndTitleCache> getUuidAndTitleCacheOfNames(Integer limit, String pattern) {
922 return dao.getUuidAndTitleCacheOfNames(limit, pattern);
923 }
924
925 @Override
926 public Pager<TaxonName> findByName(Class<TaxonName> clazz, String queryString, MatchMode matchmode, List<Criterion> criteria,
927 Integer pageSize,Integer pageNumber, List<OrderHint> orderHints,List<String> propertyPaths) {
928 Long numberOfResults = dao.countByName(clazz, queryString, matchmode, criteria);
929
930 List<TaxonName> results = new ArrayList<>();
931 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
932 results = dao.findByName(clazz, queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths);
933 }
934
935 return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
936 }
937
938 @Override
939 public List<TaxonName> findByFullTitle(Class<TaxonName> clazz, String queryString, MatchMode matchmode, List<Criterion> criteria,
940 Integer pageSize,Integer pageNumber, List<OrderHint> orderHints,List<String> propertyPaths) {
941 Long numberOfResults = dao.countByFullTitle(clazz, queryString, matchmode, criteria);
942
943 List<TaxonName> results = new ArrayList<>();
944 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
945 results = dao.findByFullTitle(queryString, matchmode, pageSize, pageNumber, criteria, propertyPaths);
946 }
947
948 return results;
949 }
950
951
952 @Override
953 public HomotypicalGroup findHomotypicalGroup(UUID uuid) {
954 return homotypicalGroupDao.findByUuid(uuid);
955 }
956
957 @Override
958 @Transactional(readOnly = false)
959 public UpdateResult updateCaches(Class<? extends TaxonName> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonName> cacheStrategy, IProgressMonitor monitor) {
960 if (clazz == null){
961 clazz = TaxonName.class;
962 }
963 return super.updateCachesImpl(clazz, stepSize, cacheStrategy, monitor);
964 }
965
966
967 @Override
968 public List<TaggedText> getTaggedName(UUID uuid) {
969 TaxonName taxonName = dao.load(uuid);
970 List<TaggedText> taggedName = taxonName.getTaggedName();
971 return taggedName;
972 }
973
974
975 public DeleteResult isDeletable(TaxonName name, DeleteConfiguratorBase config, UUID taxonUuid){
976 DeleteResult result = new DeleteResult();
977
978 NameDeletionConfigurator nameConfig = null;
979 if (config instanceof NameDeletionConfigurator){
980 nameConfig = (NameDeletionConfigurator) config;
981 }else{
982 result.addException(new Exception("The delete configurator should be of the type NameDeletionConfigurator."));
983 result.setError();
984 return result;
985 }
986
987 if (!name.getNameRelations().isEmpty() && !nameConfig.isRemoveAllNameRelationships()){
988 HomotypicalGroup homotypicalGroup = HibernateProxyHelper.deproxy(name.getHomotypicalGroup(), HomotypicalGroup.class);
989
990 if (!nameConfig.isIgnoreIsBasionymFor() && homotypicalGroup.getBasionyms().contains(name)){
991 result.addException(new Exception( "Name can't be deleted as it is a basionym."));
992 result.setAbort();
993 }
994 if (!nameConfig.isIgnoreHasBasionym() && (name.getBasionyms().size()>0)){
995 result.addException(new Exception( "Name can't be deleted as it has a basionym."));
996 result.setAbort();
997 }
998 Set<NameRelationship> relationships = name.getNameRelations();
999 for (NameRelationship rel: relationships){
1000 if (!rel.getType().equals(NameRelationshipType.BASIONYM())){
1001 result.addException(new Exception("Name can't be deleted as it is used in name relationship(s). Remove name relationships prior to deletion."));
1002 result.setAbort();
1003 break;
1004 }
1005 }
1006 }
1007 //concepts
1008 if (!name.getTaxonBases().isEmpty()){
1009 boolean isDeletableTaxon = true;
1010 List <TaxonBase> notDeletedTaxonBases = name.getTaxonBases().stream()
1011 .filter((taxonBase) -> !taxonBase.getUuid().equals(taxonUuid))
1012 .collect(Collectors.toList());
1013 if (!notDeletedTaxonBases.isEmpty()){
1014 result.addException(new Exception("Name can't be deleted as it is used in concept(s). Remove or change concept prior to deletion."));
1015 result.setAbort();
1016 }
1017 }
1018
1019 //hybrid relationships
1020 if (name.isNonViral()){
1021 INonViralName nvn = name;
1022 Set<HybridRelationship> parentHybridRelations = nvn.getHybridParentRelations();
1023 //Hibernate.initialize(parentHybridRelations);
1024 if (! parentHybridRelations.isEmpty()){
1025 result.addException(new Exception("Name can't be deleted as it is a parent in (a) hybrid relationship(s). Remove hybrid relationships prior to deletion."));
1026 result.setAbort();
1027 }
1028 }
1029 Set<CdmBase> referencingObjects = genericDao.getReferencingObjectsForDeletion(name);
1030 for (CdmBase referencingObject : referencingObjects){
1031 //DerivedUnit?.storedUnder
1032 if (referencingObject.isInstanceOf(DerivedUnit.class)){
1033 String message = "Name can't be deleted as it is used as derivedUnit#storedUnder by %s. Remove 'stored under' prior to deleting this name";
1034 message = String.format(message, CdmBase.deproxy(referencingObject, DerivedUnit.class).getTitleCache());
1035 result.addException(new ReferencedObjectUndeletableException(message));
1036 result.addRelatedObject(referencingObject);
1037 result.setAbort();
1038 }
1039
1040 //DescriptionElementSource#nameUsedInSource
1041 else if (referencingObject.isInstanceOf(DescriptionElementSource.class) && !referencingObject.isInstanceOf(NomenclaturalSource.class) ){
1042 String message = "Name can't be deleted as it is used as descriptionElementSource#nameUsedInSource";
1043 result.addException(new ReferencedObjectUndeletableException(message));
1044 result.addRelatedObject(referencingObject);
1045 result.setAbort();
1046 }
1047 //NameTypeDesignation#typeName
1048 else if (referencingObject.isInstanceOf(NameTypeDesignation.class)){
1049 NameTypeDesignation typeDesignation = HibernateProxyHelper.deproxy(referencingObject, NameTypeDesignation.class);
1050
1051 if (typeDesignation.getTypeName().equals(name) && !typeDesignation.getTypifiedNames().isEmpty()){
1052 String message = "Name can't be deleted as it is used as a name type in a NameTypeDesignation";
1053 result.addException(new ReferencedObjectUndeletableException(message));
1054 result.addRelatedObject(referencingObject);
1055 result.setAbort();
1056 }
1057 }
1058 //DeterminationEvent#taxonName
1059 else if (referencingObject.isInstanceOf(DeterminationEvent.class)){
1060 String message = "Name can't be deleted as it is used as a determination event";
1061 result.addException(new ReferencedObjectUndeletableException(message));
1062 result.addRelatedObject(referencingObject);
1063 result.setAbort();
1064 }
1065 //original spelling
1066 else if (referencingObject.isInstanceOf(NomenclaturalSource.class) && !((NameDeletionConfigurator)config).isIgnoreIsOriginalSpellingFor()){
1067 if (((NomenclaturalSource)referencingObject).getNameUsedInSource() != null && ((NomenclaturalSource)referencingObject).getNameUsedInSource().equals(name)){
1068 String message = "Name can't be deleted as it is used as original spelling";
1069 result.addException(new ReferencedObjectUndeletableException(message));
1070 result.addRelatedObject(referencingObject);
1071 result.setAbort();
1072 }
1073 }
1074 if (referencingObject.isInstanceOf(NomenclaturalSource.class)){
1075 if (((NomenclaturalSource)referencingObject).getNameUsedInSource() != null && ((NomenclaturalSource)referencingObject).getNameUsedInSource().equals(name)){
1076 result.addRelatedObject(referencingObject);
1077 }
1078
1079 }
1080 }
1081
1082 //TODO inline references
1083
1084
1085 if (!nameConfig.isIgnoreIsReplacedSynonymFor() && name.isReplacedSynonym()){
1086 String message = "Name can't be deleted as it is a replaced synonym.";
1087 result.addException(new Exception(message));
1088 result.setAbort();
1089 }
1090 if (!nameConfig.isIgnoreHasReplacedSynonym() && (name.getReplacedSynonyms().size()>0)){
1091 String message = "Name can't be deleted as it has a replaced synonym.";
1092 result.addException(new Exception(message));
1093 result.setAbort();
1094 }
1095 return result;
1096
1097 }
1098
1099
1100 @Override
1101 public DeleteResult isDeletable(UUID nameUUID, DeleteConfiguratorBase config){
1102 TaxonName name = this.load(nameUUID);
1103 return isDeletable(name, config, null);
1104 }
1105
1106 @Override
1107 public DeleteResult isDeletable(UUID nameUUID, DeleteConfiguratorBase config, UUID taxonUuid){
1108 TaxonName name = this.load(nameUUID);
1109 return isDeletable(name, config, taxonUuid);
1110 }
1111
1112 @Override
1113 @Transactional(readOnly = true)
1114 public UpdateResult setAsGroupsBasionym(UUID nameUuid) {
1115 TaxonName name = dao.load(nameUuid);
1116 UpdateResult result = new UpdateResult();
1117 name.setAsGroupsBasionym();
1118 result.addUpdatedObject(name);
1119 return result;
1120
1121 }
1122
1123 @Override
1124 public List<HashMap<String,String>> getNameRecords(){
1125 return dao.getNameRecords();
1126
1127 }
1128
1129 @Override
1130 public List<TypeDesignationStatusBase> getTypeDesignationStatusInUse(){
1131 return typeDesignationDao.getTypeDesignationStatusInUse();
1132 }
1133
1134 @Override
1135 public Collection<TypeDesignationStatusFilter> getTypeDesignationStatusFilterTerms(List<Language> preferredLanguages){
1136 List<TypeDesignationStatusBase> termList = typeDesignationDao.getTypeDesignationStatusInUse();
1137 Map<String, TypeDesignationStatusFilter> filterMap = new HashMap<>();
1138 for(TypeDesignationStatusBase term : termList){
1139 TypeDesignationStatusFilter filter = new TypeDesignationStatusFilter(term, preferredLanguages, true);
1140 String key = filter.getKey();
1141 if(filterMap.containsKey(key)){
1142 filterMap.get(key).addStatus(term);
1143 } else {
1144 filterMap.put(key, filter);
1145 }
1146 }
1147 return filterMap.values();
1148 }
1149
1150 @Override
1151 public <S extends TaxonName> Pager<S> page(Class<S> clazz, List<Restriction<?>> restrictions, Integer pageSize,
1152 Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths) {
1153 return page(clazz, restrictions, pageSize, pageIndex, orderHints, propertyPaths, INCLUDE_UNPUBLISHED);
1154 }
1155
1156 @Override
1157 public <S extends TaxonName> Pager<S> page(Class<S> clazz, List<Restriction<?>> restrictions, Integer pageSize,
1158 Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths, boolean includeUnpublished) {
1159
1160 List<S> records;
1161 long resultSize = dao.count(clazz, restrictions);
1162 if(AbstractPagerImpl.hasResultsInRange(resultSize, pageIndex, pageSize)){
1163 records = dao.list(clazz, restrictions, pageSize, pageIndex, orderHints, propertyPaths, includeUnpublished);
1164 } else {
1165 records = new ArrayList<>();
1166 }
1167 Pager<S> pager = new DefaultPagerImpl<>(pageIndex, resultSize, pageSize, records);
1168 return pager;
1169 }
1170
1171 @Override
1172 public List<UuidAndTitleCache> getUuidAndTitleCacheOfSynonymy(Integer limit, UUID taxonUuid) {
1173 List<String> propertyPaths = new ArrayList<>();
1174 propertyPaths.add("synonyms.name.*");
1175 TaxonBase<?> taxonBase = taxonService.load(taxonUuid, propertyPaths);
1176 if (taxonBase instanceof Taxon){
1177 Taxon taxon = (Taxon)taxonBase;
1178 Set<TaxonName> names = taxon.getSynonymNames();
1179 List<UuidAndTitleCache> uuidAndTitleCacheList = new ArrayList<>();
1180 UuidAndTitleCache<TaxonName> uuidAndTitleCache;
1181 for (TaxonName name: names){
1182 uuidAndTitleCache = new UuidAndTitleCache<TaxonName>(TaxonName.class, name.getUuid(), name.getId(), name.getTitleCache());
1183 uuidAndTitleCacheList.add(uuidAndTitleCache);
1184 }
1185 }
1186 return null;
1187 }
1188
1189 @Override
1190 public UpdateResult parseName(String stringToBeParsed, NomenclaturalCode code, Rank preferredRank, boolean doDeduplicate) {
1191 TaxonName name = TaxonNameFactory.NewNameInstance(code, preferredRank);
1192 return parseName(name, stringToBeParsed, preferredRank, true, doDeduplicate);
1193 }
1194
1195 @Override
1196 public UpdateResult parseName(TaxonName nameToBeFilled, String stringToBeParsed, Rank preferredRank,
1197 boolean doEmpty, boolean doDeduplicate){
1198
1199 UpdateResult result = new UpdateResult();
1200 NonViralNameParserImpl nonViralNameParser = NonViralNameParserImpl.NewInstance();
1201 nonViralNameParser.parseReferencedName(nameToBeFilled, stringToBeParsed, preferredRank, doEmpty);
1202 TaxonName name = nameToBeFilled;
1203 if(doDeduplicate) {
1204 try {
1205 // Level sqlLogLevel = Logger.getLogger("org.hibernate.SQL").getLevel();
1206 // Logger.getLogger("org.hibernate.SQL").setLevel(Level.TRACE);
1207
1208 //references
1209 if (name.getNomenclaturalReference()!= null && !name.getNomenclaturalReference().isPersited()){
1210 Reference nomRef = name.getNomenclaturalReference();
1211 IMatchStrategy referenceMatcher = MatchStrategyFactory.NewParsedReferenceInstance(nomRef);
1212 List<Reference> matchingReferences = commonService.findMatching(nomRef, referenceMatcher);
1213 if(matchingReferences.size() >= 1){
1214 Reference duplicate = findBestMatching(nomRef, matchingReferences, referenceMatcher);
1215 name.setNomenclaturalReference(duplicate);
1216 }else{
1217 if (nomRef.getInReference() != null){
1218 List<Reference> matchingInReferences = commonService.findMatching(nomRef.getInReference(), MatchStrategyFactory.NewParsedReferenceInstance(nomRef.getInReference()));
1219 if(matchingInReferences.size() >= 1){
1220 Reference duplicate = findBestMatching(nomRef, matchingInReferences, referenceMatcher);
1221 nomRef.setInReference(duplicate);
1222 }
1223 }
1224 TeamOrPersonBase<?> author = deduplicateAuthor(nomRef.getAuthorship());
1225 nomRef.setAuthorship(author);
1226 }
1227 }
1228 Reference nomRef = name.getNomenclaturalReference();
1229
1230 //authors
1231 IParsedMatchStrategy authorMatcher = MatchStrategyFactory.NewParsedTeamOrPersonInstance();
1232 if (name.getCombinationAuthorship()!= null && !name.getCombinationAuthorship().isPersited()){
1233 //use same nom.ref. author if possible (should always be possible if nom.ref. exists)
1234 if (nomRef != null && nomRef.getAuthorship() != null){
1235 if(authorMatcher.invoke(name.getCombinationAuthorship(), nomRef.getAuthorship()).isSuccessful()){
1236 name.setCombinationAuthorship(nomRef.getAuthorship());
1237 }
1238 }
1239 name.setCombinationAuthorship(deduplicateAuthor(name.getCombinationAuthorship()));
1240 }
1241 if (name.getExCombinationAuthorship()!= null && !name.getExCombinationAuthorship().isPersited()){
1242 name.setExCombinationAuthorship(deduplicateAuthor(name.getExCombinationAuthorship()));
1243 }
1244 if (name.getBasionymAuthorship()!= null && !name.getBasionymAuthorship().isPersited()){
1245 name.setBasionymAuthorship(deduplicateAuthor(name.getBasionymAuthorship()));
1246 }
1247 if (name.getExBasionymAuthorship()!= null && !name.getExBasionymAuthorship().isPersited()){
1248 name.setExBasionymAuthorship(deduplicateAuthor(name.getExBasionymAuthorship()));
1249 }
1250
1251 //originalSpelling
1252 if (name.getOriginalSpelling()!= null && !name.getOriginalSpelling().isPersited()){
1253 TaxonName origName = name.getOriginalSpelling();
1254 IMatchStrategy nameMatcher = MatchStrategyFactory.NewParsedOriginalSpellingInstance();
1255 List<TaxonName> matchingNames = commonService.findMatching(origName, nameMatcher);
1256 if(matchingNames.size() >= 1){
1257 TaxonName duplicate = findBestMatching(origName, matchingNames, nameMatcher);
1258 name.setOriginalSpelling(duplicate);
1259 }
1260 }
1261
1262 // Logger.getLogger("org.hibernate.SQL").setLevel(sqlLogLevel);
1263 } catch (MatchException e) {
1264 throw new RuntimeException(e);
1265 }
1266 }
1267 result.setCdmEntity(name);
1268 return result;
1269 }
1270
1271 private TeamOrPersonBase<?> deduplicateAuthor(TeamOrPersonBase<?> authorship) throws MatchException {
1272 if (authorship == null){
1273 return null;
1274 }
1275 IParsedMatchStrategy authorMatcher = MatchStrategyFactory.NewParsedTeamOrPersonInstance();
1276 List<TeamOrPersonBase<?>> matchingAuthors = commonService.findMatching(authorship, authorMatcher);
1277 if(matchingAuthors.size() >= 1){
1278 TeamOrPersonBase<?> duplicate = findBestMatching(authorship, matchingAuthors, authorMatcher);
1279 return duplicate;
1280 }else{
1281 if (authorship instanceof Team){
1282 deduplicateTeam((Team)authorship);
1283 }
1284 return authorship;
1285 }
1286 }
1287
1288 private void deduplicateTeam(Team team) throws MatchException {
1289 List<Person> members = team.getTeamMembers();
1290 IParsedMatchStrategy personMatcher = MatchStrategyFactory.NewParsedPersonInstance();
1291 for (int i =0; i< members.size(); i++){
1292 Person person = CdmBase.deproxy(members.get(i));
1293 List<Person> matchingPersons = commonService.findMatching(person, personMatcher);
1294 if (matchingPersons.size() > 0){
1295 person = findBestMatching(person, matchingPersons, personMatcher);
1296 members.set(i, person);
1297 }
1298 }
1299 }
1300
1301 private <M extends IMatchable> M findBestMatching(M matchable, List<M> matchingList,
1302 IMatchStrategy matcher) {
1303 // FIXME TODO resolve multiple duplications. Use first match for a start
1304 if(matchingList.isEmpty()){
1305 return null;
1306 }
1307 M bestMatching = matchingList.iterator().next();
1308 return bestMatching;
1309 }
1310 }