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