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