- when moving derivate also set "derivedFrom" to the new DerivationEvent
[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.HashMap;
20 import java.util.HashSet;
21 import java.util.LinkedHashSet;
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.stereotype.Service;
39 import org.springframework.transaction.annotation.Transactional;
40
41 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade;
42 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeConfigurator;
43 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeNotSupportedException;
44 import eu.etaxonomy.cdm.api.service.DeleteResult.DeleteStatus;
45 import eu.etaxonomy.cdm.api.service.config.SpecimenDeleteConfigurator;
46 import eu.etaxonomy.cdm.api.service.dto.DerivateHierarchyDTO;
47 import eu.etaxonomy.cdm.api.service.dto.DerivateHierarchyDTO.ContigFile;
48 import eu.etaxonomy.cdm.api.service.dto.DerivateHierarchyDTO.MolecularData;
49 import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
50 import eu.etaxonomy.cdm.api.service.molecular.ISequenceService;
51 import eu.etaxonomy.cdm.api.service.pager.Pager;
52 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
53 import eu.etaxonomy.cdm.api.service.search.ILuceneIndexToolProvider;
54 import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;
55 import eu.etaxonomy.cdm.api.service.search.LuceneSearch;
56 import eu.etaxonomy.cdm.api.service.search.LuceneSearch.TopGroupsWithMaxScore;
57 import eu.etaxonomy.cdm.api.service.search.QueryFactory;
58 import eu.etaxonomy.cdm.api.service.search.SearchResult;
59 import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;
60 import eu.etaxonomy.cdm.api.service.util.TaxonRelationshipEdge;
61 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
62 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
63 import eu.etaxonomy.cdm.model.CdmBaseType;
64 import eu.etaxonomy.cdm.model.agent.AgentBase;
65 import eu.etaxonomy.cdm.model.common.CdmBase;
66 import eu.etaxonomy.cdm.model.common.DefinedTerm;
67 import eu.etaxonomy.cdm.model.common.DefinedTermBase;
68 import eu.etaxonomy.cdm.model.common.ICdmBase;
69 import eu.etaxonomy.cdm.model.common.Language;
70 import eu.etaxonomy.cdm.model.common.UuidAndTitleCache;
71 import eu.etaxonomy.cdm.model.description.DescriptionBase;
72 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
73 import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
74 import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
75 import eu.etaxonomy.cdm.model.location.Country;
76 import eu.etaxonomy.cdm.model.location.NamedArea;
77 import eu.etaxonomy.cdm.model.media.Media;
78 import eu.etaxonomy.cdm.model.media.MediaRepresentation;
79 import eu.etaxonomy.cdm.model.media.MediaRepresentationPart;
80 import eu.etaxonomy.cdm.model.media.MediaUtils;
81 import eu.etaxonomy.cdm.model.molecular.DnaSample;
82 import eu.etaxonomy.cdm.model.molecular.Sequence;
83 import eu.etaxonomy.cdm.model.molecular.SingleRead;
84 import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
85 import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
86 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
87 import eu.etaxonomy.cdm.model.name.TypeDesignationStatusBase;
88 import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;
89 import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
90 import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
91 import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
92 import eu.etaxonomy.cdm.model.occurrence.GatheringEvent;
93 import eu.etaxonomy.cdm.model.occurrence.MediaSpecimen;
94 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
95 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;
96 import eu.etaxonomy.cdm.model.taxon.Taxon;
97 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
98 import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;
99 import eu.etaxonomy.cdm.persistence.dao.common.IDefinedTermDao;
100 import eu.etaxonomy.cdm.persistence.dao.initializer.AbstractBeanInitializer;
101 import eu.etaxonomy.cdm.persistence.dao.occurrence.IOccurrenceDao;
102 import eu.etaxonomy.cdm.persistence.query.OrderHint;
103 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
104
105 /**
106 * @author a.babadshanjan
107 * @created 01.09.2008
108 */
109 @Service
110 @Transactional(readOnly = true)
111 public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObservationBase,IOccurrenceDao> implements IOccurrenceService {
112
113 static private final Logger logger = Logger.getLogger(OccurrenceServiceImpl.class);
114
115 @Autowired
116 private IDefinedTermDao definedTermDao;
117
118 @Autowired
119 private IDescriptionService descriptionService;
120
121 @Autowired
122 private ITaxonService taxonService;
123
124 @Autowired
125 private ITermService termService;
126
127 @Autowired
128 private INameService nameService;
129
130 @Autowired
131 private ISequenceService sequenceService;
132
133 @Autowired
134 private AbstractBeanInitializer beanInitializer;
135
136 @Autowired
137 private ILuceneIndexToolProvider luceneIndexToolProvider;
138
139 @Autowired
140 private ICdmGenericDao genericDao;
141
142
143 public OccurrenceServiceImpl() {
144 logger.debug("Load OccurrenceService Bean");
145 }
146
147
148 /* (non-Javadoc)
149 * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
150 */
151 @Override
152 @Transactional(readOnly = false)
153 public void updateTitleCache(Class<? extends SpecimenOrObservationBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<SpecimenOrObservationBase> cacheStrategy, IProgressMonitor monitor) {
154 if (clazz == null){
155 clazz = SpecimenOrObservationBase.class;
156 }
157 super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
158 }
159
160
161 /**
162 * FIXME Candidate for harmonization
163 * move to termService
164 */
165 @Override
166 public Country getCountryByIso(String iso639) {
167 return this.definedTermDao.getCountryByIso(iso639);
168
169 }
170
171 /**
172 * FIXME Candidate for harmonization
173 * move to termService
174 */
175 @Override
176 public List<Country> getCountryByName(String name) {
177 List<? extends DefinedTermBase> terms = this.definedTermDao.findByTitle(Country.class, name, null, null, null, null, null, null) ;
178 List<Country> countries = new ArrayList<Country>();
179 for (int i=0;i<terms.size();i++){
180 countries.add((Country)terms.get(i));
181 }
182 return countries;
183 }
184
185 @Override
186 @Autowired
187 protected void setDao(IOccurrenceDao dao) {
188 this.dao = dao;
189 }
190
191 @Override
192 public Pager<DerivationEvent> getDerivationEvents(SpecimenOrObservationBase occurence, Integer pageSize,Integer pageNumber, List<String> propertyPaths) {
193 Integer numberOfResults = dao.countDerivationEvents(occurence);
194
195 List<DerivationEvent> results = new ArrayList<DerivationEvent>();
196 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
197 results = dao.getDerivationEvents(occurence, pageSize, pageNumber,propertyPaths);
198 }
199
200 return new DefaultPagerImpl<DerivationEvent>(pageNumber, numberOfResults, pageSize, results);
201 }
202
203 /* (non-Javadoc)
204 * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#countDeterminations(eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase, eu.etaxonomy.cdm.model.taxon.TaxonBase)
205 */
206 @Override
207 public int countDeterminations(SpecimenOrObservationBase occurence, TaxonBase taxonbase) {
208 return dao.countDeterminations(occurence, taxonbase);
209 }
210
211 @Override
212 public Pager<DeterminationEvent> getDeterminations(SpecimenOrObservationBase occurrence, TaxonBase taxonBase, Integer pageSize,Integer pageNumber, List<String> propertyPaths) {
213 Integer numberOfResults = dao.countDeterminations(occurrence, taxonBase);
214
215 List<DeterminationEvent> results = new ArrayList<DeterminationEvent>();
216 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
217 results = dao.getDeterminations(occurrence,taxonBase, pageSize, pageNumber, propertyPaths);
218 }
219
220 return new DefaultPagerImpl<DeterminationEvent>(pageNumber, numberOfResults, pageSize, results);
221 }
222
223 @Override
224 public Pager<Media> getMedia(SpecimenOrObservationBase occurence,Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
225 Integer numberOfResults = dao.countMedia(occurence);
226
227 List<Media> results = new ArrayList<Media>();
228 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
229 results = dao.getMedia(occurence, pageSize, pageNumber, propertyPaths);
230 }
231
232 return new DefaultPagerImpl<Media>(pageNumber, numberOfResults, pageSize, results);
233 }
234
235 /* (non-Javadoc)
236 * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#list(java.lang.Class, eu.etaxonomy.cdm.model.taxon.TaxonBase, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
237 */
238 @Override
239 public Pager<SpecimenOrObservationBase> list(Class<? extends SpecimenOrObservationBase> type, TaxonBase determinedAs, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
240 Integer numberOfResults = dao.count(type,determinedAs);
241 List<SpecimenOrObservationBase> results = new ArrayList<SpecimenOrObservationBase>();
242 pageNumber = pageNumber == null ? 0 : pageNumber;
243 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
244 Integer start = pageSize == null ? 0 : pageSize * pageNumber;
245 results = dao.list(type,determinedAs, pageSize, start, orderHints,propertyPaths);
246 }
247 return new DefaultPagerImpl<SpecimenOrObservationBase>(pageNumber, numberOfResults, pageSize, results);
248 }
249
250 @Override
251 public List<UuidAndTitleCache<DerivedUnit>> getDerivedUnitUuidAndTitleCache() {
252 return dao.getDerivedUnitUuidAndTitleCache();
253 }
254
255 @Override
256 public List<UuidAndTitleCache<FieldUnit>> getFieldUnitUuidAndTitleCache() {
257 return dao.getFieldUnitUuidAndTitleCache();
258 }
259
260 /* (non-Javadoc)
261 * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#getDerivedUnitFacade(eu.etaxonomy.cdm.model.occurrence.DerivedUnit)
262 */
263 @Override
264 public DerivedUnitFacade getDerivedUnitFacade(DerivedUnit derivedUnit, List<String> propertyPaths) throws DerivedUnitFacadeNotSupportedException {
265 derivedUnit = (DerivedUnit)dao.load(derivedUnit.getUuid(), null);
266 DerivedUnitFacadeConfigurator config = DerivedUnitFacadeConfigurator.NewInstance();
267 config.setThrowExceptionForNonSpecimenPreservationMethodRequest(false);
268 DerivedUnitFacade derivedUnitFacade = DerivedUnitFacade.NewInstance(derivedUnit, config);
269 beanInitializer.initialize(derivedUnitFacade, propertyPaths);
270 return derivedUnitFacade;
271 }
272
273 /* (non-Javadoc)
274 * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#listDerivedUnitFacades(eu.etaxonomy.cdm.model.description.DescriptionBase, java.util.List)
275 */
276 @Override
277 public List<DerivedUnitFacade> listDerivedUnitFacades(
278 DescriptionBase description, List<String> propertyPaths) {
279
280 List<DerivedUnitFacade> derivedUnitFacadeList = new ArrayList<DerivedUnitFacade>();
281 IndividualsAssociation tempIndividualsAssociation;
282 SpecimenOrObservationBase tempSpecimenOrObservationBase;
283 List<DescriptionElementBase> elements = descriptionService.listDescriptionElements(description, null, IndividualsAssociation.class, null, 0, Arrays.asList(new String []{"associatedSpecimenOrObservation"}));
284 for(DescriptionElementBase element : elements){
285 if(element instanceof IndividualsAssociation){
286 tempIndividualsAssociation = (IndividualsAssociation)element;
287 if(tempIndividualsAssociation.getAssociatedSpecimenOrObservation() != null){
288 tempSpecimenOrObservationBase = HibernateProxyHelper.deproxy(tempIndividualsAssociation.getAssociatedSpecimenOrObservation(), SpecimenOrObservationBase.class);
289 if(tempSpecimenOrObservationBase instanceof DerivedUnit){
290 try {
291 derivedUnitFacadeList.add(DerivedUnitFacade.NewInstance((DerivedUnit)tempSpecimenOrObservationBase));
292 } catch (DerivedUnitFacadeNotSupportedException e) {
293 logger.warn(tempIndividualsAssociation.getAssociatedSpecimenOrObservation().getTitleCache() + " : " +e.getMessage());
294 }
295 }
296 }
297
298 }
299 }
300
301 beanInitializer.initializeAll(derivedUnitFacadeList, propertyPaths);
302
303 return derivedUnitFacadeList;
304 }
305
306
307 /* (non-Javadoc)
308 * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#listByAnyAssociation(java.lang.Class, java.util.Set, eu.etaxonomy.cdm.model.taxon.Taxon, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
309 */
310 @Override
311 public <T extends SpecimenOrObservationBase> List<T> listByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
312 Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
313
314 return pageByAssociatedTaxon(type, includeRelationships, associatedTaxon, maxDepth, pageSize, pageNumber, orderHints, propertyPaths).getRecords();
315 }
316
317 /* (non-Javadoc)
318 * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#listByAnyAssociation(java.lang.Class, java.util.Set, eu.etaxonomy.cdm.model.taxon.Taxon, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
319 */
320 @Override
321 public Collection<FieldUnit> listFieldUnitsByAssociatedTaxon(Set<TaxonRelationshipEdge> includeRelationships,
322 Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
323
324 if(!getSession().contains(associatedTaxon)){
325 associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid());
326 }
327
328 Set<FieldUnit> fieldUnits = new HashSet<FieldUnit>();
329
330 List<SpecimenOrObservationBase> records = pageByAssociatedTaxon(null, includeRelationships, associatedTaxon, maxDepth, pageSize, pageNumber, orderHints, propertyPaths).getRecords();
331 for(SpecimenOrObservationBase<?> specimen:records){
332 fieldUnits.addAll(getFieldUnits(specimen.getUuid()));
333 }
334 return fieldUnits;
335 }
336
337 @Override
338 public DerivateHierarchyDTO assembleDerivateHierarchyDTO(FieldUnit fieldUnit, UUID associatedTaxonUuid){
339
340 if(!getSession().contains(fieldUnit)){
341 fieldUnit = (FieldUnit) load(fieldUnit.getUuid());
342 }
343 TaxonBase associatedTaxon = taxonService.load(associatedTaxonUuid);
344
345 DerivateHierarchyDTO dto = new DerivateHierarchyDTO();
346 Map<UUID, TypeDesignationStatusBase> typeSpecimenUUIDtoTypeDesignationStatus = new HashMap<UUID, TypeDesignationStatusBase>();
347
348 //gather types for this taxon name
349 TaxonNameBase<?,?> name = associatedTaxon.getName();
350 Set<?> typeDesignations = name.getSpecimenTypeDesignations();
351 for (Object object : typeDesignations) {
352 if(object instanceof SpecimenTypeDesignation){
353 SpecimenTypeDesignation specimenTypeDesignation = (SpecimenTypeDesignation)object;
354 DerivedUnit typeSpecimen = specimenTypeDesignation.getTypeSpecimen();
355 final TypeDesignationStatusBase typeStatus = specimenTypeDesignation.getTypeStatus();
356 typeSpecimenUUIDtoTypeDesignationStatus.put(typeSpecimen.getUuid(), typeStatus);
357 }
358 }
359
360 if(fieldUnit.getGatheringEvent()!=null){
361 GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent();
362 //Country
363 final NamedArea country = gatheringEvent.getCountry();
364 dto.setCountry(country!=null?country.getDescription():"");
365 //Collection
366 final AgentBase collector = gatheringEvent.getCollector();
367 final String fieldNumber = fieldUnit.getFieldNumber();
368 dto.setCollection(((collector!=null?collector:"") + " " + (fieldNumber!=null?fieldNumber:"")).trim());
369 //Date
370 final Partial gatheringDate = gatheringEvent.getGatheringDate();
371 dto.setDate(gatheringDate!=null?gatheringDate.toString():"");
372 }
373
374 //Taxon Name
375 dto.setTaxonName(associatedTaxon.getName().getFullTitleCache());
376
377
378 Collection<DerivedUnit> derivedUnits = new ArrayList<DerivedUnit>();
379 getDerivedUnitsFor(fieldUnit, derivedUnits);
380
381 //Herbaria map
382 Map<eu.etaxonomy.cdm.model.occurrence.Collection, Integer> collectionToCountMap = new HashMap<eu.etaxonomy.cdm.model.occurrence.Collection, Integer>();
383 //List of accession numbers for citation
384 List<String> preservedSpecimenAccessionNumbers = new ArrayList<String>();
385
386 //iterate over sub derivates
387 for (DerivedUnit derivedUnit : derivedUnits) {
388 //current accession number
389 String currentAccessionNumber = derivedUnit.getAccessionNumber()!=null?derivedUnit.getAccessionNumber():"";
390 //current herbarium
391 String currentHerbarium = "";
392 eu.etaxonomy.cdm.model.occurrence.Collection collection = derivedUnit.getCollection();
393 if(collection!=null){
394 currentHerbarium = collection.getCode()!=null?collection.getCode():"";
395 //count herbaria
396 Integer count = collectionToCountMap.get(collection);
397 if(count==null){
398 count = 1;
399 }
400 else{
401 count++;
402 }
403 collectionToCountMap.put(collection, count);
404 }
405 //check if derived unit is a type
406 if(typeSpecimenUUIDtoTypeDesignationStatus.keySet().contains(derivedUnit.getUuid())){
407 dto.setHasType(true);
408 TypeDesignationStatusBase typeDesignationStatus = typeSpecimenUUIDtoTypeDesignationStatus.get(derivedUnit.getUuid());
409 String typeStatus = typeDesignationStatus.getLabel();
410 dto.addTypes(typeStatus, currentAccessionNumber);
411 }
412 //assemble molecular data
413 //pattern: DNAMarker [contig1, primer1_1, primer1_2, ...][contig2, primer2_1, ...]...
414 if(derivedUnit instanceof DnaSample){
415 if(derivedUnit.getRecordBasis()==SpecimenOrObservationType.TissueSample){
416 //TODO implement TissueSample assembly for web service
417 }
418 if(derivedUnit.getRecordBasis()==SpecimenOrObservationType.DnaSample){
419
420 DnaSample dna = (DnaSample)derivedUnit;
421 if(!dna.getSequences().isEmpty()){
422 dto.setHasDna(true);
423 }
424 for(Sequence sequence:dna.getSequences()){
425 URI boldUri = null;
426 try {
427 boldUri = sequence.getBoldUri();
428 } catch (URISyntaxException e1) {
429 logger.error("Could not create BOLD URI", e1);
430 }
431 final DefinedTerm dnaMarker = sequence.getDnaMarker();
432 MolecularData molecularData = dto.addProviderLink(boldUri!=null?boldUri:null,dnaMarker!=null?dnaMarker.getLabel():"[no marker]");
433
434 //contig file FIXME show primer although contig not present?
435 if(sequence.getContigFile()!=null){
436 MediaRepresentationPart contigMediaRepresentationPart = MediaUtils.getFirstMediaRepresentationPart(sequence.getContigFile());
437 if(contigMediaRepresentationPart!=null){
438 ContigFile contigFile = molecularData.addContigFile(contigMediaRepresentationPart.getUri(), "contig");
439 //primer files
440 if(sequence.getSingleReads()!=null){
441 for (SingleRead singleRead : sequence.getSingleReads()) {
442 MediaRepresentationPart pherogramMediaRepresentationPart = MediaUtils.getFirstMediaRepresentationPart(singleRead.getPherogram());
443 if(pherogramMediaRepresentationPart!=null){
444 contigFile.addPrimerLink(pherogramMediaRepresentationPart.getUri(), "primer");
445 }
446 }
447 }
448 }
449 }
450 }
451 }
452 }
453 //assemble media data
454 else if(derivedUnit instanceof MediaSpecimen){
455
456 MediaSpecimen media = (MediaSpecimen)derivedUnit;
457 String mediaUriString = getMediaUriString(media);
458 if(media.getKindOfUnit()!=null){
459 //specimen scan
460 if(media.getKindOfUnit().getUuid().equals(UUID.fromString("acda15be-c0e2-4ea8-8783-b9b0c4ad7f03"))){
461 dto.setHasSpecimenScan(true);
462 final String imageLinkText = currentHerbarium+" "+currentAccessionNumber;
463 dto.addSpecimenScan(mediaUriString==null?"":mediaUriString, !imageLinkText.equals(" ")?imageLinkText:"[no accession]");
464 }
465 //detail image
466 else if(media.getKindOfUnit().getUuid().equals(UUID.fromString("31eb8d02-bf5d-437c-bcc6-87a626445f34"))){
467 dto.setHasDetailImage(true);
468 String motif = "";
469 if(media.getMediaSpecimen()!=null && media.getMediaSpecimen().getTitle()!=null){
470 motif = media.getMediaSpecimen().getTitle().getText();
471 }
472 dto.addDetailImage(mediaUriString==null?"":mediaUriString, motif!=null?motif:"[no motif]");
473 }
474 }
475 }
476 //assemble preserved specimen data
477 else if(derivedUnit.getRecordBasis()==SpecimenOrObservationType.PreservedSpecimen){
478 if(!currentAccessionNumber.isEmpty()){
479 preservedSpecimenAccessionNumbers.add(currentAccessionNumber);
480 }
481 }
482 }
483
484 final String separator = ", ";
485 //assemble citation
486 String citation = "";
487 citation += !dto.getCountry().isEmpty()?dto.getCountry()+separator:"";
488 if(fieldUnit.getGatheringEvent()!=null){
489 if(fieldUnit.getGatheringEvent().getLocality()!=null){
490 citation += fieldUnit.getGatheringEvent().getLocality().getText();
491 citation += separator;
492 }
493 if(fieldUnit.getGatheringEvent().getExactLocation()!=null
494 && fieldUnit.getGatheringEvent().getExactLocation().getLatitude()!=null
495 && fieldUnit.getGatheringEvent().getExactLocation().getLongitude()!=null){
496 citation += fieldUnit.getGatheringEvent().getExactLocation().getLatitude().toString();
497 citation += separator;
498 citation += fieldUnit.getGatheringEvent().getExactLocation().getLongitude().toString();
499 citation += separator;
500 }
501 }
502 citation += !dto.getCollection().isEmpty()?dto.getCollection():"";
503 if(!preservedSpecimenAccessionNumbers.isEmpty()){
504 citation += " (";
505 for(String accessionNumber:preservedSpecimenAccessionNumbers){
506 if(!accessionNumber.isEmpty()){
507 citation += accessionNumber+separator;
508 }
509 }
510 citation = removeTail(citation, separator);
511 citation += ")";
512 }
513 citation = removeTail(citation, separator);
514 dto.setCitation(citation);
515
516 //assemble herbaria string
517 String herbariaString = "";
518 for(Entry<eu.etaxonomy.cdm.model.occurrence.Collection, Integer> e:collectionToCountMap.entrySet()){
519 eu.etaxonomy.cdm.model.occurrence.Collection collection = e.getKey();
520 if(collection.getCode()!=null){
521 herbariaString += collection.getCode();
522 }
523 if(e.getValue()>1){
524 herbariaString += "("+e.getValue()+")";
525 }
526 herbariaString += separator;
527 }
528 herbariaString = removeTail(herbariaString, separator);
529 dto.setHerbarium(herbariaString);
530
531 return dto;
532 }
533
534
535 /**
536 * @param string
537 * @param tail
538 * @return
539 */
540 private String removeTail(String string, final String tail) {
541 if(string.endsWith(tail)){
542 string = string.substring(0, string.length()-tail.length());
543 }
544 return string;
545 }
546
547 private String getMediaUriString(MediaSpecimen mediaSpecimen){
548 String mediaUri = null;
549 Collection<MediaRepresentation> mediaRepresentations = mediaSpecimen.getMediaSpecimen().getRepresentations();
550 if(mediaRepresentations!=null && !mediaRepresentations.isEmpty()){
551 Collection<MediaRepresentationPart> mediaRepresentationParts = mediaRepresentations.iterator().next().getParts();
552 if(mediaRepresentationParts!=null && !mediaRepresentationParts.isEmpty()){
553 MediaRepresentationPart part = mediaRepresentationParts.iterator().next();
554 if(part.getUri()!=null){
555 mediaUri = part.getUri().toASCIIString();
556 }
557 }
558 }
559 return mediaUri;
560 }
561
562 private void getDerivedUnitsFor(SpecimenOrObservationBase<?> specimen, Collection<DerivedUnit> derivedUnits){
563 for(DerivationEvent derivationEvent:specimen.getDerivationEvents()){
564 for(DerivedUnit derivative:derivationEvent.getDerivatives()){
565 derivedUnits.add(derivative);
566 getDerivedUnitsFor(derivative, derivedUnits);
567 }
568 }
569 }
570
571
572 /* (non-Javadoc)
573 * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#pageByAssociatedTaxon(java.lang.Class, java.util.Set, eu.etaxonomy.cdm.model.taxon.Taxon, java.lang.Integer, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
574 */
575 @SuppressWarnings("unchecked")
576 @Override
577 public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
578 Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
579
580 Set<Taxon> taxa = new HashSet<Taxon>();
581 Set<Integer> occurrenceIds = new HashSet<Integer>();
582 List<T> occurrences = new ArrayList<T>();
583
584 // Integer limit = PagerUtils.limitFor(pageSize);
585 // Integer start = PagerUtils.startFor(pageSize, pageNumber);
586
587 if(!getSession().contains(associatedTaxon)){
588 associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid());
589 }
590
591 if(includeRelationships != null) {
592 taxa = taxonService.listRelatedTaxa(associatedTaxon, includeRelationships, maxDepth, null, null, propertyPaths);
593 }
594
595 taxa.add(associatedTaxon);
596
597 for (Taxon taxon : taxa) {
598 List<T> perTaxonOccurrences = dao.listByAssociatedTaxon(type, taxon, null, null, orderHints, propertyPaths);
599 for (SpecimenOrObservationBase o : perTaxonOccurrences) {
600 occurrenceIds.add(o.getId());
601 }
602 }
603 occurrences = (List<T>) dao.listByIds(occurrenceIds, pageSize, pageNumber, orderHints, propertyPaths);
604
605 return new DefaultPagerImpl<T>(pageNumber, occurrenceIds.size(), pageSize, occurrences);
606
607 }
608
609
610 @Override
611 public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
612 String taxonUUID, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
613
614 UUID uuid = UUID.fromString(taxonUUID);
615 Taxon tax = (Taxon) taxonService.load(uuid);
616 //TODO REMOVE NULL STATEMENT
617 type=null;
618 return pageByAssociatedTaxon( type,includeRelationships,tax, maxDepth, pageSize, pageNumber, orderHints, propertyPaths );
619
620 }
621
622
623 @Override
624 public Pager<SearchResult<SpecimenOrObservationBase>> findByFullText(
625 Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle boundingBox, List<Language> languages,
626 boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
627 List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {
628
629 LuceneSearch luceneSearch = prepareByFullTextSearch(clazz, queryString, boundingBox, languages, highlightFragments);
630
631 // --- execute search
632 TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
633
634 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
635 idFieldMap.put(CdmBaseType.SPECIMEN_OR_OBSERVATIONBASE, "id");
636
637 // --- initialize taxa, highlight matches ....
638 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
639 @SuppressWarnings("rawtypes")
640 List<SearchResult<SpecimenOrObservationBase>> searchResults = searchResultBuilder.createResultSet(
641 topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
642
643 int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;
644
645 return new DefaultPagerImpl<SearchResult<SpecimenOrObservationBase>>(pageNumber, totalHits, pageSize,
646 searchResults);
647
648 }
649
650
651 /**
652 * @param clazz
653 * @param queryString
654 * @param languages
655 * @param highlightFragments
656 * @return
657 */
658 private LuceneSearch prepareByFullTextSearch(Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle bbox,
659 List<Language> languages, boolean highlightFragments) {
660
661 BooleanQuery finalQuery = new BooleanQuery();
662 BooleanQuery textQuery = new BooleanQuery();
663
664 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, FieldUnit.class);
665 QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(FieldUnit.class);
666
667 // --- criteria
668 luceneSearch.setCdmTypRestriction(clazz);
669 if(queryString != null){
670 textQuery.add(queryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);
671 finalQuery.add(textQuery, Occur.MUST);
672 }
673
674 // --- spacial query
675 if(bbox != null){
676 finalQuery.add(QueryFactory.buildSpatialQueryByRange(bbox, "gatheringEvent.exactLocation.point"), Occur.MUST);
677 }
678
679 luceneSearch.setQuery(finalQuery);
680
681 // --- sorting
682 SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
683 luceneSearch.setSortFields(sortFields);
684
685 if(highlightFragments){
686 luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
687 }
688 return luceneSearch;
689 }
690
691
692 /* (non-Javadoc)
693 * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#getFieldUnits(eu.etaxonomy.cdm.model.occurrence.DerivedUnit)
694 */
695 @Override
696 public Collection<FieldUnit> getFieldUnits(UUID derivedUnitUuid) {
697 //It will search recursively over all {@link DerivationEvent}s and get the "originals" ({@link SpecimenOrObservationBase})
698 //from which this DerivedUnit was derived until all FieldUnits are found.
699
700 //FIXME: use HQL queries to increase performance
701 SpecimenOrObservationBase<?> specimen = load(derivedUnitUuid);
702 // specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
703 Collection<FieldUnit> fieldUnits = new ArrayList<FieldUnit>();
704
705 if(specimen instanceof FieldUnit){
706 fieldUnits.add((FieldUnit) specimen);
707 }
708 else if(specimen instanceof DerivedUnit){
709 getFieldUnits((DerivedUnit) specimen, fieldUnits);
710 }
711 return fieldUnits;
712 }
713
714
715 /**
716 * @param original
717 * @param fieldUnits
718 */
719 private void getFieldUnits(DerivedUnit derivedUnit, Collection<FieldUnit> fieldUnits) {
720 Set<SpecimenOrObservationBase> originals = derivedUnit.getOriginals();
721 if(originals!=null && !originals.isEmpty()){
722 for(SpecimenOrObservationBase<?> original:originals){
723 if(original instanceof FieldUnit){
724 fieldUnits.add((FieldUnit) original);
725 }
726 else if(original instanceof DerivedUnit){
727 getFieldUnits((DerivedUnit) original, fieldUnits);
728 }
729 }
730 }
731 }
732
733 /* (non-Javadoc)
734 * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#moveSequence(eu.etaxonomy.cdm.model.molecular.DnaSample, eu.etaxonomy.cdm.model.molecular.DnaSample, eu.etaxonomy.cdm.model.molecular.Sequence)
735 */
736 @Override
737 public boolean moveSequence(DnaSample from, DnaSample to, Sequence sequence) {
738 //reload specimens to avoid session conflicts
739 from = (DnaSample) load(from.getUuid());
740 to = (DnaSample) load(to.getUuid());
741 sequence = sequenceService.load(sequence.getUuid());
742
743 if(from==null || to==null || sequence==null){
744 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" +
745 "Operation was move "+sequence+ " from "+from+" to "+to);
746 }
747 from.removeSequence(sequence);
748 saveOrUpdate(from);
749 to.addSequence(sequence);
750 saveOrUpdate(to);
751 return true;
752 }
753
754 /* (non-Javadoc)
755 * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#moveDerivate(eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase, eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase, eu.etaxonomy.cdm.model.occurrence.DerivedUnit)
756 */
757 @Override
758 public boolean moveDerivate(SpecimenOrObservationBase<?> from, SpecimenOrObservationBase<?> to, DerivedUnit derivate) {
759 //reload specimens to avoid session conflicts
760 from = load(from.getUuid());
761 to = load(to.getUuid());
762 derivate = (DerivedUnit) load(derivate.getUuid());
763
764 if(from==null || to==null || derivate==null){
765 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" +
766 "Operation was move "+derivate+ " from "+from+" to "+to);
767 }
768
769 SpecimenOrObservationType derivateType = derivate.getRecordBasis();
770 SpecimenOrObservationType toType = to.getRecordBasis();
771 //check if type is a sub derivate type
772 if(toType==SpecimenOrObservationType.FieldUnit //moving to FieldUnit always works
773 || derivateType==SpecimenOrObservationType.Media //moving media always works
774 || (derivateType.isKindOf(toType) && toType!=derivateType)){ //moving only to parent derivate type
775 //remove derivation event from parent specimen of dragged object
776 DerivationEvent eventToRemove = null;
777 for(DerivationEvent event:from.getDerivationEvents()){
778 if(event.getDerivatives().contains(derivate)){
779 eventToRemove = event;
780 break;
781 }
782 }
783 from.removeDerivationEvent(eventToRemove);
784 saveOrUpdate(from);
785 //add new derivation event to target
786 DerivationEvent derivedFromNewOriginalEvent = DerivationEvent.NewSimpleInstance(to, derivate, eventToRemove==null?null:eventToRemove.getType());
787 to.addDerivationEvent(derivedFromNewOriginalEvent);
788 derivate.setDerivedFrom(derivedFromNewOriginalEvent);
789 saveOrUpdate(to);
790 return true;
791 }
792 return false;
793 }
794
795 @Override
796 public Collection<ICdmBase> getNonCascadedAssociatedElements(SpecimenOrObservationBase<?> specimen){
797 //potential fields that are not persisted cascadingly
798 /*
799 * SOOB
800 -DescriptionBase
801 -determinations
802 --modifier TERM
803 -kindOfUnit TERM
804 -lifeStage TERM
805 -sex TERM
806
807 FieldUnit
808 -GatheringEvent
809 --Country TERM
810 --CollectingAreas TERM
811
812 DerivedUnit
813 -collection
814 --institute
815 ---types TERM
816 -preservationMethod
817 --medium TERM
818 -storedUnder CDM TaxonNameBase
819 */
820
821 Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<ICdmBase>();
822
823 //Choose the correct entry point to traverse the graph (FieldUnit or DerivedUnit)
824
825 //FieldUnit
826 if(specimen instanceof FieldUnit){
827 nonCascadedCdmEntities.addAll(getFieldUnitNonCascadedAssociatedElements((FieldUnit)specimen));
828 }
829 //DerivedUnit
830 else if(specimen instanceof DerivedUnit){
831 DerivedUnit derivedUnit = (DerivedUnit)specimen;
832 if(derivedUnit.getDerivedFrom()!=null){
833 Collection<FieldUnit> fieldUnits = new ArrayList<FieldUnit>();
834 getFieldUnits(derivedUnit, fieldUnits);
835 for(FieldUnit fieldUnit:fieldUnits){
836 nonCascadedCdmEntities.addAll(getFieldUnitNonCascadedAssociatedElements(fieldUnit));
837 }
838 }
839 }
840 return nonCascadedCdmEntities;
841 }
842
843 private Collection<ICdmBase> getFieldUnitNonCascadedAssociatedElements(FieldUnit fieldUnit){
844 //get non cascaded element on SpecimenOrObservationBase level
845 Collection<ICdmBase> nonCascadedCdmEntities = getSpecimenOrObservationNonCascadedAssociatedElements(fieldUnit);
846
847 //get FieldUnit specific elements
848 GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent();
849 if(gatheringEvent!=null){
850 //country
851 if(gatheringEvent.getCountry()!=null){
852 nonCascadedCdmEntities.add(gatheringEvent.getCountry());
853 }
854 //collecting areas
855 for (NamedArea namedArea : gatheringEvent.getCollectingAreas()) {
856 nonCascadedCdmEntities.add(namedArea);
857 }
858 }
859 for (DerivationEvent derivationEvent : fieldUnit.getDerivationEvents()) {
860 for (DerivedUnit derivedUnit : derivationEvent.getDerivatives()) {
861 nonCascadedCdmEntities.addAll(getDerivedUnitNonCascadedAssociatedElements(derivedUnit));
862 }
863 }
864 return nonCascadedCdmEntities;
865 }
866
867 private Collection<ICdmBase> getDerivedUnitNonCascadedAssociatedElements(DerivedUnit derivedUnit){
868 //get non cascaded element on SpecimenOrObservationBase level
869 Collection<ICdmBase> nonCascadedCdmEntities = getSpecimenOrObservationNonCascadedAssociatedElements(derivedUnit);
870
871 //get DerivedUnit specific elements
872 if(derivedUnit.getCollection()!=null && derivedUnit.getCollection().getInstitute()!=null){
873 for (DefinedTerm type : derivedUnit.getCollection().getInstitute().getTypes()) {
874 nonCascadedCdmEntities.add(type);
875 }
876 }
877 if(derivedUnit.getPreservation()!=null && derivedUnit.getPreservation().getMedium()!=null){
878 nonCascadedCdmEntities.add(derivedUnit.getPreservation().getMedium());
879 }
880 if(derivedUnit.getStoredUnder()!=null){
881 nonCascadedCdmEntities.add(derivedUnit.getStoredUnder());
882 }
883 return nonCascadedCdmEntities;
884 }
885
886 /**
887 * @param specimen
888 * @return
889 */
890 private Collection<ICdmBase> getSpecimenOrObservationNonCascadedAssociatedElements(
891 SpecimenOrObservationBase<?> specimen) {
892 Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<ICdmBase>();
893 //scan SpecimenOrObservationBase
894 for(DeterminationEvent determinationEvent:specimen.getDeterminations()){
895 //modifier
896 if(determinationEvent.getModifier()!=null){
897 nonCascadedCdmEntities.add(determinationEvent.getModifier());
898 }
899 }
900 //kindOfUnit
901 if(specimen.getKindOfUnit()!=null){
902 nonCascadedCdmEntities.add(specimen.getKindOfUnit());
903 }
904 //lifeStage
905 if(specimen.getLifeStage()!=null){
906 nonCascadedCdmEntities.add(specimen.getLifeStage());
907 }
908 //sex
909 if(specimen.getSex()!=null){
910 nonCascadedCdmEntities.add(specimen.getSex());
911 }
912 return nonCascadedCdmEntities;
913 }
914
915
916 /* (non-Javadoc)
917 * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#delete(eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase, eu.etaxonomy.cdm.api.service.config.SpecimenDeleteConfigurator)
918 */
919 @Override
920 public DeleteResult delete(SpecimenOrObservationBase<?> specimen, SpecimenDeleteConfigurator config) {
921 DeleteResult deleteResult = new DeleteResult();
922 Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
923 for (DerivationEvent derivationEvent : derivationEvents) {
924 if(!derivationEvent.getDerivatives().isEmpty()){
925 deleteResult.setAbort();
926 deleteResult.addException(new ReferencedObjectUndeletableException("Derivate with children cannot be deleted."));
927 return deleteResult;
928 }
929 }
930 if(specimen instanceof DerivedUnit){
931 DerivationEvent derivedFromEvent = ((DerivedUnit) specimen).getDerivedFrom();
932 if(derivedFromEvent!=null){
933 derivedFromEvent.removeDerivative((DerivedUnit) specimen);
934 }
935 }
936 if(!config.isDeleteChildren()){
937 Set<CdmBase> referencingObjects = genericDao.getReferencingObjects(specimen);
938 for (CdmBase referencingObject : referencingObjects){
939 //DerivedUnit?.storedUnder
940 if (referencingObject.isInstanceOf(DerivedUnit.class)){
941 String message = "Name can't be deleted as it is used as derivedUnit#storedUnder by %s. Remove 'stored under' prior to deleting this name";
942 message = String.format(message, CdmBase.deproxy(referencingObject, DerivedUnit.class).getTitleCache());
943 }
944 //DescriptionElementSource#nameUsedInSource
945 if (referencingObject.isInstanceOf(DescriptionElementSource.class)){
946 String message = "Name can't be deleted as it is used as descriptionElementSource#nameUsedInSource";
947 }
948 //NameTypeDesignation#typeName
949 if (referencingObject.isInstanceOf(NameTypeDesignation.class)){
950 String message = "Name can't be deleted as it is used as a name type in a NameTypeDesignation";
951 }
952 }
953 deleteResult = delete(specimen);
954 }
955 else{
956 //TODO implement deep delete
957 }
958 return deleteResult;
959 }
960
961 /* (non-Javadoc)
962 * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#deleteDerivateHierarchy(eu.etaxonomy.cdm.model.common.ICdmBase)
963 */
964 @Override
965 public DeleteResult deleteDerivateHierarchy(ICdmBase from, SpecimenDeleteConfigurator config) {
966 DeleteResult deleteResult = new DeleteResult();
967 if(from instanceof Sequence){
968 Sequence sequence = (Sequence)from;
969 sequence.getDnaSample().removeSequence(sequence);
970 deleteResult.setStatus(DeleteStatus.OK);
971 }
972 else if(from instanceof SpecimenOrObservationBase<?>) {
973 deleteResult = delete((SpecimenOrObservationBase<?>) from, config);
974 }
975 return deleteResult;
976 }
977
978 private Set<ICdmBase> collectEntitiesToDelete(ICdmBase entity){
979 Set<ICdmBase> entitiesToDelete = new LinkedHashSet<ICdmBase>();
980
981 if(entity instanceof SpecimenOrObservationBase<?>){
982 SpecimenOrObservationBase<?> specimen = (SpecimenOrObservationBase<?>) entity;
983 if(entity instanceof DerivedUnit){
984 DerivedUnit derivedUnit = (DerivedUnit)entity;
985 DerivationEvent derivedFrom = derivedUnit.getDerivedFrom();
986 Set<SpecimenOrObservationBase> originals = derivedFrom.getOriginals();
987 for (SpecimenOrObservationBase<?> original: originals) {
988 original.removeDerivationEvent(derivedFrom);
989 // saveOrUpdate(original);
990 }
991 }
992 if(entity instanceof DnaSample && ((DnaSample) entity).getRecordBasis()==SpecimenOrObservationType.DnaSample){
993 DnaSample dnaSample = (DnaSample)entity;
994 for (Sequence sequence : dnaSample.getSequences()) {
995 entitiesToDelete.addAll(collectEntitiesToDelete(sequence));
996 dnaSample.removeSequence(sequence);
997 // saveOrUpdate(dnaSample);
998 }
999 }
1000 Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1001 for (DerivationEvent derivationEvent : derivationEvents) {
1002 for (DerivedUnit derivedUnit : derivationEvent.getDerivatives()) {
1003 entitiesToDelete.addAll(collectEntitiesToDelete(derivedUnit));
1004 }
1005 }
1006 }
1007 else if(entity instanceof Sequence){
1008 Sequence sequence = (Sequence)entity;
1009 for (SingleRead singleRead : sequence.getSingleReads()) {
1010 entitiesToDelete.addAll(collectEntitiesToDelete(singleRead));
1011 sequence.removeSingleRead(singleRead);
1012 }
1013 // sequenceService.saveOrUpdate(sequence);
1014 }
1015 entitiesToDelete.add(entity);
1016 return entitiesToDelete;
1017 }
1018
1019 }