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