Project

General

Profile

Download (16.5 KB) Statistics
| Branch: | Tag: | Revision:
1
package eu.etaxonomy.cdm.api.service;
2

    
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;
9
import java.util.List;
10
import java.util.Map;
11
import java.util.Set;
12
import java.util.UUID;
13

    
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;
18

    
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;
46

    
47
@Service
48
@Transactional(readOnly = false)
49
public class DescriptiveDataSetService
50
        extends IdentifiableServiceBase<DescriptiveDataSet, IDescriptiveDataSetDao>
51
        implements IDescriptiveDataSetService {
52

    
53
    private static Logger logger = Logger.getLogger(DescriptiveDataSetService.class);
54

    
55
    @Autowired
56
    private IOccurrenceService occurrenceService;
57

    
58
    @Autowired
59
    private IDescriptionService descriptionService;
60

    
61
    @Autowired
62
    private ITaxonNodeService taxonNodeService;
63

    
64
    @Autowired
65
    private IProgressMonitorService progressMonitorService;
66

    
67
	@Override
68
	@Autowired
69
	protected void setDao(IDescriptiveDataSetDao dao) {
70
		this.dao = dao;
71
	}
72

    
73
	@Override
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);
77
	}
78

    
79
	@Override
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);
83
	}
84

    
85
	@Override
86
    public List<UuidAndTitleCache<DescriptiveDataSet>> getDescriptiveDataSetUuidAndTitleCache(Integer limitOfInitialElements, String pattern) {
87
        return dao.getDescriptiveDataSetUuidAndTitleCache( limitOfInitialElements, pattern);
88
    }
89

    
90

    
91
    @Override
92
    @Transactional
93
    public UUID monitGetRowWrapper(DescriptiveDataSet descriptiveDataSet) {
94
        RemotingProgressMonitorThread monitorThread = new RemotingProgressMonitorThread() {
95
            @Override
96
            public Serializable doRun(IRemotingProgressMonitor monitor) {
97
                return getRowWrapper(descriptiveDataSet, monitor);
98
            }
99
        };
100
        UUID uuid = progressMonitorService.registerNewRemotingMonitor(monitorThread);
101
        monitorThread.setPriority(3);
102
        monitorThread.start();
103
        return uuid;
104
    }
105

    
106
	@Override
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<>();
114
            }
115
            wrappers.add(createRowWrapper(null, description, null, descriptiveDataSet));
116
            monitor.worked(1);
117
        }
118
	    return wrappers;
119
	}
120

    
121
    @Override
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);
128

    
129
        List<UUID> filteredNodes = taxonNodeService.uuidList(filter);
130
        return occurrenceService.listUuidAndTitleCacheByAssociatedTaxon(filteredNodes, null, null);
131
    }
132

    
133
    @Override
134
    public RowWrapperDTO createRowWrapper(DescriptionBase description, DescriptiveDataSet descriptiveDataSet){
135
        return createRowWrapper(null, description, null, descriptiveDataSet);
136
    }
137

    
138
    @Override
139
    public RowWrapperDTO createRowWrapper(SpecimenOrObservationBase specimen, DescriptiveDataSet descriptiveDataSet){
140
        return createRowWrapper(specimen, null, null, descriptiveDataSet);
141
    }
142

    
143
	private RowWrapperDTO createRowWrapper(SpecimenOrObservationBase specimen, DescriptionBase description, TaxonNode taxonNode, DescriptiveDataSet descriptiveDataSet){
144
	    if(description!=null){
145
	        specimen = description.getDescribedSpecimenOrObservation();
146
	    }
147
        FieldUnit fieldUnit = null;
148
        String identifier = null;
149
        NamedArea country = null;
150
        //supplemental information
151
        if(specimen!=null){
152
            if(taxonNode==null){
153
                Collection<TaxonBase<?>> associatedTaxa = occurrenceService.listAssociatedTaxa(specimen, null, null, null,
154
                        Arrays.asList(new String[]{
155
                                "taxonNodes",
156
                                "taxonNodes.classification",
157
                        }));
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());
164
                    }
165
                }
166
            }
167
            Collection<FieldUnit> fieldUnits = occurrenceService.findFieldUnits(specimen.getUuid(),
168
                    Arrays.asList(new String[]{
169
                            "gatheringEvent",
170
                            "gatheringEvent.country"
171
                            }));
