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.Collections;
|
15
|
import java.util.EnumSet;
|
16
|
import java.util.HashMap;
|
17
|
import java.util.HashSet;
|
18
|
import java.util.Iterator;
|
19
|
import java.util.List;
|
20
|
import java.util.Map;
|
21
|
import java.util.Set;
|
22
|
import java.util.UUID;
|
23
|
import java.util.stream.Collectors;
|
24
|
|
25
|
import javax.persistence.EntityNotFoundException;
|
26
|
|
27
|
import org.apache.commons.lang3.StringUtils;
|
28
|
import org.apache.logging.log4j.LogManager;
|
29
|
import org.apache.logging.log4j.Logger;
|
30
|
import org.apache.lucene.queryparser.classic.ParseException;
|
31
|
import org.apache.lucene.search.BooleanClause.Occur;
|
32
|
import org.apache.lucene.search.BooleanQuery;
|
33
|
import org.apache.lucene.search.BooleanQuery.Builder;
|
34
|
import org.apache.lucene.search.Query;
|
35
|
import org.apache.lucene.search.SortField;
|
36
|
import org.apache.lucene.search.grouping.TopGroups;
|
37
|
import org.apache.lucene.search.join.ScoreMode;
|
38
|
import org.apache.lucene.util.BytesRef;
|
39
|
import org.hibernate.criterion.Criterion;
|
40
|
import org.springframework.beans.factory.annotation.Autowired;
|
41
|
import org.springframework.stereotype.Service;
|
42
|
import org.springframework.transaction.annotation.Transactional;
|
43
|
|
44
|
import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase;
|
45
|
import eu.etaxonomy.cdm.api.service.config.IFindTaxaAndNamesConfigurator;
|
46
|
import eu.etaxonomy.cdm.api.service.config.IncludedTaxonConfiguration;
|
47
|
import eu.etaxonomy.cdm.api.service.config.MatchingTaxonConfigurator;
|
48
|
import eu.etaxonomy.cdm.api.service.config.NodeDeletionConfigurator.ChildHandling;
|
49
|
import eu.etaxonomy.cdm.api.service.config.SynonymDeletionConfigurator;
|
50
|
import eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator;
|
51
|
import eu.etaxonomy.cdm.api.service.dto.IdentifiedEntityDTO;
|
52
|
import eu.etaxonomy.cdm.api.service.dto.IncludedTaxaDTO;
|
53
|
import eu.etaxonomy.cdm.api.service.dto.MarkedEntityDTO;
|
54
|
import eu.etaxonomy.cdm.api.service.dto.TaxonRelationshipsDTO;
|
55
|
import eu.etaxonomy.cdm.api.service.exception.DataChangeNoRollbackException;
|
56
|
import eu.etaxonomy.cdm.api.service.exception.HomotypicalGroupChangeException;
|
57
|
import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
|
58
|
import eu.etaxonomy.cdm.api.service.pager.Pager;
|
59
|
import eu.etaxonomy.cdm.api.service.pager.impl.AbstractPagerImpl;
|
60
|
import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
|
61
|
import eu.etaxonomy.cdm.api.service.search.ILuceneIndexToolProvider;
|
62
|
import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;
|
63
|
import eu.etaxonomy.cdm.api.service.search.LuceneMultiSearch;
|
64
|
import eu.etaxonomy.cdm.api.service.search.LuceneMultiSearchException;
|
65
|
import eu.etaxonomy.cdm.api.service.search.LuceneParseException;
|
66
|
import eu.etaxonomy.cdm.api.service.search.LuceneSearch;
|
67
|
import eu.etaxonomy.cdm.api.service.search.QueryFactory;
|
68
|
import eu.etaxonomy.cdm.api.service.search.SearchResult;
|
69
|
import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;
|
70
|
import eu.etaxonomy.cdm.api.util.TaxonRelationshipEdge;
|
71
|
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
|
72
|
import eu.etaxonomy.cdm.compare.taxon.HomotypicGroupTaxonComparator;
|
73
|
import eu.etaxonomy.cdm.compare.taxon.TaxonComparator;
|
74
|
import eu.etaxonomy.cdm.exception.UnpublishedException;
|
75
|
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
|
76
|
import eu.etaxonomy.cdm.hibernate.search.AcceptedTaxonBridge;
|
77
|
import eu.etaxonomy.cdm.hibernate.search.GroupByTaxonClassBridge;
|
78
|
import eu.etaxonomy.cdm.model.CdmBaseType;
|
79
|
import eu.etaxonomy.cdm.model.common.Annotation;
|
80
|
import eu.etaxonomy.cdm.model.common.AnnotationType;
|
81
|
import eu.etaxonomy.cdm.model.common.CdmBase;
|
82
|
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
|
83
|
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
|
84
|
import eu.etaxonomy.cdm.model.common.Language;
|
85
|
import eu.etaxonomy.cdm.model.common.MarkerType;
|
86
|
import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
|
87
|
import eu.etaxonomy.cdm.model.description.CommonTaxonName;
|
88
|
import eu.etaxonomy.cdm.model.description.DescriptionBase;
|
89
|
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
|
90
|
import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
|
91
|
import eu.etaxonomy.cdm.model.description.Distribution;
|
92
|
import eu.etaxonomy.cdm.model.description.Feature;
|
93
|
import eu.etaxonomy.cdm.model.description.IIdentificationKey;
|
94
|
import eu.etaxonomy.cdm.model.description.PolytomousKeyNode;
|
95
|
import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
|
96
|
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
|
97
|
import eu.etaxonomy.cdm.model.description.TaxonDescription;
|
98
|
import eu.etaxonomy.cdm.model.description.TaxonInteraction;
|
99
|
import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
|
100
|
import eu.etaxonomy.cdm.model.location.NamedArea;
|
101
|
import eu.etaxonomy.cdm.model.media.ExternalLink;
|
102
|
import eu.etaxonomy.cdm.model.media.ExternalLinkType;
|
103
|
import eu.etaxonomy.cdm.model.media.Media;
|
104
|
import eu.etaxonomy.cdm.model.metadata.SecReferenceHandlingEnum;
|
105
|
import eu.etaxonomy.cdm.model.metadata.SecReferenceHandlingSwapEnum;
|
106
|
import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
|
107
|
import eu.etaxonomy.cdm.model.name.IZoologicalName;
|
108
|
import eu.etaxonomy.cdm.model.name.Rank;
|
109
|
import eu.etaxonomy.cdm.model.name.TaxonName;
|
110
|
import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
|
111
|
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
|
112
|
import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
|
113
|
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
|
114
|
import eu.etaxonomy.cdm.model.reference.OriginalSourceType;
|
115
|
import eu.etaxonomy.cdm.model.reference.Reference;
|
116
|
import eu.etaxonomy.cdm.model.taxon.Classification;
|
117
|
import eu.etaxonomy.cdm.model.taxon.ITaxonTreeNode;
|
118
|
import eu.etaxonomy.cdm.model.taxon.SecundumSource;
|
119
|
import eu.etaxonomy.cdm.model.taxon.Synonym;
|
120
|
import eu.etaxonomy.cdm.model.taxon.SynonymType;
|
121
|
import eu.etaxonomy.cdm.model.taxon.Taxon;
|
122
|
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
|
123
|
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
|
124
|
import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
|
125
|
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
|
126
|
import eu.etaxonomy.cdm.model.term.DefinedTerm;
|
127
|
import eu.etaxonomy.cdm.persistence.dao.common.Restriction;
|
128
|
import eu.etaxonomy.cdm.persistence.dao.initializer.AbstractBeanInitializer;
|
129
|
import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;
|
130
|
import eu.etaxonomy.cdm.persistence.dao.occurrence.IOccurrenceDao;
|
131
|
import eu.etaxonomy.cdm.persistence.dao.taxon.IClassificationDao;
|
132
|
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
|
133
|
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeDao;
|
134
|
import eu.etaxonomy.cdm.persistence.dto.MergeResult;
|
135
|
import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
|
136
|
import eu.etaxonomy.cdm.persistence.query.MatchMode;
|
137
|
import eu.etaxonomy.cdm.persistence.query.OrderHint;
|
138
|
import eu.etaxonomy.cdm.persistence.query.OrderHint.SortOrder;
|
139
|
import eu.etaxonomy.cdm.persistence.query.TaxonTitleType;
|
140
|
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
|
141
|
|
142
|
|
143
|
/**
|
144
|
* @author a.kohlbecker
|
145
|
* @since 10.09.2010
|
146
|
*/
|
147
|
@Service
|
148
|
@Transactional(readOnly = true)
|
149
|
public class TaxonServiceImpl
|
150
|
extends IdentifiableServiceBase<TaxonBase,ITaxonDao>
|
151
|
implements ITaxonService{
|
152
|
|
153
|
private static final Logger logger = LogManager.getLogger(TaxonServiceImpl.class);
|
154
|
|
155
|
public static final String POTENTIAL_COMBINATION_NAMESPACE = "Potential combination";
|
156
|
|
157
|
public static final String INFERRED_EPITHET_NAMESPACE = "Inferred epithet";
|
158
|
|
159
|
public static final String INFERRED_GENUS_NAMESPACE = "Inferred genus";
|
160
|
|
161
|
@Autowired
|
162
|
private ITaxonNodeDao taxonNodeDao;
|
163
|
|
164
|
@Autowired
|
165
|
private ITaxonNameDao nameDao;
|
166
|
|
167
|
@Autowired
|
168
|
private INameService nameService;
|
169
|
|
170
|
@Autowired
|
171
|
private IOccurrenceService occurrenceService;
|
172
|
|
173
|
@Autowired
|
174
|
private ITaxonNodeService nodeService;
|
175
|
|
176
|
@Autowired
|
177
|
private IDescriptionService descriptionService;
|
178
|
|
179
|
@Autowired
|
180
|
private IReferenceService referenceService;
|
181
|
//
|
182
|
// @Autowired
|
183
|
// private IOrderedTermVocabularyDao orderedVocabularyDao;
|
184
|
|
185
|
@Autowired
|
186
|
private IOccurrenceDao occurrenceDao;
|
187
|
|
188
|
@Autowired
|
189
|
private IClassificationDao classificationDao;
|
190
|
|
191
|
@Autowired
|
192
|
private AbstractBeanInitializer beanInitializer;
|
193
|
|
194
|
@Autowired
|
195
|
private ILuceneIndexToolProvider luceneIndexToolProvider;
|
196
|
|
197
|
//************************ CONSTRUCTOR ****************************/
|
198
|
public TaxonServiceImpl(){
|
199
|
if (logger.isDebugEnabled()) { logger.debug("Load TaxonService Bean"); }
|
200
|
}
|
201
|
|
202
|
// ****************************** METHODS ********************************/
|
203
|
|
204
|
@Override
|
205
|
public TaxonBase load(UUID uuid, boolean includeUnpublished, List<String> propertyPaths) {
|
206
|
return dao.load(uuid, includeUnpublished, propertyPaths);
|
207
|
}
|
208
|
|
209
|
@Override
|
210
|
public List<TaxonBase> searchByName(String name, boolean includeUnpublished, Reference sec) {
|
211
|
return dao.getTaxaByName(name, includeUnpublished, sec);
|
212
|
}
|
213
|
|
214
|
@Override
|
215
|
@Transactional(readOnly = false)
|
216
|
public UpdateResult swapSynonymAndAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon, boolean setNameInSource, boolean newUuidForAcceptedTaxon, SecReferenceHandlingSwapEnum secHandling, Reference newSecAcc, Reference newSecSyn){
|
217
|
if (newUuidForAcceptedTaxon){
|
218
|
return swapSynonymAndAcceptedTaxonNewUuid(synonym, acceptedTaxon, setNameInSource, secHandling, newSecAcc, newSecSyn);
|
219
|
}else{
|
220
|
return swapSynonymAndAcceptedTaxon(synonym, acceptedTaxon, setNameInSource, secHandling, newSecAcc, newSecSyn);
|
221
|
}
|
222
|
}
|
223
|
|
224
|
private UpdateResult swapSynonymAndAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon, boolean setNameInSource, SecReferenceHandlingSwapEnum secHandling, Reference newSecAcc, Reference newSecSyn){
|
225
|
UpdateResult result = new UpdateResult();
|
226
|
String oldTaxonTitleCache = acceptedTaxon.getTitleCache();
|
227
|
|
228
|
TaxonName synonymName = synonym.getName();
|
229
|
TaxonName taxonName = HibernateProxyHelper.deproxy(acceptedTaxon.getName());
|
230
|
Reference secAccepted = acceptedTaxon.getSec();
|
231
|
String microRefSecAccepted = acceptedTaxon.getSecMicroReference();
|
232
|
Reference secSynonym = synonym.getSec();
|
233
|
String microRefSecSynonym = synonym.getSecMicroReference();
|
234
|
|
235
|
// if (secHandling.equals(SecReferenceHandlingSwapEnum.AlwaysDelete) || (secAccepted != null && secSynonym != null && !secAccepted.getUuid().equals(secSynonym.getUuid()) && (secHandling.equals(SecReferenceHandlingSwapEnum.AlwaysSelect) || secHandling.equals(SecReferenceHandlingSwapEnum.KeepOrSelect)))){
|
236
|
// secAccepted = null;
|
237
|
// microRefSecAccepted = null;
|
238
|
// secSynonym = null;
|
239
|
// microRefSecSynonym = null;
|
240
|
// }
|
241
|
|
242
|
|
243
|
Set<ExternalLink> accLinks = new HashSet<>();
|
244
|
if (acceptedTaxon.getSecSource() != null){
|
245
|
for (ExternalLink link: acceptedTaxon.getSecSource().getLinks()){
|
246
|
accLinks.add(ExternalLink.NewInstance(ExternalLinkType.Unknown, link.getUri()));
|
247
|
}
|
248
|
}
|
249
|
acceptedTaxon.setName(synonymName);
|
250
|
acceptedTaxon.setSec(newSecAcc);
|
251
|
// acceptedTaxon.setSecMicroReference(synonym.getSecMicroReference());
|
252
|
|
253
|
// if (synonym.getSecSource()!= null && synonym.getSecSource().getLinks() != null){
|
254
|
// acceptedTaxon.getSecSource().getLinks().clear();
|
255
|
// for (ExternalLink link: synonym.getSecSource().getLinks()){
|
256
|
// acceptedTaxon.getSecSource().addLink(ExternalLink.NewInstance(ExternalLinkType.Unknown, link.getUri()));
|
257
|
// }
|
258
|
// }
|
259
|
|
260
|
synonym.setName(taxonName);
|
261
|
synonym.setSec(newSecSyn);
|
262
|
// synonym.setSecMicroReference(microRefSecAccepted);
|
263
|
// if (synonym.getSecSource() != null){
|
264
|
// synonym.getSecSource().getLinks().clear();
|
265
|
// for (ExternalLink link: accLinks){
|
266
|
// synonym.getSecSource().addLink(link);
|
267
|
// }
|
268
|
// }
|
269
|
|
270
|
//nameUsedInSource
|
271
|
handleNameUsedInSourceForSwap(setNameInSource, taxonName, oldTaxonTitleCache, acceptedTaxon.getDescriptions());
|
272
|
|
273
|
acceptedTaxon.resetTitleCache();
|
274
|
synonym.resetTitleCache();
|
275
|
|
276
|
MergeResult mergeTaxon = merge(acceptedTaxon, true);
|
277
|
MergeResult mergeSynonym = merge(synonym, true);
|
278
|
result.setCdmEntity((CdmBase) mergeTaxon.getMergedEntity());
|
279
|
result.addUpdatedObject((CdmBase) mergeSynonym.getMergedEntity());
|
280
|
|
281
|
return result;
|
282
|
}
|
283
|
|
284
|
private UpdateResult swapSynonymAndAcceptedTaxonNewUuid(Synonym oldSynonym, Taxon oldAcceptedTaxon, boolean setNameInSource, SecReferenceHandlingSwapEnum secHandling, Reference newSecAcc, Reference newSecSyn){
|
285
|
UpdateResult result = new UpdateResult();
|
286
|
oldAcceptedTaxon.removeSynonym(oldSynonym);
|
287
|
TaxonName synonymName = oldSynonym.getName();
|
288
|
TaxonName taxonName = HibernateProxyHelper.deproxy(oldAcceptedTaxon.getName());
|
289
|
String oldTaxonTitleCache = oldAcceptedTaxon.getTitleCache();
|
290
|
|
291
|
boolean sameHomotypicGroup = synonymName.getHomotypicalGroup().equals(taxonName.getHomotypicalGroup());
|
292
|
synonymName.removeTaxonBase(oldSynonym);
|
293
|
|
294
|
List<Synonym> synonyms = new ArrayList<>();
|
295
|
for (Synonym syn: oldAcceptedTaxon.getSynonyms()){
|
296
|
syn = HibernateProxyHelper.deproxy(syn, Synonym.class);
|
297
|
synonyms.add(syn);
|
298
|
}
|
299
|
for (Synonym syn: synonyms){
|
300
|
oldAcceptedTaxon.removeSynonym(syn);
|
301
|
}
|
302
|
Taxon newTaxon = oldAcceptedTaxon.clone(true, true, false, true);
|
303
|
newTaxon.setSec(newSecAcc);
|
304
|
|
305
|
//move descriptions
|
306
|
Set<TaxonDescription> descriptionsToCopy = new HashSet<>(oldAcceptedTaxon.getDescriptions());
|
307
|
for (TaxonDescription description: descriptionsToCopy){
|
308
|
newTaxon.addDescription(description);
|
309
|
}
|
310
|
//nameUsedInSource
|
311
|
handleNameUsedInSourceForSwap(setNameInSource, taxonName, oldTaxonTitleCache, newTaxon.getDescriptions());
|
312
|
|
313
|
newTaxon.setName(synonymName);
|
314
|
|
315
|
newTaxon.setPublish(oldSynonym.isPublish());
|
316
|
for (Synonym syn: synonyms){
|
317
|
if (!syn.getName().equals(newTaxon.getName())){
|
318
|
newTaxon.addSynonym(syn, syn.getType());
|
319
|
}
|
320
|
}
|
321
|
|
322
|
//move all data to new taxon
|
323
|
//Move Taxon RelationShips to new Taxon
|
324
|
for(TaxonRelationship taxonRelationship : newTaxon.getTaxonRelations()){
|
325
|
newTaxon.removeTaxonRelation(taxonRelationship);
|
326
|
}
|
327
|
|
328
|
for(TaxonRelationship taxonRelationship : oldAcceptedTaxon.getTaxonRelations()){
|
329
|
Taxon fromTaxon = HibernateProxyHelper.deproxy(taxonRelationship.getFromTaxon());
|
330
|
Taxon toTaxon = HibernateProxyHelper.deproxy(taxonRelationship.getToTaxon());
|
331
|
if (fromTaxon == oldAcceptedTaxon){
|
332
|
newTaxon.addTaxonRelation(taxonRelationship.getToTaxon(), taxonRelationship.getType(),
|
333
|
taxonRelationship.getCitation(), taxonRelationship.getCitationMicroReference());
|
334
|
|
335
|
}else if(toTaxon == oldAcceptedTaxon){
|
336
|
fromTaxon.addTaxonRelation(newTaxon, taxonRelationship.getType(),
|
337
|
taxonRelationship.getCitation(), taxonRelationship.getCitationMicroReference());
|
338
|
saveOrUpdate(fromTaxon);
|
339
|
|
340
|
}else{
|
341
|
logger.warn("Taxon is not part of its own Taxonrelationship");
|
342
|
}
|
343
|
|
344
|
// Remove old relationships
|
345
|
fromTaxon.removeTaxonRelation(taxonRelationship);
|
346
|
toTaxon.removeTaxonRelation(taxonRelationship);
|
347
|
taxonRelationship.setToTaxon(null);
|
348
|
taxonRelationship.setFromTaxon(null);
|
349
|
}
|
350
|
|
351
|
//taxon nodes
|
352
|
List<TaxonNode> nodes = new ArrayList<>(oldAcceptedTaxon.getTaxonNodes());
|
353
|
for (TaxonNode node: nodes){
|
354
|
node = HibernateProxyHelper.deproxy(node);
|
355
|
TaxonNode parent = node.getParent();
|
356
|
oldAcceptedTaxon.removeTaxonNode(node);
|
357
|
node.setTaxon(newTaxon);
|
358
|
if (parent != null){
|
359
|
parent.addChildNode(node, null, null);
|
360
|
}
|
361
|
}
|
362
|
|
363
|
//synonym
|
364
|
Synonym newSynonym = oldSynonym.clone();
|
365
|
newSynonym.setName(taxonName);
|
366
|
newSynonym.setPublish(oldAcceptedTaxon.isPublish());
|
367
|
newSynonym.setSec(newSecSyn);
|
368
|
if (sameHomotypicGroup){
|
369
|
newTaxon.addSynonym(newSynonym, SynonymType.HOMOTYPIC_SYNONYM_OF());
|
370
|
}else{
|
371
|
newTaxon.addSynonym(newSynonym, SynonymType.HETEROTYPIC_SYNONYM_OF());
|
372
|
}
|
373
|
|
374
|
//deletes
|
375
|
TaxonDeletionConfigurator conf = new TaxonDeletionConfigurator();
|
376
|
conf.setDeleteNameIfPossible(false);
|
377
|
SynonymDeletionConfigurator confSyn = new SynonymDeletionConfigurator();
|
378
|
confSyn.setDeleteNameIfPossible(false);
|
379
|
result.setCdmEntity(newTaxon);
|
380
|
|
381
|
DeleteResult deleteResult = deleteTaxon(oldAcceptedTaxon.getUuid(), conf, null);
|
382
|
if (oldSynonym.isPersited()){
|
383
|
oldSynonym.setSecSource(null);
|
384
|
deleteResult.includeResult(deleteSynonym(oldSynonym.getUuid(), confSyn));
|
385
|
}
|
386
|
result.includeResult(deleteResult);
|
387
|
|
388
|
return result;
|
389
|
}
|
390
|
|
391
|
private void handleNameUsedInSourceForSwap(boolean setNameInSource, TaxonName taxonName, String oldTaxonTitleCache,
|
392
|
Set<TaxonDescription> descriptions) {
|
393
|
for(TaxonDescription description : descriptions){
|
394
|
String message = "Description copied from former accepted taxon: %s (Old title: %s)";
|
395
|
message = String.format(message, oldTaxonTitleCache, description.getTitleCache());
|
396
|
description.setTitleCache(message, true);
|
397
|
if(setNameInSource){
|
398
|
for (DescriptionElementBase element: description.getElements()){
|
399
|
for (DescriptionElementSource source: element.getSources()){
|
400
|
if (source.getNameUsedInSource() == null){
|
401
|
source.setNameUsedInSource(taxonName);
|
402
|
}
|
403
|
}
|
404
|
}
|
405
|
}
|
406
|
}
|
407
|
}
|
408
|
|
409
|
@Override
|
410
|
@Transactional(readOnly = false)
|
411
|
public UpdateResult changeSynonymToAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon, Reference newSecRef, String microRef, SecReferenceHandlingEnum secHandling, boolean deleteSynonym) {
|
412
|
UpdateResult result = new UpdateResult();
|
413
|
TaxonName acceptedName = acceptedTaxon.getName();
|
414
|
TaxonName synonymName = synonym.getName();
|
415
|
HomotypicalGroup synonymHomotypicGroup = synonymName.getHomotypicalGroup();
|
416
|
|
417
|
//check synonym is not homotypic
|
418
|
if (acceptedName.getHomotypicalGroup().equals(synonymHomotypicGroup)){
|
419
|
String message = "The accepted taxon and the synonym are part of the same homotypical group and therefore can not be both accepted.";
|
420
|
result.addException(new HomotypicalGroupChangeException(message));
|
421
|
result.setAbort();
|
422
|
return result;
|
423
|
}
|
424
|
|
425
|
Taxon newAcceptedTaxon = Taxon.NewInstance(synonymName, newSecRef, microRef);
|
426
|
newAcceptedTaxon.setPublish(synonym.isPublish());
|
427
|
dao.save(newAcceptedTaxon);
|
428
|
result.setCdmEntity(newAcceptedTaxon);
|
429
|
SynonymType relTypeForGroup = SynonymType.HOMOTYPIC_SYNONYM_OF();
|
430
|
List<Synonym> heteroSynonyms = acceptedTaxon.getSynonymsInGroup(synonymHomotypicGroup);
|
431
|
|
432
|
for (Synonym heteroSynonym : heteroSynonyms){
|
433
|
if (secHandling == null){
|
434
|
heteroSynonym.setSec(newSecRef);
|
435
|
}
|
436
|
if (synonym.equals(heteroSynonym)){
|
437
|
acceptedTaxon.removeSynonym(heteroSynonym, false);
|
438
|
}else{
|
439
|
//move synonyms in same homotypic group to new accepted taxon
|
440
|
newAcceptedTaxon.addSynonym(heteroSynonym, relTypeForGroup);
|
441
|
}
|
442
|
}
|
443
|
dao.saveOrUpdate(acceptedTaxon);
|
444
|
result.addUpdatedObject(acceptedTaxon);
|
445
|
if (deleteSynonym){
|
446
|
|
447
|
try {
|
448
|
this.dao.flush();
|
449
|
SynonymDeletionConfigurator config = new SynonymDeletionConfigurator();
|
450
|
config.setDeleteNameIfPossible(false);
|
451
|
this.deleteSynonym(synonym, config);
|
452
|
|
453
|
} catch (Exception e) {
|
454
|
result.addException(e);
|
455
|
}
|
456
|
}
|
457
|
|
458
|
return result;
|
459
|
}
|
460
|
|
461
|
@Override
|
462
|
@Transactional(readOnly = false)
|
463
|
public UpdateResult changeSynonymToAcceptedTaxon(UUID synonymUuid,
|
464
|
UUID acceptedTaxonUuid,
|
465
|
UUID newParentNodeUuid,
|
466
|
UUID newSec,
|
467
|
String microReference,
|
468
|
SecReferenceHandlingEnum secHandling,
|
469
|
boolean deleteSynonym) {
|
470
|
UpdateResult result = new UpdateResult();
|
471
|
Synonym synonym = CdmBase.deproxy(dao.load(synonymUuid), Synonym.class);
|
472
|
Taxon acceptedTaxon = CdmBase.deproxy(dao.load(acceptedTaxonUuid), Taxon.class);
|
473
|
TaxonNode newParentNode = taxonNodeDao.load(newParentNodeUuid);
|
474
|
Reference newSecRef = null;
|
475
|
switch (secHandling){
|
476
|
case AlwaysDelete:
|
477
|
newSecRef = null;
|
478
|
break;
|
479
|
case UseNewParentSec:
|
480
|
newSecRef = newParentNode.getTaxon() != null? newParentNode.getTaxon().getSec(): null;
|
481
|
break;
|
482
|
case KeepOrWarn:
|
483
|
Reference parentSec = newParentNode.getTaxon() != null? newParentNode.getTaxon().getSec(): null;
|
484
|
Reference synSec = synonym.getSec();
|
485
|
if (synSec != null ){
|
486
|
newSecRef = CdmBase.deproxy(synSec);
|
487
|
}else{
|
488
|
newSecRef = CdmBase.deproxy(referenceService.load(newSec));
|
489
|
}
|
490
|
break;
|
491
|
case KeepOrSelect:
|
492
|
newSecRef = CdmBase.deproxy(referenceService.load(newSec));
|
493
|
break;
|
494
|
default:
|
495
|
break;
|
496
|
}
|
497
|
|
498
|
result = changeSynonymToAcceptedTaxon(synonym, acceptedTaxon, newSecRef, microReference, secHandling, deleteSynonym);
|
499
|
Taxon newTaxon = (Taxon)result.getCdmEntity();
|
500
|
|
501
|
TaxonNode newNode = newParentNode.addChildTaxon(newTaxon, null, null);
|
502
|
taxonNodeDao.save(newNode);
|
503
|
result.addUpdatedObject(newTaxon);
|
504
|
result.addUpdatedObject(acceptedTaxon);
|
505
|
result.setCdmEntity(newNode);
|
506
|
return result;
|
507
|
}
|
508
|
|
509
|
@Override
|
510
|
@Transactional(readOnly = false)
|
511
|
public UpdateResult changeSynonymToRelatedTaxon(UUID synonymUuid,
|
512
|
UUID toTaxonUuid,
|
513
|
TaxonRelationshipType taxonRelationshipType,
|
514
|
Reference citation,
|
515
|
String microcitation){
|
516
|
|
517
|
UpdateResult result = new UpdateResult();
|
518
|
Taxon toTaxon = (Taxon) dao.load(toTaxonUuid);
|
519
|
Synonym synonym = (Synonym) dao.load(synonymUuid);
|
520
|
result = changeSynonymToRelatedTaxon(synonym, toTaxon, taxonRelationshipType, citation, microcitation);
|
521
|
Taxon relatedTaxon = (Taxon)result.getCdmEntity();
|
522
|
// result.setCdmEntity(relatedTaxon);
|
523
|
result.addUpdatedObject(relatedTaxon);
|
524
|
result.addUpdatedObject(toTaxon);
|
525
|
return result;
|
526
|
}
|
527
|
|
528
|
@Override
|
529
|
@Transactional(readOnly = false)
|
530
|
public UpdateResult changeSynonymToRelatedTaxon(Synonym synonym, Taxon toTaxon, TaxonRelationshipType taxonRelationshipType, Reference citation, String microcitation){
|
531
|
// Get name from synonym
|
532
|
if (synonym == null){
|
533
|
return null;
|
534
|
}
|
535
|
|
536
|
UpdateResult result = new UpdateResult();
|
537
|
|
538
|
TaxonName synonymName = synonym.getName();
|
539
|
|
540
|
/* // remove synonym from taxon
|
541
|
toTaxon.removeSynonym(synonym);
|
542
|
*/
|
543
|
// Create a taxon with synonym name
|
544
|
Taxon fromTaxon = Taxon.NewInstance(synonymName, null);
|
545
|
fromTaxon.setPublish(synonym.isPublish());
|
546
|
save(fromTaxon);
|
547
|
fromTaxon.setAppendedPhrase(synonym.getAppendedPhrase());
|
548
|
|
549
|
// Add taxon relation
|
550
|
fromTaxon.addTaxonRelation(toTaxon, taxonRelationshipType, citation, microcitation);
|
551
|
result.setCdmEntity(fromTaxon);
|
552
|
// since we are swapping names, we have to detach the name from the synonym completely.
|
553
|
// Otherwise the synonym will still be in the list of typified names.
|
554
|
// synonym.getName().removeTaxonBase(synonym);
|
555
|
result.includeResult(this.deleteSynonym(synonym, null));
|
556
|
|
557
|
return result;
|
558
|
}
|
559
|
|
560
|
@Transactional(readOnly = false)
|
561
|
@Override
|
562
|
public void changeHomotypicalGroupOfSynonym(Synonym synonym, HomotypicalGroup newHomotypicalGroup,
|
563
|
Taxon targetTaxon, boolean setBasionymRelationIfApplicable){
|
564
|
// Get synonym name
|
565
|
TaxonName synonymName = synonym.getName();
|
566
|
HomotypicalGroup oldHomotypicalGroup = synonymName.getHomotypicalGroup();
|
567
|
|
568
|
// Switch groups
|
569
|
oldHomotypicalGroup.removeTypifiedName(synonymName, false);
|
570
|
newHomotypicalGroup.addTypifiedName(synonymName);
|
571
|
|
572
|
//remove existing basionym relationships
|
573
|
synonymName.removeBasionyms();
|
574
|
|
575
|
//add basionym relationship
|
576
|
if (setBasionymRelationIfApplicable){
|
577
|
Set<TaxonName> basionyms = newHomotypicalGroup.getBasionyms();
|
578
|
for (TaxonName basionym : basionyms){
|
579
|
synonymName.addBasionym(basionym);
|
580
|
}
|
581
|
}
|
582
|
|
583
|
//set synonym relationship correctly
|
584
|
Taxon acceptedTaxon = synonym.getAcceptedTaxon();
|
585
|
|
586
|
boolean hasNewTargetTaxon = targetTaxon != null && !targetTaxon.equals(acceptedTaxon);
|
587
|
if (acceptedTaxon != null){
|
588
|
|
589
|
HomotypicalGroup acceptedGroup = acceptedTaxon.getHomotypicGroup();
|
590
|
boolean isHomotypicToTaxon = acceptedGroup.equals(newHomotypicalGroup);
|
591
|
SynonymType newRelationType = isHomotypicToTaxon? SynonymType.HOMOTYPIC_SYNONYM_OF() : SynonymType.HETEROTYPIC_SYNONYM_OF();
|
592
|
synonym.setType(newRelationType);
|
593
|
|
594
|
if (hasNewTargetTaxon){
|
595
|
acceptedTaxon.removeSynonym(synonym, false);
|
596
|
}
|
597
|
}
|
598
|
if (hasNewTargetTaxon ){
|
599
|
@SuppressWarnings("null")
|
600
|
HomotypicalGroup acceptedGroup = targetTaxon.getHomotypicGroup();
|
601
|
boolean isHomotypicToTaxon = acceptedGroup.equals(newHomotypicalGroup);
|
602
|
SynonymType relType = isHomotypicToTaxon? SynonymType.HOMOTYPIC_SYNONYM_OF() : SynonymType.HETEROTYPIC_SYNONYM_OF();
|
603
|
targetTaxon.addSynonym(synonym, relType);
|
604
|
}
|
605
|
}
|
606
|
|
607
|
@Override
|
608
|
@Transactional(readOnly = false)
|
609
|
public UpdateResult updateCaches(Class<? extends TaxonBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonBase> cacheStrategy, IProgressMonitor monitor) {
|
610
|
if (clazz == null){
|
611
|
clazz = TaxonBase.class;
|
612
|
}
|
613
|
return super.updateCachesImpl(clazz, stepSize, cacheStrategy, monitor);
|
614
|
}
|
615
|
|
616
|
@Override
|
617
|
@Autowired
|
618
|
protected void setDao(ITaxonDao dao) {
|
619
|
this.dao = dao;
|
620
|
}
|
621
|
|
622
|
@Override
|
623
|
public <T extends TaxonBase> Pager<T> findTaxaByName(Class<T> clazz, String uninomial, String infragenericEpithet, String specificEpithet,
|
624
|
String infraspecificEpithet, String authorshipCache, Rank rank, Integer pageSize,Integer pageNumber, List<String> propertyPaths) {
|
625
|
long numberOfResults = dao.countTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, authorshipCache, rank);
|
626
|
|
627
|
List<T> results = new ArrayList<>();
|
628
|
if(numberOfResults > 0) { // no point checking again
|
629
|
results = dao.findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, authorshipCache, rank,
|
630
|
pageSize, pageNumber, propertyPaths);
|
631
|
}
|
632
|
|
633
|
return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
|
634
|
}
|
635
|
|
636
|
@Override
|
637
|
public <T extends TaxonBase> List<T> listTaxaByName(Class<T> clazz, String uninomial, String infragenericEpithet, String specificEpithet,
|
638
|
String infraspecificEpithet, String authorshipCache, Rank rank, Integer pageSize,Integer pageNumber, List<String> propertyPaths) {
|
639
|
|
640
|
return findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infragenericEpithet, authorshipCache, rank,
|
641
|
pageSize, pageNumber, propertyPaths).getRecords();
|
642
|
}
|
643
|
|
644
|
@Override
|
645
|
public List<TaxonRelationship> listToTaxonRelationships(Taxon taxon, TaxonRelationshipType type,
|
646
|
boolean includeUnpublished, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){
|
647
|
long numberOfResults = dao.countTaxonRelationships(taxon, type, includeUnpublished, TaxonRelationship.Direction.relatedTo);
|
648
|
|
649
|
List<TaxonRelationship> results = new ArrayList<>();
|
650
|
if(numberOfResults > 0) { // no point checking again
|
651
|
results = dao.getTaxonRelationships(taxon, type, includeUnpublished, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedTo);
|
652
|
}
|
653
|
return results;
|
654
|
}
|
655
|
|
656
|
@Override
|
657
|
public Pager<TaxonRelationship> pageToTaxonRelationships(Taxon taxon, TaxonRelationshipType type,
|
658
|
boolean includeUnpublished, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
|
659
|
long numberOfResults = dao.countTaxonRelationships(taxon, type, includeUnpublished, TaxonRelationship.Direction.relatedTo);
|
660
|
|
661
|
List<TaxonRelationship> results = new ArrayList<>();
|
662
|
if(numberOfResults > 0) { // no point checking again
|
663
|
results = dao.getTaxonRelationships(taxon, type, includeUnpublished, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedTo);
|
664
|
}
|
665
|
return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
|
666
|
}
|
667
|
|
668
|
@Override
|
669
|
public List<TaxonRelationship> listFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type,
|
670
|
boolean includeUnpublished, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){
|
671
|
long numberOfResults = dao.countTaxonRelationships(taxon, type, includeUnpublished, TaxonRelationship.Direction.relatedFrom);
|
672
|
|
673
|
List<TaxonRelationship> results = new ArrayList<>();
|
674
|
if(numberOfResults > 0) { // no point checking again
|
675
|
results = dao.getTaxonRelationships(taxon, type, includeUnpublished, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedFrom);
|
676
|
}
|
677
|
return results;
|
678
|
}
|
679
|
|
680
|
@Override
|
681
|
public Pager<TaxonRelationship> pageFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type,
|
682
|
boolean includeUnpublished, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
|
683
|
long numberOfResults = dao.countTaxonRelationships(taxon, type, includeUnpublished, TaxonRelationship.Direction.relatedFrom);
|
684
|
|
685
|
List<TaxonRelationship> results = new ArrayList<>();
|
686
|
if(numberOfResults > 0) { // no point checking again
|
687
|
results = dao.getTaxonRelationships(taxon, type, includeUnpublished, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedFrom);
|
688
|
}
|
689
|
return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
|
690
|
}
|
691
|
|
692
|
@Override
|
693
|
public List<TaxonRelationship> listTaxonRelationships(Set<TaxonRelationshipType> types,
|
694
|
Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
|
695
|
|
696
|
Long numberOfResults = dao.countTaxonRelationships(types);
|
697
|
List<TaxonRelationship> results = new ArrayList<>();
|
698
|
if(numberOfResults > 0) {
|
699
|
results = dao.getTaxonRelationships(types, pageSize, pageNumber, orderHints, propertyPaths);
|
700
|
}
|
701
|
return results;
|
702
|
}
|
703
|
|
704
|
@Override
|
705
|
public <S extends TaxonBase> Pager<S> page(Class<S> clazz, List<Restriction<?>> restrictions, Integer pageSize,
|
706
|
Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths) {
|
707
|
return page(clazz, restrictions, pageSize, pageIndex, orderHints, propertyPaths, INCLUDE_UNPUBLISHED);
|
708
|
}
|
709
|
|
710
|
@Override
|
711
|
public <S extends TaxonBase> Pager<S> page(Class<S> clazz, List<Restriction<?>> restrictions, Integer pageSize,
|
712
|
Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths, boolean includeUnpublished) {
|
713
|
|
714
|
List<S> records;
|
715
|
long resultSize = dao.count(clazz, restrictions);
|
716
|
if(AbstractPagerImpl.hasResultsInRange(resultSize, pageIndex, pageSize)){
|
717
|
records = dao.list(clazz, restrictions, pageSize, pageIndex, orderHints, propertyPaths, includeUnpublished);
|
718
|
} else {
|
719
|
records = new ArrayList<>();
|
720
|
}
|
721
|
Pager<S> pager = new DefaultPagerImpl<>(pageIndex, resultSize, pageSize, records);
|
722
|
return pager;
|
723
|
}
|
724
|
|
725
|
@Override
|
726
|
public Taxon findAcceptedTaxonFor(UUID synonymUuid, UUID classificationUuid,
|
727
|
boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException{
|
728
|
|
729
|
Synonym synonym = null;
|
730
|
|
731
|
try {
|
732
|
synonym = (Synonym) dao.load(synonymUuid);
|
733
|
checkPublished(synonym, includeUnpublished, "Synoym is unpublished.");
|
734
|
} catch (ClassCastException e){
|
735
|
throw new EntityNotFoundException("The TaxonBase entity referenced by " + synonymUuid + " is not a Synonmy");
|
736
|
} catch (NullPointerException e){
|
737
|
throw new EntityNotFoundException("No TaxonBase entity found for " + synonymUuid);
|
738
|
}
|
739
|
|
740
|
Classification classificationFilter = null;
|
741
|
if(classificationUuid != null){
|
742
|
try {
|
743
|
classificationFilter = classificationDao.load(classificationUuid);
|
744
|
} catch (NullPointerException e){
|
745
|
//TODO not sure, why an NPE should be thrown in the above load method
|
746
|
throw new EntityNotFoundException("No Classification entity found for " + classificationUuid);
|
747
|
}
|
748
|
if(classificationFilter == null){
|
749
|
throw new EntityNotFoundException("No Classification entity found for " + classificationUuid);
|
750
|
}
|
751
|
}
|
752
|
|
753
|
long count = dao.countAcceptedTaxonFor(synonym, classificationFilter) ;
|
754
|
if(count > 0){
|
755
|
Taxon result = dao.acceptedTaxonFor(synonym, classificationFilter, propertyPaths);
|
756
|
checkPublished(result, includeUnpublished, "Accepted taxon unpublished");
|
757
|
return result;
|
758
|
}else{
|
759
|
return null;
|
760
|
}
|
761
|
}
|
762
|
|
763
|
@Override
|
764
|
public Set<Taxon> listRelatedTaxa(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships, Integer maxDepth,
|
765
|
boolean includeUnpublished, Integer limit, Integer start, List<String> propertyPaths) {
|
766
|
|
767
|
Set<Taxon> relatedTaxa = collectRelatedTaxa(taxon, includeRelationships, new HashSet<>(), includeUnpublished, maxDepth);
|
768
|
relatedTaxa.remove(taxon);
|
769
|
beanInitializer.initializeAll(relatedTaxa, propertyPaths);
|
770
|
return relatedTaxa;
|
771
|
}
|
772
|
|
773
|
/**
|
774
|
* Recursively collect related taxa for the given <code>taxon</code> . The returned list will also include the
|
775
|
* <code>taxon</code> supplied as parameter.
|
776
|
*
|
777
|
* @param taxon
|
778
|
* @param includeRelationships
|
779
|
* @param taxa
|
780
|
* @param maxDepth can be <code>null</code> for infinite depth
|
781
|
* @return
|
782
|
*/
|
783
|
private Set<Taxon> collectRelatedTaxa(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships, Set<Taxon> taxa,
|
784
|
boolean includeUnpublished, Integer maxDepth) {
|
785
|
|
786
|
if(taxa.isEmpty()) {
|
787
|
taxa.add(taxon);
|
788
|
}
|
789
|
|
790
|
if(includeRelationships.isEmpty()){
|
791
|
return taxa;
|
792
|
}
|
793
|
|
794
|
if(maxDepth != null) {
|
795
|
maxDepth--;
|
796
|
}
|
797
|
if(logger.isDebugEnabled()){
|
798
|
logger.debug("collecting related taxa for " + taxon + " with maxDepth=" + maxDepth);
|
799
|
}
|
800
|
List<TaxonRelationship> taxonRelationships = dao.getTaxonRelationships(taxon,
|
801
|
(Set<TaxonRelationshipType>)null, includeUnpublished, null, null, null, null, null);
|
802
|
for (TaxonRelationship taxRel : taxonRelationships) {
|
803
|
|
804
|
// skip invalid data
|
805
|
if (taxRel.getToTaxon() == null || taxRel.getFromTaxon() == null || taxRel.getType() == null) {
|
806
|
continue;
|
807
|
}
|
808
|
// filter by includeRelationships
|
809
|
for (TaxonRelationshipEdge relationshipEdgeFilter : includeRelationships) {
|
810
|
if ( relationshipEdgeFilter.getRelationshipTypes().equals(taxRel.getType()) ) {
|
811
|
if (relationshipEdgeFilter.getDirections().contains(Direction.relatedTo) && !taxa.contains(taxRel.getToTaxon())) {
|
812
|
if(logger.isDebugEnabled()){
|
813
|
logger.debug(maxDepth + ": " + taxon.getTitleCache() + " --[" + taxRel.getType().getLabel() + "]--> " + taxRel.getToTaxon().getTitleCache());
|
814
|
}
|
815
|
taxa.add(taxRel.getToTaxon());
|
816
|
if(maxDepth == null || maxDepth > 0) {
|
817
|
taxa.addAll(collectRelatedTaxa(taxRel.getToTaxon(), includeRelationships, taxa, includeUnpublished, maxDepth));
|
818
|
}
|
819
|
}
|
820
|
if(relationshipEdgeFilter.getDirections().contains(Direction.relatedFrom) && !taxa.contains(taxRel.getFromTaxon())) {
|
821
|
taxa.add(taxRel.getFromTaxon());
|
822
|
if(logger.isDebugEnabled()){
|
823
|
logger.debug(maxDepth + ": " +taxRel.getFromTaxon().getTitleCache() + " --[" + taxRel.getType().getLabel() + "]--> " + taxon.getTitleCache() );
|
824
|
}
|
825
|
if(maxDepth == null || maxDepth > 0) {
|
826
|
taxa.addAll(collectRelatedTaxa(taxRel.getFromTaxon(), includeRelationships, taxa, includeUnpublished, maxDepth));
|
827
|
}
|
828
|
}
|
829
|
}
|
830
|
}
|
831
|
}
|
832
|
return taxa;
|
833
|
}
|
834
|
|
835
|
@Override
|
836
|
public Pager<Synonym> getSynonyms(Taxon taxon, SynonymType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
|
837
|
Long numberOfResults = dao.countSynonyms(taxon, type);
|
838
|
|
839
|
List<Synonym> results = new ArrayList<>();
|
840
|
if(numberOfResults > 0) { // no point checking again
|
841
|
results = dao.getSynonyms(taxon, type, pageSize, pageNumber, orderHints, propertyPaths);
|
842
|
}
|
843
|
|
844
|
return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
|
845
|
}
|
846
|
|
847
|
@Override
|
848
|
public List<List<Synonym>> getSynonymsByHomotypicGroup(Taxon taxon, List<String> propertyPaths){
|
849
|
List<List<Synonym>> result = new ArrayList<>();
|
850
|
taxon = (Taxon)dao.load(taxon.getUuid(), propertyPaths);
|
851
|
HomotypicGroupTaxonComparator comparator = new HomotypicGroupTaxonComparator(taxon);
|
852
|
|
853
|
//homotypic
|
854
|
result.add(taxon.getHomotypicSynonymsByHomotypicGroup(comparator));
|
855
|
|
856
|
//heterotypic
|
857
|
List<HomotypicalGroup> homotypicalGroups = taxon.getHeterotypicSynonymyGroups(); //currently the list is sorted by the Taxon.defaultTaxonComparator
|
858
|
for(HomotypicalGroup homotypicalGroup : homotypicalGroups){
|
859
|
result.add(taxon.getSynonymsInGroup(homotypicalGroup, comparator));
|
860
|
}
|
861
|
|
862
|
return result;
|
863
|
}
|
864
|
|
865
|
@Override
|
866
|
public List<Synonym> getHomotypicSynonymsByHomotypicGroup(Taxon taxon, List<String> propertyPaths){
|
867
|
Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);
|
868
|
HomotypicGroupTaxonComparator comparator = new HomotypicGroupTaxonComparator(taxon);
|
869
|
|
870
|
return t.getHomotypicSynonymsByHomotypicGroup(comparator);
|
871
|
}
|
872
|
|
873
|
@Override
|
874
|
public List<List<Synonym>> getHeterotypicSynonymyGroups(Taxon taxon, List<String> propertyPaths){
|
875
|
Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);
|
876
|
List<HomotypicalGroup> homotypicalGroups = t.getHeterotypicSynonymyGroups();
|
877
|
List<List<Synonym>> heterotypicSynonymyGroups = new ArrayList<>(homotypicalGroups.size());
|
878
|
for(HomotypicalGroup homotypicalGroup : homotypicalGroups){
|
879
|
heterotypicSynonymyGroups.add(t.getSynonymsInGroup(homotypicalGroup));
|
880
|
}
|
881
|
return heterotypicSynonymyGroups;
|
882
|
}
|
883
|
|
884
|
@Override
|
885
|
public List<UuidAndTitleCache<? extends IdentifiableEntity>> findTaxaAndNamesForEditor(IFindTaxaAndNamesConfigurator config){
|
886
|
|
887
|
if (config.isDoSynonyms() || config.isDoTaxa() || config.isDoNamesWithoutTaxa() || config.isDoTaxaByCommonNames()){
|
888
|
return dao.getTaxaByNameForEditor(config.isDoTaxa(), config.isDoSynonyms(), config.isDoNamesWithoutTaxa(),
|
889
|
config.isDoMisappliedNames(), config.isDoTaxaByCommonNames(), config.isIncludeUnpublished(), config.isDoIncludeAuthors(),
|
890
|
config.getTitleSearchStringSqlized(), config.getClassification(), config.getSubtree(),
|
891
|
config.getMatchMode(), config.getNamedAreas(), config.getOrder());
|
892
|
}else{
|
893
|
return new ArrayList<>();
|
894
|
}
|
895
|
}
|
896
|
|
897
|
@Override
|
898
|
public Pager<IdentifiableEntity> findTaxaAndNames(IFindTaxaAndNamesConfigurator configurator) {
|
899
|
|
900
|
@SuppressWarnings("rawtypes")
|
901
|
List<IdentifiableEntity> results = new ArrayList<>();
|
902
|
long numberOfResults = 0; // overall number of results (as opposed to number of results per page)
|
903
|
List<TaxonBase> taxa = null;
|
904
|
|
905
|
// Taxa and synonyms
|
906
|
long numberTaxaResults = 0L;
|
907
|
|
908
|
List<String> propertyPath = new ArrayList<>();
|
909
|
if(configurator.getTaxonPropertyPath() != null){
|
910
|
propertyPath.addAll(configurator.getTaxonPropertyPath());
|
911
|
}
|
912
|
|
913
|
if (configurator.isDoMisappliedNames() || configurator.isDoSynonyms() || configurator.isDoTaxa() || configurator.isDoTaxaByCommonNames()){
|
914
|
if(configurator.getPageSize() != null){ // no point counting if we need all anyway
|
915
|
numberTaxaResults =
|
916
|
dao.countTaxaByName(configurator.isDoTaxa(),configurator.isDoSynonyms(), configurator.isDoMisappliedNames(),
|
917
|
configurator.isDoTaxaByCommonNames(), configurator.isDoIncludeAuthors(), configurator.getTitleSearchStringSqlized(),
|
918
|
configurator.getClassification(), configurator.getSubtree(), configurator.getMatchMode(),
|
919
|
configurator.getNamedAreas(), configurator.isIncludeUnpublished());
|
920
|
}
|
921
|
|
922
|
if(configurator.getPageSize() == null || numberTaxaResults > configurator.getPageSize() * configurator.getPageNumber()){ // no point checking again if less results
|
923
|
taxa = dao.getTaxaByName(configurator.isDoTaxa(), configurator.isDoSynonyms(),
|
924
|
configurator.isDoMisappliedNames(), configurator.isDoTaxaByCommonNames(), configurator.isDoIncludeAuthors(),
|
925
|
configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getSubtree(),
|
926
|
configurator.getMatchMode(), configurator.getNamedAreas(), configurator.isIncludeUnpublished(),
|
927
|
configurator.getOrder(), configurator.getPageSize(), configurator.getPageNumber(), propertyPath);
|
928
|
}
|
929
|
}
|
930
|
|
931
|
if (logger.isDebugEnabled()) { logger.debug(numberTaxaResults + " matching taxa counted"); }
|
932
|
|
933
|
if(taxa != null){
|
934
|
results.addAll(taxa);
|
935
|
}
|
936
|
|
937
|
numberOfResults += numberTaxaResults;
|
938
|
|
939
|
// Names without taxa
|
940
|
if (configurator.isDoNamesWithoutTaxa()) {
|
941
|
int numberNameResults = 0;
|
942
|
|
943
|
List<TaxonName> names =
|
944
|
nameDao.findByName(configurator.isDoIncludeAuthors(), configurator.getTitleSearchStringSqlized(), configurator.getMatchMode(),
|
945
|
configurator.getPageSize(), configurator.getPageNumber(), null, configurator.getTaxonNamePropertyPath());
|
946
|
if (logger.isDebugEnabled()) { logger.debug(names.size() + " matching name(s) found"); }
|
947
|
if (names.size() > 0) {
|
948
|
for (TaxonName taxonName : names) {
|
949
|
if (taxonName.getTaxonBases().size() == 0) {
|
950
|
results.add(taxonName);
|
951
|
numberNameResults++;
|
952
|
}
|
953
|
}
|
954
|
if (logger.isDebugEnabled()) { logger.debug(numberNameResults + " matching name(s) without taxa found"); }
|
955
|
numberOfResults += numberNameResults;
|
956
|
}
|
957
|
}
|
958
|
|
959
|
return new DefaultPagerImpl<> (configurator.getPageNumber(), numberOfResults, configurator.getPageSize(), results);
|
960
|
}
|
961
|
|
962
|
public List<UuidAndTitleCache<TaxonBase>> getTaxonUuidAndTitleCache(Integer limit, String pattern){
|
963
|
return dao.getUuidAndTitleCache(limit, pattern);
|
964
|
}
|
965
|
|
966
|
@Override
|
967
|
public List<Media> listTaxonDescriptionMedia(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships, boolean limitToGalleries, List<String> propertyPath){
|
968
|
return listMedia(taxon, includeRelationships, limitToGalleries, true, false, false, propertyPath);
|
969
|
}
|
970
|
|
971
|
@Override
|
972
|
public List<Media> listMedia(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships,
|
973
|
Boolean limitToGalleries, Boolean includeTaxonDescriptions, Boolean includeOccurrences,
|
974
|
Boolean includeTaxonNameDescriptions, List<String> propertyPath) {
|
975
|
return listMedia(taxon, includeRelationships, limitToGalleries, includeTaxonDescriptions, includeOccurrences, false,
|
976
|
includeTaxonNameDescriptions, propertyPath);
|
977
|
}
|
978
|
|
979
|
@Override
|
980
|
public List<Media> listMedia(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships,
|
981
|
Boolean limitToGalleries, Boolean includeTaxonDescriptions, Boolean includeOccurrences, Boolean includeOriginalOccurences,
|
982
|
Boolean includeTaxonNameDescriptions, List<String> propertyPath) {
|
983
|
|
984
|
//TODO let inherit
|
985
|
boolean includeUnpublished = INCLUDE_UNPUBLISHED;
|
986
|
|
987
|
// LogUtils.setLevel(logger, Level.TRACE);
|
988
|
// LogUtils.setLevel("org.hibernate.SQL", Level.TRACE);
|
989
|
|
990
|
if (logger.isTraceEnabled()){logger.trace("listMedia() - START");}
|
991
|
|
992
|
Set<Taxon> taxa = new HashSet<>();
|
993
|
List<Media> taxonMedia = new ArrayList<>();
|
994
|
List<Media> nonImageGalleryImages = new ArrayList<>();
|
995
|
|
996
|
if (limitToGalleries == null) {
|
997
|
limitToGalleries = false;
|
998
|
}
|
999
|
|
1000
|
// --- resolve related taxa
|
1001
|
if (includeRelationships != null && ! includeRelationships.isEmpty()) {
|
1002
|
if (logger.isTraceEnabled()){logger.trace("listMedia() - resolve related taxa");}
|
1003
|
taxa = listRelatedTaxa(taxon, includeRelationships, null, includeUnpublished, null, null, null);
|
1004
|
}
|
1005
|
|
1006
|
taxa.add((Taxon) dao.load(taxon.getUuid()));
|
1007
|
|
1008
|
if(includeTaxonDescriptions != null && includeTaxonDescriptions){
|
1009
|
if (logger.isTraceEnabled()){logger.trace("listMedia() - includeTaxonDescriptions");}
|
1010
|
List<TaxonDescription> taxonDescriptions = new ArrayList<>();
|
1011
|
// --- TaxonDescriptions
|
1012
|
for (Taxon t : taxa) {
|
1013
|
taxonDescriptions.addAll(descriptionService.listTaxonDescriptions(t, null, null, null, null, propertyPath));
|
1014
|
}
|
1015
|
for (TaxonDescription taxonDescription : taxonDescriptions) {
|
1016
|
if (!limitToGalleries || taxonDescription.isImageGallery()) {
|
1017
|
for (DescriptionElementBase element : taxonDescription.getElements()) {
|
1018
|
for (Media media : element.getMedia()) {
|
1019
|
if(taxonDescription.isImageGallery()){
|
1020
|
taxonMedia.add(media);
|
1021
|
}
|
1022
|
else{
|
1023
|
nonImageGalleryImages.add(media);
|
1024
|
}
|
1025
|
}
|
1026
|
}
|
1027
|
}
|
1028
|
}
|
1029
|
//put images from image gallery first (#3242)
|
1030
|
taxonMedia.addAll(nonImageGalleryImages);
|
1031
|
}
|
1032
|
|
1033
|
|
1034
|
if(includeOccurrences != null && includeOccurrences) {
|
1035
|
if (logger.isTraceEnabled()){logger.trace("listMedia() - includeOccurrences");}
|
1036
|
@SuppressWarnings("rawtypes")
|
1037
|
Set<SpecimenOrObservationBase> specimensOrObservations = new HashSet<>();
|
1038
|
// --- Specimens
|
1039
|
for (Taxon t : taxa) {
|
1040
|
specimensOrObservations.addAll(occurrenceDao.listByAssociatedTaxon(null, t, null, null, null, null));
|
1041
|
}
|
1042
|
for (SpecimenOrObservationBase<?> occurrence : specimensOrObservations) {
|
1043
|
|
1044
|
// direct media removed from specimen #3597
|
1045
|
// taxonMedia.addAll(occurrence.getMedia());
|
1046
|
|
1047
|
// SpecimenDescriptions
|
1048
|
Set<SpecimenDescription> specimenDescriptions = occurrence.getSpecimenDescriptions();
|
1049
|
for (DescriptionBase<?> specimenDescription : specimenDescriptions) {
|
1050
|
if (!limitToGalleries || specimenDescription.isImageGallery()) {
|
1051
|
Set<DescriptionElementBase> elements = specimenDescription.getElements();
|
1052
|
for (DescriptionElementBase element : elements) {
|
1053
|
for (Media media : element.getMedia()) {
|
1054
|
taxonMedia.add(media);
|
1055
|
}
|
1056
|
}
|
1057
|
}
|
1058
|
}
|
1059
|
|
1060
|
if (occurrence.isInstanceOf(DerivedUnit.class)) {
|
1061
|
DerivedUnit derivedUnit = CdmBase.deproxy(occurrence, DerivedUnit.class);
|
1062
|
// Collection
|
1063
|
//TODO why may collections have media attached? #
|
1064
|
if (derivedUnit.getCollection() != null){
|
1065
|
taxonMedia.addAll(derivedUnit.getCollection().getMedia());
|
1066
|
}
|
1067
|
}
|
1068
|
//media in hierarchy
|
1069
|
|
1070
|
taxonMedia.addAll(occurrenceService.getMediaInHierarchy(occurrence, includeOriginalOccurences, includeOccurrences, null, null, propertyPath).getRecords());
|
1071
|
|
1072
|
}
|
1073
|
}
|
1074
|
|
1075
|
if(includeTaxonNameDescriptions != null && includeTaxonNameDescriptions) {
|
1076
|
if (logger.isTraceEnabled()){logger.trace("listMedia() - includeTaxonNameDescriptions");}
|
1077
|
// --- TaxonNameDescription
|
1078
|
Set<TaxonNameDescription> nameDescriptions = new HashSet<>();
|
1079
|
for (Taxon t : taxa) {
|
1080
|
nameDescriptions.addAll(t.getName().getDescriptions());
|
1081
|
}
|
1082
|
for(TaxonNameDescription nameDescription: nameDescriptions){
|
1083
|
if (!limitToGalleries || nameDescription.isImageGallery()) {
|
1084
|
Set<DescriptionElementBase> elements = nameDescription.getElements();
|
1085
|
for (DescriptionElementBase element : elements) {
|
1086
|
for (Media media : element.getMedia()) {
|
1087
|
taxonMedia.add(media);
|
1088
|
}
|
1089
|
}
|
1090
|
}
|
1091
|
}
|
1092
|
}
|
1093
|
|
1094
|
taxonMedia = deduplicateMedia(taxonMedia);
|
1095
|
|
1096
|
if (logger.isTraceEnabled()){logger.trace("listMedia() - initialize");}
|
1097
|
beanInitializer.initializeAll(taxonMedia, propertyPath);
|
1098
|
|
1099
|
if (logger.isTraceEnabled()){logger.trace("listMedia() - END");}
|
1100
|
|
1101
|
return taxonMedia;
|
1102
|
}
|
1103
|
|
1104
|
private List<Media> deduplicateMedia(List<Media> taxonMedia) {
|
1105
|
return taxonMedia.stream().distinct().collect(Collectors.toList());
|
1106
|
}
|
1107
|
|
1108
|
@Override
|
1109
|
public List<TaxonBase> findTaxaByID(Set<Integer> listOfIDs) {
|
1110
|
return this.dao.loadList(listOfIDs, null, null);
|
1111
|
}
|
1112
|
|
1113
|
@Override
|
1114
|
public TaxonBase findTaxonByUuid(UUID uuid, List<String> propertyPaths){
|
1115
|
return this.dao.findByUuid(uuid, null ,propertyPaths);
|
1116
|
}
|
1117
|
|
1118
|
@Override
|
1119
|
public long countSynonyms(boolean onlyAttachedToTaxon){
|
1120
|
return this.dao.countSynonyms(onlyAttachedToTaxon);
|
1121
|
}
|
1122
|
|
1123
|
@Override
|
1124
|
@Transactional(readOnly=false)
|
1125
|
public DeleteResult deleteTaxon(UUID taxonUUID, TaxonDeletionConfigurator config, UUID classificationUuid) {
|
1126
|
|
1127
|
if (config == null){
|
1128
|
config = new TaxonDeletionConfigurator();
|
1129
|
}
|
1130
|
Taxon taxon = (Taxon)dao.load(taxonUUID);
|
1131
|
DeleteResult result = new DeleteResult();
|
1132
|
if (taxon == null){
|
1133
|
result.setAbort();
|
1134
|
result.addException(new Exception ("The taxon was already deleted."));
|
1135
|
return result;
|
1136
|
}
|
1137
|
taxon = HibernateProxyHelper.deproxy(taxon);
|
1138
|
Classification classification = HibernateProxyHelper.deproxy(classificationDao.load(classificationUuid), Classification.class);
|
1139
|
config.setClassificationUuid(classificationUuid);
|
1140
|
result = isDeletable(taxonUUID, config);
|
1141
|
|
1142
|
if (result.isOk()){
|
1143
|
// --- DeleteSynonymRelations
|
1144
|
if (config.isDeleteSynonymRelations()){
|
1145
|
boolean removeSynonymNameFromHomotypicalGroup = false;
|
1146
|
// use tmp Set to avoid concurrent modification
|
1147
|
Set<Synonym> synsToDelete = new HashSet<>();
|
1148
|
synsToDelete.addAll(taxon.getSynonyms());
|
1149
|
for (Synonym synonym : synsToDelete){
|
1150
|
taxon.removeSynonym(synonym, removeSynonymNameFromHomotypicalGroup);
|
1151
|
|
1152
|
// --- DeleteSynonymsIfPossible
|
1153
|
if (config.isDeleteSynonymsIfPossible()){
|
1154
|
//TODO which value
|
1155
|
boolean newHomotypicGroupIfNeeded = true;
|
1156
|
SynonymDeletionConfigurator synConfig = new SynonymDeletionConfigurator();
|
1157
|
result.includeResult(deleteSynonym(synonym, synConfig));
|
1158
|
}
|
1159
|
}
|
1160
|
}
|
1161
|
|
1162
|
// --- DeleteTaxonRelationships
|
1163
|
if (! config.isDeleteTaxonRelationships()){
|
1164
|
if (taxon.getTaxonRelations().size() > 0){
|
1165
|
result.setAbort();
|
1166
|
result.addException(new Exception("Taxon can't be deleted as it is related to another taxon. " +
|
1167
|
"Remove taxon from all relations to other taxa prior to deletion."));
|
1168
|
}
|
1169
|
} else{
|
1170
|
TaxonDeletionConfigurator configRelTaxon = new TaxonDeletionConfigurator();
|
1171
|
configRelTaxon.setDeleteTaxonNodes(false);
|
1172
|
configRelTaxon.setDeleteConceptRelationships(true);
|
1173
|
|
1174
|
for (TaxonRelationship taxRel: taxon.getTaxonRelations()){
|
1175
|
if (config.isDeleteMisappliedNames()
|
1176
|
&& taxRel.getType().isMisappliedName()
|
1177
|
&& taxon.equals(taxRel.getToTaxon())){
|
1178
|
this.deleteTaxon(taxRel.getFromTaxon().getUuid(), config, classificationUuid);
|
1179
|
} else if (config.isDeleteConceptRelationships() && taxRel.getType().isConceptRelationship()){
|
1180
|
|
1181
|
if (taxon.equals(taxRel.getToTaxon()) && isDeletable(taxRel.getFromTaxon().getUuid(), configRelTaxon).isOk()){
|
1182
|
this.deleteTaxon(taxRel.getFromTaxon().getUuid(), configRelTaxon, classificationUuid);
|
1183
|
}else if (isDeletable(taxRel.getToTaxon().getUuid(), configRelTaxon).isOk()){
|
1184
|
this.deleteTaxon(taxRel.getToTaxon().getUuid(), configRelTaxon, classificationUuid);
|
1185
|
}
|
1186
|
}
|
1187
|
taxon.removeTaxonRelation(taxRel);
|
1188
|
}
|
1189
|
}
|
1190
|
|
1191
|
// TaxonDescription
|
1192
|
if (config.isDeleteDescriptions()){
|
1193
|
Set<TaxonDescription> descriptions = taxon.getDescriptions();
|
1194
|
List<TaxonDescription> removeDescriptions = new ArrayList<>();
|
1195
|
for (TaxonDescription desc: descriptions){
|
1196
|
//TODO use description delete configurator ?
|
1197
|
//FIXME check if description is ALWAYS deletable
|
1198
|
if (desc.getDescribedSpecimenOrObservation() != null){
|
1199
|
result.setAbort();
|
1200
|
result.addException(new Exception("Taxon can't be deleted as it is used in a TaxonDescription" +
|
1201
|
" which also describes specimens or observations"));
|
1202
|
break;
|
1203
|
}
|
1204
|
removeDescriptions.add(desc);
|
1205
|
}
|
1206
|
if (result.isOk()){
|
1207
|
for (TaxonDescription desc: removeDescriptions){
|
1208
|
taxon.removeDescription(desc);
|
1209
|
descriptionService.delete(desc);
|
1210
|
}
|
1211
|
} else {
|
1212
|
return result;
|
1213
|
}
|
1214
|
}
|
1215
|
|
1216
|
if (! config.isDeleteTaxonNodes() || (!config.isDeleteInAllClassifications() && classification == null && taxon.getTaxonNodes().size() > 1)){
|
1217
|
result.addException(new Exception( "Taxon can't be deleted as it is used in more than one classification."));
|
1218
|
}else{
|
1219
|
if (taxon.getTaxonNodes().size() != 0){
|
1220
|
Set<TaxonNode> nodes = taxon.getTaxonNodes();
|
1221
|
Iterator<TaxonNode> iterator = nodes.iterator();
|
1222
|
TaxonNode node = null;
|
1223
|
boolean deleteChildren;
|
1224
|
if (config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.DELETE)){
|
1225
|
deleteChildren = true;
|
1226
|
}else {
|
1227
|
deleteChildren = false;
|
1228
|
}
|
1229
|
boolean success = true;
|
1230
|
if (!config.isDeleteInAllClassifications() && !(classification == null)){
|
1231
|
while (iterator.hasNext()){
|
1232
|
node = iterator.next();
|
1233
|
if (node.getClassification().equals(classification)){
|
1234
|
break;
|
1235
|
}
|
1236
|
node = null;
|
1237
|
}
|
1238
|
if (node != null){
|
1239
|
HibernateProxyHelper.deproxy(node, TaxonNode.class);
|
1240
|
success =taxon.removeTaxonNode(node, deleteChildren);
|
1241
|
nodeService.delete(node);
|
1242
|
result.addDeletedObject(node);
|
1243
|
} else {
|
1244
|
result.setError();
|
1245
|
result.addException(new Exception("The taxon can not be deleted because it is not used in defined classification."));
|
1246
|
}
|
1247
|
} else if (config.isDeleteInAllClassifications()){
|
1248
|
List<TaxonNode> nodesList = new ArrayList<>();
|
1249
|
nodesList.addAll(taxon.getTaxonNodes());
|
1250
|
for (ITaxonTreeNode treeNode: nodesList){
|
1251
|
TaxonNode taxonNode = (TaxonNode) treeNode;
|
1252
|
if(!deleteChildren){
|
1253
|
Object[] childNodes = taxonNode.getChildNodes().toArray();
|
1254
|
for (Object childNode: childNodes){
|
1255
|
TaxonNode childNodeCast = (TaxonNode) childNode;
|
1256
|
taxonNode.getParent().addChildNode(childNodeCast, childNodeCast.getReference(), childNodeCast.getMicroReference());
|
1257
|
}
|
1258
|
}
|
1259
|
}
|
1260
|
config.getTaxonNodeConfig().setDeleteElement(false);
|
1261
|
DeleteResult resultNodes = nodeService.deleteTaxonNodes(nodesList, config);
|
1262
|
if (!resultNodes.isOk()){
|
1263
|
result.addExceptions(resultNodes.getExceptions());
|
1264
|
result.setStatus(resultNodes.getStatus());
|
1265
|
} else {
|
1266
|
result.addUpdatedObjects(resultNodes.getUpdatedObjects());
|
1267
|
}
|
1268
|
}
|
1269
|
if (!success){
|
1270
|
result.setError();
|
1271
|
result.addException(new Exception("The taxon can not be deleted because the taxon node can not be removed."));
|
1272
|
}
|
1273
|
}
|
1274
|
}
|
1275
|
TaxonName name = taxon.getName();
|
1276
|
taxon.setName(null);
|
1277
|
this.saveOrUpdate(taxon);
|
1278
|
|
1279
|
if ((taxon.getTaxonNodes() == null || taxon.getTaxonNodes().size()== 0) && result.isOk()){
|
1280
|
try{
|
1281
|
dao.delete(taxon);
|
1282
|
result.addDeletedObject(taxon);
|
1283
|
}catch(Exception e){
|
1284
|
result.addException(e);
|
1285
|
result.setError();
|
1286
|
}
|
1287
|
} else {
|
1288
|
result.setError();
|
1289
|
result.addException(new Exception("The Taxon can't be deleted because it is used in a classification."));
|
1290
|
|
1291
|
}
|
1292
|
//TaxonName
|
1293
|
if (config.isDeleteNameIfPossible() && result.isOk()){
|
1294
|
DeleteResult nameResult = new DeleteResult();
|
1295
|
//remove name if possible (and required)
|
1296
|
if (name != null ){
|
1297
|
nameResult = nameService.delete(name.getUuid(), config.getNameDeletionConfig());
|
1298
|
}
|
1299
|
if (nameResult.isError() || nameResult.isAbort()){
|
1300
|
result.addRelatedObject(name);
|
1301
|
result.addExceptions(nameResult.getExceptions());
|
1302
|
}else{
|
1303
|
result.includeResult(nameResult);
|
1304
|
}
|
1305
|
}
|
1306
|
}
|
1307
|
|
1308
|
return result;
|
1309
|
}
|
1310
|
|
1311
|
@Override
|
1312
|
@Transactional(readOnly = false)
|
1313
|
public DeleteResult delete(UUID synUUID){
|
1314
|
Synonym syn = (Synonym)dao.load(synUUID);
|
1315
|
return this.deleteSynonym(syn, null);
|
1316
|
}
|
1317
|
|
1318
|
@Override
|
1319
|
@Transactional(readOnly = false)
|
1320
|
public DeleteResult deleteSynonym(UUID synonymUuid, SynonymDeletionConfigurator config) {
|
1321
|
return deleteSynonym((Synonym)dao.load(synonymUuid), config);
|
1322
|
}
|
1323
|
|
1324
|
@Override
|
1325
|
@Transactional(readOnly = false)
|
1326
|
public DeleteResult deleteSynonym(Synonym synonym, SynonymDeletionConfigurator config) {
|
1327
|
DeleteResult result = new DeleteResult();
|
1328
|
if (synonym == null){
|
1329
|
result.setAbort();
|
1330
|
result.addException(new Exception("The synonym was already deleted."));
|
1331
|
return result;
|
1332
|
}
|
1333
|
|
1334
|
if (config == null){
|
1335
|
config = new SynonymDeletionConfigurator();
|
1336
|
}
|
1337
|
|
1338
|
result = isDeletable(synonym.getUuid(), config);
|
1339
|
|
1340
|
if (result.isOk()){
|
1341
|
|
1342
|
synonym = HibernateProxyHelper.deproxy(this.load(synonym.getUuid()), Synonym.class);
|
1343
|
|
1344
|
//remove synonym
|
1345
|
Taxon accTaxon = synonym.getAcceptedTaxon();
|
1346
|
|
1347
|
if (accTaxon != null){
|
1348
|
accTaxon = HibernateProxyHelper.deproxy(accTaxon, Taxon.class);
|
1349
|
accTaxon.removeSynonym(synonym, false);
|
1350
|
this.saveOrUpdate(accTaxon);
|
1351
|
result.addUpdatedObject(accTaxon);
|
1352
|
}
|
1353
|
this.saveOrUpdate(synonym);
|
1354
|
//#6281
|
1355
|
dao.flush();
|
1356
|
|
1357
|
TaxonName name = synonym.getName();
|
1358
|
synonym.setName(null);
|
1359
|
|
1360
|
dao.delete(synonym);
|
1361
|
result.addDeletedObject(synonym);
|
1362
|
|
1363
|
//remove name if possible (and required)
|
1364
|
if (name != null && config.isDeleteNameIfPossible()){
|
1365
|
|
1366
|
DeleteResult nameDeleteResult = nameService.delete(name, config.getNameDeletionConfig());
|
1367
|
if (nameDeleteResult.isAbort() || nameDeleteResult.isError()){
|
1368
|
result.addExceptions(nameDeleteResult.getExceptions());
|
1369
|
result.addRelatedObject(name);
|
1370
|
result.addUpdatedObject(name);
|
1371
|
}else{
|
1372
|
result.addDeletedObject(name);
|
1373
|
}
|
1374
|
}
|
1375
|
}
|
1376
|
return result;
|
1377
|
}
|
1378
|
|
1379
|
@Override
|
1380
|
public Map<String, Map<UUID,Set<TaxonName>>> findIdenticalTaxonNames(List<UUID> sourceRefUuids, List<String> propertyPaths) {
|
1381
|
return this.dao.findIdenticalNames(sourceRefUuids, propertyPaths);
|
1382
|
}
|
1383
|
|
1384
|
@Override
|
1385
|
public Taxon findBestMatchingTaxon(String taxonName) {
|
1386
|
MatchingTaxonConfigurator config = MatchingTaxonConfigurator.NewInstance();
|
1387
|
config.setTaxonNameTitle(taxonName);
|
1388
|
return findBestMatchingTaxon(config);
|
1389
|
}
|
1390
|
|
1391
|
@Override
|
1392
|
public Taxon findBestMatchingTaxon(MatchingTaxonConfigurator config) {
|
1393
|
|
1394
|
Taxon bestCandidate = null;
|
1395
|
try{
|
1396
|
// 1. search for accepted taxa
|
1397
|
List<TaxonBase> taxonList = dao.findByNameTitleCache(true, false, config.isIncludeUnpublished(),
|
1398
|
config.getTaxonNameTitle(), null, null, MatchMode.EXACT, null, null, 0, null, null);
|
1399
|
boolean bestCandidateMatchesSecUuid = false;
|
1400
|
boolean bestCandidateIsInClassification = false;
|
1401
|
int countEqualCandidates = 0;
|
1402
|
for(TaxonBase<?> taxonBaseCandidate : taxonList){
|
1403
|
if(taxonBaseCandidate instanceof Taxon){
|
1404
|
Taxon newCanditate = CdmBase.deproxy(taxonBaseCandidate, Taxon.class);
|
1405
|
boolean newCandidateMatchesSecUuid = isMatchesSecUuid(newCanditate, config);
|
1406
|
if (! newCandidateMatchesSecUuid && config.isOnlyMatchingSecUuid() ){
|
1407
|
continue;
|
1408
|
}else if(newCandidateMatchesSecUuid && ! bestCandidateMatchesSecUuid){
|
1409
|
bestCandidate = newCanditate;
|
1410
|
countEqualCandidates = 1;
|
1411
|
bestCandidateMatchesSecUuid = true;
|
1412
|
continue;
|
1413
|
}
|
1414
|
|
1415
|
boolean newCandidateInClassification = isInClassification(newCanditate, config);
|
1416
|
if (! newCandidateInClassification && config.isOnlyMatchingClassificationUuid()){
|
1417
|
continue;
|
1418
|
}else if (newCandidateInClassification && ! bestCandidateIsInClassification){
|
1419
|
bestCandidate = newCanditate;
|
1420
|
countEqualCandidates = 1;
|
1421
|
bestCandidateIsInClassification = true;
|
1422
|
continue;
|
1423
|
}
|
1424
|
if (bestCandidate == null){
|
1425
|
bestCandidate = newCanditate;
|
1426
|
countEqualCandidates = 1;
|
1427
|
continue;
|
1428
|
}
|
1429
|
}else{ //not Taxon.class
|
1430
|
continue;
|
1431
|
}
|
1432
|
countEqualCandidates++;
|
1433
|
|
1434
|
}
|
1435
|
if (bestCandidate != null){
|
1436
|
if(countEqualCandidates > 1){
|
1437
|
logger.info(countEqualCandidates + " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate.getTitleCache());
|
1438
|
return bestCandidate;
|
1439
|
} else {
|
1440
|
logger.info("using accepted Taxon: " + bestCandidate.getTitleCache());
|
1441
|
return bestCandidate;
|
1442
|
}
|
1443
|
}
|
1444
|
|
1445
|
// 2. search for synonyms
|
1446
|
if (config.isIncludeSynonyms()){
|
1447
|
List<TaxonBase> synonymList = dao.findByNameTitleCache(false, true, config.isIncludeUnpublished(),
|
1448
|
config.getTaxonNameTitle(), null, null, MatchMode.EXACT, null, null, 0, null, null);
|
1449
|
for(TaxonBase taxonBase : synonymList){
|
1450
|
if(taxonBase instanceof Synonym){
|
1451
|
Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
|
1452
|
bestCandidate = synonym.getAcceptedTaxon();
|
1453
|
if(bestCandidate != null){
|
1454
|
logger.info("using accepted Taxon " + bestCandidate.getTitleCache() + " for synonym " + taxonBase.getTitleCache());
|
1455
|
return bestCandidate;
|
1456
|
}
|
1457
|
//TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
|
1458
|
}
|
1459
|
}
|
1460
|
}
|
1461
|
|
1462
|
} catch (Exception e){
|
1463
|
logger.error(e);
|
1464
|
e.printStackTrace();
|
1465
|
}
|
1466
|
|
1467
|
return bestCandidate;
|
1468
|
}
|
1469
|
|
1470
|
private boolean isInClassification(Taxon taxon, MatchingTaxonConfigurator config) {
|
1471
|
UUID configClassificationUuid = config.getClassificationUuid();
|
1472
|
if (configClassificationUuid == null){
|
1473
|
return false;
|
1474
|
}
|
1475
|
for (TaxonNode node : taxon.getTaxonNodes()){
|
1476
|
UUID classUuid = node.getClassification().getUuid();
|
1477
|
if (configClassificationUuid.equals(classUuid)){
|
1478
|
return true;
|
1479
|
}
|
1480
|
}
|
1481
|
return false;
|
1482
|
}
|
1483
|
|
1484
|
private boolean isMatchesSecUuid(Taxon taxon, MatchingTaxonConfigurator config) {
|
1485
|
UUID configSecUuid = config.getSecUuid();
|
1486
|
if (configSecUuid == null){
|
1487
|
return false;
|
1488
|
}
|
1489
|
UUID taxonSecUuid = (taxon.getSec() == null)? null : taxon.getSec().getUuid();
|
1490
|
return configSecUuid.equals(taxonSecUuid);
|
1491
|
}
|
1492
|
|
1493
|
@Override
|
1494
|
public Synonym findBestMatchingSynonym(String taxonName, boolean includeUnpublished) {
|
1495
|
List<TaxonBase> synonymList = dao.findByNameTitleCache(false, true, includeUnpublished, taxonName, null, null, MatchMode.EXACT, null, null, 0, null, null);
|
1496
|
if(! synonymList.isEmpty()){
|
1497
|
Synonym result = CdmBase.deproxy(synonymList.iterator().next(), Synonym.class);
|
1498
|
if(synonymList.size() == 1){
|
1499
|
logger.info(synonymList.size() + " Synonym found " + result.getTitleCache() );
|
1500
|
return result;
|
1501
|
} else {
|
1502
|
logger.info("Several matching synonyms found. Using first: " + result.getTitleCache());
|
1503
|
return result;
|
1504
|
}
|
1505
|
}
|
1506
|
return null;
|
1507
|
}
|
1508
|
|
1509
|
@Override
|
1510
|
@Transactional(readOnly = false)
|
1511
|
public UpdateResult moveSynonymToAnotherTaxon(Synonym oldSynonym, UUID newTaxonUUID, boolean moveHomotypicGroup,
|
1512
|
SynonymType newSynonymType, UUID newSecundumUuid, String newSecundumDetail,
|
1513
|
boolean keepSecundumIfUndefined) throws HomotypicalGroupChangeException {
|
1514
|
|
1515
|
UpdateResult result = new UpdateResult();
|
1516
|
Taxon newTaxon = CdmBase.deproxy(dao.load(newTaxonUUID), Taxon.class);
|
1517
|
result = moveSynonymToAnotherTaxon(oldSynonym, newTaxon, moveHomotypicGroup, newSynonymType,
|
1518
|
newSecundumUuid, newSecundumDetail, keepSecundumIfUndefined);
|
1519
|
|
1520
|
return result;
|
1521
|
}
|
1522
|
|
1523
|
@Override
|
1524
|
@Transactional(readOnly = false)
|
1525
|
public UpdateResult moveSynonymToAnotherTaxon(Synonym oldSynonym,
|
1526
|
Taxon newTaxon,
|
1527
|
boolean moveHomotypicGroup,
|
1528
|
SynonymType newSynonymType) throws HomotypicalGroupChangeException {
|
1529
|
return moveSynonymToAnotherTaxon(oldSynonym, newTaxon, moveHomotypicGroup,
|
1530
|
newSynonymType,
|
1531
|
oldSynonym.getSec()!= null? oldSynonym.getSec().getUuid(): null,
|
1532
|
oldSynonym.getSecMicroReference(),
|
1533
|
true);
|
1534
|
}
|
1535
|
|
1536
|
@Override
|
1537
|
@Transactional(readOnly = false)
|
1538
|
public UpdateResult moveSynonymToAnotherTaxon(Synonym oldSynonym,
|
1539
|
Taxon newTaxon,
|
1540
|
boolean moveHomotypicGroup,
|
1541
|
SynonymType newSynonymType,
|
1542
|
UUID newSecundumUuid,
|
1543
|
String newSecundumDetail,
|
1544
|
boolean keepSecundumIfUndefined) throws HomotypicalGroupChangeException {
|
1545
|
|
1546
|
Synonym synonym = CdmBase.deproxy(dao.load(oldSynonym.getUuid()), Synonym.class);
|
1547
|
Taxon oldTaxon = CdmBase.deproxy(dao.load(synonym.getAcceptedTaxon().getUuid()), Taxon.class);
|
1548
|
//TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
|
1549
|
TaxonName synonymName = synonym.getName();
|
1550
|
TaxonName fromTaxonName = oldTaxon.getName();
|
1551
|
//set default relationship type
|
1552
|
if (newSynonymType == null){
|
1553
|
newSynonymType = SynonymType.HETEROTYPIC_SYNONYM_OF();
|
1554
|
}
|
1555
|
boolean newRelTypeIsHomotypic = newSynonymType.equals(SynonymType.HOMOTYPIC_SYNONYM_OF());
|
1556
|
|
1557
|
HomotypicalGroup homotypicGroup = synonymName.getHomotypicalGroup();
|
1558
|
int hgSize = homotypicGroup.getTypifiedNames().size();
|
1559
|
boolean isSingleInGroup = !(hgSize > 1);
|
1560
|
|
1561
|
if (! isSingleInGroup){
|
1562
|
boolean isHomotypicToAccepted = synonymName.isHomotypic(fromTaxonName);
|
1563
|
boolean hasHomotypicSynonymRelatives = isHomotypicToAccepted ? hgSize > 2 : hgSize > 1;
|
1564
|
if (isHomotypicToAccepted){
|
1565
|
String message = "Synonym is in homotypic group with accepted taxon%s. First remove synonym from homotypic group of accepted taxon before moving to other taxon.";
|
1566
|
String homotypicRelatives = hasHomotypicSynonymRelatives ? " and other synonym(s)":"";
|
1567
|
message = String.format(message, homotypicRelatives);
|
1568
|
throw new HomotypicalGroupChangeException(message);
|
1569
|
}
|
1570
|
if (! moveHomotypicGroup){
|
1571
|
String message = "Synonym is in homotypic group with other synonym(s). Either move complete homotypic group or remove synonym from homotypic group prior to moving to other taxon.";
|
1572
|
throw new HomotypicalGroupChangeException(message);
|
1573
|
}
|
1574
|
}else{
|
1575
|
moveHomotypicGroup = true; //single synonym always allows to moveCompleteGroup
|
1576
|
}
|
1577
|
// Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
|
1578
|
|
1579
|
UpdateResult result = new UpdateResult();
|
1580
|
//move all synonyms to new taxon
|
1581
|
List<Synonym> homotypicSynonyms = oldTaxon.getSynonymsInGroup(homotypicGroup);
|
1582
|
Reference newSecundum = referenceService.load(newSecundumUuid);
|
1583
|
for (Synonym synRelation: homotypicSynonyms){
|
1584
|
|
1585
|
newTaxon = HibernateProxyHelper.deproxy(newTaxon, Taxon.class);
|
1586
|
oldTaxon = HibernateProxyHelper.deproxy(oldTaxon, Taxon.class);
|
1587
|
oldTaxon.removeSynonym(synRelation, false);
|
1588
|
newTaxon.addSynonym(synRelation, newSynonymType);
|
1589
|
|
1590
|
if (newSecundum != null || !keepSecundumIfUndefined){
|
1591
|
synRelation.setSec(newSecundum);
|
1592
|
}
|
1593
|
if (newSecundumDetail != null || !keepSecundumIfUndefined){
|
1594
|
synRelation.setSecMicroReference(newSecundumDetail);
|
1595
|
}
|
1596
|
|
1597
|
//set result //why is this needed? Seems wrong to me (AM 10.10.2016)
|
1598
|
if (!synRelation.equals(oldSynonym)){
|
1599
|
result.setError();
|
1600
|
}
|
1601
|
}
|
1602
|
|
1603
|
result.addUpdatedObject(oldTaxon);
|
1604
|
result.addUpdatedObject(newTaxon);
|
1605
|
result.addUpdatedObject(homotypicGroup);
|
1606
|
result.addUpdatedObject(synonym);
|
1607
|
saveOrUpdate(oldTaxon);
|
1608
|
saveOrUpdate(newTaxon);
|
1609
|
|
1610
|
return result;
|
1611
|
}
|
1612
|
|
1613
|
@Override
|
1614
|
public <T extends TaxonBase>List<UuidAndTitleCache<T>> getUuidAndTitleCache(Class<T> clazz, Integer limit, String pattern) {
|
1615
|
|
1616
|
return dao.getUuidAndTitleCache(clazz, limit, pattern);
|
1617
|
}
|
1618
|
|
1619
|
@Override
|
1620
|
public Pager<SearchResult<TaxonBase>> findByFullText(
|
1621
|
Class<? extends TaxonBase> clazz, String queryString,
|
1622
|
Classification classification, TaxonNode subtree, boolean includeUnpublished, List<Language> languages,
|
1623
|
boolean highlightFragments, Integer pageSize, Integer pageNumber,
|
1624
|
List<OrderHint> orderHints, List<String> propertyPaths) throws IOException, LuceneParseException {
|
1625
|
|
1626
|
LuceneSearch luceneSearch = prepareFindByFullTextSearch(clazz, queryString, classification, subtree,
|
1627
|
null, includeUnpublished, languages, highlightFragments, null);
|
1628
|
|
1629
|
// --- execute search
|
1630
|
TopGroups<BytesRef> topDocsResultSet;
|
1631
|
try {
|
1632
|
topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
|
1633
|
} catch (ParseException e) {
|
1634
|
LuceneParseException luceneParseException = new LuceneParseException(e.getMessage());
|
1635
|
luceneParseException.setStackTrace(e.getStackTrace());
|
1636
|
throw luceneParseException;
|
1637
|
}
|
1638
|
|
1639
|
Map<CdmBaseType, String> idFieldMap = new HashMap<>();
|
1640
|
idFieldMap.put(CdmBaseType.TAXON_BASE, "id");
|
1641
|
|
1642
|
// --- initialize taxa, thighlight matches ....
|
1643
|
ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
|
1644
|
@SuppressWarnings("rawtypes")
|
1645
|
List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(
|
1646
|
topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
|
1647
|
|
1648
|
long totalHits = topDocsResultSet != null ? Long.valueOf(topDocsResultSet.totalGroupCount) : 0;
|
1649
|
return new DefaultPagerImpl<>(pageNumber, totalHits, pageSize, searchResults);
|
1650
|
}
|
1651
|
|
1652
|
@Transactional(readOnly = true)
|
1653
|
@Override
|
1654
|
public <S extends TaxonBase> Pager<S> findByTitleWithRestrictions(Class<S> clazz, String queryString, MatchMode matchmode, List<Restriction<?>> restrictions, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
|
1655
|
long numberOfResults = dao.countByTitleWithRestrictions(clazz, queryString, matchmode, restrictions);
|
1656
|
|
1657
|
long numberOfResults_doubtful = dao.countByTitleWithRestrictions(clazz, "?".concat(queryString), matchmode, restrictions);
|
1658
|
List<S> results = new ArrayList<>();
|
1659
|
if(numberOfResults > 0 || numberOfResults_doubtful > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
|
1660
|
|
1661
|
results = dao.findByTitleWithRestrictions(clazz, queryString, matchmode, restrictions, pageSize, pageNumber, orderHints, propertyPaths);
|
1662
|
results.addAll(dao.findByTitleWithRestrictions(clazz, "?".concat(queryString), matchmode, restrictions, pageSize, pageNumber, orderHints, propertyPaths));
|
1663
|
}
|
1664
|
Collections.sort(results, new TaxonComparator());
|
1665
|
return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
|
1666
|
}
|
1667
|
|
1668
|
@Transactional(readOnly = true)
|
1669
|
@Override
|
1670
|
public <S extends TaxonBase> Pager<S> findByTitle(Class<S> clazz, String queryString,MatchMode matchmode, List<Criterion> criteria, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
|
1671
|
long numberOfResults = dao.countByTitle(clazz, queryString, matchmode, criteria);
|
1672
|
//check whether there are doubtful taxa matching
|
1673
|
long numberOfResults_doubtful = dao.countByTitle(clazz, "?".concat(queryString), matchmode, criteria);
|
1674
|
List<S> results = new ArrayList<>();
|
1675
|
if(numberOfResults > 0 || numberOfResults_doubtful > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
|
1676
|
if (numberOfResults > 0){
|
1677
|
results = dao.findByTitle(clazz, queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths);
|
1678
|
}else{
|
1679
|
results = new ArrayList<>();
|
1680
|
}
|
1681
|
if (numberOfResults_doubtful > 0){
|
1682
|
results.addAll(dao.findByTitle(clazz, "?".concat(queryString), matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths));
|
1683
|
}
|
1684
|
}
|
1685
|
Collections.sort(results, new TaxonComparator());
|
1686
|
return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
|
1687
|
}
|
1688
|
|
1689
|
@Override
|
1690
|
public Pager<SearchResult<TaxonBase>> findByDistribution(List<NamedArea> areaFilter, List<PresenceAbsenceTerm> statusFilter,
|
1691
|
Classification classification, TaxonNode subtree,
|
1692
|
Integer pageSize, Integer pageNumber,
|
1693
|
List<OrderHint> orderHints, List<String> propertyPaths) throws IOException, LuceneParseException {
|
1694
|
|
1695
|
LuceneSearch luceneSearch = prepareByDistributionSearch(areaFilter, statusFilter, classification, subtree);
|
1696
|
|
1697
|
// --- execute search
|
1698
|
TopGroups<BytesRef> topDocsResultSet;
|
1699
|
try {
|
1700
|
topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
|
1701
|
} catch (ParseException e) {
|
1702
|
LuceneParseException luceneParseException = new LuceneParseException(e.getMessage());
|
1703
|
luceneParseException.setStackTrace(e.getStackTrace());
|
1704
|
throw luceneParseException;
|
1705
|
}
|
1706
|
|
1707
|
Map<CdmBaseType, String> idFieldMap = new HashMap<>();
|
1708
|
idFieldMap.put(CdmBaseType.TAXON_BASE, "id");
|
1709
|
|
1710
|
// --- initialize taxa, thighlight matches ....
|
1711
|
ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
|
1712
|
List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(
|
1713
|
topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
|
1714
|
|
1715
|
long totalHits = topDocsResultSet != null ? Long.valueOf(topDocsResultSet.totalGroupCount) : 0;
|
1716
|
return new DefaultPagerImpl<>(pageNumber, totalHits, pageSize, searchResults);
|
1717
|
}
|
1718
|
|
1719
|
/**
|
1720
|
* @param clazz
|
1721
|
* @param queryString
|
1722
|
* @param classification
|
1723
|
* @param includeUnpublished
|
1724
|
* @param languages
|
1725
|
* @param highlightFragments
|
1726
|
* @param sortFields TODO
|
1727
|
* @param directorySelectClass
|
1728
|
* @return
|
1729
|
*/
|
1730
|
protected LuceneSearch prepareFindByFullTextSearch(Class<? extends CdmBase> clazz, String queryString,
|
1731
|
Classification classification, TaxonNode subtree, String className, boolean includeUnpublished, List<Language> languages,
|
1732
|
boolean highlightFragments, SortField[] sortFields) {
|
1733
|
|
1734
|
Builder finalQueryBuilder = new Builder();
|
1735
|
Builder textQueryBuilder = new Builder();
|
1736
|
|
1737
|
LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, TaxonBase.class);
|
1738
|
QueryFactory taxonBaseQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonBase.class);
|
1739
|
|
1740
|
if(sortFields == null){
|
1741
|
sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.Type.STRING, false)};
|
1742
|
}
|
1743
|
luceneSearch.setSortFields(sortFields);
|
1744
|
|
1745
|
// ---- search criteria
|
1746
|
luceneSearch.setCdmTypRestriction(clazz);
|
1747
|
|
1748
|
if(!StringUtils.isEmpty(queryString) && !queryString.equals("*") && !queryString.equals("?") ) {
|
1749
|
textQueryBuilder.add(taxonBaseQueryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);
|
1750
|
textQueryBuilder.add(taxonBaseQueryFactory.newDefinedTermQuery("name.rank", queryString, languages), Occur.SHOULD);
|
1751
|
}
|
1752
|
if(className != null){
|
1753
|
textQueryBuilder.add(taxonBaseQueryFactory.newTermQuery("classInfo.name", className, false), Occur.MUST);
|
1754
|
}
|
1755
|
|
1756
|
BooleanQuery textQuery = textQueryBuilder.build();
|
1757
|
if(textQuery.clauses().size() > 0) {
|
1758
|
finalQueryBuilder.add(textQuery, Occur.MUST);
|
1759
|
}
|
1760
|
|
1761
|
if(classification != null){
|
1762
|
finalQueryBuilder.add(taxonBaseQueryFactory.newEntityIdQuery(AcceptedTaxonBridge.DOC_KEY_CLASSIFICATION_ID, classification), Occur.MUST);
|
1763
|
}
|
1764
|
if(subtree != null){
|
1765
|
finalQueryBuilder.add(taxonBaseQueryFactory.newTermQuery(AcceptedTaxonBridge.DOC_KEY_TREEINDEX, subtree.treeIndexWc(), true), Occur.MUST);
|
1766
|
}
|
1767
|
if(!includeUnpublished) {
|
1768
|
String accPublishParam = TaxonBase.ACC_TAXON_BRIDGE_PREFIX + AcceptedTaxonBridge.DOC_KEY_PUBLISH_SUFFIX;
|
1769
|
finalQueryBuilder.add(taxonBaseQueryFactory.newBooleanQuery(accPublishParam, true), Occur.MUST);
|
1770
|
finalQueryBuilder.add(taxonBaseQueryFactory.newBooleanQuery("publish", true), Occur.MUST);
|
1771
|
}
|
1772
|
|
1773
|
luceneSearch.setQuery(finalQueryBuilder.build());
|
1774
|
|
1775
|
if(highlightFragments){
|
1776
|
luceneSearch.setHighlightFields(taxonBaseQueryFactory.getTextFieldNamesAsArray());
|
1777
|
}
|
1778
|
return luceneSearch;
|
1779
|
}
|
1780
|
|
1781
|
/**
|
1782
|
* Uses org.apache.lucene.search.join.JoinUtil for query time joining, alternatively
|
1783
|
* the BlockJoinQuery could be used. The latter might be more memory save but has the
|
1784
|
* drawback of requiring to do the join an indexing time.
|
1785
|
* see https://dev.e-taxonomy.eu/redmine/projects/edit/wiki/LuceneNotes#JoinsinLucene for more information on this.
|
1786
|
*
|
1787
|
* Joins TaxonRelationShip with Taxon depending on the direction of the given edge:
|
1788
|
* <ul>
|
1789
|
* <li>direct, everted: {@link Direction.relatedTo}: TaxonRelationShip.relatedTo.id --> Taxon.id </li>
|
1790
|
* <li>inverse: {@link Direction.relatedFrom}: TaxonRelationShip.relatedFrom.id --> Taxon.id </li>
|
1791
|
* <ul>
|
1792
|
* @param queryString
|
1793
|
* @param classification
|
1794
|
* @param languages
|
1795
|
* @param highlightFragments
|
1796
|
* @param sortFields TODO
|
1797
|
*
|
1798
|
* @return
|
1799
|
* @throws IOException
|
1800
|
*/
|
1801
|
protected LuceneSearch prepareFindByTaxonRelationFullTextSearch(TaxonRelationshipEdge edge, String queryString,
|
1802
|
Classification classification, TaxonNode subtree, boolean includeUnpublished, List<Language> languages,
|
1803
|
boolean highlightFragments, SortField[] sortFields) throws IOException {
|
1804
|
|
1805
|
String fromField;
|
1806
|
String queryTermField;
|
1807
|
String toField = "id"; // TaxonBase.uuid
|
1808
|
String publishField;
|
1809
|
String publishFieldInvers;
|
1810
|
|
1811
|
if(edge.isBidirectional()){
|
1812
|
throw new RuntimeException("Bidirectional joining not supported!");
|
1813
|
}
|
1814
|
if(edge.isEvers()){
|
1815
|
fromField = "relatedFrom.id";
|
1816
|
queryTermField = "relatedFrom.titleCache";
|
1817
|
publishField = "relatedFrom.publish";
|
1818
|
publishFieldInvers = "relatedTo.publish";
|
1819
|
} else if(edge.isInvers()) {
|
1820
|
fromField = "relatedTo.id";
|
1821
|
queryTermField = "relatedTo.titleCache";
|
1822
|
publishField = "relatedTo.publish";
|
1823
|
publishFieldInvers = "relatedFrom.publish";
|
1824
|
} else {
|
1825
|
throw new RuntimeException("Invalid direction: " + edge.getDirections());
|
1826
|
}
|
1827
|
|
1828
|
Builder finalQueryBuilder = new Builder();
|
1829
|
|
1830
|
LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, TaxonBase.class);
|
1831
|
QueryFactory taxonBaseQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonBase.class);
|
1832
|
|
1833
|
Builder joinFromQueryBuilder = new Builder();
|
1834
|
if(StringUtils.isNotEmpty(queryString)){
|
1835
|
joinFromQueryBuilder.add(taxonBaseQueryFactory.newTermQuery(queryTermField, queryString), Occur.MUST);
|
1836
|
}
|
1837
|
joinFromQueryBuilder.add(taxonBaseQueryFactory.newEntityIdsQuery("type.id", edge.getRelationshipTypes()), Occur.MUST);
|
1838
|
if(!includeUnpublished){
|
1839
|
joinFromQueryBuilder.add(taxonBaseQueryFactory.newBooleanQuery(publishField, true), Occur.MUST);
|
1840
|
joinFromQueryBuilder.add(taxonBaseQueryFactory.newBooleanQuery(publishFieldInvers, true), Occur.MUST);
|
1841
|
}
|
1842
|
|
1843
|
Query joinQuery = taxonBaseQueryFactory.newJoinQuery(TaxonRelationship.class, fromField, false, joinFromQueryBuilder.build(), toField, null, ScoreMode.Max);
|
1844
|
|
1845
|
if(sortFields == null){
|
1846
|
sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.Type.STRING, false)};
|
1847
|
}
|
1848
|
luceneSearch.setSortFields(sortFields);
|
1849
|
|
1850
|
finalQueryBuilder.add(joinQuery, Occur.MUST);
|
1851
|
|
1852
|
if(classification != null){
|
1853
|
finalQueryBuilder.add(taxonBaseQueryFactory.newEntityIdQuery(AcceptedTaxonBridge.DOC_KEY_CLASSIFICATION_ID, classification), Occur.MUST);
|
1854
|
}
|
1855
|
if(subtree != null){
|
1856
|
finalQueryBuilder.add(taxonBaseQueryFactory.newTermQuery(AcceptedTaxonBridge.DOC_KEY_TREEINDEX, subtree.treeIndexWc(), true), Occur.MUST);
|
1857
|
}
|
1858
|
|
1859
|
luceneSearch.setQuery(finalQueryBuilder.build());
|
1860
|
|
1861
|
if(highlightFragments){
|
1862
|
luceneSearch.setHighlightFields(taxonBaseQueryFactory.getTextFieldNamesAsArray());
|
1863
|
}
|
1864
|
return luceneSearch;
|
1865
|
}
|
1866
|
|
1867
|
@Override
|
1868
|
public Pager<SearchResult<TaxonBase>> findTaxaAndNamesByFullText(
|
1869
|
EnumSet<TaxaAndNamesSearchMode> searchModes, String queryString,
|
1870
|
Classification classification, TaxonNode subtree,
|
1871
|
Set<NamedArea> namedAreas, Set<PresenceAbsenceTerm> distributionStatus, List<Language> languages,
|
1872
|
boolean highlightFragments, Integer pageSize,
|
1873
|
Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths)
|
1874
|
throws IOException, LuceneParseException, LuceneMultiSearchException {
|
1875
|
|
1876
|
// FIXME: allow taxonomic ordering
|
1877
|
// hql equivalent: order by t.name.genusOrUninomial, case when t.name.specificEpithet
|
1878
|
// like '\"%\"' then 1 else 0 end, t.name.specificEpithet, t.name.rank desc, t.name.nameCache";
|
1879
|
// this requires building a special sort column by a special classBridge
|
1880
|
if(highlightFragments){
|
1881
|
logger.warn("findTaxaAndNamesByFullText() : fragment highlighting is " +
|
1882
|
"currently not fully supported by this method and thus " +
|
1883
|
"may not work with common names and misapplied names.");
|
1884
|
}
|
1885
|
|
1886
|
// convert sets to lists
|
1887
|
List<NamedArea> namedAreaList = null;
|
1888
|
List<PresenceAbsenceTerm> distributionStatusList = null;
|
1889
|
if(namedAreas != null){
|
1890
|
namedAreaList = new ArrayList<>(namedAreas.size());
|
1891
|
namedAreaList.addAll(namedAreas);
|
1892
|
}
|
1893
|
if(distributionStatus != null){
|
1894
|
distributionStatusList = new ArrayList<>(distributionStatus.size());
|
1895
|
distributionStatusList.addAll(distributionStatus);
|
1896
|
}
|
1897
|
|
1898
|
// set default if parameter is null
|
1899
|
if(searchModes == null){
|
1900
|
searchModes = EnumSet.of(TaxaAndNamesSearchMode.doTaxa);
|
1901
|
}
|
1902
|
|
1903
|
// set sort order and thus override any sort orders which may have been
|
1904
|
// defined by prepare*Search methods
|
1905
|
if(orderHints == null){
|
1906
|
orderHints = OrderHint.NOMENCLATURAL_SORT_ORDER.asList();
|
1907
|
}
|
1908
|
SortField[] sortFields = new SortField[orderHints.size()];
|
1909
|
int i = 0;
|
1910
|
for(OrderHint orderHint : orderHints){
|
1911
|
sortFields[i++] = orderHint.toSortField();
|
1912
|
}
|
1913
|
// SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("id", SortField.STRING, false)};
|
1914
|
// SortField[] sortFields = new SortField[]{new SortField(NomenclaturalSortOrderBrigde.NAME_SORT_FIELD_NAME, SortField.STRING, false)};
|
1915
|
|
1916
|
boolean addDistributionFilter = namedAreas != null && namedAreas.size() > 0;
|
1917
|
|
1918
|
List<LuceneSearch> luceneSearches = new ArrayList<>();
|
1919
|
Map<CdmBaseType, String> idFieldMap = new HashMap<>();
|
1920
|
|
1921
|
/*
|
1922
|
======== filtering by distribution , HOWTO ========
|
1923
|
|
1924
|
- http://www.javaranch.com/journal/2009/02/filtering-a-lucene-search.html
|
1925
|
- http://stackoverflow.com/questions/17709256/lucene-solr-using-complex-filters -> QueryWrapperFilter
|
1926
|
add Filter to search as http://lucene.apache.org/core/3_6_0/api/all/org/apache/lucene/search/Filter.html
|
1927
|
which will be put into a FilteredQuersy in the end ?
|
1928
|
|
1929
|
|
1930
|
3. how does it work in spatial?
|
1931
|
see
|
1932
|
- http://www.nsshutdown.com/projects/lucene/whitepaper/locallucene_v2.html
|
1933
|
- http://www.infoq.com/articles/LuceneSpatialSupport
|
1934
|
- http://www.mhaller.de/archives/156-Spatial-search-with-Lucene.html
|
1935
|
------------------------------------------------------------------------
|
1936
|
|
1937
|
filter strategies:
|
1938
|
A) use a separate distribution filter per index sub-query/search:
|
1939
|
- byTaxonSyonym (query TaxaonBase):
|
1940
|
use a join area filter (Distribution -> TaxonBase)
|
1941
|
- byCommonName (query DescriptionElementBase): use an area filter on
|
1942
|
DescriptionElementBase !!! PROBLEM !!!
|
1943
|
This cannot work since the distributions are different entities than the
|
1944
|
common names and thus these are different lucene documents.
|
1945
|
- byMisaplliedNames (join query TaxonRelationship -> TaxonBase):
|
1946
|
use a join area filter (Distribution -> TaxonBase)
|
1947
|
|
1948
|
B) use a common distribution filter for all index sub-query/searches:
|
1949
|
- use a common join area filter (Distribution -> TaxonBase)
|
1950
|
- also implement the byCommonName as join query (CommonName -> TaxonBase)
|
1951
|
PROBLEM in this case: we are losing the fragment highlighting for the
|
1952
|
common names, since the returned documents are always TaxonBases
|
1953
|
*/
|
1954
|
|
1955
|
/* The QueryFactory for creating filter queries on Distributions should
|
1956
|
* The query factory used for the common names query cannot be reused
|
1957
|
* for this case, since we want to only record the text fields which are
|
1958
|
* actually used in the primary query
|
1959
|
*/
|
1960
|
QueryFactory distributionFilterQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(Distribution.class);
|
1961
|
|
1962
|
Builder multiIndexByAreaFilterBuilder = new Builder();
|
1963
|
boolean includeUnpublished = searchModes.contains(TaxaAndNamesSearchMode.includeUnpublished);
|
1964
|
|
1965
|
// search for taxa or synonyms
|
1966
|
if(searchModes.contains(TaxaAndNamesSearchMode.doTaxa) || searchModes.contains(TaxaAndNamesSearchMode.doSynonyms) ) {
|
1967
|
@SuppressWarnings("rawtypes")
|
1968
|
Class<? extends TaxonBase> taxonBaseSubclass = TaxonBase.class;
|
1969
|
String className = null;
|
1970
|
if(searchModes.contains(TaxaAndNamesSearchMode.doTaxa) && !searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)){
|
1971
|
taxonBaseSubclass = Taxon.class;
|
1972
|
} else if (!searchModes.contains(TaxaAndNamesSearchMode.doTaxa) && searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)) {
|
1973
|
className = "eu.etaxonomy.cdm.model.taxon.Synonym";
|
1974
|
}
|
1975
|
luceneSearches.add(prepareFindByFullTextSearch(taxonBaseSubclass,
|
1976
|
queryString, classification, subtree, className,
|
1977
|
includeUnpublished, languages, highlightFragments, sortFields));
|
1978
|
idFieldMap.put(CdmBaseType.TAXON_BASE, "id");
|
1979
|
/* A) does not work!!!!
|
1980
|
if(addDistributionFilter){
|
1981
|
// in this case we need a filter which uses a join query
|
1982
|
// to get the TaxonBase documents for the DescriptionElementBase documents
|
1983
|
// which are matching the areas in question
|
1984
|
Query taxonAreaJoinQuery = createByDistributionJoinQuery(
|
1985
|
namedAreaList,
|
1986
|
distributionStatusList,
|
1987
|
distributionFilterQueryFactory
|
1988
|
);
|
1989
|
multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);
|
1990
|
}
|
1991
|
*/
|
1992
|
if(addDistributionFilter && searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)){
|
1993
|
// add additional area filter for synonyms
|
1994
|
String fromField = "inDescription.taxon.id"; // in DescriptionElementBase index
|
1995
|
String toField = "accTaxon" + AcceptedTaxonBridge.DOC_KEY_ID_SUFFIX; // id in TaxonBase index
|
1996
|
|
1997
|
//TODO replace by createByDistributionJoinQuery
|
1998
|
BooleanQuery byDistributionQuery = createByDistributionQuery(namedAreaList, distributionStatusList, distributionFilterQueryFactory);
|
1999
|
Query taxonAreaJoinQuery = distributionFilterQueryFactory.newJoinQuery(Distribution.class, fromField, true, byDistributionQuery, toField, Taxon.class, ScoreMode.None);
|
2000
|
multiIndexByAreaFilterBuilder.add(taxonAreaJoinQuery, Occur.SHOULD);
|
2001
|
|
2002
|
}
|
2003
|
}
|
2004
|
|
2005
|
// search by CommonTaxonName
|
2006
|
if(searchModes.contains(TaxaAndNamesSearchMode.doTaxaByCommonNames)) {
|
2007
|
// B)
|
2008
|
QueryFactory descriptionElementQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(DescriptionElementBase.class);
|
2009
|
Query byCommonNameJoinQuery = descriptionElementQueryFactory.newJoinQuery(
|
2010
|
CommonTaxonName.class,
|
2011
|
"inDescription.taxon.id",
|
2012
|
true,
|
2013
|
QueryFactory.addTypeRestriction(
|
2014
|
createByDescriptionElementFullTextQuery(queryString, classification, subtree, null, languages, descriptionElementQueryFactory)
|
2015
|
, CommonTaxonName.class
|
2016
|
).build(), "id", null, ScoreMode.Max);
|
2017
|
if (logger.isDebugEnabled()){logger.debug("byCommonNameJoinQuery: " + byCommonNameJoinQuery.toString());}
|
2018
|
LuceneSearch byCommonNameSearch = new LuceneSearch(luceneIndexToolProvider,
|
2019
|
GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, Taxon.class);
|
2020
|
byCommonNameSearch.setCdmTypRestriction(Taxon.class);
|
2021
|
Builder builder = new BooleanQuery.Builder();
|
2022
|
builder.add(byCommonNameJoinQuery, Occur.MUST);
|
2023
|
if(!includeUnpublished) {
|
2024
|
QueryFactory taxonBaseQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonBase.class);
|
2025
|
builder.add(taxonBaseQueryFactory.newBooleanQuery("publish", true), Occur.MUST);
|
2026
|
}
|
2027
|
byCommonNameSearch.setQuery(builder.build());
|
2028
|
byCommonNameSearch.setSortFields(sortFields);
|
2029
|
|
2030
|
idFieldMap.put(CdmBaseType.TAXON_BASE, "id");
|
2031
|
|
2032
|
luceneSearches.add(byCommonNameSearch);
|
2033
|
|
2034
|
/* A) does not work!!!!
|
2035
|
luceneSearches.add(
|
2036
|
prepareByDescriptionElementFullTextSearch(CommonTaxonName.class,
|
2037
|
queryString, classification, null, languages, highlightFragments)
|
2038
|
);
|
2039
|
idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
|
2040
|
if(addDistributionFilter){
|
2041
|
// in this case we are able to use DescriptionElementBase documents
|
2042
|
// which are matching the areas in question directly
|
2043
|
BooleanQuery byDistributionQuery = createByDistributionQuery(
|
2044
|
namedAreaList,
|
2045
|
distributionStatusList,
|
2046
|
distributionFilterQueryFactory
|
2047
|
);
|
2048
|
multiIndexByAreaFilter.add(new QueryWrapperFilter(byDistributionQuery), Occur.SHOULD);
|
2049
|
} */
|
2050
|
}
|
2051
|
|
2052
|
|
2053
|
// search by misapplied names
|
2054
|
//TODO merge with pro parte synonym search once #7487 is fixed
|
2055
|
if(searchModes.contains(TaxaAndNamesSearchMode.doMisappliedNames) /*|| searchModes.contains(TaxaAndNamesSearchMode.doSynonyms) */) {
|
2056
|
// NOTE:
|
2057
|
// prepareFindByTaxonRelationFullTextSearch() is making use of JoinUtil.createJoinQuery()
|
2058
|
// which allows doing query time joins
|
2059
|
// finds the misapplied name (Taxon B) which is an misapplication for
|
2060
|
// a related Taxon A.
|
2061
|
//
|
2062
|
Set<TaxonRelationshipType> relTypes = new HashSet<>();
|
2063
|
if (searchModes.contains(TaxaAndNamesSearchMode.doMisappliedNames)){
|
2064
|
relTypes.addAll(TaxonRelationshipType.allMisappliedNameTypes());
|
2065
|
}
|
2066
|
// if (searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)){
|
2067
|
// relTypes.addAll(TaxonRelationshipType.allSynonymTypes());
|
2068
|
// }
|
2069
|
|
2070
|
luceneSearches.add(prepareFindByTaxonRelationFullTextSearch(
|
2071
|
new TaxonRelationshipEdge(relTypes, Direction.relatedTo),
|
2072
|
queryString, classification, subtree, includeUnpublished, languages, highlightFragments, sortFields));
|
2073
|
idFieldMap.put(CdmBaseType.TAXON_BASE, "id");
|
2074
|
|
2075
|
if(addDistributionFilter){
|
2076
|
String fromField = "inDescription.taxon.id"; // in DescriptionElementBase index
|
2077
|
|
2078
|
//TODO replace by createByDistributionJoinQuery
|
2079
|
BooleanQuery byDistributionQuery = createByDistributionQuery(namedAreaList, distributionStatusList, distributionFilterQueryFactory);
|
2080
|
|
2081
|
/*
|
2082
|
* Here I was facing a weird and nasty bug which took me bugging be really for hours until I found this solution.
|
2083
|
* Maybe this is a bug in java itself.
|
2084
|
*
|
2085
|
* When the string toField is constructed by using the expression TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString()
|
2086
|
* directly:
|
2087
|
*
|
2088
|
* String toField = "relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id";
|
2089
|
*
|
2090
|
* The byDistributionQuery fails, however when the uuid is first stored in another string variable the query
|
2091
|
* will execute as expected:
|
2092
|
*
|
2093
|
* String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
|
2094
|
* String toField = "relation." + misappliedNameForUuid +".to.id";
|
2095
|
*
|
2096
|
* Comparing both strings by the String.equals method returns true, so both String are identical.
|
2097
|
*
|
2098
|
* The bug occurs when running eu.etaxonomy.cdm.api.service.TaxonServiceSearchTest in eclipse and in maven and seems to to be
|
2099
|
* dependent from a specific jvm (openjdk6 6b27-1.12.6-1ubuntu0.13.04.2, openjdk7 7u25-2.3.10-1ubuntu0.13.04.2, oracle jdk1.7.0_25 tested)
|
2100
|
* The bug is persistent after a reboot of the development computer.
|
2101
|
*/
|
2102
|
// String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
|
2103
|
String toField = "relation." + TaxonRelationshipType.uuidMisappliedNameFor +".to.id";
|
2104
|
// String toField = "relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id";
|
2105
|
// System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + misappliedNameForUuid +".to.id") ? " > identical" : " > different");
|
2106
|
// System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id") ? " > identical" : " > different");
|
2107
|
|
2108
|
|
2109
|
Query taxonAreaJoinQuery = distributionFilterQueryFactory.newJoinQuery(Distribution.class,
|
2110
|
fromField, true, byDistributionQuery, toField, null, ScoreMode.None);
|
2111
|
multiIndexByAreaFilterBuilder.add(taxonAreaJoinQuery, Occur.SHOULD);
|
2112
|
|
2113
|
String toFieldProParte = "relation." + TaxonRelationshipType.uuidProParteMisappliedNameFor +".to.id";
|
2114
|
Query taxonAreaJoinQueryProParte = distributionFilterQueryFactory.newJoinQuery(Distribution.class,
|
2115
|
fromField, true, byDistributionQuery, toFieldProParte, null, ScoreMode.None);
|
2116
|
multiIndexByAreaFilterBuilder.add(taxonAreaJoinQueryProParte, Occur.SHOULD);
|
2117
|
|
2118
|
String toFieldPartial = "relation." + TaxonRelationshipType.uuidPartialMisappliedNameFor +".to.id";
|
2119
|
Query taxonAreaJoinQueryPartial = distributionFilterQueryFactory.newJoinQuery(Distribution.class,
|
2120
|
fromField, true, byDistributionQuery, toFieldPartial, null, ScoreMode.None);
|
2121
|
multiIndexByAreaFilterBuilder.add(taxonAreaJoinQueryPartial, Occur.SHOULD);
|
2122
|
}
|
2123
|
}
|
2124
|
|
2125
|
// search by pro parte synonyms
|
2126
|
if(searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)) {
|
2127
|
//TODO merge with misapplied name search once #7487 is fixed
|
2128
|
Set<TaxonRelationshipType> relTypes = new HashSet<>();
|
2129
|
relTypes.addAll(TaxonRelationshipType.allSynonymTypes());
|
2130
|
|
2131
|
luceneSearches.add(prepareFindByTaxonRelationFullTextSearch(
|
2132
|
new TaxonRelationshipEdge(relTypes, Direction.relatedTo),
|
2133
|
queryString, classification, subtree, includeUnpublished, languages, highlightFragments, sortFields));
|
2134
|
idFieldMap.put(CdmBaseType.TAXON_BASE, "id");
|
2135
|
|
2136
|
if(addDistributionFilter){
|
2137
|
String fromField = "inDescription.taxon.id"; // in DescriptionElementBase index
|
2138
|
|
2139
|
//proparte synonyms
|
2140
|
String toField = "relation."+TaxonRelationshipType.uuidProParteSynonymFor+".to.id";
|
2141
|
BooleanQuery byDistributionQuery = createByDistributionQuery(namedAreaList, distributionStatusList, distributionFilterQueryFactory);
|
2142
|
Query taxonAreaJoinQuery = distributionFilterQueryFactory.newJoinQuery(Distribution.class,
|
2143
|
fromField, true, byDistributionQuery, toField, null, ScoreMode.None);
|
2144
|
multiIndexByAreaFilterBuilder.add(taxonAreaJoinQuery, Occur.SHOULD);
|
2145
|
|
2146
|
//partial synonyms
|
2147
|
toField = "relation."+TaxonRelationshipType.uuidPartialSynonymFor+".to.id";
|
2148
|
// BooleanQuery byDistributionQuery2 = createByDistributionQuery(namedAreaList, distributionStatusList, distributionFilterQueryFactory);
|
2149
|
Query taxonAreaJoinQuery2 = distributionFilterQueryFactory.newJoinQuery(Distribution.class,
|
2150
|
fromField, true, byDistributionQuery, toField, null, ScoreMode.None);
|
2151
|
multiIndexByAreaFilterBuilder.add(taxonAreaJoinQuery2, Occur.SHOULD);
|
2152
|
}
|
2153
|
}//end pro parte synonyms
|
2154
|
|
2155
|
LuceneMultiSearch multiSearch = new LuceneMultiSearch(luceneIndexToolProvider,
|
2156
|
luceneSearches.toArray(new LuceneSearch[luceneSearches.size()]));
|
2157
|
|
2158
|
if(addDistributionFilter){
|
2159
|
|
2160
|
// B)
|
2161
|
// in this case we need a filter which uses a join query
|
2162
|
// to get the TaxonBase documents for the DescriptionElementBase documents
|
2163
|
// which are matching the areas in question
|
2164
|
//
|
2165
|
// for doTaxa, doByCommonName
|
2166
|
Query taxonAreaJoinQuery = createByDistributionJoinQuery(
|
2167
|
namedAreaList,
|
2168
|
distributionStatusList,
|
2169
|
distributionFilterQueryFactory,
|
2170
|
Taxon.class, true
|
2171
|
);
|
2172
|
multiIndexByAreaFilterBuilder.add(taxonAreaJoinQuery, Occur.SHOULD);
|
2173
|
}
|
2174
|
|
2175
|
if (addDistributionFilter){
|
2176
|
multiSearch.setFilter(multiIndexByAreaFilterBuilder.build());
|
2177
|
}
|
2178
|
|
2179
|
|
2180
|
// --- execute search
|
2181
|
TopGroups<BytesRef> topDocsResultSet;
|
2182
|
try {
|
2183
|
topDocsResultSet = multiSearch.executeSearch(pageSize, pageNumber);
|
2184
|
} catch (ParseException e) {
|
2185
|
LuceneParseException luceneParseException = new LuceneParseException(e.getMessage());
|
2186
|
luceneParseException.setStackTrace(e.getStackTrace());
|
2187
|
throw luceneParseException;
|
2188
|
}
|
2189
|
|
2190
|
// --- initialize taxa, highlight matches ....
|
2191
|
ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(multiSearch, multiSearch.getQuery());
|
2192
|
|
2193
|
|
2194
|
List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(
|
2195
|
topDocsResultSet, multiSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
|
2196
|
|
2197
|
long totalHits = (topDocsResultSet != null) ? Long.valueOf(topDocsResultSet.totalGroupCount) : 0;
|
2198
|
return new DefaultPagerImpl<>(pageNumber, totalHits, pageSize, searchResults);
|
2199
|
}
|
2200
|
|
2201
|
/**
|
2202
|
* @param namedAreaList at least one area must be in the list
|
2203
|
* @param distributionStatusList optional
|
2204
|
* @param toType toType
|
2205
|
* Optional parameter. Only used for debugging to print the toType documents
|
2206
|
* @param asFilter TODO
|
2207
|
* @return
|
2208
|
* @throws IOException
|
2209
|
*/
|
2210
|
protected Query createByDistributionJoinQuery(
|
2211
|
List<NamedArea> namedAreaList,
|
2212
|
List<PresenceAbsenceTerm> distributionStatusList,
|
2213
|
QueryFactory queryFactory, Class<? extends CdmBase> toType, boolean asFilter
|
2214
|
) throws IOException {
|
2215
|
|
2216
|
String fromField = "inDescription.taxon.id"; // in DescriptionElementBase index
|
2217
|
String toField = "id"; // id in toType usually this is the TaxonBase index
|
2218
|
|
2219
|
BooleanQuery byDistributionQuery = createByDistributionQuery(namedAreaList, distributionStatusList, queryFactory);
|
2220
|
|
2221
|
ScoreMode scoreMode = asFilter ? ScoreMode.None : ScoreMode.Max;
|
2222
|
|
2223
|
Query taxonAreaJoinQuery = queryFactory.newJoinQuery(Distribution.class, fromField, false, byDistributionQuery, toField, toType, scoreMode);
|
2224
|
|
2225
|
return taxonAreaJoinQuery;
|
2226
|
}
|
2227
|
|
2228
|
/**
|
2229
|
* @param namedAreaList
|
2230
|
* @param distributionStatusList
|
2231
|
* @param queryFactory
|
2232
|
* @return
|
2233
|
*/
|
2234
|
private BooleanQuery createByDistributionQuery(List<NamedArea> namedAreaList,
|
2235
|
List<PresenceAbsenceTerm> distributionStatusList, QueryFactory queryFactory) {
|
2236
|
Builder areaQueryBuilder = new Builder();
|
2237
|
// area field from Distribution
|
2238
|
areaQueryBuilder.add(queryFactory.newEntityIdsQuery("area.id", namedAreaList), Occur.MUST);
|
2239
|
|
2240
|
// status field from Distribution
|
2241
|
if(distributionStatusList != null && distributionStatusList.size() > 0){
|
2242
|
areaQueryBuilder.add(queryFactory.newEntityIdsQuery("status.id", distributionStatusList), Occur.MUST);
|
2243
|
}
|
2244
|
|
2245
|
BooleanQuery areaQuery = areaQueryBuilder.build();
|
2246
|
logger.debug("createByDistributionQuery() query: " + areaQuery.toString());
|
2247
|
return areaQuery;
|
2248
|
}
|
2249
|
|
2250
|
/**
|
2251
|
* This method has been primarily created for testing the area join query but might
|
2252
|
* also be useful in other situations
|
2253
|
*
|
2254
|
* @param namedAreaList
|
2255
|
* @param distributionStatusList
|
2256
|
* @param classification
|
2257
|
* @param highlightFragments
|
2258
|
* @return
|
2259
|
* @throws IOException
|
2260
|
*/
|
2261
|
protected LuceneSearch prepareByDistributionSearch(
|
2262
|
List<NamedArea> namedAreaList, List<PresenceAbsenceTerm> distributionStatusList,
|
2263
|
Classification classification, TaxonNode subtree) throws IOException {
|
2264
|
|
2265
|
Builder finalQueryBuilder = new Builder();
|
2266
|
|
2267
|
LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, Taxon.class);
|
2268
|
|
2269
|
// FIXME is this query factory using the wrong type?
|
2270
|
QueryFactory taxonQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(Taxon.class);
|
2271
|
|
2272
|
SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.Type.STRING, false)};
|
2273
|
luceneSearch.setSortFields(sortFields);
|
2274
|
|
2275
|
|
2276
|
Query byAreaQuery = createByDistributionJoinQuery(namedAreaList, distributionStatusList, taxonQueryFactory, null, false);
|
2277
|
|
2278
|
finalQueryBuilder.add(byAreaQuery, Occur.MUST);
|
2279
|
|
2280
|
if(classification != null){
|
2281
|
finalQueryBuilder.add(taxonQueryFactory.newEntityIdQuery(AcceptedTaxonBridge.DOC_KEY_CLASSIFICATION_ID, classification), Occur.MUST);
|
2282
|
}
|
2283
|
if(subtree != null){
|
2284
|
finalQueryBuilder.add(taxonQueryFactory.newTermQuery(AcceptedTaxonBridge.DOC_KEY_TREEINDEX, subtree.treeIndexWc(), true), Occur.MUST);
|
2285
|
}
|
2286
|
BooleanQuery finalQuery = finalQueryBuilder.build();
|
2287
|
logger.info("prepareByAreaSearch() query: " + finalQuery.toString());
|
2288
|
luceneSearch.setQuery(finalQuery);
|
2289
|
|
2290
|
return luceneSearch;
|
2291
|
}
|
2292
|
|
2293
|
@Override
|
2294
|
public Pager<SearchResult<TaxonBase>> findByDescriptionElementFullText(
|
2295
|
Class<? extends DescriptionElementBase> clazz, String queryString,
|
2296
|
Classification classification, TaxonNode subtree, List<Feature> features, List<Language> languages,
|
2297
|
boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws IOException, LuceneParseException {
|
2298
|
|
2299
|
LuceneSearch luceneSearch = prepareByDescriptionElementFullTextSearch(clazz, queryString, classification, subtree, features, languages, highlightFragments);
|
2300
|
|
2301
|
// --- execute search
|
2302
|
TopGroups<BytesRef> topDocsResultSet;
|
2303
|
try {
|
2304
|
topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
|
2305
|
} catch (ParseException e) {
|
2306
|
LuceneParseException luceneParseException = new LuceneParseException(e.getMessage());
|
2307
|
luceneParseException.setStackTrace(e.getStackTrace());
|
2308
|
throw luceneParseException;
|
2309
|
}
|
2310
|
|
2311
|
Map<CdmBaseType, String> idFieldMap = new HashMap<>();
|
2312
|
idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
|
2313
|
|
2314
|
// --- initialize taxa, highlight matches ....
|
2315
|
ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
|
2316
|
@SuppressWarnings("rawtypes")
|
2317
|
List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(
|
2318
|
topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
|
2319
|
|
2320
|
int totalHits = topDocsResultSet != null ? topDocsResultSet.totalGroupCount : 0;
|
2321
|
return new DefaultPagerImpl<>(pageNumber, Long.valueOf(totalHits), pageSize, searchResults);
|
2322
|
}
|
2323
|
|
2324
|
@Override
|
2325
|
public Pager<SearchResult<TaxonBase>> findByEverythingFullText(String queryString,
|
2326
|
Classification classification, TaxonNode subtree, boolean includeUnpublished, List<Language> languages, boolean highlightFragments,
|
2327
|
Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws IOException, LuceneParseException, LuceneMultiSearchException {
|
2328
|
|
2329
|
LuceneSearch luceneSearchByDescriptionElement = prepareByDescriptionElementFullTextSearch(null, queryString,
|
2330
|
classification, subtree,
|
2331
|
null, languages, highlightFragments);
|
2332
|
LuceneSearch luceneSearchByTaxonBase = prepareFindByFullTextSearch(null, queryString, classification, subtree, null,
|
2333
|
includeUnpublished, languages, highlightFragments, null);
|
2334
|
|
2335
|
LuceneMultiSearch multiSearch = new LuceneMultiSearch(luceneIndexToolProvider, luceneSearchByDescriptionElement, luceneSearchByTaxonBase);
|
2336
|
|
2337
|
// --- execute search
|
2338
|
TopGroups<BytesRef> topDocsResultSet;
|
2339
|
try {
|
2340
|
topDocsResultSet = multiSearch.executeSearch(pageSize, pageNumber);
|
2341
|
} catch (ParseException e) {
|
2342
|
LuceneParseException luceneParseException = new LuceneParseException(e.getMessage());
|
2343
|
luceneParseException.setStackTrace(e.getStackTrace());
|
2344
|
throw luceneParseException;
|
2345
|
}
|
2346
|
|
2347
|
// --- initialize taxa, highlight matches ....
|
2348
|
ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(multiSearch, multiSearch.getQuery());
|
2349
|
|
2350
|
Map<CdmBaseType, String> idFieldMap = new HashMap<>();
|
2351
|
idFieldMap.put(CdmBaseType.TAXON_BASE, "id");
|
2352
|
idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
|
2353
|
|
2354
|
List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(
|
2355
|
topDocsResultSet, multiSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
|
2356
|
|
2357
|
int totalHits = topDocsResultSet != null ? topDocsResultSet.totalGroupCount : 0;
|
2358
|
return new DefaultPagerImpl<>(pageNumber, Long.valueOf(totalHits), pageSize, searchResults);
|
2359
|
}
|
2360
|
|
2361
|
/**
|
2362
|
* @param clazz
|
2363
|
* @param queryString
|
2364
|
* @param classification
|
2365
|
* @param features
|
2366
|
* @param languages
|
2367
|
* @param highlightFragments
|
2368
|
* @param directorySelectClass
|
2369
|
* @return
|
2370
|
*/
|
2371
|
protected LuceneSearch prepareByDescriptionElementFullTextSearch(Class<? extends CdmBase> clazz,
|
2372
|
String queryString, Classification classification, TaxonNode subtree, List<Feature> features,
|
2373
|
List<Language> languages, boolean highlightFragments) {
|
2374
|
|
2375
|
LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, DescriptionElementBase.class);
|
2376
|
QueryFactory descriptionElementQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(DescriptionElementBase.class);
|
2377
|
|
2378
|
SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("inDescription.taxon.titleCache__sort", SortField.Type.STRING, false)};
|
2379
|
|
2380
|
BooleanQuery finalQuery = createByDescriptionElementFullTextQuery(queryString, classification, subtree, features,
|
2381
|
languages, descriptionElementQueryFactory);
|
2382
|
|
2383
|
luceneSearch.setSortFields(sortFields);
|
2384
|
luceneSearch.setCdmTypRestriction(clazz);
|
2385
|
luceneSearch.setQuery(finalQuery);
|
2386
|
if(highlightFragments){
|
2387
|
luceneSearch.setHighlightFields(descriptionElementQueryFactory.getTextFieldNamesAsArray());
|
2388
|
}
|
2389
|
|
2390
|
return luceneSearch;
|
2391
|
}
|
2392
|
|
2393
|
/**
|
2394
|
* @param queryString
|
2395
|
* @param classification
|
2396
|
* @param features
|
2397
|
* @param languages
|
2398
|
* @param descriptionElementQueryFactory
|
2399
|
* @return
|
2400
|
*/
|
2401
|
private BooleanQuery createByDescriptionElementFullTextQuery(String queryString,
|
2402
|
Classification classification, TaxonNode subtree, List<Feature> features,
|
2403
|
List<Language> languages, QueryFactory descriptionElementQueryFactory) {
|
2404
|
|
2405
|
Builder finalQueryBuilder = new Builder();
|
2406
|
Builder textQueryBuilder = new Builder();
|
2407
|
|
2408
|
if(!StringUtils.isEmpty(queryString)){
|
2409
|
|
2410
|
textQueryBuilder.add(descriptionElementQueryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);
|
2411
|
|
2412
|
// common name
|
2413
|
Builder nameQueryBuilder = new Builder();
|
2414
|
if(languages == null || languages.size() == 0){
|
2415
|
nameQueryBuilder.add(descriptionElementQueryFactory.newTermQuery("name", queryString), Occur.MUST);
|
2416
|
} else {
|
2417
|
Builder languageSubQueryBuilder = new Builder();
|
2418
|
for(Language lang : languages){
|
2419
|
languageSubQueryBuilder.add(descriptionElementQueryFactory.newTermQuery("language.uuid", lang.getUuid().toString(), false), Occur.SHOULD);
|
2420
|
}
|
2421
|
nameQueryBuilder.add(descriptionElementQueryFactory.newTermQuery("name", queryString), Occur.MUST);
|
2422
|
nameQueryBuilder.add(languageSubQueryBuilder.build(), Occur.MUST);
|
2423
|
}
|
2424
|
textQueryBuilder.add(nameQueryBuilder.build(), Occur.SHOULD);
|
2425
|
|
2426
|
|
2427
|
// text field from TextData
|
2428
|
textQueryBuilder.add(descriptionElementQueryFactory.newMultilanguageTextQuery("text", queryString, languages), Occur.SHOULD);
|
2429
|
|
2430
|
// --- TermBase fields - by representation ----
|
2431
|
// state field from CategoricalData
|
2432
|
textQueryBuilder.add(descriptionElementQueryFactory.newDefinedTermQuery("stateData.state", queryString, languages), Occur.SHOULD);
|
2433
|
|
2434
|
// state field from CategoricalData
|
2435
|
textQueryBuilder.add(descriptionElementQueryFactory.newDefinedTermQuery("stateData.modifyingText", queryString, languages), Occur.SHOULD);
|
2436
|
|
2437
|
// area field from Distribution
|
2438
|
textQueryBuilder.add(descriptionElementQueryFactory.newDefinedTermQuery("area", queryString, languages), Occur.SHOULD);
|
2439
|
|
2440
|
// status field from Distribution
|
2441
|
textQueryBuilder.add(descriptionElementQueryFactory.newDefinedTermQuery("status", queryString, languages), Occur.SHOULD);
|
2442
|
|
2443
|
finalQueryBuilder.add(textQueryBuilder.build(), Occur.MUST);
|
2444
|
|
2445
|
}
|
2446
|
// --- classification ----
|
2447
|
|
2448
|
|
2449
|
if(classification != null){
|
2450
|
finalQueryBuilder.add(descriptionElementQueryFactory.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification), Occur.MUST);
|
2451
|
}
|
2452
|
if(subtree != null){
|
2453
|
finalQueryBuilder.add(descriptionElementQueryFactory.newTermQuery("inDescription.taxon.taxonNodes.treeIndex", subtree.treeIndexWc(), true), Occur.MUST);
|
2454
|
}
|
2455
|
|
2456
|
// --- IdentifieableEntity fields - by uuid
|
2457
|
if(features != null && features.size() > 0 ){
|
2458
|
finalQueryBuilder.add(descriptionElementQueryFactory.newEntityUuidsQuery("feature.uuid", features), Occur.MUST);
|
2459
|
}
|
2460
|
|
2461
|
// the description must be associated with a taxon
|
2462
|
finalQueryBuilder.add(descriptionElementQueryFactory.newIsNotNullQuery("inDescription.taxon.id"), Occur.MUST);
|
2463
|
|
2464
|
BooleanQuery finalQuery = finalQueryBuilder.build();
|
2465
|
logger.info("prepareByDescriptionElementFullTextSearch() query: " + finalQuery.toString());
|
2466
|
return finalQuery;
|
2467
|
}
|
2468
|
|
2469
|
@Override
|
2470
|
public List<Synonym> createInferredSynonyms(Taxon taxon, Classification classification, SynonymType type, boolean doWithMisappliedNames){
|
2471
|
|
2472
|
List <Synonym> inferredSynonyms = new ArrayList<>();
|
2473
|
List<Synonym> inferredSynonymsToBeRemoved = new ArrayList<>();
|
2474
|
|
2475
|
Map <UUID, IZoologicalName> zooHashMap = new HashMap<>();
|
2476
|
boolean includeUnpublished = INCLUDE_UNPUBLISHED;
|
2477
|
|
2478
|
UUID nameUuid= taxon.getName().getUuid();
|
2479
|
IZoologicalName taxonName = getZoologicalName(nameUuid, zooHashMap);
|
2480
|
String epithetOfTaxon = null;
|
2481
|
String infragenericEpithetOfTaxon = null;
|
2482
|
String infraspecificEpithetOfTaxon = null;
|
2483
|
if (taxonName.isSpecies()){
|
2484
|
epithetOfTaxon= taxonName.getSpecificEpithet();
|
2485
|
} else if (taxonName.isInfraGeneric()){
|
2486
|
infragenericEpithetOfTaxon = taxonName.getInfraGenericEpithet();
|
2487
|
} else if (taxonName.isInfraSpecific()){
|
2488
|
infraspecificEpithetOfTaxon = taxonName.getInfraSpecificEpithet();
|
2489
|
}
|
2490
|
String genusOfTaxon = taxonName.getGenusOrUninomial();
|
2491
|
Set<TaxonNode> nodes = taxon.getTaxonNodes();
|
2492
|
List<String> taxonNames = new ArrayList<>();
|
2493
|
|
2494
|
for (TaxonNode node: nodes){
|
2495
|
// Map<String, String> synonymsGenus = new HashMap<>(); // Changed this to be able to store the idInSource to a genusName
|
2496
|
// List<String> synonymsEpithet = new ArrayList<>();
|
2497
|
|
2498
|
if (node.getClassification().equals(classification)){
|
2499
|
if (!node.isTopmostNode()){
|
2500
|
TaxonNode parent = node.getParent();
|
2501
|
parent = CdmBase.deproxy(parent);
|
2502
|
TaxonName parentName = parent.getTaxon().getName();
|
2503
|
IZoologicalName zooParentName = CdmBase.deproxy(parentName);
|
2504
|
Taxon parentTaxon = CdmBase.deproxy(parent.getTaxon());
|
2505
|
|
2506
|
//create inferred synonyms for species, subspecies
|
2507
|
if ((parentName.isGenus() || parentName.isSpecies() || parentName.getRank().equals(Rank.SUBGENUS())) ){
|
2508
|
|
2509
|
Synonym inferredEpithet = null;
|
2510
|
Synonym inferredGenus = null;
|
2511
|
Synonym potentialCombination = null;
|
2512
|
|
2513
|
List<String> propertyPaths = new ArrayList<>();
|
2514
|
propertyPaths.add("synonym");
|
2515
|
propertyPaths.add("synonym.name");
|
2516
|
List<OrderHint> orderHintsSynonyms = new ArrayList<>();
|
2517
|
orderHintsSynonyms.add(new OrderHint("titleCache", SortOrder.ASCENDING));
|
2518
|
|
2519
|
List<Synonym> synonyMsOfParent = dao.getSynonyms(parentTaxon, SynonymType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHintsSynonyms,propertyPaths);
|
2520
|
List<Synonym> synonymsOfTaxon= dao.getSynonyms(taxon, SynonymType.HETEROTYPIC_SYNONYM_OF(),
|
2521
|
null, null,orderHintsSynonyms,propertyPaths);
|
2522
|
|
2523
|
List<TaxonRelationship> taxonRelListParent = new ArrayList<>();
|
2524
|
List<TaxonRelationship> taxonRelListTaxon = new ArrayList<>();
|
2525
|
if (doWithMisappliedNames){
|
2526
|
List<OrderHint> orderHintsMisapplied = new ArrayList<>();
|
2527
|
orderHintsMisapplied.add(new OrderHint("relatedFrom.titleCache", SortOrder.ASCENDING));
|
2528
|
taxonRelListParent = dao.getTaxonRelationships(parentTaxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(),
|
2529
|
includeUnpublished, null, null, orderHintsMisapplied, propertyPaths, Direction.relatedTo);
|
2530
|
taxonRelListTaxon = dao.getTaxonRelationships(taxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(),
|
2531
|
includeUnpublished, null, null, orderHintsMisapplied, propertyPaths, Direction.relatedTo);
|
2532
|
}
|
2533
|
|
2534
|
if (type.equals(SynonymType.INFERRED_EPITHET_OF())){
|
2535
|
for (Synonym synonymRelationOfParent:synonyMsOfParent){
|
2536
|
|
2537
|
inferredEpithet = createInferredEpithets(taxon,
|
2538
|
zooHashMap, taxonName, epithetOfTaxon,
|
2539
|
infragenericEpithetOfTaxon,
|
2540
|
infraspecificEpithetOfTaxon,
|
2541
|
taxonNames, parentName,
|
2542
|
synonymRelationOfParent);
|
2543
|
|
2544
|
inferredSynonyms.add(inferredEpithet);
|
2545
|
zooHashMap.put(inferredEpithet.getName().getUuid(), inferredEpithet.getName());
|
2546
|
taxonNames.add(inferredEpithet.getName().getNameCache());
|
2547
|
}
|
2548
|
|
2549
|
if (doWithMisappliedNames){
|
2550
|
|
2551
|
for (TaxonRelationship taxonRelationship: taxonRelListParent){
|
2552
|
Taxon misappliedName = taxonRelationship.getFromTaxon();
|
2553
|
|
2554
|
inferredEpithet = createInferredEpithets(taxon,
|
2555
|
zooHashMap, taxonName, epithetOfTaxon,
|
2556
|
infragenericEpithetOfTaxon,
|
2557
|
infraspecificEpithetOfTaxon,
|
2558
|
taxonNames, parentName,
|
2559
|
misappliedName);
|
2560
|
|
2561
|
inferredSynonyms.add(inferredEpithet);
|
2562
|
zooHashMap.put(inferredEpithet.getName().getUuid(), inferredEpithet.getName());
|
2563
|
taxonNames.add(inferredEpithet.getName().getNameCache());
|
2564
|
}
|
2565
|
}
|
2566
|
|
2567
|
if (!taxonNames.isEmpty()){
|
2568
|
List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
|
2569
|
if (!synNotInCDM.isEmpty()){
|
2570
|
inferredSynonymsToBeRemoved.clear();
|
2571
|
|
2572
|
for (Synonym syn :inferredSynonyms){
|
2573
|
IZoologicalName name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
|
2574
|
if (!synNotInCDM.contains(name.getNameCache())){
|
2575
|
inferredSynonymsToBeRemoved.add(syn);
|
2576
|
}
|
2577
|
}
|
2578
|
|
2579
|
// Remove identified Synonyms from inferredSynonyms
|
2580
|
for (Synonym synonym : inferredSynonymsToBeRemoved) {
|
2581
|
inferredSynonyms.remove(synonym);
|
2582
|
}
|
2583
|
}
|
2584
|
}
|
2585
|
|
2586
|
}else if (type.equals(SynonymType.INFERRED_GENUS_OF())){
|
2587
|
|
2588
|
for (Synonym synonymRelationOfTaxon:synonymsOfTaxon){
|
2589
|
|
2590
|
inferredGenus = createInferredGenus(taxon,
|
2591
|
zooHashMap, taxonName, epithetOfTaxon,
|
2592
|
genusOfTaxon, taxonNames, zooParentName, synonymRelationOfTaxon);
|
2593
|
|
2594
|
inferredSynonyms.add(inferredGenus);
|
2595
|
zooHashMap.put(inferredGenus.getName().getUuid(), inferredGenus.getName());
|
2596
|
taxonNames.add(inferredGenus.getName().getNameCache());
|
2597
|
}
|
2598
|
|
2599
|
if (doWithMisappliedNames){
|
2600
|
|
2601
|
for (TaxonRelationship taxonRelationship: taxonRelListTaxon){
|
2602
|
Taxon misappliedName = taxonRelationship.getFromTaxon();
|
2603
|
inferredGenus = createInferredGenus(taxon, zooHashMap, taxonName, infraspecificEpithetOfTaxon, genusOfTaxon, taxonNames, zooParentName, misappliedName);
|
2604
|
|
2605
|
inferredSynonyms.add(inferredGenus);
|
2606
|
zooHashMap.put(inferredGenus.getName().getUuid(), inferredGenus.getName());
|
2607
|
taxonNames.add(inferredGenus.getName().getNameCache());
|
2608
|
}
|
2609
|
}
|
2610
|
|
2611
|
|
2612
|
if (!taxonNames.isEmpty()){
|
2613
|
List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
|
2614
|
IZoologicalName name;
|
2615
|
if (!synNotInCDM.isEmpty()){
|
2616
|
inferredSynonymsToBeRemoved.clear();
|
2617
|
|
2618
|
for (Synonym syn :inferredSynonyms){
|
2619
|
name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
|
2620
|
if (!synNotInCDM.contains(name.getNameCache())){
|
2621
|
inferredSynonymsToBeRemoved.add(syn);
|
2622
|
}
|
2623
|
}
|
2624
|
|
2625
|
// Remove identified Synonyms from inferredSynonyms
|
2626
|
for (Synonym synonym : inferredSynonymsToBeRemoved) {
|
2627
|
inferredSynonyms.remove(synonym);
|
2628
|
}
|
2629
|
}
|
2630
|
}
|
2631
|
|
2632
|
}else if (type.equals(SynonymType.POTENTIAL_COMBINATION_OF())){
|
2633
|
|
2634
|
Reference sourceReference = null; // TODO: Determination of sourceReference is redundant
|
2635
|
//for all synonyms of the parent...
|
2636
|
for (Synonym synonymRelationOfParent:synonyMsOfParent){
|
2637
|
TaxonName synName;
|
2638
|
HibernateProxyHelper.deproxy(synonymRelationOfParent);
|
2639
|
|
2640
|
synName = synonymRelationOfParent.getName();
|
2641
|
|
2642
|
// Set the sourceReference
|
2643
|
sourceReference = synonymRelationOfParent.getSec();
|
2644
|
|
2645
|
// Determine the idInSource
|
2646
|
String idInSourceParent = getIdInSource(synonymRelationOfParent);
|
2647
|
|
2648
|
IZoologicalName parentSynZooName = getZoologicalName(synName.getUuid(), zooHashMap);
|
2649
|
String synParentGenus = parentSynZooName.getGenusOrUninomial();
|
2650
|
String synParentInfragenericName = null;
|
2651
|
String synParentSpecificEpithet = null;
|
2652
|
|
2653
|
if (parentSynZooName.isInfraGeneric()){
|
2654
|
synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();
|
2655
|
}
|
2656
|
if (parentSynZooName.isSpecies()){
|
2657
|
synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();
|
2658
|
}
|
2659
|
|
2660
|
/* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
|
2661
|
synonymsGenus.put(synGenusName, idInSource);
|
2662
|
}*/
|
2663
|
|
2664
|
//for all synonyms of the taxon
|
2665
|
|
2666
|
for (Synonym synonymRelationOfTaxon:synonymsOfTaxon){
|
2667
|
|
2668
|
IZoologicalName zooSynName = getZoologicalName(synonymRelationOfTaxon.getName().getUuid(), zooHashMap);
|
2669
|
potentialCombination = createPotentialCombination(idInSourceParent, parentSynZooName, zooSynName,
|
2670
|
synParentGenus,
|
2671
|
synParentInfragenericName,
|
2672
|
synParentSpecificEpithet, synonymRelationOfTaxon, zooHashMap);
|
2673
|
|
2674
|
taxon.addSynonym(potentialCombination, SynonymType.POTENTIAL_COMBINATION_OF());
|
2675
|
inferredSynonyms.add(potentialCombination);
|
2676
|
zooHashMap.put(potentialCombination.getName().getUuid(), potentialCombination.getName());
|
2677
|
taxonNames.add(potentialCombination.getName().getNameCache());
|
2678
|
}
|
2679
|
}
|
2680
|
|
2681
|
if (doWithMisappliedNames){
|
2682
|
|
2683
|
for (TaxonRelationship parentRelationship: taxonRelListParent){
|
2684
|
|
2685
|
TaxonName misappliedParentName;
|
2686
|
|
2687
|
Taxon misappliedParent = parentRelationship.getFromTaxon();
|
2688
|
misappliedParentName = misappliedParent.getName();
|
2689
|
|
2690
|
HibernateProxyHelper.deproxy(misappliedParent);
|
2691
|
|
2692
|
// Set the sourceReference
|
2693
|
sourceReference = misappliedParent.getSec();
|
2694
|
|
2695
|
// Determine the idInSource
|
2696
|
String idInSourceParent = getIdInSource(misappliedParent);
|
2697
|
|
2698
|
IZoologicalName parentSynZooName = getZoologicalName(misappliedParentName.getUuid(), zooHashMap);
|
2699
|
String synParentGenus = parentSynZooName.getGenusOrUninomial();
|
2700
|
String synParentInfragenericName = null;
|
2701
|
String synParentSpecificEpithet = null;
|
2702
|
|
2703
|
if (parentSynZooName.isInfraGeneric()){
|
2704
|
synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();
|
2705
|
}
|
2706
|
if (parentSynZooName.isSpecies()){
|
2707
|
synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();
|
2708
|
}
|
2709
|
|
2710
|
for (TaxonRelationship taxonRelationship: taxonRelListTaxon){
|
2711
|
Taxon misappliedName = taxonRelationship.getFromTaxon();
|
2712
|
IZoologicalName zooMisappliedName = getZoologicalName(misappliedName.getName().getUuid(), zooHashMap);
|
2713
|
potentialCombination = createPotentialCombination(
|
2714
|
idInSourceParent, parentSynZooName, zooMisappliedName,
|
2715
|
synParentGenus,
|
2716
|
synParentInfragenericName,
|
2717
|
synParentSpecificEpithet, misappliedName, zooHashMap);
|
2718
|
|
2719
|
taxon.addSynonym(potentialCombination, SynonymType.POTENTIAL_COMBINATION_OF());
|
2720
|
inferredSynonyms.add(potentialCombination);
|
2721
|
zooHashMap.put(potentialCombination.getName().getUuid(), potentialCombination.getName());
|
2722
|
taxonNames.add(potentialCombination.getName().getNameCache());
|
2723
|
}
|
2724
|
}
|
2725
|
}
|
2726
|
|
2727
|
if (!taxonNames.isEmpty()){
|
2728
|
List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
|
2729
|
IZoologicalName name;
|
2730
|
if (!synNotInCDM.isEmpty()){
|
2731
|
inferredSynonymsToBeRemoved.clear();
|
2732
|
for (Synonym syn :inferredSynonyms){
|
2733
|
try{
|
2734
|
name = syn.getName();
|
2735
|
}catch (ClassCastException e){
|
2736
|
name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
|
2737
|
}
|
2738
|
if (!synNotInCDM.contains(name.getNameCache())){
|
2739
|
inferredSynonymsToBeRemoved.add(syn);
|
2740
|
}
|
2741
|
}
|
2742
|
// Remove identified Synonyms from inferredSynonyms
|
2743
|
for (Synonym synonym : inferredSynonymsToBeRemoved) {
|
2744
|
inferredSynonyms.remove(synonym);
|
2745
|
}
|
2746
|
}
|
2747
|
}
|
2748
|
}
|
2749
|
}else {
|
2750
|
logger.info("The synonym type is not defined.");
|
2751
|
return inferredSynonyms;
|
2752
|
}
|
2753
|
}
|
2754
|
}
|
2755
|
}
|
2756
|
|
2757
|
return inferredSynonyms;
|
2758
|
}
|
2759
|
|
2760
|
private Synonym createPotentialCombination(String idInSourceParent,
|
2761
|
IZoologicalName parentSynZooName, IZoologicalName zooSynName, String synParentGenus,
|
2762
|
String synParentInfragenericName, String synParentSpecificEpithet,
|
2763
|
TaxonBase<?> syn, Map<UUID, IZoologicalName> zooHashMap) {
|
2764
|
Synonym potentialCombination;
|
2765
|
Reference sourceReference;
|
2766
|
IZoologicalName inferredSynName;
|
2767
|
HibernateProxyHelper.deproxy(syn);
|
2768
|
|
2769
|
// Set sourceReference
|
2770
|
sourceReference = syn.getSec();
|
2771
|
if (sourceReference == null){
|
2772
|
logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
|
2773
|
//TODO:Remove
|
2774
|
if (!parentSynZooName.getTaxa().isEmpty()){
|
2775
|
TaxonBase<?> taxon = parentSynZooName.getTaxa().iterator().next();
|
2776
|
|
2777
|
sourceReference = taxon.getSec();
|
2778
|
}
|
2779
|
}
|
2780
|
String synTaxonSpecificEpithet = zooSynName.getSpecificEpithet();
|
2781
|
|
2782
|
String synTaxonInfraSpecificName= null;
|
2783
|
|
2784
|
if (parentSynZooName.isSpecies()){
|
2785
|
synTaxonInfraSpecificName = zooSynName.getInfraSpecificEpithet();
|
2786
|
}
|
2787
|
|
2788
|
/*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
|
2789
|
synonymsEpithet.add(epithetName);
|
2790
|
}*/
|
2791
|
|
2792
|
//create potential combinations...
|
2793
|
inferredSynName = TaxonNameFactory.NewZoologicalInstance(syn.getName().getRank());
|
2794
|
|
2795
|
inferredSynName.setGenusOrUninomial(synParentGenus);
|
2796
|
if (zooSynName.isSpecies()){
|
2797
|
inferredSynName.setSpecificEpithet(synTaxonSpecificEpithet);
|
2798
|
if (parentSynZooName.isInfraGeneric()){
|
2799
|
inferredSynName.setInfraGenericEpithet(synParentInfragenericName);
|
2800
|
}
|
2801
|
}
|
2802
|
if (zooSynName.isInfraSpecific()){
|
2803
|
inferredSynName.setSpecificEpithet(synParentSpecificEpithet);
|
2804
|
inferredSynName.setInfraSpecificEpithet(synTaxonInfraSpecificName);
|
2805
|
}
|
2806
|
if (parentSynZooName.isInfraGeneric()){
|
2807
|
inferredSynName.setInfraGenericEpithet(synParentInfragenericName);
|
2808
|
}
|
2809
|
|
2810
|
potentialCombination = Synonym.NewInstance(inferredSynName, null);
|
2811
|
|
2812
|
// Set the sourceReference
|
2813
|
potentialCombination.setSec(sourceReference);
|
2814
|
|
2815
|
|
2816
|
// Determine the idInSource
|
2817
|
String idInSourceSyn= getIdInSource(syn);
|
2818
|
|
2819
|
if (idInSourceParent != null && idInSourceSyn != null) {
|
2820
|
IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation, idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);
|
2821
|
inferredSynName.addSource(originalSource);
|
2822
|
originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation, idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);
|
2823
|
potentialCombination.addSource(originalSource);
|
2824
|
}
|
2825
|
|
2826
|
return potentialCombination;
|
2827
|
}
|
2828
|
|
2829
|
private Synonym createInferredGenus(Taxon taxon,
|
2830
|
Map<UUID, IZoologicalName> zooHashMap, IZoologicalName taxonName,
|
2831
|
String epithetOfTaxon, String genusOfTaxon,
|
2832
|
List<String> taxonNames, IZoologicalName zooParentName,
|
2833
|
TaxonBase syn) {
|
2834
|
|
2835
|
Synonym inferredGenus;
|
2836
|
TaxonName synName;
|
2837
|
IZoologicalName inferredSynName;
|
2838
|
synName =syn.getName();
|
2839
|
HibernateProxyHelper.deproxy(syn);
|
2840
|
|
2841
|
// Determine the idInSource
|
2842
|
String idInSourceSyn = getIdInSource(syn);
|
2843
|
String idInSourceTaxon = getIdInSource(taxon);
|
2844
|
// Determine the sourceReference
|
2845
|
Reference sourceReference = syn.getSec();
|
2846
|
|
2847
|
//logger.warn(sourceReference.getTitleCache());
|
2848
|
|
2849
|
synName = syn.getName();
|
2850
|
IZoologicalName synZooName = getZoologicalName(synName.getUuid(), zooHashMap);
|
2851
|
String synSpeciesEpithetName = synZooName.getSpecificEpithet();
|
2852
|
/* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
|
2853
|
synonymsEpithet.add(synSpeciesEpithetName);
|
2854
|
}*/
|
2855
|
|
2856
|
inferredSynName = TaxonNameFactory.NewZoologicalInstance(taxon.getName().getRank());
|
2857
|
//TODO:differ between parent is genus and taxon is species, parent is subgenus and taxon is species, parent is species and taxon is subspecies and parent is genus and taxon is subgenus...
|
2858
|
|
2859
|
inferredSynName.setGenusOrUninomial(genusOfTaxon);
|
2860
|
if (zooParentName.isInfraGeneric()){
|
2861
|
inferredSynName.setInfraGenericEpithet(zooParentName.getInfraGenericEpithet());
|
2862
|
}
|
2863
|
|
2864
|
if (taxonName.isSpecies()){
|
2865
|
inferredSynName.setSpecificEpithet(synSpeciesEpithetName);
|
2866
|
}
|
2867
|
if (taxonName.isInfraSpecific()){
|
2868
|
inferredSynName.setSpecificEpithet(epithetOfTaxon);
|
2869
|
inferredSynName.setInfraSpecificEpithet(synZooName.getInfraGenericEpithet());
|
2870
|
}
|
2871
|
|
2872
|
inferredGenus = Synonym.NewInstance(inferredSynName, null);
|
2873
|
|
2874
|
// Set the sourceReference
|
2875
|
inferredGenus.setSec(sourceReference);
|
2876
|
|
2877
|
// Add the original source
|
2878
|
if (idInSourceSyn != null && idInSourceTaxon != null) {
|
2879
|
IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,
|
2880
|
idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
|
2881
|
inferredGenus.addSource(originalSource);
|
2882
|
|
2883
|
originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,
|
2884
|
idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
|
2885
|
inferredSynName.addSource(originalSource);
|
2886
|
originalSource = null;
|
2887
|
|
2888
|
}else{
|
2889
|
logger.error("There is an idInSource missing: " + idInSourceSyn + " of Synonym or " + idInSourceTaxon + " of Taxon");
|
2890
|
IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,
|
2891
|
idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
|
2892
|
inferredGenus.addSource(originalSource);
|
2893
|
|
2894
|
originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,
|
2895
|
idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
|
2896
|
inferredSynName.addSource(originalSource);
|
2897
|
originalSource = null;
|
2898
|
}
|
2899
|
|
2900
|
taxon.addSynonym(inferredGenus, SynonymType.INFERRED_GENUS_OF());
|
2901
|
|
2902
|
return inferredGenus;
|
2903
|
}
|
2904
|
|
2905
|
private Synonym createInferredEpithets(Taxon taxon,
|
2906
|
Map<UUID, IZoologicalName> zooHashMap, IZoologicalName taxonName,
|
2907
|
String epithetOfTaxon, String infragenericEpithetOfTaxon,
|
2908
|
String infraspecificEpithetOfTaxon, List<String> taxonNames,
|
2909
|
TaxonName parentName, TaxonBase<?> syn) {
|
2910
|
|
2911
|
Synonym inferredEpithet;
|
2912
|
TaxonName synName;
|
2913
|
IZoologicalName inferredSynName;
|
2914
|
HibernateProxyHelper.deproxy(syn);
|
2915
|
|
2916
|
// Determine the idInSource
|
2917
|
String idInSourceSyn = getIdInSource(syn);
|
2918
|
String idInSourceTaxon = getIdInSource(taxon);
|
2919
|
// Determine the sourceReference
|
2920
|
Reference sourceReference = syn.getSec();
|
2921
|
|
2922
|
if (sourceReference == null){
|
2923
|
logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon.getSec());
|
2924
|
sourceReference = taxon.getSec();
|
2925
|
}
|
2926
|
|
2927
|
synName = syn.getName();
|
2928
|
IZoologicalName zooSynName = getZoologicalName(synName.getUuid(), zooHashMap);
|
2929
|
String synGenusName = zooSynName.getGenusOrUninomial();
|
2930
|
String synInfraGenericEpithet = null;
|
2931
|
String synSpecificEpithet = null;
|
2932
|
|
2933
|
if (zooSynName.getInfraGenericEpithet() != null){
|
2934
|
synInfraGenericEpithet = zooSynName.getInfraGenericEpithet();
|
2935
|
}
|
2936
|
|
2937
|
if (zooSynName.isInfraSpecific()){
|
2938
|
synSpecificEpithet = zooSynName.getSpecificEpithet();
|
2939
|
}
|
2940
|
|
2941
|
/* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
|
2942
|
synonymsGenus.put(synGenusName, idInSource);
|
2943
|
}*/
|
2944
|
|
2945
|
inferredSynName = TaxonNameFactory.NewZoologicalInstance(taxon.getName().getRank());
|
2946
|
|
2947
|
// DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
|
2948
|
if (epithetOfTaxon == null && infragenericEpithetOfTaxon == null && infraspecificEpithetOfTaxon == null) {
|
2949
|
logger.error("This specificEpithet is NULL" + taxon.getTitleCache());
|
2950
|
}
|
2951
|
inferredSynName.setGenusOrUninomial(synGenusName);
|
2952
|
|
2953
|
if (parentName.isInfraGeneric()){
|
2954
|
inferredSynName.setInfraGenericEpithet(synInfraGenericEpithet);
|
2955
|
}
|
2956
|
if (taxonName.isSpecies()){
|
2957
|
inferredSynName.setSpecificEpithet(epithetOfTaxon);
|
2958
|
}else if (taxonName.isInfraSpecific()){
|
2959
|
inferredSynName.setSpecificEpithet(synSpecificEpithet);
|
2960
|
inferredSynName.setInfraSpecificEpithet(infraspecificEpithetOfTaxon);
|
2961
|
}
|
2962
|
|
2963
|
inferredEpithet = Synonym.NewInstance(inferredSynName, null);
|
2964
|
|
2965
|
// Set the sourceReference
|
2966
|
inferredEpithet.setSec(sourceReference);
|
2967
|
|
2968
|
/* Add the original source
|
2969
|
if (idInSource != null) {
|
2970
|
IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
|
2971
|
|
2972
|
// Add the citation
|
2973
|
Reference citation = getCitation(syn);
|
2974
|
if (citation != null) {
|
2975
|
originalSource.setCitation(citation);
|
2976
|
inferredEpithet.addSource(originalSource);
|
2977
|
}
|
2978
|
}*/
|
2979
|
String taxonId = idInSourceTaxon+ "; " + idInSourceSyn;
|
2980
|
|
2981
|
|
2982
|
IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,
|
2983
|
taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);
|
2984
|
|
2985
|
inferredEpithet.addSource(originalSource);
|
2986
|
|
2987
|
originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,
|
2988
|
taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);
|
2989
|
|
2990
|
inferredSynName.addSource(originalSource);
|
2991
|
|
2992
|
taxon.addSynonym(inferredEpithet, SynonymType.INFERRED_EPITHET_OF());
|
2993
|
|
2994
|
return inferredEpithet;
|
2995
|
}
|
2996
|
|
2997
|
/**
|
2998
|
* Returns an existing IZoologicalName or extends an internal hashmap if it does not exist.
|
2999
|
* Very likely only useful for createInferredSynonyms().
|
3000
|
* @param uuid
|
3001
|
* @param zooHashMap
|
3002
|
* @return
|
3003
|
*/
|
3004
|
private IZoologicalName getZoologicalName(UUID uuid, Map <UUID, IZoologicalName> zooHashMap) {
|
3005
|
IZoologicalName taxonName =nameDao.findZoologicalNameByUUID(uuid);
|
3006
|
if (taxonName == null) {
|
3007
|
taxonName = zooHashMap.get(uuid);
|
3008
|
}
|
3009
|
return taxonName;
|
3010
|
}
|
3011
|
|
3012
|
/**
|
3013
|
* Returns the idInSource for a given Synonym.
|
3014
|
* @param syn
|
3015
|
*/
|
3016
|
private String getIdInSource(TaxonBase<?> taxonBase) {
|
3017
|
String idInSource = null;
|
3018
|
Set<IdentifiableSource> sources = taxonBase.getSources();
|
3019
|
if (sources.size() == 1) {
|
3020
|
IdentifiableSource source = sources.iterator().next();
|
3021
|
if (source != null) {
|
3022
|
idInSource = source.getIdInSource();
|
3023
|
}
|
3024
|
} else if (sources.size() > 1) {
|
3025
|
int count = 1;
|
3026
|
idInSource = "";
|
3027
|
for (IdentifiableSource source : sources) {
|
3028
|
idInSource += source.getIdInSource();
|
3029
|
if (count < sources.size()) {
|
3030
|
idInSource += "; ";
|
3031
|
}
|
3032
|
count++;
|
3033
|
}
|
3034
|
} else if (sources.size() == 0){
|
3035
|
logger.warn("No idInSource for TaxonBase " + taxonBase.getUuid() + " - " + taxonBase.getTitleCache());
|
3036
|
}
|
3037
|
|
3038
|
return idInSource;
|
3039
|
}
|
3040
|
|
3041
|
/**
|
3042
|
* Returns the citation for a given Synonym.
|
3043
|
* @param syn
|
3044
|
*/
|
3045
|
private Reference getCitation(Synonym syn) {
|
3046
|
Reference citation = null;
|
3047
|
Set<IdentifiableSource> sources = syn.getSources();
|
3048
|
if (sources.size() == 1) {
|
3049
|
IdentifiableSource source = sources.iterator().next();
|
3050
|
if (source != null) {
|
3051
|
citation = source.getCitation();
|
3052
|
}
|
3053
|
} else if (sources.size() > 1) {
|
3054
|
logger.warn("This Synonym has more than one source: " + syn.getUuid() + " (" + syn.getTitleCache() +")");
|
3055
|
}
|
3056
|
|
3057
|
return citation;
|
3058
|
}
|
3059
|
|
3060
|
@Override
|
3061
|
public List<Synonym> createAllInferredSynonyms(Taxon taxon, Classification tree, boolean doWithMisappliedNames){
|
3062
|
List <Synonym> inferredSynonyms = new ArrayList<>();
|
3063
|
|
3064
|
inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymType.INFERRED_EPITHET_OF(), doWithMisappliedNames));
|
3065
|
inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymType.INFERRED_GENUS_OF(), doWithMisappliedNames));
|
3066
|
inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymType.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames));
|
3067
|
|
3068
|
return inferredSynonyms;
|
3069
|
}
|
3070
|
|
3071
|
@Override
|
3072
|
public List<Classification> listClassifications(TaxonBase taxonBase, Integer limit, Integer start, List<String> propertyPaths) {
|
3073
|
|
3074
|
// TODO quickly implemented, create according dao !!!!
|
3075
|
Set<TaxonNode> nodes = new HashSet<>();
|
3076
|
Set<Classification> classifications = new HashSet<>();
|
3077
|
List<Classification> list = new ArrayList<>();
|
3078
|
|
3079
|
if (taxonBase == null) {
|
3080
|
return list;
|
3081
|
}
|
3082
|
|
3083
|
taxonBase = load(taxonBase.getUuid());
|
3084
|
|
3085
|
if (taxonBase instanceof Taxon) {
|
3086
|
nodes.addAll(((Taxon)taxonBase).getTaxonNodes());
|
3087
|
} else {
|
3088
|
Taxon taxon = ((Synonym)taxonBase).getAcceptedTaxon();
|
3089
|
if (taxon != null){
|
3090
|
nodes.addAll(taxon.getTaxonNodes());
|
3091
|
}
|
3092
|
}
|
3093
|
for (TaxonNode node : nodes) {
|
3094
|
classifications.add(node.getClassification());
|
3095
|
}
|
3096
|
list.addAll(classifications);
|
3097
|
return list;
|
3098
|
}
|
3099
|
|
3100
|
@Override
|
3101
|
@Transactional(readOnly = false)
|
3102
|
public UpdateResult changeRelatedTaxonToSynonym(UUID fromTaxonUuid,
|
3103
|
UUID toTaxonUuid,
|
3104
|
TaxonRelationshipType oldRelationshipType,
|
3105
|
SynonymType synonymType) throws DataChangeNoRollbackException {
|
3106
|
UpdateResult result = new UpdateResult();
|
3107
|
Taxon fromTaxon = (Taxon) dao.load(fromTaxonUuid);
|
3108
|
Taxon toTaxon = (Taxon) dao.load(toTaxonUuid);
|
3109
|
result = changeRelatedTaxonToSynonym(fromTaxon, toTaxon, oldRelationshipType, synonymType);
|
3110
|
|
3111
|
result.addUpdatedObject(toTaxon);
|
3112
|
result.addUpdatedObject(result.getCdmEntity());
|
3113
|
|
3114
|
return result;
|
3115
|
}
|
3116
|
|
3117
|
@Override
|
3118
|
@Transactional(readOnly = false)
|
3119
|
public UpdateResult changeRelatedTaxonToSynonym(Taxon fromTaxon, Taxon toTaxon, TaxonRelationshipType oldRelationshipType,
|
3120
|
SynonymType synonymType) throws DataChangeNoRollbackException {
|
3121
|
|
3122
|
UpdateResult result = new UpdateResult();
|
3123
|
// Create new synonym using concept name
|
3124
|
TaxonName synonymName = fromTaxon.getName();
|
3125
|
|
3126
|
// Remove concept relation from taxon
|
3127
|
toTaxon.removeTaxon(fromTaxon, oldRelationshipType);
|
3128
|
|
3129
|
// Create a new synonym for the taxon
|
3130
|
Synonym synonym;
|
3131
|
if (synonymType != null
|
3132
|
&& synonymType.equals(SynonymType.HOMOTYPIC_SYNONYM_OF())){
|
3133
|
synonym = Synonym.NewInstance(synonymName, fromTaxon.getSec());
|
3134
|
toTaxon.addHomotypicSynonym(synonym);
|
3135
|
} else{
|
3136
|
synonym = toTaxon.addHeterotypicSynonymName(synonymName);
|
3137
|
}
|
3138
|
//keep the publish flag
|
3139
|
synonym.setPublish(fromTaxon.isPublish());
|
3140
|
this.saveOrUpdate(toTaxon);
|
3141
|
//TODO: configurator and classification
|
3142
|
TaxonDeletionConfigurator config = new TaxonDeletionConfigurator();
|
3143
|
config.setDeleteNameIfPossible(false);
|
3144
|
result.includeResult(this.deleteTaxon(fromTaxon.getUuid(), config, null));
|
3145
|
result.setCdmEntity(synonym);
|
3146
|
result.addUpdatedObject(toTaxon);
|
3147
|
result.addUpdatedObject(synonym);
|
3148
|
return result;
|
3149
|
}
|
3150
|
|
3151
|
@Override
|
3152
|
public DeleteResult isDeletable(UUID taxonBaseUuid, DeleteConfiguratorBase config){
|
3153
|
DeleteResult result = new DeleteResult();
|
3154
|
TaxonBase<?> taxonBase = load(taxonBaseUuid);
|
3155
|
Set<CdmBase> references = commonService.getReferencingObjectsForDeletion(taxonBase);
|
3156
|
if (taxonBase instanceof Taxon){
|
3157
|
TaxonDeletionConfigurator taxonConfig = (TaxonDeletionConfigurator) config;
|
3158
|
List<String> propertyPaths = new ArrayList<>();
|
3159
|
propertyPaths.add("taxonNodes");
|
3160
|
Taxon taxon = (Taxon)load(taxonBaseUuid, propertyPaths);
|
3161
|
|
3162
|
result = isDeletableForTaxon(references, taxonConfig );
|
3163
|
|
3164
|
if (taxonConfig.isDeleteNameIfPossible()){
|
3165
|
if (taxonBase.getName() != null){
|
3166
|
DeleteResult nameResult = nameService.isDeletable(taxonBase.getName().getUuid(), taxonConfig.getNameDeletionConfig(), taxon.getUuid());
|
3167
|
if (!nameResult.isOk()){
|
3168
|
result.addExceptions(nameResult.getExceptions());
|
3169
|
}
|
3170
|
}
|
3171
|
|
3172
|
}
|
3173
|
}else{
|
3174
|
SynonymDeletionConfigurator synonymConfig = (SynonymDeletionConfigurator) config;
|
3175
|
result = isDeletableForSynonym(references, synonymConfig);
|
3176
|
if (synonymConfig.isDeleteNameIfPossible() && taxonBase.getName() != null){
|
3177
|
DeleteResult nameResult = nameService.isDeletable(taxonBase.getName().getUuid(), synonymConfig.getNameDeletionConfig(), taxonBase.getUuid());
|
3178
|
if (!nameResult.isOk()){
|
3179
|
result.addExceptions(nameResult.getExceptions());
|
3180
|
|
3181
|
}
|
3182
|
}
|
3183
|
}
|
3184
|
|
3185
|
return result;
|
3186
|
}
|
3187
|
|
3188
|
private DeleteResult isDeletableForSynonym(Set<CdmBase> references, SynonymDeletionConfigurator config){
|
3189
|
|
3190
|
DeleteResult result = new DeleteResult();
|
3191
|
for (CdmBase ref: references){
|
3192
|
if (!(ref instanceof Taxon || ref instanceof TaxonName || ref instanceof SecundumSource)){
|
3193
|
String message = "The Synonym can't be deleted as long as it is referenced by " + ref.getClass().getSimpleName() + " with id "+ ref.getId();
|
3194
|
result.addException(new ReferencedObjectUndeletableException(message));
|
3195
|
result.addRelatedObject(ref);
|
3196
|
result.setAbort();
|
3197
|
}
|
3198
|
}
|
3199
|
|
3200
|
return result;
|
3201
|
}
|
3202
|
|
3203
|
private DeleteResult isDeletableForTaxon(Set<CdmBase> references, TaxonDeletionConfigurator config){
|
3204
|
String message = null;
|
3205
|
DeleteResult result = new DeleteResult();
|
3206
|
for (CdmBase ref: references){
|
3207
|
if (!(ref instanceof TaxonName || ref instanceof SecundumSource)){
|
3208
|
message = null;
|
3209
|
if (!config.isDeleteSynonymRelations() && (ref instanceof Synonym)){
|
3210
|
message = "The taxon can't be deleted as long as it has synonyms.";
|
3211
|
}
|
3212
|
if (!config.isDeleteDescriptions() && (ref instanceof DescriptionBase)){
|
3213
|
message = "The taxon can't be deleted as long as it has factual data.";
|
3214
|
}
|
3215
|
|
3216
|
if (!config.isDeleteTaxonNodes() && (ref instanceof TaxonNode)){
|
3217
|
message = "The taxon can't be deleted as long as it belongs to a taxon node.";
|
3218
|
}
|
3219
|
if (ref instanceof TaxonNode && config.getClassificationUuid() != null && !config.isDeleteInAllClassifications() && !((TaxonNode)ref).getClassification().getUuid().equals(config.getClassificationUuid())){
|
3220
|
message = "The taxon can't be deleted as long as it is used in more than one classification";
|
3221
|
|
3222
|
}
|
3223
|
if (!config.isDeleteTaxonRelationships() && (ref instanceof TaxonRelationship)){
|
3224
|
if (!config.isDeleteMisappliedNames() &&
|
3225
|
(((TaxonRelationship)ref).getType().isMisappliedName())){
|
3226
|
message = "The taxon can't be deleted as long as it has misapplied names or invalid designations.";
|
3227
|
} else{
|
3228
|
message = "The taxon can't be deleted as long as it belongs to taxon relationship.";
|
3229
|
}
|
3230
|
}
|
3231
|
if (ref instanceof PolytomousKeyNode){
|
3232
|
message = "The taxon can't be deleted as long as it is referenced by a polytomous key node.";
|
3233
|
}
|
3234
|
|
3235
|
if (HibernateProxyHelper.isInstanceOf(ref, IIdentificationKey.class)){
|
3236
|
message = "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this taxon";
|
3237
|
}
|
3238
|
|
3239
|
/* //PolytomousKeyNode
|
3240
|
if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
|
3241
|
String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";
|
3242
|
return message;
|
3243
|
}*/
|
3244
|
|
3245
|
//TaxonInteraction
|
3246
|
if (ref.isInstanceOf(TaxonInteraction.class)){
|
3247
|
message = "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
|
3248
|
}
|
3249
|
|
3250
|
//TaxonInteraction
|
3251
|
if (ref.isInstanceOf(DeterminationEvent.class)){
|
3252
|
message = "Taxon can't be deleted as it is used in a determination event";
|
3253
|
}
|
3254
|
}
|
3255
|
if (message != null){
|
3256
|
result.addException(new ReferencedObjectUndeletableException(message));
|
3257
|
result.addRelatedObject(ref);
|
3258
|
result.setAbort();
|
3259
|
}
|
3260
|
}
|
3261
|
|
3262
|
return result;
|
3263
|
}
|
3264
|
|
3265
|
@Override
|
3266
|
public IncludedTaxaDTO listIncludedTaxa(UUID taxonUuid, IncludedTaxonConfiguration config) {
|
3267
|
IncludedTaxaDTO result = new IncludedTaxaDTO(taxonUuid);
|
3268
|
|
3269
|
//preliminary implementation
|
3270
|
|
3271
|
Set<Taxon> taxa = new HashSet<>();
|
3272
|
TaxonBase<?> taxonBase = find(taxonUuid);
|
3273
|
if (taxonBase == null){
|
3274
|
return new IncludedTaxaDTO();
|
3275
|
}else if (taxonBase.isInstanceOf(Taxon.class)){
|
3276
|
Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
|
3277
|
taxa.add(taxon);
|
3278
|
}else if (taxonBase.isInstanceOf(Synonym.class)){
|
3279
|
//TODO partial synonyms ??
|
3280
|
//TODO synonyms in general
|
3281
|
Synonym syn = CdmBase.deproxy(taxonBase, Synonym.class);
|
3282
|
taxa.add(syn.getAcceptedTaxon());
|
3283
|
}else{
|
3284
|
throw new IllegalArgumentException("Unhandled class " + taxonBase.getClass().getSimpleName());
|
3285
|
}
|
3286
|
|
3287
|
Set<Taxon> related = makeRelatedIncluded(taxa, result, config);
|
3288
|
int i = 0;
|
3289
|
while((! related.isEmpty()) && i++ < 100){ //to avoid
|
3290
|
related = makeRelatedIncluded(related, result, config);
|
3291
|
}
|
3292
|
|
3293
|
return result;
|
3294
|
}
|
3295
|
|
3296
|
/**
|
3297
|
* @param uncheckedTaxa
|
3298
|
* @param existingTaxa
|
3299
|
* @param config
|
3300
|
*
|
3301
|
* Computes all children and conceptually congruent and included taxa and adds them to the existingTaxa
|
3302
|
* data structure.
|
3303
|
* @return the set of conceptually related taxa for further use
|
3304
|
*/
|
3305
|
private Set<Taxon> makeRelatedIncluded(Set<Taxon> uncheckedTaxa, IncludedTaxaDTO existingTaxa, IncludedTaxonConfiguration config) {
|
3306
|
|
3307
|
//children
|
3308
|
Set<TaxonNode> taxonNodes = new HashSet<>();
|
3309
|
for (Taxon taxon: uncheckedTaxa){
|
3310
|
taxonNodes.addAll(taxon.getTaxonNodes());
|
3311
|
}
|
3312
|
|
3313
|
Set<Taxon> children = new HashSet<>();
|
3314
|
if (! config.onlyCongruent){
|
3315
|
for (TaxonNode node: taxonNodes){
|
3316
|
List<TaxonNode> childNodes = nodeService.loadChildNodesOfTaxonNode(node, null, true, config.includeUnpublished, null);
|
3317
|
for (TaxonNode child : childNodes){
|
3318
|
children.add(child.getTaxon());
|
3319
|
}
|
3320
|
}
|
3321
|
children.remove(null); // just to be on the save side
|
3322
|
}
|
3323
|
|
3324
|
Iterator<Taxon> it = children.iterator();
|
3325
|
while(it.hasNext()){
|
3326
|
UUID uuid = it.next().getUuid();
|
3327
|
if (existingTaxa.contains(uuid)){
|
3328
|
it.remove();
|
3329
|
}else{
|
3330
|
existingTaxa.addIncludedTaxon(uuid, new ArrayList<>(), false);
|
3331
|
}
|
3332
|
}
|
3333
|
|
3334
|
//concept relations
|
3335
|
Set<Taxon> uncheckedAndChildren = new HashSet<>(uncheckedTaxa);
|
3336
|
uncheckedAndChildren.addAll(children);
|
3337
|
|
3338
|
Set<Taxon> relatedTaxa = makeConceptIncludedTaxa(uncheckedAndChildren, existingTaxa, config);
|
3339
|
|
3340
|
|
3341
|
Set<Taxon> result = new HashSet<>(relatedTaxa);
|
3342
|
return result;
|
3343
|
}
|
3344
|
|
3345
|
/**
|
3346
|
* Computes all conceptually congruent or included taxa and adds them to the existingTaxa data structure.
|
3347
|
* @return the set of these computed taxa
|
3348
|
*/
|
3349
|
private Set<Taxon> makeConceptIncludedTaxa(Set<Taxon> unchecked, IncludedTaxaDTO existingTaxa, IncludedTaxonConfiguration config) {
|
3350
|
Set<Taxon> result = new HashSet<>();
|
3351
|
|
3352
|
for (Taxon taxon : unchecked){
|
3353
|
Set<TaxonRelationship> fromRelations = taxon.getRelationsFromThisTaxon();
|
3354
|
Set<TaxonRelationship> toRelations = taxon.getRelationsToThisTaxon();
|
3355
|
|
3356
|
for (TaxonRelationship fromRel : fromRelations){
|
3357
|
if (config.includeDoubtful == false && fromRel.isDoubtful()){
|
3358
|
continue;
|
3359
|
}
|
3360
|
TaxonRelationshipType fromRelType = fromRel.getType();
|
3361
|
if (fromRelType.equals(TaxonRelationshipType.CONGRUENT_TO()) ||
|
3362
|
!config.onlyCongruent && (
|
3363
|
fromRelType.equals(TaxonRelationshipType.INCLUDES()) ||
|
3364
|
fromRelType.equals(TaxonRelationshipType.CONGRUENT_OR_INCLUDES())
|
3365
|
)
|
3366
|
){
|
3367
|
result.add(fromRel.getToTaxon());
|
3368
|
}
|
3369
|
}
|
3370
|
|
3371
|
for (TaxonRelationship toRel : toRelations){
|
3372
|
if (config.includeDoubtful == false && toRel.isDoubtful()){
|
3373
|
continue;
|
3374
|
}
|
3375
|
TaxonRelationshipType fromRelType = toRel.getType();
|
3376
|
if (fromRelType.equals(TaxonRelationshipType.CONGRUENT_TO()) ||
|
3377
|
!config.includeDoubtful && fromRelType.equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN())){
|
3378
|
result.add(toRel.getFromTaxon());
|
3379
|
}
|
3380
|
}
|
3381
|
}
|
3382
|
|
3383
|
Iterator<Taxon> it = result.iterator();
|
3384
|
while(it.hasNext()){
|
3385
|
UUID uuid = it.next().getUuid();
|
3386
|
if (existingTaxa.contains(uuid)){
|
3387
|
it.remove();
|
3388
|
}else{
|
3389
|
existingTaxa.addIncludedTaxon(uuid, new ArrayList<>(), false);
|
3390
|
}
|
3391
|
}
|
3392
|
return result;
|
3393
|
}
|
3394
|
|
3395
|
@Override
|
3396
|
public List<TaxonBase> findTaxaByName(MatchingTaxonConfigurator config){
|
3397
|
@SuppressWarnings("rawtypes")
|
3398
|
List<TaxonBase> taxonList = dao.getTaxaByName(true, config.isIncludeSynonyms(), false, false, false,
|
3399
|
config.getTaxonNameTitle(), null, null, MatchMode.EXACT, null, config.isIncludeSynonyms(), null, null, null, config.getPropertyPath());
|
3400
|
return taxonList;
|
3401
|
}
|
3402
|
|
3403
|
@Override
|
3404
|
@Transactional(readOnly = true)
|
3405
|
public <S extends TaxonBase> Pager<IdentifiedEntityDTO<S>> findByIdentifier(
|
3406
|
Class<S> clazz, String identifier, DefinedTerm identifierType, TaxonNode subtreeFilter,
|
3407
|
MatchMode matchmode, boolean includeEntity, Integer pageSize,
|
3408
|
Integer pageNumber, List<String> propertyPaths) {
|
3409
|
if (subtreeFilter == null){
|
3410
|
return findByIdentifier(clazz, identifier, identifierType, matchmode, includeEntity, pageSize, pageNumber, propertyPaths);
|
3411
|
}
|
3412
|
|
3413
|
long numberOfResults = dao.countByIdentifier(clazz, identifier, identifierType, subtreeFilter, matchmode);
|
3414
|
List<Object[]> daoResults = new ArrayList<>();
|
3415
|
if(numberOfResults > 0) { // no point checking again
|
3416
|
daoResults = dao.findByIdentifier(clazz, identifier, identifierType, subtreeFilter,
|
3417
|
matchmode, includeEntity, pageSize, pageNumber, propertyPaths);
|
3418
|
}
|
3419
|
|
3420
|
List<IdentifiedEntityDTO<S>> result = new ArrayList<>();
|
3421
|
for (Object[] daoObj : daoResults){
|
3422
|
if (includeEntity){
|
3423
|
result.add(new IdentifiedEntityDTO<>((DefinedTerm)daoObj[0], (String)daoObj[1], (S)daoObj[2]));
|
3424
|
}else{
|
3425
|
result.add(new IdentifiedEntityDTO<>((DefinedTerm)daoObj[0], (String)daoObj[1], (UUID)daoObj[2], (String)daoObj[3], null));
|
3426
|
}
|
3427
|
}
|
3428
|
return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, result);
|
3429
|
}
|
3430
|
|
3431
|
@Override
|
3432
|
@Transactional(readOnly = true)
|
3433
|
public <S extends TaxonBase> Pager<MarkedEntityDTO<S>> findByMarker(
|
3434
|
Class<S> clazz, MarkerType markerType, Boolean markerValue,
|
3435
|
TaxonNode subtreeFilter, boolean includeEntity, TaxonTitleType titleType,
|
3436
|
Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
|
3437
|
if (subtreeFilter == null){
|
3438
|
return super.findByMarker (clazz, markerType, markerValue, includeEntity, pageSize, pageNumber, propertyPaths);
|
3439
|
}
|
3440
|
|
3441
|
Long numberOfResults = dao.countByMarker(clazz, markerType, markerValue, subtreeFilter);
|
3442
|
List<Object[]> daoResults = new ArrayList<>();
|
3443
|
if(numberOfResults > 0) { // no point checking again
|
3444
|
daoResults = dao.findByMarker(clazz, markerType, markerValue, subtreeFilter,
|
3445
|
includeEntity, titleType, pageSize, pageNumber, propertyPaths);
|
3446
|
}
|
3447
|
|
3448
|
List<MarkedEntityDTO<S>> result = new ArrayList<>();
|
3449
|
for (Object[] daoObj : daoResults){
|
3450
|
if (includeEntity){
|
3451
|
result.add(new MarkedEntityDTO<S>((MarkerType)daoObj[0], (Boolean)daoObj[1], (S)daoObj[2]));
|
3452
|
}else{
|
3453
|
result.add(new MarkedEntityDTO<S>((MarkerType)daoObj[0], (Boolean)daoObj[1], (UUID)daoObj[2], (String)daoObj[3]));
|
3454
|
}
|
3455
|
}
|
3456
|
return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, result);
|
3457
|
}
|
3458
|
|
3459
|
@Override
|
3460
|
public UpdateResult moveFactualDateToAnotherTaxon(UUID fromTaxonUuid, UUID toTaxonUuid){
|
3461
|
UpdateResult result = new UpdateResult();
|
3462
|
|
3463
|
Taxon fromTaxon = (Taxon)dao.load(fromTaxonUuid);
|
3464
|
Taxon toTaxon = (Taxon) dao.load(toTaxonUuid);
|
3465
|
for(TaxonDescription description : fromTaxon.getDescriptions()){
|
3466
|
//reload to avoid session conflicts
|
3467
|
description = HibernateProxyHelper.deproxy(description, TaxonDescription.class);
|
3468
|
|
3469
|
String moveMessage = String.format("Description moved from %s", fromTaxon);
|
3470
|
if(description.isProtectedTitleCache()){
|
3471
|
String separator = "";
|
3472
|
if(!StringUtils.isBlank(description.getTitleCache())){
|
3473
|
separator = " - ";
|
3474
|
}
|
3475
|
description.setTitleCache(description.getTitleCache() + separator + moveMessage, true);
|
3476
|
}
|
3477
|
Annotation annotation = Annotation.NewInstance(moveMessage, Language.getDefaultLanguage());
|
3478
|
annotation.setAnnotationType(AnnotationType.TECHNICAL());
|
3479
|
description.addAnnotation(annotation);
|
3480
|
toTaxon.addDescription(description);
|
3481
|
dao.saveOrUpdate(toTaxon);
|
3482
|
dao.saveOrUpdate(fromTaxon);
|
3483
|
result.addUpdatedObject(toTaxon);
|
3484
|
result.addUpdatedObject(fromTaxon);
|
3485
|
|
3486
|
}
|
3487
|
|
3488
|
return result;
|
3489
|
}
|
3490
|
|
3491
|
@Override
|
3492
|
@Transactional(readOnly = false)
|
3493
|
public UpdateResult swapSynonymAndAcceptedTaxon(UUID synonymUUid,
|
3494
|
UUID acceptedTaxonUuid, boolean setNameInSource, boolean newUuidForAcceptedTaxon, SecReferenceHandlingSwapEnum secHandling, UUID newSecAcc, UUID newSecSyn) {
|
3495
|
TaxonBase<?> base = this.load(synonymUUid);
|
3496
|
Synonym syn = HibernateProxyHelper.deproxy(base, Synonym.class);
|
3497
|
base = this.load(acceptedTaxonUuid);
|
3498
|
Taxon taxon = HibernateProxyHelper.deproxy(base, Taxon.class);
|
3499
|
|
3500
|
Reference refAcc = referenceService.load(newSecAcc);
|
3501
|
Reference refSyn = referenceService.load(newSecSyn);
|
3502
|
|
3503
|
return this.swapSynonymAndAcceptedTaxon(syn, taxon, setNameInSource, newUuidForAcceptedTaxon, secHandling, refAcc, refSyn);
|
3504
|
}
|
3505
|
|
3506
|
@Override
|
3507
|
public TaxonRelationshipsDTO listTaxonRelationships(UUID taxonUuid, Set<TaxonRelationshipType> directTypes,
|
3508
|
Set<TaxonRelationshipType> inversTypes,
|
3509
|
Direction direction, boolean groupMisapplications,
|
3510
|
boolean includeUnpublished,
|
3511
|
Integer pageSize, Integer pageNumber) {
|
3512
|
TaxonBase<?> taxonBase = dao.load(taxonUuid);
|
3513
|
if (taxonBase == null || !taxonBase.isInstanceOf(TaxonBase.class)){
|
3514
|
//TODO handle
|
3515
|
throw new RuntimeException("Taxon for uuid " + taxonUuid + " not found");
|
3516
|
}else{
|
3517
|
Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
|
3518
|
boolean doDirect = (direction == null || direction == Direction.relatedTo);
|
3519
|
boolean doInvers = (direction == null || direction == Direction.relatedFrom);
|
3520
|
|
3521
|
TaxonRelationshipsDTO dto = new TaxonRelationshipsDTO();
|
3522
|
|
3523
|
//TODO paging is difficult because misapplication string is an attribute
|
3524
|
//of toplevel dto
|
3525
|
// long numberOfResults = dao.countTaxonRelationships(taxon, type, includeUnpublished, TaxonRelationship.Direction.relatedFrom);
|
3526
|
// List<TaxonRelationshipsDTO> results = new ArrayList<>();
|
3527
|
// if(numberOfResults > 0) { // no point checking again
|
3528
|
// results = dao.getTaxonRelationships(taxon, type, includeUnpublished, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedFrom);
|
3529
|
// }
|
3530
|
//
|
3531
|
// return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);;
|
3532
|
|
3533
|
//TODO languages
|
3534
|
List<Language> languages = null;
|
3535
|
if (doDirect){
|
3536
|
direction = Direction.relatedTo;
|
3537
|
//TODO order hints, property path
|
3538
|
List<TaxonRelationship> relations = dao.getTaxonRelationships(taxon, directTypes, includeUnpublished, pageSize, pageNumber, null, null, direction.invers());
|
3539
|
for (TaxonRelationship relation : relations){
|
3540
|
dto.addRelation(relation, direction, languages);
|
3541
|
}
|
3542
|
}
|
3543
|
if (doInvers){
|
3544
|
direction = Direction.relatedFrom;
|
3545
|
//TODO order hints, property path
|
3546
|
List<TaxonRelationship> relations = dao.getTaxonRelationships(taxon, inversTypes, includeUnpublished, pageSize, pageNumber, null, null, direction.invers());
|
3547
|
for (TaxonRelationship relation : relations){
|
3548
|
dto.addRelation(relation, direction, languages);
|
3549
|
}
|
3550
|
}
|
3551
|
if (groupMisapplications){
|
3552
|
//TODO
|
3553
|
dto.createMisapplicationString();
|
3554
|
}
|
3555
|
return dto;
|
3556
|
}
|
3557
|
}
|
3558
|
}
|