Project

General

Profile

Download (16.6 KB) Statistics
| Branch: | Tag: | Revision:
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.Arrays;
15
import java.util.HashMap;
16
import java.util.List;
17
import java.util.Map;
18
import java.util.Set;
19
import java.util.UUID;
20
import java.util.stream.Collectors;
21

    
22
import org.apache.http.HttpException;
23
import org.springframework.beans.factory.annotation.Autowired;
24
import org.springframework.stereotype.Service;
25
import org.springframework.transaction.annotation.Transactional;
26

    
27
import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase;
28
import eu.etaxonomy.cdm.api.service.config.MediaDeletionConfigurator;
29
import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
30
import eu.etaxonomy.cdm.api.service.media.MediaInfoFactory;
31
import eu.etaxonomy.cdm.api.service.pager.Pager;
32
import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
33
import eu.etaxonomy.cdm.common.media.CdmImageInfo;
34
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
35
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
36
import eu.etaxonomy.cdm.model.common.CdmBase;
37
import eu.etaxonomy.cdm.model.common.ICdmBase;
38
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
39
import eu.etaxonomy.cdm.model.description.DescriptionBase;
40
import eu.etaxonomy.cdm.model.description.IDescribable;
41
import eu.etaxonomy.cdm.model.description.MediaKey;
42
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
43
import eu.etaxonomy.cdm.model.description.TaxonDescription;
44
import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
45
import eu.etaxonomy.cdm.model.description.TextData;
46
import eu.etaxonomy.cdm.model.location.NamedArea;
47
import eu.etaxonomy.cdm.model.media.Media;
48
import eu.etaxonomy.cdm.model.media.MediaRepresentation;
49
import eu.etaxonomy.cdm.model.media.MediaRepresentationPart;
50
import eu.etaxonomy.cdm.model.media.Rights;
51
import eu.etaxonomy.cdm.model.metadata.CdmPreference;
52
import eu.etaxonomy.cdm.model.metadata.PreferencePredicate;
53
import eu.etaxonomy.cdm.model.metadata.PreferenceSubject;
54
import eu.etaxonomy.cdm.model.name.TaxonName;
55
import eu.etaxonomy.cdm.model.occurrence.MediaSpecimen;
56
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
57
import eu.etaxonomy.cdm.model.taxon.Taxon;
58
import eu.etaxonomy.cdm.persistence.dao.media.IMediaDao;
59
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
60

    
61
@Service
62
@Transactional(readOnly=true)
63
public class MediaServiceImpl extends IdentifiableServiceBase<Media,IMediaDao> implements IMediaService {
64

    
65
    @Override
66
    @Autowired
67
	protected void setDao(IMediaDao dao) {
68
		this.dao = dao;
69
	}
70

    
71
	@Autowired
72
    private IOccurrenceService specimenService;
73
	@Autowired
74
    private ITaxonService taxonService;
75
	@Autowired
76
    private INameService nameService;
77
	@Autowired
78
	private IPreferenceService prefsService;
79
    @Autowired
80
    private MediaInfoFactory mediaInfoFactory; // FIXME define and use interface
81

    
82

    
83
	@Override
84
    public Pager<MediaKey> getMediaKeys(Set<Taxon> taxonomicScope, Set<NamedArea> geoScopes, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
85
        long numberOfResults = dao.countMediaKeys(taxonomicScope, geoScopes);
86

    
87
		List<MediaKey> results = new ArrayList<>();
88
		if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
89
			results = dao.getMediaKeys(taxonomicScope, geoScopes, pageSize, pageNumber, propertyPaths);
90
		}
91

    
92
		return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
93
	}
94

    
95
	@Override
96
    public Pager<Rights> getRights(Media t, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
97
        long numberOfResults = dao.countRights(t);
98

    
99
		List<Rights> results = new ArrayList<>();
100
		if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
101
			results = dao.getRights(t, pageSize, pageNumber,propertyPaths);
102
		}
103

    
104
		return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
105
	}
106

    
107
	@Override
108
	@Transactional(readOnly = false)
