Add method for single read deletion
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / OccurrenceServiceImpl.java
1 // $Id$
2 /**
3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
9 */
10
11 package eu.etaxonomy.cdm.api.service;
12
13 import java.io.IOException;
14 import java.net.URI;
15 import java.net.URISyntaxException;
16 import java.util.ArrayList;
17 import java.util.Arrays;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Map.Entry;
25 import java.util.Set;
26 import java.util.UUID;
27
28 import org.apache.log4j.Logger;
29 import org.apache.lucene.index.CorruptIndexException;
30 import org.apache.lucene.queryParser.ParseException;
31 import org.apache.lucene.search.BooleanClause.Occur;
32 import org.apache.lucene.search.BooleanQuery;
33 import org.apache.lucene.search.SortField;
34 import org.hibernate.TransientObjectException;
35 import org.hibernate.search.spatial.impl.Rectangle;
36 import org.joda.time.Partial;
37 import org.springframework.beans.factory.annotation.Autowired;
38 import org.springframework.dao.DataRetrievalFailureException;
39 import org.springframework.stereotype.Service;
40 import org.springframework.transaction.annotation.Transactional;
41
42 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade;
43 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeConfigurator;
44 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeNotSupportedException;
45 import eu.etaxonomy.cdm.api.service.UpdateResult.Status;
46 import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase;
47 import eu.etaxonomy.cdm.api.service.config.FindOccurrencesConfigurator;
48 import eu.etaxonomy.cdm.api.service.config.IIdentifiableEntityServiceConfigurator;
49 import eu.etaxonomy.cdm.api.service.config.SpecimenDeleteConfigurator;
50 import eu.etaxonomy.cdm.api.service.dto.DerivateDTO;
51 import eu.etaxonomy.cdm.api.service.dto.DerivateDataDTO;
52 import eu.etaxonomy.cdm.api.service.dto.DerivateDataDTO.ContigFile;
53 import eu.etaxonomy.cdm.api.service.dto.DerivateDataDTO.MolecularData;
54 import eu.etaxonomy.cdm.api.service.dto.FieldUnitDTO;
55 import eu.etaxonomy.cdm.api.service.dto.PreservedSpecimenDTO;
56 import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
57 import eu.etaxonomy.cdm.api.service.molecular.ISequenceService;
58 import eu.etaxonomy.cdm.api.service.pager.Pager;
59 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
60 import eu.etaxonomy.cdm.api.service.search.ILuceneIndexToolProvider;
61 import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;
62 import eu.etaxonomy.cdm.api.service.search.LuceneSearch;
63 import eu.etaxonomy.cdm.api.service.search.LuceneSearch.TopGroupsWithMaxScore;
64 import eu.etaxonomy.cdm.api.service.search.QueryFactory;
65 import eu.etaxonomy.cdm.api.service.search.SearchResult;
66 import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;
67 import eu.etaxonomy.cdm.api.service.util.TaxonRelationshipEdge;
68 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
69 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
70 import eu.etaxonomy.cdm.model.CdmBaseType;
71 import eu.etaxonomy.cdm.model.agent.AgentBase;
72 import eu.etaxonomy.cdm.model.common.CdmBase;
73 import eu.etaxonomy.cdm.model.common.DefinedTerm;
74 import eu.etaxonomy.cdm.model.common.DefinedTermBase;
75 import eu.etaxonomy.cdm.model.common.ICdmBase;
76 import eu.etaxonomy.cdm.model.common.Language;
77 import eu.etaxonomy.cdm.model.description.CategoricalData;
78 import eu.etaxonomy.cdm.model.description.DescriptionBase;
79 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
80 import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
81 import eu.etaxonomy.cdm.model.description.QuantitativeData;
82 import eu.etaxonomy.cdm.model.description.SpecimenDescription;
83 import eu.etaxonomy.cdm.model.description.TaxonDescription;
84 import eu.etaxonomy.cdm.model.location.Country;
85 import eu.etaxonomy.cdm.model.location.NamedArea;
86 import eu.etaxonomy.cdm.model.media.Media;
87 import eu.etaxonomy.cdm.model.media.MediaRepresentation;
88 import eu.etaxonomy.cdm.model.media.MediaRepresentationPart;
89 import eu.etaxonomy.cdm.model.media.MediaUtils;
90 import eu.etaxonomy.cdm.model.molecular.AmplificationResult;
91 import eu.etaxonomy.cdm.model.molecular.DnaSample;
92 import eu.etaxonomy.cdm.model.molecular.Sequence;
93 import eu.etaxonomy.cdm.model.molecular.SingleRead;
94 import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
95 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
96 import eu.etaxonomy.cdm.model.name.TypeDesignationStatusBase;
97 import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;
98 import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
99 import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
100 import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
101 import eu.etaxonomy.cdm.model.occurrence.GatheringEvent;
102 import eu.etaxonomy.cdm.model.occurrence.MediaSpecimen;
103 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
104 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;
105 import eu.etaxonomy.cdm.model.taxon.Taxon;
106 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
107 import eu.etaxonomy.cdm.persistence.dao.common.IDefinedTermDao;
108 import eu.etaxonomy.cdm.persistence.dao.initializer.AbstractBeanInitializer;
109 import eu.etaxonomy.cdm.persistence.dao.occurrence.IOccurrenceDao;
110 import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
111 import eu.etaxonomy.cdm.persistence.query.OrderHint;
112 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
113
114 /**
115 * @author a.babadshanjan
116 * @created 01.09.2008
117 */
118 @Service
119 @Transactional(readOnly = true)
120 public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObservationBase, IOccurrenceDao> implements IOccurrenceService {
121
122 static private final Logger logger = Logger.getLogger(OccurrenceServiceImpl.class);
123
124 @Autowired
125 private IDefinedTermDao definedTermDao;
126
127 @Autowired
128 private IDescriptionService descriptionService;
129
130 @Autowired
131 private ITaxonService taxonService;
132
133 @Autowired
134 private ISequenceService sequenceService;
135
136 @Autowired
137 private AbstractBeanInitializer beanInitializer;
138
139 @Autowired
140 private ILuceneIndexToolProvider luceneIndexToolProvider;
141
142 private static final String SEPARATOR_STRING = ", ";
143
144 public OccurrenceServiceImpl() {
145 logger.debug("Load OccurrenceService Bean");
146 }
147
148
149 @Override
150 @Transactional(readOnly = false)
151 public void updateTitleCache(Class<? extends SpecimenOrObservationBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<SpecimenOrObservationBase> cacheStrategy, IProgressMonitor monitor) {
152 if (clazz == null) {
153 clazz = SpecimenOrObservationBase.class;
154 }
155 super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
156 }
157
158 /**
159 * FIXME Candidate for harmonization
160 * move to termService
161 */
162 @Override
163 public Country getCountryByIso(String iso639) {
164 return this.definedTermDao.getCountryByIso(iso639);
165
166 }
167
168 /**
169 * FIXME Candidate for harmonization
170 * move to termService
171 */
172 @Override
173 public List<Country> getCountryByName(String name) {
174 List<? extends DefinedTermBase> terms = this.definedTermDao.findByTitle(Country.class, name, null, null, null, null, null, null);
175 List<Country> countries = new ArrayList<Country>();
176 for (int i = 0; i < terms.size(); i++) {
177 countries.add((Country) terms.get(i));
178 }
179 return countries;
180 }
181
182 @Override
183 @Autowired
184 protected void setDao(IOccurrenceDao dao) {
185 this.dao = dao;
186 }
187
188 @Override
189 public Pager<DerivationEvent> getDerivationEvents(SpecimenOrObservationBase occurence, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
190 Integer numberOfResults = dao.countDerivationEvents(occurence);
191
192 List<DerivationEvent> results = new ArrayList<DerivationEvent>();
193 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
194 results = dao.getDerivationEvents(occurence, pageSize, pageNumber, propertyPaths);
195 }
196
197 return new DefaultPagerImpl<DerivationEvent>(pageNumber, numberOfResults, pageSize, results);
198 }
199
200 @Override
201 public int countDeterminations(SpecimenOrObservationBase occurence, TaxonBase taxonbase) {
202 return dao.countDeterminations(occurence, taxonbase);
203 }
204
205 @Override
206 public Pager<DeterminationEvent> getDeterminations(SpecimenOrObservationBase occurrence, TaxonBase taxonBase, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
207 Integer numberOfResults = dao.countDeterminations(occurrence, taxonBase);
208
209 List<DeterminationEvent> results = new ArrayList<DeterminationEvent>();
210 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
211 results = dao.getDeterminations(occurrence, taxonBase, pageSize, pageNumber, propertyPaths);
212 }
213
214 return new DefaultPagerImpl<DeterminationEvent>(pageNumber, numberOfResults, pageSize, results);
215 }
216
217 @Override
218 public Pager<Media> getMedia(SpecimenOrObservationBase occurence, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
219 Integer numberOfResults = dao.countMedia(occurence);
220
221 List<Media> results = new ArrayList<Media>();
222 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
223 results = dao.getMedia(occurence, pageSize, pageNumber, propertyPaths);
224 }
225
226 return new DefaultPagerImpl<Media>(pageNumber, numberOfResults, pageSize, results);
227 }
228
229 @Override
230 public Pager<SpecimenOrObservationBase> list(Class<? extends SpecimenOrObservationBase> type, TaxonBase determinedAs, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
231 Integer numberOfResults = dao.count(type, determinedAs);
232 List<SpecimenOrObservationBase> results = new ArrayList<SpecimenOrObservationBase>();
233 pageNumber = pageNumber == null ? 0 : pageNumber;
234 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
235 Integer start = pageSize == null ? 0 : pageSize * pageNumber;
236 results = dao.list(type, determinedAs, pageSize, start, orderHints, propertyPaths);
237 }
238 return new DefaultPagerImpl<SpecimenOrObservationBase>(pageNumber, numberOfResults, pageSize, results);
239 }
240
241 @Override
242 public List<UuidAndTitleCache<DerivedUnit>> getDerivedUnitUuidAndTitleCache() {
243 return dao.getDerivedUnitUuidAndTitleCache();
244 }
245
246 @Override
247 public List<UuidAndTitleCache<FieldUnit>> getFieldUnitUuidAndTitleCache() {
248 return dao.getFieldUnitUuidAndTitleCache();
249 }
250
251 @Override
252 public DerivedUnitFacade getDerivedUnitFacade(DerivedUnit derivedUnit, List<String> propertyPaths) throws DerivedUnitFacadeNotSupportedException {
253 derivedUnit = (DerivedUnit) dao.load(derivedUnit.getUuid(), null);
254 DerivedUnitFacadeConfigurator config = DerivedUnitFacadeConfigurator.NewInstance();
255 config.setThrowExceptionForNonSpecimenPreservationMethodRequest(false);
256 DerivedUnitFacade derivedUnitFacade = DerivedUnitFacade.NewInstance(derivedUnit, config);
257 beanInitializer.initialize(derivedUnitFacade, propertyPaths);
258 return derivedUnitFacade;
259 }
260
261 @Override
262 public List<DerivedUnitFacade> listDerivedUnitFacades(
263 DescriptionBase description, List<String> propertyPaths) {
264
265 List<DerivedUnitFacade> derivedUnitFacadeList = new ArrayList<DerivedUnitFacade>();
266 IndividualsAssociation tempIndividualsAssociation;
267 SpecimenOrObservationBase tempSpecimenOrObservationBase;
268 List<DescriptionElementBase> elements = descriptionService.listDescriptionElements(description, null, IndividualsAssociation.class, null, 0, Arrays.asList(new String []{"associatedSpecimenOrObservation"}));
269 for (DescriptionElementBase element : elements) {
270 if (element.isInstanceOf(IndividualsAssociation.class)) {
271 tempIndividualsAssociation = HibernateProxyHelper.deproxy(element, IndividualsAssociation.class);
272 if (tempIndividualsAssociation.getAssociatedSpecimenOrObservation() != null) {
273 tempSpecimenOrObservationBase = HibernateProxyHelper.deproxy(tempIndividualsAssociation.getAssociatedSpecimenOrObservation(), SpecimenOrObservationBase.class);
274 if (tempSpecimenOrObservationBase.isInstanceOf(DerivedUnit.class)) {
275 try {
276 derivedUnitFacadeList.add(DerivedUnitFacade.NewInstance(HibernateProxyHelper.deproxy(tempSpecimenOrObservationBase, DerivedUnit.class)));
277 } catch (DerivedUnitFacadeNotSupportedException e) {
278 logger.warn(tempIndividualsAssociation.getAssociatedSpecimenOrObservation().getTitleCache() + " : " + e.getMessage());
279 }
280 }
281 }
282
283 }
284 }
285
286 beanInitializer.initializeAll(derivedUnitFacadeList, propertyPaths);
287
288 return derivedUnitFacadeList;
289 }
290
291
292 @Override
293 public <T extends SpecimenOrObservationBase> List<T> listByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
294 Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
295
296 return pageByAssociatedTaxon(type, includeRelationships, associatedTaxon, maxDepth, pageSize, pageNumber, orderHints, propertyPaths).getRecords();
297 }
298
299 @Override
300 public Collection<SpecimenOrObservationBase> listFieldUnitsByAssociatedTaxon(Taxon associatedTaxon, List<OrderHint> orderHints, List<String> propertyPaths) {
301 return pageFieldUnitsByAssociatedTaxon(null, associatedTaxon, null, null, null, null, propertyPaths).getRecords();
302 }
303
304 @Override
305 public Pager<SpecimenOrObservationBase> pageFieldUnitsByAssociatedTaxon(Set<TaxonRelationshipEdge> includeRelationships,
306 Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
307 List<String> propertyPaths) {
308
309 if (!getSession().contains(associatedTaxon)) {
310 associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid());
311 }
312
313 // gather the IDs of all relevant field units
314 Set<Integer> fieldUnitIds = new HashSet<Integer>();
315 List<SpecimenOrObservationBase> records = listByAssociatedTaxon(null, includeRelationships, associatedTaxon, maxDepth, null, null, orderHints, propertyPaths);
316 for (SpecimenOrObservationBase<?> specimen : records) {
317 for (FieldUnit fieldUnit : getFieldUnits(specimen.getUuid())) {
318 fieldUnitIds.add(fieldUnit.getId());
319 }
320 }
321 //dao.listByIds() does the paging of the field units. Passing the field units directly to the Pager would not work
322 List<SpecimenOrObservationBase> fieldUnits = dao.listByIds(fieldUnitIds, pageSize, pageNumber, orderHints, propertyPaths);
323 return new DefaultPagerImpl<SpecimenOrObservationBase>(pageNumber, fieldUnitIds.size(), pageSize, fieldUnits);
324 }
325
326 @Override
327 public FieldUnitDTO assembleFieldUnitDTO(FieldUnit fieldUnit, UUID associatedTaxonUuid) {
328
329 if (!getSession().contains(fieldUnit)) {
330 fieldUnit = (FieldUnit) load(fieldUnit.getUuid());
331 }
332 TaxonBase associatedTaxon = taxonService.load(associatedTaxonUuid);
333
334 FieldUnitDTO fieldUnitDTO = new FieldUnitDTO();
335
336 if (fieldUnit.getGatheringEvent() != null) {
337 GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent();
338 // Country
339 NamedArea country = gatheringEvent.getCountry();
340 fieldUnitDTO.setCountry(country != null ? country.getDescription() : null);
341 // Collection
342 AgentBase collector = gatheringEvent.getCollector();
343 String fieldNumber = fieldUnit.getFieldNumber();
344 String collectionString = "";
345 if (collector != null || fieldNumber != null) {
346 collectionString += collector != null ? collector : "";
347 if (!collectionString.isEmpty()) {
348 collectionString += " ";
349 }
350 collectionString += (fieldNumber != null ? fieldNumber : "");
351 collectionString.trim();
352 }
353 fieldUnitDTO.setCollection(collectionString);
354 // Date
355 Partial gatheringDate = gatheringEvent.getGatheringDate();
356 String dateString = null;
357 if (gatheringDate != null) {
358 gatheringDate.toString();
359 }
360 else if(gatheringEvent.getTimeperiod()!=null && gatheringEvent.getTimeperiod().getFreeText()!=null){
361 dateString = gatheringEvent.getTimeperiod().getFreeText();
362 }
363 fieldUnitDTO.setDate(dateString);
364 }
365
366 // Taxon Name
367 fieldUnitDTO.setTaxonName(associatedTaxon.getName().getTitleCache());
368
369 // Herbaria map
370 Map<eu.etaxonomy.cdm.model.occurrence.Collection, Integer> collectionToCountMap = new HashMap<eu.etaxonomy.cdm.model.occurrence.Collection, Integer>();
371 // List of accession numbers for citation
372 List<String> preservedSpecimenAccessionNumbers = new ArrayList<String>();
373
374 // assemble preserved specimen DTOs
375 Set<DerivationEvent> derivationEvents = fieldUnit.getDerivationEvents();
376 for (DerivationEvent derivationEvent : derivationEvents) {
377 Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
378 for (DerivedUnit derivedUnit : derivatives) {
379 // collect accession numbers for citation
380 String mostSignificantIdentifier = getMostSignificantIdentifier(derivedUnit);
381 if (mostSignificantIdentifier != null) {
382 preservedSpecimenAccessionNumbers.add(mostSignificantIdentifier);
383 }
384 // collect collections for herbaria column
385 if (derivedUnit.getCollection() != null) {
386 Integer herbariumCount = collectionToCountMap.get(derivedUnit.getCollection());
387 if (herbariumCount == null) {
388 herbariumCount = 0;
389 }
390 collectionToCountMap.put(derivedUnit.getCollection(), herbariumCount + 1);
391 }
392 if (derivedUnit.getRecordBasis().equals(SpecimenOrObservationType.PreservedSpecimen)) {
393 PreservedSpecimenDTO preservedSpecimenDTO = assemblePreservedSpecimenDTO(derivedUnit, fieldUnitDTO);
394 fieldUnitDTO.addPreservedSpecimenDTO(preservedSpecimenDTO);
395 fieldUnitDTO.setHasCharacterData(fieldUnitDTO.isHasCharacterData() || preservedSpecimenDTO.isHasCharacterData());
396 fieldUnitDTO.setHasDetailImage(fieldUnitDTO.isHasDetailImage() || preservedSpecimenDTO.isHasDetailImage());
397 fieldUnitDTO.setHasDna(fieldUnitDTO.isHasDna() || preservedSpecimenDTO.isHasDna());
398 fieldUnitDTO.setHasSpecimenScan(fieldUnitDTO.isHasSpecimenScan() || preservedSpecimenDTO.isHasSpecimenScan());
399 }
400 }
401 }
402 // assemble derivate data DTO
403 assembleDerivateDataDTO(fieldUnitDTO, fieldUnit);
404
405 // assemble citation
406 String citation = fieldUnit.getTitleCache();
407 if (!preservedSpecimenAccessionNumbers.isEmpty()) {
408 citation += " (";
409 for (String accessionNumber : preservedSpecimenAccessionNumbers) {
410 if (!accessionNumber.isEmpty()) {
411 citation += accessionNumber + SEPARATOR_STRING;
412 }
413 }
414 citation = removeTail(citation, SEPARATOR_STRING);
415 citation += ")";
416 }
417 fieldUnitDTO.setCitation(citation);
418
419 // assemble herbaria string
420 String herbariaString = "";
421 for (Entry<eu.etaxonomy.cdm.model.occurrence.Collection, Integer> e : collectionToCountMap.entrySet()) {
422 eu.etaxonomy.cdm.model.occurrence.Collection collection = e.getKey();
423 if (collection.getCode() != null) {
424 herbariaString += collection.getCode();
425 }
426 if (e.getValue() > 1) {
427 herbariaString += "(" + e.getValue() + ")";
428 }
429 herbariaString += SEPARATOR_STRING;
430 }
431 herbariaString = removeTail(herbariaString, SEPARATOR_STRING);
432 fieldUnitDTO.setHerbarium(herbariaString);
433
434 return fieldUnitDTO;
435 }
436
437 @Override
438 public PreservedSpecimenDTO assemblePreservedSpecimenDTO(DerivedUnit derivedUnit) {
439 return assemblePreservedSpecimenDTO(derivedUnit, null);
440 }
441
442 @Override
443 public String getMostSignificantIdentifier(DerivedUnit derivedUnit) {
444 if (derivedUnit.getAccessionNumber() != null && !derivedUnit.getAccessionNumber().isEmpty()) {
445 return derivedUnit.getAccessionNumber();
446 }
447 else if(derivedUnit.getBarcode()!=null && !derivedUnit.getBarcode().isEmpty()){
448 return derivedUnit.getBarcode();
449 }
450 else if(derivedUnit.getCatalogNumber()!=null && !derivedUnit.getCatalogNumber().isEmpty()){
451 return derivedUnit.getCatalogNumber();
452 }
453 return null;
454 }
455
456 public PreservedSpecimenDTO assemblePreservedSpecimenDTO(DerivedUnit derivedUnit, FieldUnitDTO fieldUnitDTO) {
457 if (!getSession().contains(derivedUnit)) {
458 derivedUnit = (DerivedUnit) load(derivedUnit.getUuid());
459 }
460 PreservedSpecimenDTO preservedSpecimenDTO = new PreservedSpecimenDTO();
461
462 // check identifiers in priority order accNo>barCode>catalogNumber
463 if (derivedUnit.getAccessionNumber() != null && !derivedUnit.getAccessionNumber().isEmpty()) {
464 preservedSpecimenDTO.setAccessionNumber(derivedUnit.getAccessionNumber());
465 }
466 else if(derivedUnit.getBarcode()!=null && !derivedUnit.getBarcode().isEmpty()){
467 preservedSpecimenDTO.setAccessionNumber(derivedUnit.getBarcode());
468 }
469 else if(derivedUnit.getCatalogNumber()!=null && !derivedUnit.getCatalogNumber().isEmpty()){
470 preservedSpecimenDTO.setAccessionNumber(derivedUnit.getCatalogNumber());
471 }
472 preservedSpecimenDTO.setUuid(derivedUnit.getUuid().toString());
473
474 // citation
475 Collection<FieldUnit> fieldUnits = getFieldUnits(derivedUnit);
476 if (fieldUnits.size() == 1) {
477 preservedSpecimenDTO.setCitation(fieldUnits.iterator().next().getTitleCache());
478 }
479 else{
480 preservedSpecimenDTO.setCitation("No Citation available. This specimen either has no or multiple field units.");
481 }
482
483 // character state data
484 Collection<DescriptionElementBase> characterDataForSpecimen = getCharacterDataForSpecimen(derivedUnit);
485 if (!characterDataForSpecimen.isEmpty()) {
486 if (fieldUnitDTO != null) {
487 fieldUnitDTO.setHasCharacterData(true);
488 }
489 }
490 for (DescriptionElementBase descriptionElementBase : characterDataForSpecimen) {
491 String character = descriptionElementBase.getFeature().getLabel();
492 ArrayList<Language> languages = new ArrayList<Language>(Collections.singleton(Language.DEFAULT()));
493 if (descriptionElementBase instanceof QuantitativeData) {
494 QuantitativeData quantitativeData = (QuantitativeData) descriptionElementBase;
495 DefaultQuantitativeDescriptionBuilder builder = new DefaultQuantitativeDescriptionBuilder();
496 String state = builder.build(quantitativeData, languages).getText(Language.DEFAULT());
497 preservedSpecimenDTO.addCharacterData(character, state);
498 }
499 else if(descriptionElementBase instanceof CategoricalData){
500 CategoricalData categoricalData = (CategoricalData) descriptionElementBase;
501 DefaultCategoricalDescriptionBuilder builder = new DefaultCategoricalDescriptionBuilder();
502 String state = builder.build(categoricalData, languages).getText(Language.DEFAULT());
503 preservedSpecimenDTO.addCharacterData(character, state);
504 }
505 }
506 // check type designations
507 Collection<SpecimenTypeDesignation> specimenTypeDesignations = listTypeDesignations(derivedUnit, null, null, null, null);
508 for (SpecimenTypeDesignation specimenTypeDesignation : specimenTypeDesignations) {
509 if (fieldUnitDTO != null) {
510 fieldUnitDTO.setHasType(true);
511 }
512 TypeDesignationStatusBase<?> typeStatus = specimenTypeDesignation.getTypeStatus();
513 if (typeStatus != null) {
514 List<String> typedTaxaNames = new ArrayList<String>();
515 String label = typeStatus.getLabel();
516 Set<TaxonNameBase> typifiedNames = specimenTypeDesignation.getTypifiedNames();
517 for (TaxonNameBase taxonNameBase : typifiedNames) {
518 typedTaxaNames.add(taxonNameBase.getFullTitleCache());
519 }
520 preservedSpecimenDTO.addTypes(label, typedTaxaNames);
521 }
522 }
523
524 // individuals associations
525 Collection<IndividualsAssociation> individualsAssociations = listIndividualsAssociations(derivedUnit, null, null, null, null);
526 for (IndividualsAssociation individualsAssociation : individualsAssociations) {
527 if (individualsAssociation.getInDescription() != null) {
528 if (individualsAssociation.getInDescription().isInstanceOf(TaxonDescription.class)) {
529 TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(individualsAssociation.getInDescription(), TaxonDescription.class);
530 Taxon taxon = taxonDescription.getTaxon();
531 if (taxon != null && taxon.getName() != null) {
532 preservedSpecimenDTO.addAssociatedTaxon(taxon.getName().getTitleCache());
533 }
534 }
535 }
536 }
537 // assemble sub derivates
538 preservedSpecimenDTO.setDerivateDataDTO(assembleDerivateDataDTO(preservedSpecimenDTO, derivedUnit));
539 return preservedSpecimenDTO;
540 }
541
542 private DerivateDataDTO assembleDerivateDataDTO(DerivateDTO derivateDTO, SpecimenOrObservationBase<?> specimenOrObservation) {
543 DerivateDataDTO derivateDataDTO = new DerivateDataDTO();
544 Collection<DerivedUnit> childDerivates = getDerivedUnitsFor(specimenOrObservation);
545 for (DerivedUnit childDerivate : childDerivates) {
546 // assemble molecular data
547 //pattern: DNAMarker [contig1, primer1_1, primer1_2, ...][contig2, primer2_1, ...]...
548 if (childDerivate.isInstanceOf(DnaSample.class)) {
549 if (childDerivate.getRecordBasis() == SpecimenOrObservationType.TissueSample) {
550 // TODO implement TissueSample assembly for web service
551 }
552 if (childDerivate.getRecordBasis() == SpecimenOrObservationType.DnaSample) {
553
554 DnaSample dna = HibernateProxyHelper.deproxy(childDerivate, DnaSample.class);
555 if (!dna.getSequences().isEmpty()) {
556 derivateDTO.setHasDna(true);
557 }
558 for (Sequence sequence : dna.getSequences()) {
559 URI boldUri = null;
560 try {
561 boldUri = sequence.getBoldUri();
562 } catch (URISyntaxException e1) {
563 logger.error("Could not create BOLD URI", e1);
564 }
565 final DefinedTerm dnaMarker = sequence.getDnaMarker();
566 MolecularData molecularData = derivateDataDTO.addProviderLink(boldUri != null ? boldUri : null, dnaMarker != null ? dnaMarker.getLabel() : "[no marker]");
567
568 //contig file
569 ContigFile contigFile = null;
570 if (sequence.getContigFile() != null) {
571 MediaRepresentationPart contigMediaRepresentationPart = MediaUtils.getFirstMediaRepresentationPart(sequence.getContigFile());
572 if (contigMediaRepresentationPart != null) {
573 contigFile = molecularData.addContigFile(contigMediaRepresentationPart.getUri(), "contig");
574 }
575 }
576 if(contigFile==null){
577 contigFile = molecularData.addContigFile(null, "[no contig]");
578 }
579 // primer files
580 if (sequence.getSingleReads() != null) {
581 int readCount = 1;
582 for (SingleRead singleRead : sequence.getSingleReads()) {
583 MediaRepresentationPart pherogramMediaRepresentationPart = MediaUtils.getFirstMediaRepresentationPart(singleRead.getPherogram());
584 if (pherogramMediaRepresentationPart != null) {
585 contigFile.addPrimerLink(pherogramMediaRepresentationPart.getUri(), "read"+readCount++);
586 }
587 }
588 }
589 }
590 }
591 }
592 // assemble media data
593 else if (childDerivate.isInstanceOf(MediaSpecimen.class)) {
594 MediaSpecimen media = HibernateProxyHelper.deproxy(childDerivate, MediaSpecimen.class);
595
596 String mediaUriString = getMediaUriString(media);
597 if (media.getKindOfUnit() != null) {
598 // specimen scan
599 if (media.getKindOfUnit().getUuid().equals(UUID.fromString("acda15be-c0e2-4ea8-8783-b9b0c4ad7f03"))) {
600 derivateDataDTO.addSpecimenScanUuid(media.getMediaSpecimen().getUuid());
601 derivateDTO.setHasSpecimenScan(true);
602 String imageLinkText = "scan";
603 if (derivateDTO instanceof PreservedSpecimenDTO && ((PreservedSpecimenDTO) derivateDTO).getAccessionNumber() != null) {
604 imageLinkText = ((PreservedSpecimenDTO) derivateDTO).getAccessionNumber();
605 }
606 derivateDataDTO.addSpecimenScan(mediaUriString == null ? "" : mediaUriString, imageLinkText);
607 }
608 // detail image
609 else if (media.getKindOfUnit().getUuid().equals(UUID.fromString("31eb8d02-bf5d-437c-bcc6-87a626445f34"))) {
610 derivateDataDTO.addDetailImageUuid(media.getMediaSpecimen().getUuid());
611 derivateDTO.setHasDetailImage(true);
612 String motif = "";
613 if (media.getMediaSpecimen() != null && media.getMediaSpecimen().getTitle() != null) {
614 motif = media.getMediaSpecimen().getTitle().getText();
615 }
616 derivateDataDTO.addDetailImage(mediaUriString == null ? "" : mediaUriString, motif != null ? motif : "[no motif]");
617 }
618 }
619 }
620 }
621 return derivateDataDTO;
622 }
623
624 private String removeTail(String string, final String tail) {
625 if (string.endsWith(tail)) {
626 string = string.substring(0, string.length() - tail.length());
627 }
628 return string;
629 }
630
631 private String getMediaUriString(MediaSpecimen mediaSpecimen) {
632 String mediaUri = null;
633 Collection<MediaRepresentation> mediaRepresentations = mediaSpecimen.getMediaSpecimen().getRepresentations();
634 if (mediaRepresentations != null && !mediaRepresentations.isEmpty()) {
635 Collection<MediaRepresentationPart> mediaRepresentationParts = mediaRepresentations.iterator().next().getParts();
636 if (mediaRepresentationParts != null && !mediaRepresentationParts.isEmpty()) {
637 MediaRepresentationPart part = mediaRepresentationParts.iterator().next();
638 if (part.getUri() != null) {
639 mediaUri = part.getUri().toASCIIString();
640 }
641 }
642 }
643 return mediaUri;
644 }
645
646 private Collection<DerivedUnit> getDerivedUnitsFor(SpecimenOrObservationBase<?> specimen) {
647 Collection<DerivedUnit> derivedUnits = new ArrayList<DerivedUnit>();
648 for (DerivationEvent derivationEvent : specimen.getDerivationEvents()) {
649 for (DerivedUnit derivative : derivationEvent.getDerivatives()) {
650 derivedUnits.add(derivative);
651 derivedUnits.addAll(getDerivedUnitsFor(derivative));
652 }
653 }
654 return derivedUnits;
655 }
656
657
658 @SuppressWarnings("unchecked")
659 @Override
660 public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
661 Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
662
663 Set<Taxon> taxa = new HashSet<Taxon>();
664 Set<Integer> occurrenceIds = new HashSet<Integer>();
665 List<T> occurrences = new ArrayList<T>();
666
667 // Integer limit = PagerUtils.limitFor(pageSize);
668 // Integer start = PagerUtils.startFor(pageSize, pageNumber);
669
670 if (!getSession().contains(associatedTaxon)) {
671 associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid());
672 }
673
674 if (includeRelationships != null) {
675 taxa = taxonService.listRelatedTaxa(associatedTaxon, includeRelationships, maxDepth, null, null, propertyPaths);
676 }
677
678 taxa.add(associatedTaxon);
679
680 for (Taxon taxon : taxa) {
681 List<T> perTaxonOccurrences = dao.listByAssociatedTaxon(type, taxon, null, null, orderHints, propertyPaths);
682 for (SpecimenOrObservationBase o : perTaxonOccurrences) {
683 occurrenceIds.add(o.getId());
684 }
685 }
686 occurrences = (List<T>) dao.listByIds(occurrenceIds, pageSize, pageNumber, orderHints, propertyPaths);
687
688 return new DefaultPagerImpl<T>(pageNumber, occurrenceIds.size(), pageSize, occurrences);
689
690 }
691
692 @Override
693 public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
694 String taxonUUID, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
695
696 UUID uuid = UUID.fromString(taxonUUID);
697 Taxon tax = (Taxon) taxonService.load(uuid);
698 // TODO REMOVE NULL STATEMENT
699 type = null;
700 return pageByAssociatedTaxon(type, includeRelationships, tax, maxDepth, pageSize, pageNumber, orderHints, propertyPaths);
701
702 }
703
704 @Override
705 public Pager<SearchResult<SpecimenOrObservationBase>> findByFullText(
706 Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle boundingBox, List<Language> languages,
707 boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
708 List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {
709
710 LuceneSearch luceneSearch = prepareByFullTextSearch(clazz, queryString, boundingBox, languages, highlightFragments);
711
712 // --- execute search
713 TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
714
715 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
716 idFieldMap.put(CdmBaseType.SPECIMEN_OR_OBSERVATIONBASE, "id");
717
718 // --- initialize taxa, highlight matches ....
719 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
720 @SuppressWarnings("rawtypes")
721 List<SearchResult<SpecimenOrObservationBase>> searchResults = searchResultBuilder.createResultSet(
722 topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
723
724 int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;
725
726 return new DefaultPagerImpl<SearchResult<SpecimenOrObservationBase>>(pageNumber, totalHits, pageSize,
727 searchResults);
728
729 }
730
731 private LuceneSearch prepareByFullTextSearch(Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle bbox,
732 List<Language> languages, boolean highlightFragments) {
733
734 BooleanQuery finalQuery = new BooleanQuery();
735 BooleanQuery textQuery = new BooleanQuery();
736
737 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, FieldUnit.class);
738 QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(FieldUnit.class);
739
740 // --- criteria
741 luceneSearch.setCdmTypRestriction(clazz);
742 if (queryString != null) {
743 textQuery.add(queryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);
744 finalQuery.add(textQuery, Occur.MUST);
745 }
746
747 // --- spacial query
748 if (bbox != null) {
749 finalQuery.add(QueryFactory.buildSpatialQueryByRange(bbox, "gatheringEvent.exactLocation.point"), Occur.MUST);
750 }
751
752 luceneSearch.setQuery(finalQuery);
753
754 // --- sorting
755 SortField[] sortFields = new SortField[] { SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false) };
756 luceneSearch.setSortFields(sortFields);
757
758 if (highlightFragments) {
759 luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
760 }
761 return luceneSearch;
762 }
763
764
765 @Override
766 public Collection<FieldUnit> getFieldUnits(UUID derivedUnitUuid) {
767 //It will search recursively over all {@link DerivationEvent}s and get the "originals" ({@link SpecimenOrObservationBase})
768 //from which this DerivedUnit was derived until all FieldUnits are found.
769
770 // FIXME: use HQL queries to increase performance
771 SpecimenOrObservationBase<?> specimen = load(derivedUnitUuid);
772 // specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
773 Collection<FieldUnit> fieldUnits = new ArrayList<FieldUnit>();
774
775 if (specimen.isInstanceOf(FieldUnit.class)) {
776 fieldUnits.add(HibernateProxyHelper.deproxy(specimen, FieldUnit.class));
777 }
778 else if(specimen.isInstanceOf(DerivedUnit.class)){
779 fieldUnits.addAll(getFieldUnits(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class)));
780 }
781 return fieldUnits;
782 }
783
784 private Collection<FieldUnit> getFieldUnits(DerivedUnit derivedUnit) {
785 Collection<FieldUnit> fieldUnits = new HashSet<FieldUnit>();
786 Set<SpecimenOrObservationBase> originals = derivedUnit.getOriginals();
787 if (originals != null && !originals.isEmpty()) {
788 for (SpecimenOrObservationBase<?> original : originals) {
789 if (original.isInstanceOf(FieldUnit.class)) {
790 fieldUnits.add(HibernateProxyHelper.deproxy(original, FieldUnit.class));
791 }
792 else if(original.isInstanceOf(DerivedUnit.class)){
793 fieldUnits.addAll(getFieldUnits(HibernateProxyHelper.deproxy(original, DerivedUnit.class)));
794 }
795 }
796 }
797 return fieldUnits;
798 }
799
800 @Override
801 public boolean moveSequence(DnaSample from, DnaSample to, Sequence sequence) {
802 // reload specimens to avoid session conflicts
803 from = (DnaSample) load(from.getUuid());
804 to = (DnaSample) load(to.getUuid());
805 sequence = sequenceService.load(sequence.getUuid());
806
807 if (from == null || to == null || sequence == null) {
808 throw new TransientObjectException("One of the CDM entities has not been saved to the data base yet. Moving only works for persisted/saved CDM entities.\n" +
809 "Operation was move "+sequence+ " from "+from+" to "+to);
810 }
811 from.removeSequence(sequence);
812 saveOrUpdate(from);
813 to.addSequence(sequence);
814 saveOrUpdate(to);
815 return true;
816 }
817
818 @Override
819 public boolean moveDerivate(SpecimenOrObservationBase<?> from, SpecimenOrObservationBase<?> to, DerivedUnit derivate) {
820 // reload specimens to avoid session conflicts
821 from = load(from.getUuid());
822 to = load(to.getUuid());
823 derivate = (DerivedUnit) load(derivate.getUuid());
824
825 if (from == null || to == null || derivate == null) {
826 throw new TransientObjectException("One of the CDM entities has not been saved to the data base yet. Moving only works for persisted/saved CDM entities.\n" +
827 "Operation was move "+derivate+ " from "+from+" to "+to);
828 }
829
830 SpecimenOrObservationType derivateType = derivate.getRecordBasis();
831 SpecimenOrObservationType toType = to.getRecordBasis();
832 // check if type is a sub derivate type
833 if(toType==SpecimenOrObservationType.FieldUnit //moving to FieldUnit always works
834 || derivateType==SpecimenOrObservationType.Media //moving media always works
835 || (derivateType.isKindOf(toType) && toType!=derivateType)){ //moving only to parent derivate type
836 // remove derivation event from parent specimen of dragged object
837 DerivationEvent eventToRemove = null;
838 for (DerivationEvent event : from.getDerivationEvents()) {
839 if (event.getDerivatives().contains(derivate)) {
840 eventToRemove = event;
841 break;
842 }
843 }
844 from.removeDerivationEvent(eventToRemove);
845 if(eventToRemove!=null){
846 // add new derivation event to target and copy the event parameters of the old one
847 DerivationEvent derivedFromNewOriginalEvent = DerivationEvent.NewSimpleInstance(to, derivate, null);
848 derivedFromNewOriginalEvent.setActor(eventToRemove.getActor());
849 derivedFromNewOriginalEvent.setDescription(eventToRemove.getDescription());
850 derivedFromNewOriginalEvent.setInstitution(eventToRemove.getInstitution());
851 derivedFromNewOriginalEvent.setTimeperiod(eventToRemove.getTimeperiod());
852 derivedFromNewOriginalEvent.setType(eventToRemove.getType());
853 to.addDerivationEvent(derivedFromNewOriginalEvent);
854 derivate.setDerivedFrom(derivedFromNewOriginalEvent);
855 }
856
857 saveOrUpdate(from);
858 saveOrUpdate(to);
859 return true;
860 }
861 return false;
862 }
863
864 @Override
865 public Collection<ICdmBase> getNonCascadedAssociatedElements(SpecimenOrObservationBase<?> specimen) {
866 // potential fields that are not persisted cascadingly
867 /*
868 * SOOB
869 -DescriptionBase
870 -determinations
871 --modifier TERM
872 -kindOfUnit TERM
873 -lifeStage TERM
874 -sex TERM
875
876 FieldUnit
877 -GatheringEvent
878 --Country TERM
879 --CollectingAreas TERM
880
881 DerivedUnit
882 -collection
883 --institute
884 ---types TERM
885 -preservationMethod
886 --medium TERM
887 -storedUnder CDM TaxonNameBase
888 */
889
890 Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<ICdmBase>();
891
892 //Choose the correct entry point to traverse the graph (FieldUnit or DerivedUnit)
893
894 // FieldUnit
895 if (specimen.isInstanceOf(FieldUnit.class)) {
896 nonCascadedCdmEntities.addAll(getFieldUnitNonCascadedAssociatedElements(HibernateProxyHelper.deproxy(specimen, FieldUnit.class)));
897 }
898 // DerivedUnit
899 else if (specimen.isInstanceOf(DerivedUnit.class)) {
900 DerivedUnit derivedUnit = HibernateProxyHelper.deproxy(specimen, DerivedUnit.class);
901 if (derivedUnit.getDerivedFrom() != null) {
902 Collection<FieldUnit> fieldUnits = getFieldUnits(derivedUnit);
903 for (FieldUnit fieldUnit : fieldUnits) {
904 nonCascadedCdmEntities.addAll(getFieldUnitNonCascadedAssociatedElements(fieldUnit));
905 }
906 }
907 }
908 return nonCascadedCdmEntities;
909 }
910
911 private Collection<ICdmBase> getFieldUnitNonCascadedAssociatedElements(FieldUnit fieldUnit) {
912 // get non cascaded element on SpecimenOrObservationBase level
913 Collection<ICdmBase> nonCascadedCdmEntities = getSpecimenOrObservationNonCascadedAssociatedElements(fieldUnit);
914
915 // get FieldUnit specific elements
916 GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent();
917 if (gatheringEvent != null) {
918 // country
919 if (gatheringEvent.getCountry() != null) {
920 nonCascadedCdmEntities.add(gatheringEvent.getCountry());
921 }
922 // collecting areas
923 for (NamedArea namedArea : gatheringEvent.getCollectingAreas()) {
924 nonCascadedCdmEntities.add(namedArea);
925 }
926 }
927 for (DerivationEvent derivationEvent : fieldUnit.getDerivationEvents()) {
928 for (DerivedUnit derivedUnit : derivationEvent.getDerivatives()) {
929 nonCascadedCdmEntities.addAll(getDerivedUnitNonCascadedAssociatedElements(derivedUnit));
930 }
931 }
932 return nonCascadedCdmEntities;
933 }
934
935 private Collection<ICdmBase> getDerivedUnitNonCascadedAssociatedElements(DerivedUnit derivedUnit) {
936 // get non cascaded element on SpecimenOrObservationBase level
937 Collection<ICdmBase> nonCascadedCdmEntities = getSpecimenOrObservationNonCascadedAssociatedElements(derivedUnit);
938
939 // get DerivedUnit specific elements
940 if (derivedUnit.getCollection() != null && derivedUnit.getCollection().getInstitute() != null) {
941 for (DefinedTerm type : derivedUnit.getCollection().getInstitute().getTypes()) {
942 nonCascadedCdmEntities.add(type);
943 }
944 }
945 if (derivedUnit.getPreservation() != null && derivedUnit.getPreservation().getMedium() != null) {
946 nonCascadedCdmEntities.add(derivedUnit.getPreservation().getMedium());
947 }
948 if (derivedUnit.getStoredUnder() != null) {
949 nonCascadedCdmEntities.add(derivedUnit.getStoredUnder());
950 }
951 return nonCascadedCdmEntities;
952 }
953
954 private Collection<ICdmBase> getSpecimenOrObservationNonCascadedAssociatedElements(
955 SpecimenOrObservationBase<?> specimen) {
956 Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<ICdmBase>();
957 // scan SpecimenOrObservationBase
958 for (DeterminationEvent determinationEvent : specimen.getDeterminations()) {
959 // modifier
960 if (determinationEvent.getModifier() != null) {
961 nonCascadedCdmEntities.add(determinationEvent.getModifier());
962 }
963 }
964 // kindOfUnit
965 if (specimen.getKindOfUnit() != null) {
966 nonCascadedCdmEntities.add(specimen.getKindOfUnit());
967 }
968 // lifeStage
969 if (specimen.getLifeStage() != null) {
970 nonCascadedCdmEntities.add(specimen.getLifeStage());
971 }
972 // sex
973 if (specimen.getSex() != null) {
974 nonCascadedCdmEntities.add(specimen.getSex());
975 }
976 return nonCascadedCdmEntities;
977 }
978
979 @Override
980 public DeleteResult isDeletable(SpecimenOrObservationBase specimen, DeleteConfiguratorBase config) {
981 DeleteResult deleteResult = new DeleteResult();
982 SpecimenDeleteConfigurator specimenDeleteConfigurator = (SpecimenDeleteConfigurator) config;
983
984 // check elements found by super method
985 Set<CdmBase> relatedObjects = super.isDeletable(specimen, config).getRelatedObjects();
986 for (CdmBase cdmBase : relatedObjects) {
987 // check for type designation
988 if (cdmBase.isInstanceOf(SpecimenTypeDesignation.class) && !specimenDeleteConfigurator.isDeleteFromTypeDesignation()) {
989 deleteResult.setAbort();
990 deleteResult.addException(new ReferencedObjectUndeletableException("Specimen is a type specimen."));
991 deleteResult.addRelatedObject(cdmBase);
992 break;
993 }
994 // check for IndividualsAssociations
995 else if (cdmBase.isInstanceOf(IndividualsAssociation.class) && !specimenDeleteConfigurator.isDeleteFromIndividualsAssociation()) {
996 deleteResult.setAbort();
997 deleteResult.addException(new ReferencedObjectUndeletableException("Specimen is still associated via IndividualsAssociations"));
998 deleteResult.addRelatedObject(cdmBase);
999 break;
1000 }
1001 // check for specimen/taxon description
1002 else if((cdmBase.isInstanceOf(SpecimenDescription.class) || cdmBase.isInstanceOf(TaxonDescription.class))
1003 && !specimenDeleteConfigurator.isDeleteFromDescription()){
1004 deleteResult.setAbort();
1005 deleteResult.addException(new ReferencedObjectUndeletableException("Specimen is still used in a Description."));
1006 deleteResult.addRelatedObject(cdmBase);
1007 break;
1008 }
1009 // check for children and parents (derivation events)
1010 else if (cdmBase.isInstanceOf(DerivationEvent.class)) {
1011 DerivationEvent derivationEvent = HibernateProxyHelper.deproxy(cdmBase, DerivationEvent.class);
1012 // check if derivation event is empty
1013 if (!derivationEvent.getDerivatives().isEmpty()) {
1014 if (derivationEvent.getDerivatives().size() == 1 && derivationEvent.getDerivatives().contains(specimen)) {
1015 //if it is the parent event with only one derivate then the specimen is still deletable
1016 continue;
1017 }
1018 else if(!specimenDeleteConfigurator.isDeleteChildren()){
1019 //if not and children should not be deleted then it is undeletable
1020 deleteResult.setAbort();
1021 deleteResult.addException(new ReferencedObjectUndeletableException("Derivative still has child derivatives."));
1022 deleteResult.addRelatedObject(cdmBase);
1023 break;
1024 }
1025 else{
1026 // check all children if they can be deleted
1027 Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1028 DeleteResult childResult = new DeleteResult();
1029 for (DerivedUnit derivedUnit : derivatives) {
1030 childResult.includeResult(isDeletable(derivedUnit, specimenDeleteConfigurator));
1031 }
1032 if (!childResult.isOk()) {
1033 deleteResult.setAbort();
1034 deleteResult.includeResult(childResult);
1035 deleteResult.addRelatedObject(cdmBase);
1036 break;
1037 }
1038 }
1039 }
1040 }
1041 // check for amplification
1042 else if (cdmBase.isInstanceOf(AmplificationResult.class) && !specimenDeleteConfigurator.isDeleteMolecularData()) {
1043 deleteResult.setAbort();
1044 deleteResult.addException(new ReferencedObjectUndeletableException("DnaSample is used in amplification results."));
1045 deleteResult.addRelatedObject(cdmBase);
1046 break;
1047 }
1048 // check for sequence
1049 else if (cdmBase.isInstanceOf(Sequence.class) && !specimenDeleteConfigurator.isDeleteMolecularData()) {
1050 deleteResult.setAbort();
1051 deleteResult.addException(new ReferencedObjectUndeletableException("DnaSample is used in sequences."));
1052 deleteResult.addRelatedObject(cdmBase);
1053 break;
1054 }
1055 }
1056 if (deleteResult.isOk()) {
1057 //add all related object if deletion is OK so they can be handled by the delete() method
1058 deleteResult.addRelatedObjects(relatedObjects);
1059 }
1060 return deleteResult;
1061 }
1062
1063 @Override
1064 public DeleteResult delete(SpecimenOrObservationBase<?> specimen, SpecimenDeleteConfigurator config) {
1065 specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
1066
1067 if (config.isDeleteChildren()) {
1068 Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1069 for (DerivationEvent derivationEvent : derivationEvents) {
1070 Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1071 for (DerivedUnit derivedUnit : derivatives) {
1072 delete(derivedUnit, config);
1073 }
1074 }
1075 }
1076
1077 DeleteResult deleteResult = isDeletable(specimen, config);
1078 if (!deleteResult.isOk()) {
1079 return deleteResult;
1080 }
1081
1082 // check related objects
1083 Set<CdmBase> relatedObjects = deleteResult.getRelatedObjects();
1084
1085 for (CdmBase relatedObject : relatedObjects) {
1086 // check for TypeDesignations
1087 if (relatedObject.isInstanceOf(SpecimenTypeDesignation.class)) {
1088 SpecimenTypeDesignation designation = HibernateProxyHelper.deproxy(relatedObject, SpecimenTypeDesignation.class);
1089 designation.setTypeSpecimen(null);
1090 Set<TaxonNameBase> typifiedNames = designation.getTypifiedNames();
1091 for (TaxonNameBase taxonNameBase : typifiedNames) {
1092 taxonNameBase.removeTypeDesignation(designation);
1093 }
1094 }
1095 // delete IndividualsAssociation
1096 if (relatedObject.isInstanceOf(IndividualsAssociation.class)) {
1097 IndividualsAssociation assciation = HibernateProxyHelper.deproxy(relatedObject, IndividualsAssociation.class);
1098 assciation.setAssociatedSpecimenOrObservation(null);
1099 assciation.getInDescription().removeElement(assciation);
1100 }
1101 // check for taxon description
1102 if (relatedObject.isInstanceOf(TaxonDescription.class)) {
1103 TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(relatedObject, TaxonDescription.class);
1104 taxonDescription.setDescribedSpecimenOrObservation(null);
1105 }
1106 // check for specimen description
1107 if (relatedObject.isInstanceOf(SpecimenDescription.class)) {
1108 SpecimenDescription specimenDescription = HibernateProxyHelper.deproxy(relatedObject, SpecimenDescription.class);
1109 // check if specimen is "described" specimen
1110 if (specimenDescription.getDescribedSpecimenOrObservation().equals(specimen)) {
1111 specimenDescription.setDescribedSpecimenOrObservation(null);
1112 }
1113 // check if description is a description of the given specimen
1114 if (specimen.getDescriptions().contains(specimenDescription)) {
1115 specimen.removeDescription(specimenDescription);
1116 }
1117 }
1118 // check for amplification
1119 if (relatedObject.isInstanceOf(AmplificationResult.class)) {
1120 AmplificationResult amplificationResult = HibernateProxyHelper.deproxy(relatedObject, AmplificationResult.class);
1121 amplificationResult.getDnaSample().removeAmplificationResult(amplificationResult);
1122 }
1123 // check for sequence
1124 if (relatedObject.isInstanceOf(Sequence.class)) {
1125 Sequence sequence = HibernateProxyHelper.deproxy(relatedObject, Sequence.class);
1126 sequence.getDnaSample().removeSequence(sequence);
1127 }
1128 // check for children and parents (derivation events)
1129 if (relatedObject.isInstanceOf(DerivationEvent.class)) {
1130 DerivationEvent derivationEvent = HibernateProxyHelper.deproxy(relatedObject, DerivationEvent.class);
1131 // parent derivation event (derivedFrom)
1132 if (derivationEvent.getDerivatives().contains(specimen) && specimen.isInstanceOf(DerivedUnit.class)) {
1133 derivationEvent.removeDerivative(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class));
1134 if (derivationEvent.getDerivatives().isEmpty()) {
1135 Set<SpecimenOrObservationBase> originals = derivationEvent.getOriginals();
1136 for (SpecimenOrObservationBase specimenOrObservationBase : originals) {
1137 specimenOrObservationBase.removeDerivationEvent(derivationEvent);
1138 }
1139 }
1140 }
1141 else{
1142 //child derivation events should not occur since we delete the hierarchy from bottom to top
1143 }
1144 }
1145 }
1146
1147 deleteResult.includeResult(delete(specimen));
1148 return deleteResult;
1149 }
1150
1151 @Override
1152 public DeleteResult deleteSingleRead(SingleRead singleRead, Sequence sequence){
1153 DeleteResult deleteResult = new DeleteResult();
1154 singleRead = HibernateProxyHelper.deproxy(singleRead, SingleRead.class);
1155 //delete from amplification result
1156 if(singleRead.getAmplificationResult()!=null){
1157 singleRead.getAmplificationResult().removeSingleRead(singleRead);
1158 }
1159 //delete from sequence
1160 sequence.removeSingleRead(singleRead);
1161 deleteResult.setStatus(Status.OK);
1162 return deleteResult;
1163 }
1164
1165 @Override
1166 public DeleteResult deleteDerivateHierarchy(CdmBase from, SpecimenDeleteConfigurator config) {
1167 DeleteResult deleteResult = new DeleteResult();
1168 String deleteMolecularNotAllowed = "Deleting molecular data is not allowed in config";
1169 if (from.isInstanceOf(Sequence.class)) {
1170 if (!config.isDeleteMolecularData()) {
1171 deleteResult.setAbort();
1172 deleteResult.addException(new ReferencedObjectUndeletableException(deleteMolecularNotAllowed));
1173 return deleteResult;
1174 }
1175 Sequence sequence = HibernateProxyHelper.deproxy(from, Sequence.class);
1176 sequence.getDnaSample().removeSequence(sequence);
1177 deleteResult = sequenceService.delete(sequence);
1178 }
1179 else if(from instanceof SingleRead){
1180 SingleRead singleRead = (SingleRead)from;
1181 //delete from amplification result
1182 if(singleRead.getAmplificationResult()!=null){
1183 singleRead.getAmplificationResult().removeSingleRead(singleRead);
1184 }
1185 deleteResult.setAbort();
1186 deleteResult.addException(new ReferencedObjectUndeletableException("Deleted ONLY from amplification. "
1187 + "Single read may still be attached to a consensus sequence."));
1188 }
1189 else if(from.isInstanceOf(SpecimenOrObservationBase.class)) {
1190 deleteResult = delete(HibernateProxyHelper.deproxy(from, SpecimenOrObservationBase.class), config);
1191 }
1192 return deleteResult;
1193 }
1194
1195 // private DeleteResult deepDelete(SpecimenOrObservationBase<?> entity, SpecimenDeleteConfigurator config){
1196 // Set<DerivationEvent> derivationEvents = entity.getDerivationEvents();
1197 // for (DerivationEvent derivationEvent : derivationEvents) {
1198 // Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1199 // for (DerivedUnit derivedUnit : derivatives) {
1200 // DeleteResult deleteResult = deepDelete(derivedUnit, config);
1201 // if(!deleteResult.isOk()){
1202 // return deleteResult;
1203 // }
1204 // }
1205 // }
1206 // return delete(entity, config);
1207 // }
1208
1209 @Override
1210 public Collection<IndividualsAssociation> listIndividualsAssociations(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1211 return dao.listIndividualsAssociations(specimen, null, null, null, null);
1212 }
1213
1214 @Override
1215 public Collection<SpecimenTypeDesignation> listTypeDesignations(SpecimenOrObservationBase<?> specimen,
1216 Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1217 return dao.listTypeDesignations(specimen, limit, start, orderHints, propertyPaths);
1218 }
1219
1220 @Override
1221 public Collection<DescriptionBase<?>> listDescriptionsWithDescriptionSpecimen(
1222 SpecimenOrObservationBase<?> specimen, Integer limit, Integer start, List<OrderHint> orderHints,
1223 List<String> propertyPaths) {
1224 return dao.listDescriptionsWithDescriptionSpecimen(specimen, limit, start, orderHints, propertyPaths);
1225 }
1226
1227 @Override
1228 public Collection<DescriptionElementBase> getCharacterDataForSpecimen(SpecimenOrObservationBase<?> specimen) {
1229 Collection<DescriptionElementBase> states = new ArrayList<DescriptionElementBase>();
1230 if (specimen != null) {
1231 Set<DescriptionBase> descriptions = specimen.getDescriptions();
1232 for (DescriptionBase<?> descriptionBase : descriptions) {
1233 if (descriptionBase.isInstanceOf(SpecimenDescription.class)) {
1234 SpecimenDescription specimenDescription = HibernateProxyHelper.deproxy(descriptionBase, SpecimenDescription.class);
1235 Set<DescriptionElementBase> elements = specimenDescription.getElements();
1236 for (DescriptionElementBase descriptionElementBase : elements) {
1237 if(descriptionElementBase.getFeature().isSupportsCategoricalData()
1238 ||descriptionElementBase.getFeature().isSupportsQuantitativeData()){
1239 states.add(descriptionElementBase);
1240 }
1241 }
1242 }
1243 }
1244 }
1245 return states;
1246 }
1247
1248 @Override
1249 public Collection<DescriptionElementBase> getCharacterDataForSpecimen(UUID specimenUuid) {
1250 SpecimenOrObservationBase<?> specimen = load(specimenUuid);
1251 if (specimen != null) {
1252 return getCharacterDataForSpecimen(specimen);
1253 }
1254 else{
1255 throw new DataRetrievalFailureException("Specimen with the given uuid not found in the data base");
1256 }
1257 }
1258
1259
1260 @Override
1261 public Pager<SpecimenOrObservationBase> findByTitle(
1262 IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config) {
1263 if (config instanceof FindOccurrencesConfigurator) {
1264 FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
1265 List<SpecimenOrObservationBase> occurrences = new ArrayList<SpecimenOrObservationBase>();
1266 Taxon taxon = null;
1267 if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
1268 TaxonBase taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
1269 if(taxonBase.isInstanceOf(Taxon.class)){
1270 taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1271 }
1272 }
1273 occurrences.addAll(dao.findOccurrences(occurrenceConfig.getClazz(),
1274 occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
1275 occurrenceConfig.getSpecimenType(), taxon, occurrenceConfig.getMatchMode(), null, null,
1276 occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths()));
1277 // indirectly associated specimens
1278 List<SpecimenOrObservationBase> indirectlyAssociatedOccurrences = new ArrayList<SpecimenOrObservationBase>(occurrences);
1279 if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens()){
1280 for (SpecimenOrObservationBase specimen : occurrences) {
1281 List<SpecimenOrObservationBase<?>> allHierarchyDerivates = getAllHierarchyDerivatives(specimen);
1282 for (SpecimenOrObservationBase<?> specimenOrObservationBase : allHierarchyDerivates) {
1283 if(!occurrences.contains(specimenOrObservationBase)){
1284 indirectlyAssociatedOccurrences.add(specimenOrObservationBase);
1285 }
1286 }
1287 }
1288 occurrences = indirectlyAssociatedOccurrences;
1289 }
1290
1291 return new DefaultPagerImpl<SpecimenOrObservationBase>(config.getPageNumber(), occurrences.size(), config.getPageSize(), occurrences);
1292 }
1293 return super.findByTitle(config);
1294 }
1295
1296 @Override
1297 public List<SpecimenOrObservationBase<?>> getAllHierarchyDerivatives(SpecimenOrObservationBase<?> specimen){
1298 List<SpecimenOrObservationBase<?>> allHierarchyDerivatives = new ArrayList<SpecimenOrObservationBase<?>>();
1299 Collection<FieldUnit> fieldUnits = getFieldUnits(specimen.getUuid());
1300 if(fieldUnits.isEmpty()){
1301 allHierarchyDerivatives.add(specimen);
1302 allHierarchyDerivatives.addAll(getAllChildDerivatives(specimen));
1303 }
1304 else{
1305 for (FieldUnit fieldUnit : fieldUnits) {
1306 allHierarchyDerivatives.add(fieldUnit);
1307 allHierarchyDerivatives.addAll(getAllChildDerivatives(fieldUnit));
1308 }
1309 }
1310 return allHierarchyDerivatives;
1311 }
1312
1313 @Override
1314 public List<DerivedUnit> getAllChildDerivatives(SpecimenOrObservationBase<?> specimen){
1315 List<DerivedUnit> childDerivate = new ArrayList<DerivedUnit>();
1316 Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1317 for (DerivationEvent derivationEvent : derivationEvents) {
1318 Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1319 for (DerivedUnit derivedUnit : derivatives) {
1320 childDerivate.add(derivedUnit);
1321 childDerivate.addAll(getAllChildDerivatives(derivedUnit));
1322 }
1323 }
1324 return childDerivate;
1325 }
1326
1327 @Override
1328 public int countOccurrences(IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config){
1329 if (config instanceof FindOccurrencesConfigurator) {
1330 FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
1331 Taxon taxon = null;
1332 if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
1333 TaxonBase taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
1334 if(taxonBase.isInstanceOf(Taxon.class)){
1335 taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1336 }
1337 }
1338 // indirectly associated specimens
1339 if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens()){
1340 return findByTitle(config).getRecords().size();
1341 }
1342 return dao.countOccurrences(occurrenceConfig.getClazz(), occurrenceConfig.getTitleSearchString(),
1343 occurrenceConfig.getSignificantIdentifier(), occurrenceConfig.getSpecimenType(), taxon,
1344 occurrenceConfig.getMatchMode(), null, null, occurrenceConfig.getOrderHints(),
1345 occurrenceConfig.getPropertyPaths());
1346 }
1347 return super.countByTitle(config);
1348 }
1349
1350 }