172
            if(fieldUnits.size()!=1){
173
                logger.error("More than one or no field unit found for specimen"); //$NON-NLS-1$
174
            }
175
            else{
176
                fieldUnit = fieldUnits.iterator().next();
177
            }
178
            if(specimen instanceof DerivedUnit){
179
                identifier = occurrenceService.getMostSignificantIdentifier(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class));
180
            }
181
            if(fieldUnit!=null && fieldUnit.getGatheringEvent()!=null){
182
                country = fieldUnit.getGatheringEvent().getCountry();
183
            }
184
        }
185
        return new RowWrapperDTO(description, specimen, taxonNode, fieldUnit, identifier, country);
186
	}
187

    
188
    @Override
189
    @Transactional(readOnly = false)
190
    public void updateTitleCache(Class<? extends DescriptiveDataSet> clazz, Integer stepSize,
191
            IIdentifiableEntityCacheStrategy<DescriptiveDataSet> cacheStrategy, IProgressMonitor monitor) {
192
        if (clazz == null) {
193
            clazz = DescriptiveDataSet.class;
194
        }
195
        super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
196
    }
197

    
198
    @Override
199
    public SpecimenDescription findDescriptionForDescriptiveDataSet(UUID descriptiveDataSetUuid, UUID specimenUuid){
200
        DescriptiveDataSet dataSet = load(descriptiveDataSetUuid);
201
        SpecimenOrObservationBase specimen = occurrenceService.load(specimenUuid);
202

    
203
        Set<Feature> datasetFeatures = dataSet.getDescriptiveSystem().getDistinctFeatures();
204
        List<DescriptionElementBase> matchingDescriptionElements = new ArrayList<>();
205

    
206
        for (SpecimenDescription specimenDescription : (Set<SpecimenDescription>) specimen.getDescriptions()) {
207
            specimenDescription = (SpecimenDescription) descriptionService.load(specimenDescription.getUuid());
208
            Set<Feature> specimenDescriptionFeatures = new HashSet<>();
209
            //gather specimen description features and check for match with dataset features
210
            for (DescriptionElementBase specimenDescriptionElement : specimenDescription.getElements()) {
211
                Feature feature = specimenDescriptionElement.getFeature();
212
                specimenDescriptionFeatures.add(feature);
213
                if(datasetFeatures.contains(feature)){
214
                    matchingDescriptionElements.add(specimenDescriptionElement);
215
                }
216
            }
217
            //if description with the exact same features is found return the description
218
            if(specimenDescriptionFeatures.equals(datasetFeatures)){
219
                return specimenDescription;
220
            }
221
        }
222
        //Create new specimen description if no match was found
223
        SpecimenDescription newDesription = SpecimenDescription.NewInstance(specimen);
224
        newDesription.setTitleCache("Dataset"+dataSet.getLabel()+": "+newDesription.generateTitle(), true); //$NON-NLS-2$
225

    
226
        //check for equals description element (same feature and same values)
227
        Map<Feature, List<DescriptionElementBase>> featureToElementMap = new HashMap<>();
228
        for(DescriptionElementBase element:matchingDescriptionElements){
229
            List<DescriptionElementBase> list = featureToElementMap.get(element.getFeature());
230
            if(list==null){
231
                list = new ArrayList<>();
232
            }
233
            list.add(element);
234
            featureToElementMap.put(element.getFeature(), list);
235
        }
236
        Set<DescriptionElementBase> descriptionElementsToClone = new HashSet<>();
237
        for(Feature feature:featureToElementMap.keySet()){
238
            List<DescriptionElementBase> elements = featureToElementMap.get(feature);
239
            //no duplicate description elements found for this feature
240
            if(elements.size()==1){
241
                descriptionElementsToClone.add(elements.get(0));
242
            }
243
            //duplicates found -> check if all are equal
244
            else{
245
                DescriptionElementBase match = null;
246
                for (DescriptionElementBase descriptionElementBase : elements) {
247
                    if(match==null){
248
                        match = descriptionElementBase;
249
                    }
250
                    else if(!new DescriptionElementCompareWrapper(match).equals(new DescriptionElementCompareWrapper(descriptionElementBase))){
251
                        match = null;
252
                        //TODO: propagate message
253
//                        MessagingUtils.informationDialog(Messages.CharacterMatrix_MULTIPLE_DATA,
254
//                                String.format(Messages.CharacterMatrix_MULTIPLE_DATA_MESSAGE, feature.getLabel()));
255
                        break;
256
                    }
257
                }
258
                if(match!=null){
259
                    descriptionElementsToClone.add(match);
260
                }
261
            }
262
        }
263
        //clone matching descriptionElements
264
        for (DescriptionElementBase descriptionElementBase : descriptionElementsToClone) {
265
            DescriptionElementBase clone;
266
            try {
267
                clone = descriptionElementBase.clone(newDesription);
268
                clone.getSources().forEach(source -> {
269
                    if(descriptionElementBase instanceof CategoricalData){
270
                        TextData label = new DefaultCategoricalDescriptionBuilder().build((CategoricalData) descriptionElementBase, null);
271
                        source.setOriginalNameString(label.getText(Language.DEFAULT()));
272
                    }
273
                    else if(descriptionElementBase instanceof QuantitativeData){
274
                        TextData label = new DefaultQuantitativeDescriptionBuilder().build((QuantitativeData) descriptionElementBase, null);
275
                        source.setOriginalNameString(label.getText(Language.DEFAULT()));
276
                    }
277
                });
278
            } catch (CloneNotSupportedException e) {
279
//                MessagingUtils.error(CharacterMatrix.class, e);
280
            }
281
        }
282

    
283
        //add all remaining description elements to the new description
284
        for(Feature wsFeature:datasetFeatures){
285
            boolean featureFound = false;
286
            for(DescriptionElementBase element:newDesription.getElements()){
287
                if(element.getFeature().equals(wsFeature)){
288
                    featureFound = true;
289
                    break;
290
                }
291
            }
292
            if(!featureFound){
293
                if(wsFeature.isSupportsCategoricalData()){
294
                    newDesription.addElement(CategoricalData.NewInstance(wsFeature));
295
                }
296
                else if(wsFeature.isSupportsQuantitativeData()){
297
                    newDesription.addElement(QuantitativeData.NewInstance(wsFeature));
298
                }
299
            }
300
        }
301
        return newDesription;
302

    
303
    }