109
    public UpdateResult updateCaches(Class<? extends Media> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<Media> cacheStrategy, IProgressMonitor monitor) {
110
		if (clazz == null){
111
			clazz = Media.class;
112
		}
113
		return super.updateCachesImpl(clazz, stepSize, cacheStrategy, monitor);
114
	}
115

    
116
	 @Override
117
	 @Transactional(readOnly=false)
118
	 public DeleteResult delete(Set<UUID> mediaUuids, MediaDeletionConfigurator config) {
119
	     DeleteResult result = new DeleteResult();
120
	     for (UUID uuid:mediaUuids){
121
	         result.includeResult(delete(uuid, config));
122
	     }
123
	     return result;
124

    
125
	 }
126

    
127
    @Override
128
    @Transactional(readOnly=false)
129
    public DeleteResult delete(UUID mediaUuid, MediaDeletionConfigurator config) {
130
        DeleteResult result = new DeleteResult();
131
        Media media = this.load(mediaUuid);
132
        if (media == null){
133
            return result;
134
        }
135
        result = isDeletable(mediaUuid, config);
136
        if (result.isOk()){
137
            Set<CdmBase> references = commonService.getReferencingObjectsForDeletion(media);
138
            for (CdmBase ref: references){
139

    
140
                IDescribable<?> updatedObject = null;
141
                IService<ICdmBase> service = null;
142
                if (ref instanceof TextData){
143

    
144
                    TextData textData = HibernateProxyHelper.deproxy(ref, TextData.class);
145
                    DescriptionBase<?> description = HibernateProxyHelper.deproxy(textData.getInDescription(), DescriptionBase.class);
146

    
147
                    IDescribable<?> objectToUpdate = null;
148
                    boolean deleteIsMatchingInstance = false;
149
                    if (description instanceof TaxonDescription){
150
                        objectToUpdate = ((TaxonDescription)description).getTaxon();
151
                        deleteIsMatchingInstance = config.getDeleteFrom() instanceof Taxon;
152
                        service = (IService)taxonService;
153
                    }else if (description instanceof SpecimenDescription){
154
                        objectToUpdate = ((SpecimenDescription)description).getDescribedSpecimenOrObservation();
155
                        deleteIsMatchingInstance = config.getDeleteFrom() instanceof SpecimenOrObservationBase;
156
                        service = (IService)specimenService;
157
                    }else if (description instanceof TaxonNameDescription){
158
                        objectToUpdate = ((TaxonNameDescription)description).getTaxonName();
159
                        deleteIsMatchingInstance = config.getDeleteFrom() instanceof TaxonName;
160
                        service = (IService)nameService;
161
                    }else{
162
                        throw new RuntimeException("Unsupported DescriptionBase class");
163
                    }
164

    
165
                    if (objectToUpdate == null ){
166
                        continue;
167
                    } else if ( (config.isDeleteFromDescription() && deleteIsMatchingInstance  &&
168
                                   config.getDeleteFrom().getId() == objectToUpdate.getId())
169
                                || config.isDeleteFromEveryWhere()){
170
                        updatedObject = handleDeleteMedia(media, textData, description,
171
                                (IDescribable)objectToUpdate);
172
                    } else {
173
                    	
174
                        // this should not be happen, because it is not deletable. see isDeletable
175
                        result.setAbort();
176
                    }
177

    
178
//                } else if (ref instanceof MediaSpecimen && config.getDeleteFrom().getId() == ref.getId() && config.getDeleteFrom() instanceof MediaSpecimen){
179
//                        MediaSpecimen mediaSpecimen = HibernateProxyHelper.deproxy(ref, MediaSpecimen.class);
180
//                        mediaSpecimen.setMediaSpecimen(null);
181
//                        updatedObject = mediaSpecimen;
182
//                        service = (IService)specimenService;
183
                }else if (ref instanceof MediaRepresentation){
184
                    continue;
185
                }else {
186
                	
187
                    result.setAbort();
188
                }
189

    
190
                if (updatedObject != null){
191
                    //service.update(updatedObject); //service should always be != null if updatedObject != null
192
                    result.addUpdatedObject((CdmBase)updatedObject);
193
                }
194
            }
195
            if (result.isOk()){
196
                dao.delete(media);
197
                result.addDeletedObject(media);
198
            }
199

    
200
        }
201
        return result;
202
    }
