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