304

    
305
    private class DescriptionElementCompareWrapper {
306

    
307
        private DescriptionElementBase element;
308
        private Set<UUID> stateUuids = new HashSet<>();
309
        private Float min = null;
310
        private Float max = null;
311

    
312
        public DescriptionElementCompareWrapper(DescriptionElementBase element) {
313
            this.element = element;
314
            if(element.isInstanceOf(CategoricalData.class)){
315
                CategoricalData elementData = (CategoricalData)element;
316
                elementData.getStatesOnly().forEach(state->stateUuids.add(state.getUuid()));
317
            }
318
            else if(element.isInstanceOf(QuantitativeData.class)){
319
                QuantitativeData elementData = (QuantitativeData)element;
320
                min = elementData.getMin();
321
                max = elementData.getMax();
322
            }
323
        }
324

    
325
        public DescriptionElementBase unwrap() {
326
            return element;
327
        }
328

    
329
        @Override
330
        public int hashCode() {
331
            final int prime = 31;
332
            int result = 1;
333
            result = prime * result + ((max == null) ? 0 : max.hashCode());
334
            result = prime * result + ((min == null) ? 0 : min.hashCode());
335
            result = prime * result + ((stateUuids == null) ? 0 : stateUuids.hashCode());
336
            return result;
337
        }
338

    
339
        @Override
340
        public boolean equals(Object obj) {
341
            if (this == obj) {
342
                return true;
343
            }
344
            if (obj == null) {
345
                return false;
346
            }
347
            if (getClass() != obj.getClass()) {
348
                return false;
349
            }
350
            DescriptionElementCompareWrapper other = (DescriptionElementCompareWrapper) obj;
351
            if (max == null) {
352
                if (other.max != null) {
353
                    return false;
354
                }
355
            } else if (!max.equals(other.max)) {
356
                return false;
357
            }
358
            if (min == null) {
359
                if (other.min != null) {
360
                    return false;
361
                }
362
            } else if (!min.equals(other.min)) {
363
                return false;
364
            }
365
            if (stateUuids == null) {
366
                if (other.stateUuids != null) {
367
                    return false;
368
                }
369
            } else if (!stateUuids.equals(other.stateUuids)) {
370
                return false;
371
            }
372
            return true;
373
        }
374
    }
375

    
376
}
(17-17/106)