203

    
204
    /**
205
     * @param media
206
     * @param textData
207
     * @param desc
208
     * @param taxon
209
     */
210
    private IDescribable<DescriptionBase<?>> handleDeleteMedia(Media media, TextData textData,
211
            DescriptionBase<?> desc, IDescribable<DescriptionBase<?>> describable) {
212
        while(textData.getMedia().contains(media)){
213
            textData.removeMedia(media);
214
        }
215

    
216
        return describable;
217
    }
218

    
219

    
220
    @Override
221
    public DeleteResult isDeletable(UUID mediaUuid, DeleteConfiguratorBase config){
222
        DeleteResult result = new DeleteResult();
223
        Media media = this.load(mediaUuid);
224
        Set<CdmBase> references = commonService.getReferencingObjectsForDeletion(media);
225
        MediaDeletionConfigurator mediaConfig = (MediaDeletionConfigurator)config;
226
        CdmBase deleteFrom = mediaConfig.getDeleteFrom();
227

    
228
//        if (mediaConfig.isDeleteFromEveryWhere()){
229
//           return result;
230
//        }
231
        for (CdmBase ref: references){
232
            String message = null;
233
            if (ref instanceof MediaRepresentation){
234
                continue;
235
            }else if (ref instanceof TextData){
236
                TextData textData = HibernateProxyHelper.deproxy(ref, TextData.class);
237
                DescriptionBase<?> description = HibernateProxyHelper.deproxy(textData.getInDescription(), DescriptionBase.class);
238

    
239
                if (description instanceof TaxonDescription){
240
                    TaxonDescription desc = HibernateProxyHelper.deproxy(description, TaxonDescription.class);
241
                    if (mediaConfig.isDeleteFromEveryWhere() ||(desc.getTaxon() == null || (mediaConfig.isDeleteFromDescription() && (deleteFrom instanceof Taxon && ((Taxon)deleteFrom).getId() == desc.getTaxon().getId())))){
242
                        continue;
243
                    } else{
244
                        message = "The media can't be deleted from the database because it is referenced by a taxon. ("+desc.getTaxon().getTitleCache()+")";
245
                        result.setAbort();
246
                    }
247

    
248
                } else if (description instanceof SpecimenDescription){
249
                    SpecimenDescription desc = HibernateProxyHelper.deproxy(description, SpecimenDescription.class);
250
                    if (mediaConfig.isDeleteFromEveryWhere() || (desc.getDescribedSpecimenOrObservation() == null || (mediaConfig.isDeleteFromDescription() && (deleteFrom instanceof SpecimenOrObservationBase && ((SpecimenOrObservationBase)deleteFrom).getId() == desc.getDescribedSpecimenOrObservation().getId())))){
251
                        continue;
252
                    } else{
253
                        message = "The media can't be deleted from the database because it is referenced by a specimen or observation. ("+desc.getDescribedSpecimenOrObservation().getTitleCache()+")";
254
                        result.setAbort();
255
                    }
256
                } else if (description instanceof TaxonNameDescription){
257
                    TaxonNameDescription desc = HibernateProxyHelper.deproxy(description, TaxonNameDescription.class);
258
                    if (mediaConfig.isDeleteFromEveryWhere() || (desc.getTaxonName() == null || (mediaConfig.isDeleteFromDescription() && (deleteFrom instanceof TaxonName && ((TaxonName)deleteFrom).getId() == desc.getTaxonName().getId())))){
259
                        continue;
260
                    } else{
261
                        message = "The media can't be deleted from the database because it is referenced by a scientific name. ("+desc.getTaxonName().getTitleCache()+")";
262
                        result.setAbort();
263
                    }
264
                }
265
            }else if (ref instanceof MediaSpecimen){
266
               message = "The media can't be deleted from the database because it is referenced by a mediaspecimen. ("+((MediaSpecimen)ref).getTitleCache()+")";
267
               result.setAbort();
268
            }else {
269
                message = "The media can't be completely deleted because it is referenced by a " + ref.getUserFriendlyTypeName();
270
                if (ref instanceof IdentifiableEntity) {
271
                	message += ": " + ((IdentifiableEntity)ref).getTitleCache();
272
                }
273
                result.setAbort();
274
            }
275
            if (message != null){
276
                result.addException(new ReferencedObjectUndeletableException(message));
277
                result.addRelatedObject(ref);
278
            }
279
        }
280

    
281
        return result;
282
    }
