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
.TextData
;
35 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
36 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
37 import eu
.etaxonomy
.cdm
.model
.occurrence
.FieldUnit
;
38 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
39 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
40 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
41 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
42 import eu
.etaxonomy
.cdm
.persistence
.dao
.description
.IDescriptiveDataSetDao
;
43 import eu
.etaxonomy
.cdm
.persistence
.dto
.SpecimenNodeWrapper
;
44 import eu
.etaxonomy
.cdm
.persistence
.dto
.UuidAndTitleCache
;
45 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
48 @Transactional(readOnly
= false)
49 public class DescriptiveDataSetService
50 extends IdentifiableServiceBase
<DescriptiveDataSet
, IDescriptiveDataSetDao
>
51 implements IDescriptiveDataSetService
{
53 private static Logger logger
= Logger
.getLogger(DescriptiveDataSetService
.class);
56 private IOccurrenceService occurrenceService
;
59 private IDescriptionService descriptionService
;
62 private ITaxonNodeService taxonNodeService
;
65 private IProgressMonitorService progressMonitorService
;
69 protected void setDao(IDescriptiveDataSetDao dao
) {
74 public Map
<DescriptionBase
, Set
<DescriptionElementBase
>> getDescriptionElements(DescriptiveDataSet descriptiveDataSet
, Set
<Feature
> features
, Integer pageSize
, Integer pageNumber
,
75 List
<String
> propertyPaths
) {
76 return dao
.getDescriptionElements(descriptiveDataSet
, features
, pageSize
, pageNumber
, propertyPaths
);
80 public <T
extends DescriptionElementBase
> Map
<UuidAndTitleCache
, Map
<UUID
, Set
<T
>>> getTaxonFeatureDescriptionElementMap(
81 Class
<T
> clazz
, UUID descriptiveDataSetUuid
, DescriptiveSystemRole role
) {
82 return dao
.getTaxonFeatureDescriptionElementMap(clazz
, descriptiveDataSetUuid
, role
);
86 public List
<UuidAndTitleCache
<DescriptiveDataSet
>> getDescriptiveDataSetUuidAndTitleCache(Integer limitOfInitialElements
, String pattern
) {
87 return dao
.getDescriptiveDataSetUuidAndTitleCache( limitOfInitialElements
, pattern
);
93 public UUID
monitGetRowWrapper(DescriptiveDataSet descriptiveDataSet
) {
94 RemotingProgressMonitorThread monitorThread
= new RemotingProgressMonitorThread() {
96 public Serializable
doRun(IRemotingProgressMonitor monitor
) {
97 return getRowWrapper(descriptiveDataSet
, monitor
);
100 UUID uuid
= progressMonitorService
.registerNewRemotingMonitor(monitorThread
);
101 monitorThread
.setPriority(3);
102 monitorThread
.start();
107 public ArrayList
<RowWrapperDTO
> getRowWrapper(DescriptiveDataSet descriptiveDataSet
, IProgressMonitor monitor
) {
108 monitor
.beginTask("Load row wrapper", descriptiveDataSet
.getDescriptions().size());
109 ArrayList
<RowWrapperDTO
> wrappers
= new ArrayList
<>();
110 Set
<DescriptionBase
> descriptions
= descriptiveDataSet
.getDescriptions();
111 for (DescriptionBase description
: descriptions
) {
112 if(monitor
.isCanceled()){
113 return new ArrayList
<>();
115 wrappers
.add(createRowWrapper(null, description
, null, descriptiveDataSet
));
122 public Collection
<SpecimenNodeWrapper
> loadSpecimens(DescriptiveDataSet descriptiveDataSet
){
123 //set filter parameters
124 TaxonNodeFilter filter
= TaxonNodeFilter
.NewRankInstance(descriptiveDataSet
.getMinRank(), descriptiveDataSet
.getMaxRank());
125 descriptiveDataSet
.getGeoFilter().forEach(area
-> filter
.orArea(area
.getUuid()));
126 descriptiveDataSet
.getTaxonSubtreeFilter().forEach(node
-> filter
.orSubtree(node
));
127 filter
.setIncludeUnpublished(true);
129 List
<UUID
> filteredNodes
= taxonNodeService
.uuidList(filter
);
130 return occurrenceService
.listUuidAndTitleCacheByAssociatedTaxon(filteredNodes
, null, null);
134 public RowWrapperDTO
createRowWrapper(DescriptionBase description
, DescriptiveDataSet descriptiveDataSet
){
135 return createRowWrapper(null, description
, null, descriptiveDataSet
);
139 public RowWrapperDTO
createRowWrapper(SpecimenOrObservationBase specimen
, DescriptiveDataSet descriptiveDataSet
){
140 return createRowWrapper(specimen
, null, null, descriptiveDataSet
);
143 private RowWrapperDTO
createRowWrapper(SpecimenOrObservationBase specimen
, DescriptionBase description
, TaxonNode taxonNode
, DescriptiveDataSet descriptiveDataSet
){
144 if(description
!=null){
145 specimen
= description
.getDescribedSpecimenOrObservation();
147 FieldUnit fieldUnit
= null;
148 String identifier
= null;
149 NamedArea country
= null;
150 //supplemental information
153 Collection
<TaxonBase
<?
>> associatedTaxa
= occurrenceService
.listAssociatedTaxa(specimen
, null, null, null,
154 Arrays
.asList(new String
[]{
156 "taxonNodes.classification",
158 if(associatedTaxa
!=null && !associatedTaxa
.isEmpty()){
159 //FIXME: what about multiple associated taxa
160 Set
<TaxonNode
> taxonSubtreeFilter
= descriptiveDataSet
.getTaxonSubtreeFilter();
161 if(taxonSubtreeFilter
!=null && !taxonSubtreeFilter
.isEmpty()){
162 Taxon taxon
= HibernateProxyHelper
.deproxy(associatedTaxa
.iterator().next(), Taxon
.class);
163 taxonNode
= taxon
.getTaxonNode(taxonSubtreeFilter
.iterator().next().getClassification());
167 Collection
<FieldUnit
> fieldUnits
= occurrenceService
.findFieldUnits(specimen
.getUuid(),
168 Arrays
.asList(new String
[]{
170 "gatheringEvent.country"
172 if(fieldUnits
.size()!=1){
173 logger
.error("More than one or no field unit found for specimen"); //$NON-NLS-1$
176 fieldUnit
= fieldUnits
.iterator().next();
178 if(specimen
instanceof DerivedUnit
){
179 identifier
= occurrenceService
.getMostSignificantIdentifier(HibernateProxyHelper
.deproxy(specimen
, DerivedUnit
.class));
181 if(fieldUnit
!=null && fieldUnit
.getGatheringEvent()!=null){
182 country
= fieldUnit
.getGatheringEvent().getCountry();
185 return new RowWrapperDTO(description
, specimen
, taxonNode
, fieldUnit
, identifier
, country
);
189 @Transactional(readOnly
= false)
190 public void updateTitleCache(Class
<?
extends DescriptiveDataSet
> clazz
, Integer stepSize
,
191 IIdentifiableEntityCacheStrategy
<DescriptiveDataSet
> cacheStrategy
, IProgressMonitor monitor
) {
193 clazz
= DescriptiveDataSet
.class;
195 super.updateTitleCacheImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
199 public SpecimenDescription
findDescriptionForDescriptiveDataSet(UUID descriptiveDataSetUuid
, UUID specimenUuid
){
200 DescriptiveDataSet dataSet
= load(descriptiveDataSetUuid
);
201 SpecimenOrObservationBase specimen
= occurrenceService
.load(specimenUuid
);
203 Set
<Feature
> datasetFeatures
= dataSet
.getDescriptiveSystem().getDistinctFeatures();
204 List
<DescriptionElementBase
> matchingDescriptionElements
= new ArrayList
<>();
206 for (SpecimenDescription specimenDescription
: (Set
<SpecimenDescription
>) specimen
.getDescriptions()) {
207 specimenDescription
= (SpecimenDescription
) descriptionService
.load(specimenDescription
.getUuid());
209 //check if description is already added to data set
210 if(dataSet
.getDescriptions().contains(specimenDescription
)){
211 return specimenDescription
;
214 //gather specimen description features and check for match with dataset features
215 Set
<Feature
> specimenDescriptionFeatures
= new HashSet
<>();
216 for (DescriptionElementBase specimenDescriptionElement
: specimenDescription
.getElements()) {
217 Feature feature
= specimenDescriptionElement
.getFeature();
218 specimenDescriptionFeatures
.add(feature
);
219 if(datasetFeatures
.contains(feature
)){
220 matchingDescriptionElements
.add(specimenDescriptionElement
);
224 //Create new specimen description if description has not already been added to the dataset
225 SpecimenDescription newDesription
= SpecimenDescription
.NewInstance(specimen
);
226 newDesription
.setTitleCache("Dataset "+dataSet
.getLabel()+": "+newDesription
.generateTitle(), true); //$NON-NLS-2$
228 //check for equals description element (same feature and same values)
229 Map
<Feature
, List
<DescriptionElementBase
>> featureToElementMap
= new HashMap
<>();
230 for(DescriptionElementBase element
:matchingDescriptionElements
){
231 List
<DescriptionElementBase
> list
= featureToElementMap
.get(element
.getFeature());
233 list
= new ArrayList
<>();
236 featureToElementMap
.put(element
.getFeature(), list
);
238 Set
<DescriptionElementBase
> descriptionElementsToClone
= new HashSet
<>();
239 for(Feature feature
:featureToElementMap
.keySet()){
240 List
<DescriptionElementBase
> elements
= featureToElementMap
.get(feature
);
241 //no duplicate description elements found for this feature
242 if(elements
.size()==1){
243 descriptionElementsToClone
.add(elements
.get(0));
245 //duplicates found -> check if all are equal
247 DescriptionElementBase match
= null;
248 for (DescriptionElementBase descriptionElementBase
: elements
) {
250 match
= descriptionElementBase
;
252 else if(!new DescriptionElementCompareWrapper(match
).equals(new DescriptionElementCompareWrapper(descriptionElementBase
))){
254 //TODO: propagate message
255 // MessagingUtils.informationDialog(Messages.CharacterMatrix_MULTIPLE_DATA,
256 // String.format(Messages.CharacterMatrix_MULTIPLE_DATA_MESSAGE, feature.getLabel()));
261 descriptionElementsToClone
.add(match
);
265 //clone matching descriptionElements
266 for (DescriptionElementBase descriptionElementBase
: descriptionElementsToClone
) {
267 DescriptionElementBase clone
;
269 clone
= descriptionElementBase
.clone(newDesription
);
270 clone
.getSources().forEach(source
-> {
271 if(descriptionElementBase
instanceof CategoricalData
){
272 TextData label
= new DefaultCategoricalDescriptionBuilder().build((CategoricalData
) descriptionElementBase
, null);
273 source
.setOriginalNameString(label
.getText(Language
.DEFAULT()));
275 else if(descriptionElementBase
instanceof QuantitativeData
){
276 TextData label
= new DefaultQuantitativeDescriptionBuilder().build((QuantitativeData
) descriptionElementBase
, null);
277 source
.setOriginalNameString(label
.getText(Language
.DEFAULT()));
280 } catch (CloneNotSupportedException e
) {
281 // MessagingUtils.error(CharacterMatrix.class, e);
285 //add all remaining description elements to the new description
286 for(Feature wsFeature
:datasetFeatures
){
287 boolean featureFound
= false;
288 for(DescriptionElementBase element
:newDesription
.getElements()){
289 if(element
.getFeature().equals(wsFeature
)){
295 if(wsFeature
.isSupportsCategoricalData()){
296 newDesription
.addElement(CategoricalData
.NewInstance(wsFeature
));
298 else if(wsFeature
.isSupportsQuantitativeData()){
299 newDesription
.addElement(QuantitativeData
.NewInstance(wsFeature
));
303 return newDesription
;
307 private class DescriptionElementCompareWrapper
{
309 private DescriptionElementBase element
;
310 private Set
<UUID
> stateUuids
= new HashSet
<>();
311 private Float min
= null;
312 private Float max
= null;
314 public DescriptionElementCompareWrapper(DescriptionElementBase element
) {
315 this.element
= element
;
316 if(element
.isInstanceOf(CategoricalData
.class)){
317 CategoricalData elementData
= (CategoricalData
)element
;
318 elementData
.getStatesOnly().forEach(state
->stateUuids
.add(state
.getUuid()));
320 else if(element
.isInstanceOf(QuantitativeData
.class)){
321 QuantitativeData elementData
= (QuantitativeData
)element
;
322 min
= elementData
.getMin();
323 max
= elementData
.getMax();
327 public DescriptionElementBase
unwrap() {
332 public int hashCode() {
333 final int prime
= 31;
335 result
= prime
* result
+ ((max
== null) ?
0 : max
.hashCode());
336 result
= prime
* result
+ ((min
== null) ?
0 : min
.hashCode());
337 result
= prime
* result
+ ((stateUuids
== null) ?
0 : stateUuids
.hashCode());
342 public boolean equals(Object obj
) {
349 if (getClass() != obj
.getClass()) {
352 DescriptionElementCompareWrapper other
= (DescriptionElementCompareWrapper
) obj
;
354 if (other
.max
!= null) {
357 } else if (!max
.equals(other
.max
)) {
361 if (other
.min
!= null) {
364 } else if (!min
.equals(other
.min
)) {
367 if (stateUuids
== null) {
368 if (other
.stateUuids
!= null) {
371 } else if (!stateUuids
.equals(other
.stateUuids
)) {