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
;
12 import java
.util
.UUID
;
14 import org
.apache
.log4j
.Logger
;
15 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
16 import org
.springframework
.stereotype
.Service
;
17 import org
.springframework
.transaction
.annotation
.Transactional
;
19 import eu
.etaxonomy
.cdm
.api
.service
.dto
.RowWrapperDTO
;
20 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
21 import eu
.etaxonomy
.cdm
.common
.monitor
.IRemotingProgressMonitor
;
22 import eu
.etaxonomy
.cdm
.common
.monitor
.RemotingProgressMonitorThread
;
23 import eu
.etaxonomy
.cdm
.filter
.TaxonNodeFilter
;
24 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
25 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
26 import eu
.etaxonomy
.cdm
.model
.description
.CategoricalData
;
27 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
28 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
29 import eu
.etaxonomy
.cdm
.model
.description
.DescriptiveDataSet
;
30 import eu
.etaxonomy
.cdm
.model
.description
.DescriptiveSystemRole
;
31 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
32 import eu
.etaxonomy
.cdm
.model
.description
.QuantitativeData
;
33 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
34 import eu
.etaxonomy
.cdm
.model
.description
.StatisticalMeasure
;
35 import eu
.etaxonomy
.cdm
.model
.description
.TextData
;
36 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
37 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
38 import eu
.etaxonomy
.cdm
.model
.occurrence
.FieldUnit
;
39 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
40 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
41 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
42 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
43 import eu
.etaxonomy
.cdm
.persistence
.dao
.description
.IDescriptiveDataSetDao
;
44 import eu
.etaxonomy
.cdm
.persistence
.dto
.SpecimenNodeWrapper
;
45 import eu
.etaxonomy
.cdm
.persistence
.dto
.UuidAndTitleCache
;
46 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
49 @Transactional(readOnly
= false)
50 public class DescriptiveDataSetService
51 extends IdentifiableServiceBase
<DescriptiveDataSet
, IDescriptiveDataSetDao
>
52 implements IDescriptiveDataSetService
{
54 private static Logger logger
= Logger
.getLogger(DescriptiveDataSetService
.class);
57 private IOccurrenceService occurrenceService
;
60 private IDescriptionService descriptionService
;
63 private ITaxonNodeService taxonNodeService
;
66 private IProgressMonitorService progressMonitorService
;
70 protected void setDao(IDescriptiveDataSetDao dao
) {
75 public Map
<DescriptionBase
, Set
<DescriptionElementBase
>> getDescriptionElements(DescriptiveDataSet descriptiveDataSet
, Set
<Feature
> features
, Integer pageSize
, Integer pageNumber
,
76 List
<String
> propertyPaths
) {
77 return dao
.getDescriptionElements(descriptiveDataSet
, features
, pageSize
, pageNumber
, propertyPaths
);
81 public <T
extends DescriptionElementBase
> Map
<UuidAndTitleCache
, Map
<UUID
, Set
<T
>>> getTaxonFeatureDescriptionElementMap(
82 Class
<T
> clazz
, UUID descriptiveDataSetUuid
, DescriptiveSystemRole role
) {
83 return dao
.getTaxonFeatureDescriptionElementMap(clazz
, descriptiveDataSetUuid
, role
);
87 public List
<UuidAndTitleCache
<DescriptiveDataSet
>> getDescriptiveDataSetUuidAndTitleCache(Integer limitOfInitialElements
, String pattern
) {
88 return dao
.getDescriptiveDataSetUuidAndTitleCache( limitOfInitialElements
, pattern
);
94 public UUID
monitGetRowWrapper(DescriptiveDataSet descriptiveDataSet
) {
95 RemotingProgressMonitorThread monitorThread
= new RemotingProgressMonitorThread() {
97 public Serializable
doRun(IRemotingProgressMonitor monitor
) {
98 return getRowWrapper(descriptiveDataSet
, monitor
);
101 UUID uuid
= progressMonitorService
.registerNewRemotingMonitor(monitorThread
);
102 monitorThread
.setPriority(3);
103 monitorThread
.start();
108 public ArrayList
<RowWrapperDTO
> getRowWrapper(DescriptiveDataSet descriptiveDataSet
, IProgressMonitor monitor
) {
109 monitor
.beginTask("Load row wrapper", descriptiveDataSet
.getDescriptions().size());
110 ArrayList
<RowWrapperDTO
> wrappers
= new ArrayList
<>();
111 Set
<DescriptionBase
> descriptions
= descriptiveDataSet
.getDescriptions();
112 for (DescriptionBase description
: descriptions
) {
113 if(monitor
.isCanceled()){
114 return new ArrayList
<>();
116 wrappers
.add(createRowWrapper(null, description
, null, descriptiveDataSet
));
123 public Collection
<SpecimenNodeWrapper
> loadSpecimens(DescriptiveDataSet descriptiveDataSet
){
124 //set filter parameters
125 TaxonNodeFilter filter
= TaxonNodeFilter
.NewRankInstance(descriptiveDataSet
.getMinRank(), descriptiveDataSet
.getMaxRank());
126 descriptiveDataSet
.getGeoFilter().forEach(area
-> filter
.orArea(area
.getUuid()));
127 descriptiveDataSet
.getTaxonSubtreeFilter().forEach(node
-> filter
.orSubtree(node
));
128 filter
.setIncludeUnpublished(true);
130 List
<UUID
> filteredNodes
= taxonNodeService
.uuidList(filter
);
131 return occurrenceService
.listUuidAndTitleCacheByAssociatedTaxon(filteredNodes
, null, null);
135 public RowWrapperDTO
createRowWrapper(DescriptionBase description
, DescriptiveDataSet descriptiveDataSet
){
136 return createRowWrapper(null, description
, null, descriptiveDataSet
);
140 public RowWrapperDTO
createRowWrapper(SpecimenOrObservationBase specimen
, DescriptiveDataSet descriptiveDataSet
){
141 return createRowWrapper(specimen
, null, null, descriptiveDataSet
);
144 private RowWrapperDTO
createRowWrapper(SpecimenOrObservationBase specimen
, DescriptionBase description
, TaxonNode taxonNode
, DescriptiveDataSet descriptiveDataSet
){
145 if(description
!=null){
146 specimen
= description
.getDescribedSpecimenOrObservation();
148 FieldUnit fieldUnit
= null;
149 String identifier
= null;
150 NamedArea country
= null;
151 //supplemental information
154 Collection
<TaxonBase
<?
>> associatedTaxa
= occurrenceService
.listAssociatedTaxa(specimen
, null, null, null,
155 Arrays
.asList(new String
[]{
157 "taxonNodes.classification",
159 if(associatedTaxa
!=null && !associatedTaxa
.isEmpty()){
160 //FIXME: what about multiple associated taxa
161 Set
<TaxonNode
> taxonSubtreeFilter
= descriptiveDataSet
.getTaxonSubtreeFilter();
162 if(taxonSubtreeFilter
!=null && !taxonSubtreeFilter
.isEmpty()){
163 Taxon taxon
= HibernateProxyHelper
.deproxy(associatedTaxa
.iterator().next(), Taxon
.class);
164 taxonNode
= taxon
.getTaxonNode(taxonSubtreeFilter
.iterator().next().getClassification());
168 Collection
<FieldUnit
> fieldUnits
= occurrenceService
.findFieldUnits(specimen
.getUuid(),
169 Arrays
.asList(new String
[]{
171 "gatheringEvent.country"
173 if(fieldUnits
.size()!=1){
174 logger
.error("More than one or no field unit found for specimen"); //$NON-NLS-1$
177 fieldUnit
= fieldUnits
.iterator().next();
179 if(specimen
instanceof DerivedUnit
){
180 identifier
= occurrenceService
.getMostSignificantIdentifier(HibernateProxyHelper
.deproxy(specimen
, DerivedUnit
.class));
182 if(fieldUnit
!=null && fieldUnit
.getGatheringEvent()!=null){
183 country
= fieldUnit
.getGatheringEvent().getCountry();
186 return new RowWrapperDTO(description
, specimen
, taxonNode
, fieldUnit
, identifier
, country
);
190 @Transactional(readOnly
= false)
191 public void updateTitleCache(Class
<?
extends DescriptiveDataSet
> clazz
, Integer stepSize
,
192 IIdentifiableEntityCacheStrategy
<DescriptiveDataSet
> cacheStrategy
, IProgressMonitor monitor
) {
194 clazz
= DescriptiveDataSet
.class;
196 super.updateTitleCacheImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
200 public SpecimenDescription
findDescriptionForDescriptiveDataSet(UUID descriptiveDataSetUuid
, UUID specimenUuid
){
201 DescriptiveDataSet dataSet
= load(descriptiveDataSetUuid
);
202 SpecimenOrObservationBase specimen
= occurrenceService
.load(specimenUuid
);
204 Set
<Feature
> datasetFeatures
= dataSet
.getDescriptiveSystem().getDistinctFeatures();
205 List
<DescriptionElementBase
> matchingDescriptionElements
= new ArrayList
<>();
207 for (SpecimenDescription specimenDescription
: (Set
<SpecimenDescription
>) specimen
.getDescriptions()) {
208 specimenDescription
= (SpecimenDescription
) descriptionService
.load(specimenDescription
.getUuid());
210 //check if description is already added to data set
211 if(dataSet
.getDescriptions().contains(specimenDescription
)){
212 return specimenDescription
;
215 //gather specimen description features and check for match with dataset features
216 Set
<Feature
> specimenDescriptionFeatures
= new HashSet
<>();
217 for (DescriptionElementBase specimenDescriptionElement
: specimenDescription
.getElements()) {
218 Feature feature
= specimenDescriptionElement
.getFeature();
219 specimenDescriptionFeatures
.add(feature
);
220 if(datasetFeatures
.contains(feature
)){
221 matchingDescriptionElements
.add(specimenDescriptionElement
);
225 //Create new specimen description if description has not already been added to the dataset
226 SpecimenDescription newDesription
= SpecimenDescription
.NewInstance(specimen
);
227 newDesription
.setTitleCache("Dataset "+dataSet
.getLabel()+": "+newDesription
.generateTitle(), true); //$NON-NLS-2$
229 //check for equals description element (same feature and same values)
230 Map
<Feature
, List
<DescriptionElementBase
>> featureToElementMap
= new HashMap
<>();
231 for(DescriptionElementBase element
:matchingDescriptionElements
){
232 List
<DescriptionElementBase
> list
= featureToElementMap
.get(element
.getFeature());
234 list
= new ArrayList
<>();
237 featureToElementMap
.put(element
.getFeature(), list
);
239 Set
<DescriptionElementBase
> descriptionElementsToClone
= new HashSet
<>();
240 for(Feature feature
:featureToElementMap
.keySet()){
241 List
<DescriptionElementBase
> elements
= featureToElementMap
.get(feature
);
242 //no duplicate description elements found for this feature
243 if(elements
.size()==1){
244 descriptionElementsToClone
.add(elements
.get(0));
246 //duplicates found -> check if all are equal
248 DescriptionElementBase match
= null;
249 for (DescriptionElementBase descriptionElementBase
: elements
) {
251 match
= descriptionElementBase
;
253 else if(!new DescriptionElementCompareWrapper(match
).equals(new DescriptionElementCompareWrapper(descriptionElementBase
))){
255 //TODO: propagate message
256 // MessagingUtils.informationDialog(Messages.CharacterMatrix_MULTIPLE_DATA,
257 // String.format(Messages.CharacterMatrix_MULTIPLE_DATA_MESSAGE, feature.getLabel()));
262 descriptionElementsToClone
.add(match
);
266 //clone matching descriptionElements
267 for (DescriptionElementBase descriptionElementBase
: descriptionElementsToClone
) {
268 DescriptionElementBase clone
;
270 clone
= descriptionElementBase
.clone(newDesription
);
271 clone
.getSources().forEach(source
-> {
272 if(descriptionElementBase
instanceof CategoricalData
){
273 TextData label
= new DefaultCategoricalDescriptionBuilder().build((CategoricalData
) descriptionElementBase
, null);
274 source
.setOriginalNameString(label
.getText(Language
.DEFAULT()));
276 else if(descriptionElementBase
instanceof QuantitativeData
){
277 TextData label
= new DefaultQuantitativeDescriptionBuilder().build((QuantitativeData
) descriptionElementBase
, null);
278 source
.setOriginalNameString(label
.getText(Language
.DEFAULT()));
281 } catch (CloneNotSupportedException e
) {
282 // MessagingUtils.error(CharacterMatrix.class, e);
286 //add all remaining description elements to the new description
287 for(Feature wsFeature
:datasetFeatures
){
288 boolean featureFound
= false;
289 for(DescriptionElementBase element
:newDesription
.getElements()){
290 if(element
.getFeature().equals(wsFeature
)){
296 if(wsFeature
.isSupportsCategoricalData()){
297 newDesription
.addElement(CategoricalData
.NewInstance(wsFeature
));
299 else if(wsFeature
.isSupportsQuantitativeData()){
300 newDesription
.addElement(QuantitativeData
.NewInstance(wsFeature
));
304 return newDesription
;
308 //TODO: this should either be solved in the model class itself
309 //OR this should cover all possibilities including modifiers for example
310 private class DescriptionElementCompareWrapper
{
312 private DescriptionElementBase element
;
313 private Set
<UUID
> stateUuids
= new HashSet
<>();
314 private Set
<Float
> avgs
= new HashSet
<>();
315 private Set
<Float
> exacts
= new HashSet
<>();
316 private Set
<Float
> maxs
= new HashSet
<>();
317 private Set
<Float
> mins
= new HashSet
<>();
318 private Set
<Float
> sampleSizes
= new HashSet
<>();
319 private Set
<Float
> standardDevs
= new HashSet
<>();
320 private Set
<Float
> lowerBounds
= new HashSet
<>();
321 private Set
<Float
> upperBounds
= new HashSet
<>();
322 private Set
<Float
> variances
= new HashSet
<>();
324 public DescriptionElementCompareWrapper(DescriptionElementBase element
) {
325 this.element
= element
;
326 if(element
.isInstanceOf(CategoricalData
.class)){
327 CategoricalData elementData
= (CategoricalData
)element
;
328 elementData
.getStatesOnly().forEach(state
->stateUuids
.add(state
.getUuid()));
330 else if(element
.isInstanceOf(QuantitativeData
.class)){
331 QuantitativeData elementData
= (QuantitativeData
)element
;
332 elementData
.getStatisticalValues().forEach(value
->{
333 if(value
.getType().equals(StatisticalMeasure
.AVERAGE())){
334 avgs
.add(value
.getValue());
336 else if(value
.getType().equals(StatisticalMeasure
.EXACT_VALUE())){
337 exacts
.add(value
.getValue());
340 else if(value
.getType().equals(StatisticalMeasure
.MAX())){
341 maxs
.add(value
.getValue());
343 else if(value
.getType().equals(StatisticalMeasure
.MIN())){
344 mins
.add(value
.getValue());
346 else if(value
.getType().equals(StatisticalMeasure
.SAMPLE_SIZE())){
347 sampleSizes
.add(value
.getValue());
350 else if(value
.getType().equals(StatisticalMeasure
.STANDARD_DEVIATION())){
351 standardDevs
.add(value
.getValue());
353 else if(value
.getType().equals(StatisticalMeasure
.TYPICAL_LOWER_BOUNDARY())){
354 lowerBounds
.add(value
.getValue());
357 else if(value
.getType().equals(StatisticalMeasure
.TYPICAL_UPPER_BOUNDARY())){
358 upperBounds
.add(value
.getValue());
360 else if(value
.getType().equals(StatisticalMeasure
.VARIANCE())){
361 variances
.add(value
.getValue());
368 public int hashCode() {
369 final int prime
= 31;
371 result
= prime
* result
+ getOuterType().hashCode();
372 result
= prime
* result
+ ((avgs
== null) ?
0 : avgs
.hashCode());
373 result
= prime
* result
+ ((element
== null) ?
0 : element
.hashCode());
374 result
= prime
* result
+ ((exacts
== null) ?
0 : exacts
.hashCode());
375 result
= prime
* result
+ ((lowerBounds
== null) ?
0 : lowerBounds
.hashCode());
376 result
= prime
* result
+ ((maxs
== null) ?
0 : maxs
.hashCode());
377 result
= prime
* result
+ ((mins
== null) ?
0 : mins
.hashCode());
378 result
= prime
* result
+ ((sampleSizes
== null) ?
0 : sampleSizes
.hashCode());
379 result
= prime
* result
+ ((standardDevs
== null) ?
0 : standardDevs
.hashCode());
380 result
= prime
* result
+ ((stateUuids
== null) ?
0 : stateUuids
.hashCode());
381 result
= prime
* result
+ ((upperBounds
== null) ?
0 : upperBounds
.hashCode());
382 result
= prime
* result
+ ((variances
== null) ?
0 : variances
.hashCode());
387 public boolean equals(Object obj
) {
394 if (getClass() != obj
.getClass()) {
397 DescriptionElementCompareWrapper other
= (DescriptionElementCompareWrapper
) obj
;
398 if (!getOuterType().equals(other
.getOuterType())) {
402 if (other
.avgs
!= null) {
405 } else if (!avgs
.equals(other
.avgs
)) {
408 if (element
== null) {
409 if (other
.element
!= null) {
412 } else if (!element
.equals(other
.element
)) {
415 if (exacts
== null) {
416 if (other
.exacts
!= null) {
419 } else if (!exacts
.equals(other
.exacts
)) {
422 if (lowerBounds
== null) {
423 if (other
.lowerBounds
!= null) {
426 } else if (!lowerBounds
.equals(other
.lowerBounds
)) {
430 if (other
.maxs
!= null) {
433 } else if (!maxs
.equals(other
.maxs
)) {
437 if (other
.mins
!= null) {
440 } else if (!mins
.equals(other
.mins
)) {
443 if (sampleSizes
== null) {
444 if (other
.sampleSizes
!= null) {
447 } else if (!sampleSizes
.equals(other
.sampleSizes
)) {
450 if (standardDevs
== null) {
451 if (other
.standardDevs
!= null) {
454 } else if (!standardDevs
.equals(other
.standardDevs
)) {
457 if (stateUuids
== null) {
458 if (other
.stateUuids
!= null) {
461 } else if (!stateUuids
.equals(other
.stateUuids
)) {
464 if (upperBounds
== null) {
465 if (other
.upperBounds
!= null) {
468 } else if (!upperBounds
.equals(other
.upperBounds
)) {
471 if (variances
== null) {
472 if (other
.variances
!= null) {
475 } else if (!variances
.equals(other
.variances
)) {
481 private DescriptiveDataSetService
getOuterType() {
482 return DescriptiveDataSetService
.this;