283

    
284
    /**
285
     * Reads the metadata as stored in the file or web resource and filters the data by the include and exclude lists of key names
286
     * as stored in the data base properties {@link PreferencePredicate#MediaMetadataKeynameExcludes} and {@link PreferencePredicate#MediaMetadataKeynameExcludes}
287
     * <p>
288
     * Metadata of multiple parts is merged into one common metadata map whereas the later part being read may overwrite data from previous parts.
289
     * The consequences of this can be neglected since we don't expect that multiple parts are actually being used.
290
     *
291
     * @param representation
292
     * @return
293
     * @throws IOException
294
     * @throws HttpException
295
     */
296
    @Override
297
    public Map<String, String> readResourceMetadataFiltered(MediaRepresentation representation) throws IOException, HttpException {
298

    
299
        List<String> includes = mediaMetadataKeyIncludes();
300
        List<String> excludes = mediaMetadataKeyExludes();
301
        Map<String, String> metadata = new HashMap<>();
302

    
303
        for(MediaRepresentationPart part : representation.getParts()) {
304
            CdmImageInfo iInfo =  mediaInfoFactory.cdmImageInfo(part.getUri(), true);
305
            if(iInfo.getMetaData() != null) {
306
                metadata.putAll(iInfo.getMetaData());
307
            }
308
        }
309
        if(logger.isDebugEnabled()) {
310
            logger.debug("meta data as read from all parts: " + metadata.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining(", ", "{", "}")));
311
        }
312

    
313
        if(!includes.isEmpty()) {
314
            metadata = metadata.entrySet()
315
                    .stream()
316
                    .filter( e -> containsCaseInsensitive(e.getKey(), includes))
317
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
318
            if(logger.isDebugEnabled()) {
319
                logger.debug("meta filtered by includes: " + metadata.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining(", ", "{", "}")));
320
            }
321
        }
322
        if(!excludes.isEmpty()) {
323
            metadata = metadata.entrySet()
324
                    .stream()
325
                    .filter( e -> !containsCaseInsensitive(e.getKey(), excludes))
326
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
327
            if(logger.isDebugEnabled()) {
328
                logger.debug("meta filtered by excludes: " + metadata.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining(", ", "{", "}")));
329
            }
330
        }
331

    
332
        if(metadata == null) {
333
            metadata = new HashMap<>();
334
        }
335
        return metadata;
336
    }
337

    
338
    private boolean containsCaseInsensitive(String s, List<String> l){
339
        return l.stream().anyMatch(x -> x.equalsIgnoreCase(s));
340
    }
341

    
342
    protected List<String> mediaMetadataKeyExludes(){
343
        CdmPreference pref = prefsService.findExact(CdmPreference.NewKey(PreferenceSubject.NewDatabaseInstance(), PreferencePredicate.MediaMetadataKeynameExcludes));
344
        if(pref == null) {
345
            return new ArrayList<>();
346
        }
347
        return pref.splitStringListValue();
348
    }
349

    
350
    protected List<String> mediaMetadataKeyIncludes(){
351
        CdmPreference pref = prefsService.findExact(CdmPreference.NewKey(PreferenceSubject.NewDatabaseInstance(), PreferencePredicate.MediaMetadataKeynameIncludes));
352
        if(pref == null) {
353
            return Arrays.asList(PreferencePredicate.MediaMetadataKeynameIncludes.getDefaultValue().toString().split(","));
354
        }
355
        return pref.splitStringListValue();
356
    }
357
}
(68-68/95)