1 package eu
.etaxonomy
.cdm
.api
.service
;
3 import java
.io
.Serializable
;
4 import java
.util
.ArrayList
;
5 import java
.util
.Arrays
;
6 import java
.util
.Collection
;
7 import java
.util
.HashMap
;
8 import java
.util
.HashSet
;
11 import java
.util
.Optional
;
13 import java
.util
.UUID
;
15 import org
.apache
.log4j
.Logger
;
16 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
17 import org
.springframework
.stereotype
.Service
;
18 import org
.springframework
.transaction
.annotation
.Transactional
;
20 import eu
.etaxonomy
.cdm
.api
.service
.dto
.RowWrapperDTO
;
21 import eu
.etaxonomy
.cdm
.api
.service
.dto
.SpecimenRowWrapperDTO
;
22 import eu
.etaxonomy
.cdm
.api
.service
.dto
.TaxonRowWrapperDTO
;
23 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
24 import eu
.etaxonomy
.cdm
.common
.monitor
.IRemotingProgressMonitor
;
25 import eu
.etaxonomy
.cdm
.common
.monitor
.RemotingProgressMonitorThread
;
26 import eu
.etaxonomy
.cdm
.filter
.TaxonNodeFilter
;
27 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
28 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
29 import eu
.etaxonomy
.cdm
.model
.description
.CategoricalData
;
30 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
31 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
32 import eu
.etaxonomy
.cdm
.model
.description
.DescriptiveDataSet
;
33 import eu
.etaxonomy
.cdm
.model
.description
.DescriptiveSystemRole
;
34 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
35 import eu
.etaxonomy
.cdm
.model
.description
.QuantitativeData
;
36 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
37 import eu
.etaxonomy
.cdm
.model
.description
.StatisticalMeasure
;
38 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
39 import eu
.etaxonomy
.cdm
.model
.description
.TextData
;
40 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
41 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
42 import eu
.etaxonomy
.cdm
.model
.occurrence
.FieldUnit
;
43 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
44 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
45 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
46 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
47 import eu
.etaxonomy
.cdm
.persistence
.dao
.description
.IDescriptiveDataSetDao
;
48 import eu
.etaxonomy
.cdm
.persistence
.dto
.SpecimenNodeWrapper
;
49 import eu
.etaxonomy
.cdm
.persistence
.dto
.UuidAndTitleCache
;
50 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
53 @Transactional(readOnly
= false)
54 public class DescriptiveDataSetService
55 extends IdentifiableServiceBase
<DescriptiveDataSet
, IDescriptiveDataSetDao
>
56 implements IDescriptiveDataSetService
{
58 private static Logger logger
= Logger
.getLogger(DescriptiveDataSetService
.class);
61 private IOccurrenceService occurrenceService
;
64 private ITaxonService taxonService
;
67 private IDescriptionService descriptionService
;
70 private ITaxonNodeService taxonNodeService
;
73 private IProgressMonitorService progressMonitorService
;
77 protected void setDao(IDescriptiveDataSetDao dao
) {
82 public Map
<DescriptionBase
, Set
<DescriptionElementBase
>> getDescriptionElements(DescriptiveDataSet descriptiveDataSet
, Set
<Feature
> features
, Integer pageSize
, Integer pageNumber
,
83 List
<String
> propertyPaths
) {
84 return dao
.getDescriptionElements(descriptiveDataSet
, features
, pageSize
, pageNumber
, propertyPaths
);
88 public <T
extends DescriptionElementBase
> Map
<UuidAndTitleCache
, Map
<UUID
, Set
<T
>>> getTaxonFeatureDescriptionElementMap(
89 Class
<T
> clazz
, UUID descriptiveDataSetUuid
, DescriptiveSystemRole role
) {
90 return dao
.getTaxonFeatureDescriptionElementMap(clazz
, descriptiveDataSetUuid
, role
);
94 public List
<UuidAndTitleCache
<DescriptiveDataSet
>> getDescriptiveDataSetUuidAndTitleCache(Integer limitOfInitialElements
, String pattern
) {
95 return dao
.getDescriptiveDataSetUuidAndTitleCache( limitOfInitialElements
, pattern
);
101 public UUID
monitGetRowWrapper(DescriptiveDataSet descriptiveDataSet
) {
102 RemotingProgressMonitorThread monitorThread
= new RemotingProgressMonitorThread() {
104 public Serializable
doRun(IRemotingProgressMonitor monitor
) {
105 return getRowWrapper(descriptiveDataSet
, monitor
);
108 UUID uuid
= progressMonitorService
.registerNewRemotingMonitor(monitorThread
);
109 monitorThread
.setPriority(3);
110 monitorThread
.start();
115 public ArrayList
<RowWrapperDTO
> getRowWrapper(DescriptiveDataSet descriptiveDataSet
, IProgressMonitor monitor
) {
116 monitor
.beginTask("Load row wrapper", descriptiveDataSet
.getDescriptions().size());
117 ArrayList
<RowWrapperDTO
> wrappers
= new ArrayList
<>();
118 Set
<DescriptionBase
> descriptions
= descriptiveDataSet
.getDescriptions();
119 for (DescriptionBase description
: descriptions
) {
120 if(monitor
.isCanceled()){
121 return new ArrayList
<>();
123 RowWrapperDTO rowWrapper
= null;
124 if(HibernateProxyHelper
.isInstanceOf(description
, TaxonDescription
.class)){
125 rowWrapper
= createTaxonRowWrapper(HibernateProxyHelper
.deproxy(description
, TaxonDescription
.class), descriptiveDataSet
);
127 else if (HibernateProxyHelper
.isInstanceOf(description
, SpecimenDescription
.class)){
128 rowWrapper
= createSpecimenRowWrapper(HibernateProxyHelper
.deproxy(description
, SpecimenDescription
.class), descriptiveDataSet
);
130 if(rowWrapper
!=null){
131 wrappers
.add(rowWrapper
);
139 public Collection
<SpecimenNodeWrapper
> loadSpecimens(DescriptiveDataSet descriptiveDataSet
){
140 List
<UUID
> filteredNodes
= findFilteredTaxonNodes(descriptiveDataSet
);
141 return occurrenceService
.listUuidAndTitleCacheByAssociatedTaxon(filteredNodes
, null, null);
145 public List
<UUID
> findFilteredTaxonNodes(DescriptiveDataSet descriptiveDataSet
){
146 TaxonNodeFilter filter
= TaxonNodeFilter
.NewRankInstance(descriptiveDataSet
.getMinRank(), descriptiveDataSet
.getMaxRank());
147 descriptiveDataSet
.getGeoFilter().forEach(area
-> filter
.orArea(area
.getUuid()));
148 descriptiveDataSet
.getTaxonSubtreeFilter().forEach(node
-> filter
.orSubtree(node
));
149 filter
.setIncludeUnpublished(true);
151 return taxonNodeService
.uuidList(filter
);
155 public List
<TaxonNode
> loadFilteredTaxonNodes(DescriptiveDataSet descriptiveDataSet
, List
<String
> propertyPaths
){
156 return taxonNodeService
.load(findFilteredTaxonNodes(descriptiveDataSet
), propertyPaths
);
159 private TaxonNode
findTaxonNodeForDescription(TaxonNode taxonNode
, SpecimenOrObservationBase specimen
){
160 Collection
<SpecimenNodeWrapper
> nodeWrapper
= occurrenceService
.listUuidAndTitleCacheByAssociatedTaxon(Arrays
.asList(taxonNode
.getUuid()), null, null);
161 for (SpecimenNodeWrapper specimenNodeWrapper
: nodeWrapper
) {
162 if(specimenNodeWrapper
.getUuidAndTitleCache().getId().equals(specimen
.getId())){
170 public TaxonRowWrapperDTO
createTaxonRowWrapper(TaxonDescription description
,
171 DescriptiveDataSet descriptiveDataSet
) {
172 TaxonNode taxonNode
= null;
173 Classification classification
= null;
174 Optional
<TaxonNode
> first
= descriptiveDataSet
.getTaxonSubtreeFilter().stream()
175 .filter(node
->node
.getClassification()!=null).findFirst();
176 Optional
<Classification
> classificationOptional
= first
.map(node
->node
.getClassification());
177 if(classificationOptional
.isPresent()){
178 classification
= classificationOptional
.get();
179 Taxon taxon
= (Taxon
) taxonService
.load(description
.getTaxon().getId(), Arrays
.asList("taxonNodes", "taxonNodes.classification"));
180 taxonNode
= taxon
.getTaxonNode(classification
);
182 return new TaxonRowWrapperDTO(description
, taxonNode
);
186 public SpecimenRowWrapperDTO
createSpecimenRowWrapper(SpecimenDescription description
, DescriptiveDataSet descriptiveDataSet
){
187 SpecimenOrObservationBase specimen
= description
.getDescribedSpecimenOrObservation();
188 TaxonNode taxonNode
= null;
189 FieldUnit fieldUnit
= null;
190 String identifier
= null;
191 NamedArea country
= null;
192 //supplemental information
195 Set
<TaxonNode
> taxonSubtreeFilter
= descriptiveDataSet
.getTaxonSubtreeFilter();
196 for (TaxonNode node
: taxonSubtreeFilter
) {
198 node
= taxonNodeService
.load(node
.getId(), Arrays
.asList("taxon"));
199 taxonNode
= findTaxonNodeForDescription(node
, specimen
);
204 //check for child nodes
205 List
<TaxonNode
> allChildren
= taxonNodeService
.loadChildNodesOfTaxonNode(node
, Arrays
.asList("taxon"), true, true, null);
206 for (TaxonNode child
: allChildren
) {
207 taxonNode
= findTaxonNodeForDescription(child
, specimen
);
217 Collection
<FieldUnit
> fieldUnits
= occurrenceService
.findFieldUnits(specimen
.getUuid(),
218 Arrays
.asList(new String
[]{
220 "gatheringEvent.country"
222 if(fieldUnits
.size()!=1){
223 logger
.error("More than one or no field unit found for specimen"); //$NON-NLS-1$
227 fieldUnit
= fieldUnits
.iterator().next();
229 if(specimen
instanceof DerivedUnit
){
230 identifier
= occurrenceService
.getMostSignificantIdentifier(HibernateProxyHelper
.deproxy(specimen
, DerivedUnit
.class));
232 if(fieldUnit
!=null && fieldUnit
.getGatheringEvent()!=null){
233 country
= fieldUnit
.getGatheringEvent().getCountry();
236 return new SpecimenRowWrapperDTO(description
, taxonNode
, fieldUnit
, identifier
, country
);
240 @Transactional(readOnly
= false)
241 public void updateTitleCache(Class
<?
extends DescriptiveDataSet
> clazz
, Integer stepSize
,
242 IIdentifiableEntityCacheStrategy
<DescriptiveDataSet
> cacheStrategy
, IProgressMonitor monitor
) {
244 clazz
= DescriptiveDataSet
.class;
246 super.updateTitleCacheImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
250 public SpecimenDescription
findDescriptionForDescriptiveDataSet(UUID descriptiveDataSetUuid
, UUID specimenUuid
){
251 DescriptiveDataSet dataSet
= load(descriptiveDataSetUuid
);
252 SpecimenOrObservationBase specimen
= occurrenceService
.load(specimenUuid
);
254 Set
<Feature
> datasetFeatures
= dataSet
.getDescriptiveSystem().getDistinctFeatures();
255 List
<DescriptionElementBase
> matchingDescriptionElements
= new ArrayList
<>();
257 for (SpecimenDescription specimenDescription
: (Set
<SpecimenDescription
>) specimen
.getDescriptions()) {
258 specimenDescription
= (SpecimenDescription
) descriptionService
.load(specimenDescription
.getUuid());
260 //check if description is already added to data set
261 if(dataSet
.getDescriptions().contains(specimenDescription
)){
262 return specimenDescription
;
265 //gather specimen description features and check for match with dataset features
266 Set
<Feature
> specimenDescriptionFeatures
= new HashSet
<>();
267 for (DescriptionElementBase specimenDescriptionElement
: specimenDescription
.getElements()) {
268 Feature feature
= specimenDescriptionElement
.getFeature();
269 specimenDescriptionFeatures
.add(feature
);
270 if(datasetFeatures
.contains(feature
)){
271 matchingDescriptionElements
.add(specimenDescriptionElement
);
275 //Create new specimen description if description has not already been added to the dataset
276 SpecimenDescription newDesription
= SpecimenDescription
.NewInstance(specimen
);
277 newDesription
.setTitleCache("Dataset "+dataSet
.getLabel()+": "+newDesription
.generateTitle(), true); //$NON-NLS-2$
279 //check for equals description element (same feature and same values)
280 Map
<Feature
, List
<DescriptionElementBase
>> featureToElementMap
= new HashMap
<>();
281 for(DescriptionElementBase element
:matchingDescriptionElements
){
282 List
<DescriptionElementBase
> list
= featureToElementMap
.get(element
.getFeature());
284 list
= new ArrayList
<>();
287 featureToElementMap
.put(element
.getFeature(), list
);
289 Set
<DescriptionElementBase
> descriptionElementsToClone
= new HashSet
<>();
290 for(Feature feature
:featureToElementMap
.keySet()){
291 List
<DescriptionElementBase
> elements
= featureToElementMap
.get(feature
);
292 //no duplicate description elements found for this feature
293 if(elements
.size()==1){
294 descriptionElementsToClone
.add(elements
.get(0));
296 //duplicates found -> check if all are equal
298 DescriptionElementBase match
= null;
299 for (DescriptionElementBase descriptionElementBase
: elements
) {
301 match
= descriptionElementBase
;
303 else if(!new DescriptionElementCompareWrapper(match
).equals(new DescriptionElementCompareWrapper(descriptionElementBase
))){
305 //TODO: propagate message
306 // MessagingUtils.informationDialog(Messages.CharacterMatrix_MULTIPLE_DATA,
307 // String.format(Messages.CharacterMatrix_MULTIPLE_DATA_MESSAGE, feature.getLabel()));
312 descriptionElementsToClone
.add(match
);
316 //clone matching descriptionElements
317 for (DescriptionElementBase descriptionElementBase
: descriptionElementsToClone
) {
318 DescriptionElementBase clone
;
320 clone
= descriptionElementBase
.clone(newDesription
);
321 clone
.getSources().forEach(source
-> {
322 if(descriptionElementBase
instanceof CategoricalData
){
323 TextData label
= new DefaultCategoricalDescriptionBuilder().build((CategoricalData
) descriptionElementBase
, null);
324 source
.setOriginalNameString(label
.getText(Language
.DEFAULT()));
326 else if(descriptionElementBase
instanceof QuantitativeData
){
327 TextData label
= new DefaultQuantitativeDescriptionBuilder().build((QuantitativeData
) descriptionElementBase
, null);
328 source
.setOriginalNameString(label
.getText(Language
.DEFAULT()));
331 } catch (CloneNotSupportedException e
) {
332 // MessagingUtils.error(CharacterMatrix.class, e);
336 //add all remaining description elements to the new description
337 for(Feature wsFeature
:datasetFeatures
){
338 boolean featureFound
= false;
339 for(DescriptionElementBase element
:newDesription
.getElements()){
340 if(element
.getFeature().equals(wsFeature
)){
346 if(wsFeature
.isSupportsCategoricalData()){
347 newDesription
.addElement(CategoricalData
.NewInstance(wsFeature
));
349 else if(wsFeature
.isSupportsQuantitativeData()){
350 newDesription
.addElement(QuantitativeData
.NewInstance(wsFeature
));
354 return newDesription
;
358 //TODO: this should either be solved in the model class itself
359 //OR this should cover all possibilities including modifiers for example
360 private class DescriptionElementCompareWrapper
{
362 private DescriptionElementBase element
;
363 private Set
<UUID
> stateUuids
= new HashSet
<>();
364 private Set
<Float
> avgs
= new HashSet
<>();
365 private Set
<Float
> exacts
= new HashSet
<>();
366 private Set
<Float
> maxs
= new HashSet
<>();
367 private Set
<Float
> mins
= new HashSet
<>();
368 private Set
<Float
> sampleSizes
= new HashSet
<>();
369 private Set
<Float
> standardDevs
= new HashSet
<>();
370 private Set
<Float
> lowerBounds
= new HashSet
<>();
371 private Set
<Float
> upperBounds
= new HashSet
<>();
372 private Set
<Float
> variances
= new HashSet
<>();
374 public DescriptionElementCompareWrapper(DescriptionElementBase element
) {
375 this.element
= element
;
376 if(element
.isInstanceOf(CategoricalData
.class)){
377 CategoricalData elementData
= (CategoricalData
)element
;
378 elementData
.getStatesOnly().forEach(state
->stateUuids
.add(state
.getUuid()));
380 else if(element
.isInstanceOf(QuantitativeData
.class)){
381 QuantitativeData elementData
= (QuantitativeData
)element
;
382 elementData
.getStatisticalValues().forEach(value
->{
383 if(value
.getType().equals(StatisticalMeasure
.AVERAGE())){
384 avgs
.add(value
.getValue());
386 else if(value
.getType().equals(StatisticalMeasure
.EXACT_VALUE())){
387 exacts
.add(value
.getValue());
390 else if(value
.getType().equals(StatisticalMeasure
.MAX())){
391 maxs
.add(value
.getValue());
393 else if(value
.getType().equals(StatisticalMeasure
.MIN())){
394 mins
.add(value
.getValue());
396 else if(value
.getType().equals(StatisticalMeasure
.SAMPLE_SIZE())){
397 sampleSizes
.add(value
.getValue());
400 else if(value
.getType().equals(StatisticalMeasure
.STANDARD_DEVIATION())){
401 standardDevs
.add(value
.getValue());
403 else if(value
.getType().equals(StatisticalMeasure
.TYPICAL_LOWER_BOUNDARY())){
404 lowerBounds
.add(value
.getValue());
407 else if(value
.getType().equals(StatisticalMeasure
.TYPICAL_UPPER_BOUNDARY())){
408 upperBounds
.add(value
.getValue());
410 else if(value
.getType().equals(StatisticalMeasure
.VARIANCE())){
411 variances
.add(value
.getValue());
418 public int hashCode() {
419 final int prime
= 31;
421 result
= prime
* result
+ getOuterType().hashCode();
422 result
= prime
* result
+ ((avgs
== null) ?
0 : avgs
.hashCode());
423 result
= prime
* result
+ ((element
== null) ?
0 : element
.hashCode());
424 result
= prime
* result
+ ((exacts
== null) ?
0 : exacts
.hashCode());
425 result
= prime
* result
+ ((lowerBounds
== null) ?
0 : lowerBounds
.hashCode());
426 result
= prime
* result
+ ((maxs
== null) ?
0 : maxs
.hashCode());
427 result
= prime
* result
+ ((mins
== null) ?
0 : mins
.hashCode());
428 result
= prime
* result
+ ((sampleSizes
== null) ?
0 : sampleSizes
.hashCode());
429 result
= prime
* result
+ ((standardDevs
== null) ?
0 : standardDevs
.hashCode());
430 result
= prime
* result
+ ((stateUuids
== null) ?
0 : stateUuids
.hashCode());
431 result
= prime
* result
+ ((upperBounds
== null) ?
0 : upperBounds
.hashCode());
432 result
= prime
* result
+ ((variances
== null) ?
0 : variances
.hashCode());
437 public boolean equals(Object obj
) {
444 if (getClass() != obj
.getClass()) {
447 DescriptionElementCompareWrapper other
= (DescriptionElementCompareWrapper
) obj
;
448 if (!getOuterType().equals(other
.getOuterType())) {
452 if (other
.avgs
!= null) {
455 } else if (!avgs
.equals(other
.avgs
)) {
458 if (element
== null) {
459 if (other
.element
!= null) {
462 } else if (!element
.equals(other
.element
)) {
465 if (exacts
== null) {
466 if (other
.exacts
!= null) {
469 } else if (!exacts
.equals(other
.exacts
)) {
472 if (lowerBounds
== null) {
473 if (other
.lowerBounds
!= null) {
476 } else if (!lowerBounds
.equals(other
.lowerBounds
)) {
480 if (other
.maxs
!= null) {
483 } else if (!maxs
.equals(other
.maxs
)) {
487 if (other
.mins
!= null) {
490 } else if (!mins
.equals(other
.mins
)) {
493 if (sampleSizes
== null) {
494 if (other
.sampleSizes
!= null) {
497 } else if (!sampleSizes
.equals(other
.sampleSizes
)) {
500 if (standardDevs
== null) {
501 if (other
.standardDevs
!= null) {
504 } else if (!standardDevs
.equals(other
.standardDevs
)) {
507 if (stateUuids
== null) {
508 if (other
.stateUuids
!= null) {
511 } else if (!stateUuids
.equals(other
.stateUuids
)) {
514 if (upperBounds
== null) {
515 if (other
.upperBounds
!= null) {
518 } else if (!upperBounds
.equals(other
.upperBounds
)) {
521 if (variances
== null) {
522 if (other
.variances
!= null) {
525 } else if (!variances
.equals(other
.variances
)) {
531 private DescriptiveDataSetService
getOuterType() {
532 return DescriptiveDataSetService
.this;