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