merging delete functionality into trunk
[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.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.HashMap;
17 import java.util.HashSet;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Set;
21
22 import org.apache.log4j.Logger;
23 import org.apache.lucene.index.CorruptIndexException;
24 import org.apache.lucene.queryParser.ParseException;
25 import org.apache.lucene.search.BooleanClause.Occur;
26 import org.apache.lucene.search.BooleanQuery;
27 import org.apache.lucene.search.SortField;
28 import org.hibernate.search.spatial.impl.Rectangle;
29 import org.springframework.beans.factory.annotation.Autowired;
30 import org.springframework.stereotype.Service;
31 import org.springframework.transaction.annotation.Transactional;
32
33 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade;
34 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeConfigurator;
35 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeNotSupportedException;
36 import eu.etaxonomy.cdm.api.service.pager.Pager;
37 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
38 import eu.etaxonomy.cdm.api.service.search.ILuceneIndexToolProvider;
39 import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;
40 import eu.etaxonomy.cdm.api.service.search.LuceneSearch;
41 import eu.etaxonomy.cdm.api.service.search.LuceneSearch.TopGroupsWithMaxScore;
42 import eu.etaxonomy.cdm.api.service.search.QueryFactory;
43 import eu.etaxonomy.cdm.api.service.search.SearchResult;
44 import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;
45 import eu.etaxonomy.cdm.api.service.util.TaxonRelationshipEdge;
46 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
47 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
48 import eu.etaxonomy.cdm.model.CdmBaseType;
49 import eu.etaxonomy.cdm.model.common.DefinedTermBase;
50 import eu.etaxonomy.cdm.model.common.Language;
51 import eu.etaxonomy.cdm.model.common.UuidAndTitleCache;
52 import eu.etaxonomy.cdm.model.description.DescriptionBase;
53 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
54 import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
55 import eu.etaxonomy.cdm.model.location.Country;
56 import eu.etaxonomy.cdm.model.media.Media;
57 import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;
58 import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
59 import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
60 import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
61 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
62 import eu.etaxonomy.cdm.model.taxon.Taxon;
63 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
64 import eu.etaxonomy.cdm.persistence.dao.common.IDefinedTermDao;
65 import eu.etaxonomy.cdm.persistence.dao.initializer.AbstractBeanInitializer;
66 import eu.etaxonomy.cdm.persistence.dao.occurrence.IOccurrenceDao;
67 import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
68 import eu.etaxonomy.cdm.persistence.query.OrderHint;
69 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
70
71 /**
72 * @author a.babadshanjan
73 * @created 01.09.2008
74 */
75 @Service
76 @Transactional(readOnly = true)
77 public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObservationBase,IOccurrenceDao> implements IOccurrenceService {
78
79 static private final Logger logger = Logger.getLogger(OccurrenceServiceImpl.class);
80
81 @Autowired
82 private IDefinedTermDao definedTermDao;
83
84 @Autowired
85 private IDescriptionService descriptionService;
86
87 @Autowired
88 private ITaxonService taxonService;
89
90 @Autowired
91 private AbstractBeanInitializer beanInitializer;
92
93 @Autowired
94 private ITaxonDao taxonDao;
95
96 @Autowired
97 private ILuceneIndexToolProvider luceneIndexToolProvider;
98
99
100 public OccurrenceServiceImpl() {
101 logger.debug("Load OccurrenceService Bean");
102 }
103
104
105 /* (non-Javadoc)
106 * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
107 */
108 @Override
109 @Transactional(readOnly = false)
110 public void updateTitleCache(Class<? extends SpecimenOrObservationBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<SpecimenOrObservationBase> cacheStrategy, IProgressMonitor monitor) {
111 if (clazz == null){
112 clazz = SpecimenOrObservationBase.class;
113 }
114 super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
115 }
116
117
118 /**
119 * FIXME Candidate for harmonization
120 * move to termService
121 */
122 @Override
123 public Country getCountryByIso(String iso639) {
124 return this.definedTermDao.getCountryByIso(iso639);
125
126 }
127
128 /**
129 * FIXME Candidate for harmonization
130 * move to termService
131 */
132 @Override
133 public List<Country> getCountryByName(String name) {
134 List<? extends DefinedTermBase> terms = this.definedTermDao.findByTitle(Country.class, name, null, null, null, null, null, null) ;
135 List<Country> countries = new ArrayList<Country>();
136 for (int i=0;i<terms.size();i++){
137 countries.add((Country)terms.get(i));
138 }
139 return countries;
140 }
141
142 @Override
143 @Autowired
144 protected void setDao(IOccurrenceDao dao) {
145 this.dao = dao;
146 }
147
148 @Override
149 public Pager<DerivationEvent> getDerivationEvents(SpecimenOrObservationBase occurence, Integer pageSize,Integer pageNumber, List<String> propertyPaths) {
150 Integer numberOfResults = dao.countDerivationEvents(occurence);
151
152 List<DerivationEvent> results = new ArrayList<DerivationEvent>();
153 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
154 results = dao.getDerivationEvents(occurence, pageSize, pageNumber,propertyPaths);
155 }
156
157 return new DefaultPagerImpl<DerivationEvent>(pageNumber, numberOfResults, pageSize, results);
158 }
159
160 @Override
161 public Pager<DeterminationEvent> getDeterminations(SpecimenOrObservationBase occurrence, TaxonBase taxonBase, Integer pageSize,Integer pageNumber, List<String> propertyPaths) {
162 Integer numberOfResults = dao.countDeterminations(occurrence, taxonBase);
163
164 List<DeterminationEvent> results = new ArrayList<DeterminationEvent>();
165 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
166 results = dao.getDeterminations(occurrence,taxonBase, pageSize, pageNumber, propertyPaths);
167 }
168
169 return new DefaultPagerImpl<DeterminationEvent>(pageNumber, numberOfResults, pageSize, results);
170 }
171
172 @Override
173 public Pager<Media> getMedia(SpecimenOrObservationBase occurence,Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
174 Integer numberOfResults = dao.countMedia(occurence);
175
176 List<Media> results = new ArrayList<Media>();
177 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
178 results = dao.getMedia(occurence, pageSize, pageNumber, propertyPaths);
179 }
180
181 return new DefaultPagerImpl<Media>(pageNumber, numberOfResults, pageSize, results);
182 }
183
184 /* (non-Javadoc)
185 * @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)
186 */
187 @Override
188 public Pager<SpecimenOrObservationBase> list(Class<? extends SpecimenOrObservationBase> type, TaxonBase determinedAs, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
189 Integer numberOfResults = dao.count(type,determinedAs);
190 List<SpecimenOrObservationBase> results = new ArrayList<SpecimenOrObservationBase>();
191 pageNumber = pageNumber == null ? 0 : pageNumber;
192 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
193 Integer start = pageSize == null ? 0 : pageSize * pageNumber;
194 results = dao.list(type,determinedAs, pageSize, start, orderHints,propertyPaths);
195 }
196 return new DefaultPagerImpl<SpecimenOrObservationBase>(pageNumber, numberOfResults, pageSize, results);
197 }
198
199 @Override
200 public List<UuidAndTitleCache<DerivedUnit>> getDerivedUnitUuidAndTitleCache() {
201 return dao.getDerivedUnitUuidAndTitleCache();
202 }
203
204 @Override
205 public List<UuidAndTitleCache<FieldUnit>> getFieldUnitUuidAndTitleCache() {
206 return dao.getFieldUnitUuidAndTitleCache();
207 }
208
209 /* (non-Javadoc)
210 * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#getDerivedUnitFacade(eu.etaxonomy.cdm.model.occurrence.DerivedUnit)
211 */
212 @Override
213 public DerivedUnitFacade getDerivedUnitFacade(DerivedUnit derivedUnit, List<String> propertyPaths) throws DerivedUnitFacadeNotSupportedException {
214 derivedUnit = (DerivedUnit)dao.load(derivedUnit.getUuid(), null);
215 DerivedUnitFacadeConfigurator config = DerivedUnitFacadeConfigurator.NewInstance();
216 config.setThrowExceptionForNonSpecimenPreservationMethodRequest(false);
217 DerivedUnitFacade derivedUnitFacade = DerivedUnitFacade.NewInstance(derivedUnit, config);
218 beanInitializer.initialize(derivedUnitFacade, propertyPaths);
219 return derivedUnitFacade;
220 }
221
222 /* (non-Javadoc)
223 * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#listDerivedUnitFacades(eu.etaxonomy.cdm.model.description.DescriptionBase, java.util.List)
224 */
225 @Override
226 public List<DerivedUnitFacade> listDerivedUnitFacades(
227 DescriptionBase description, List<String> propertyPaths) {
228
229 List<DerivedUnitFacade> derivedUnitFacadeList = new ArrayList<DerivedUnitFacade>();
230 IndividualsAssociation tempIndividualsAssociation;
231 SpecimenOrObservationBase tempSpecimenOrObservationBase;
232 List<DescriptionElementBase> elements = descriptionService.listDescriptionElements(description, null, IndividualsAssociation.class, null, 0, Arrays.asList(new String []{"associatedSpecimenOrObservation"}));
233 for(DescriptionElementBase element : elements){
234 if(element instanceof IndividualsAssociation){
235 tempIndividualsAssociation = (IndividualsAssociation)element;
236 if(tempIndividualsAssociation.getAssociatedSpecimenOrObservation() != null){
237 tempSpecimenOrObservationBase = HibernateProxyHelper.deproxy(tempIndividualsAssociation.getAssociatedSpecimenOrObservation(), SpecimenOrObservationBase.class);
238 if(tempSpecimenOrObservationBase instanceof DerivedUnit){
239 try {
240 derivedUnitFacadeList.add(DerivedUnitFacade.NewInstance((DerivedUnit)tempSpecimenOrObservationBase));
241 } catch (DerivedUnitFacadeNotSupportedException e) {
242 logger.warn(tempIndividualsAssociation.getAssociatedSpecimenOrObservation().getTitleCache() + " : " +e.getMessage());
243 }
244 }
245 }
246
247 }
248 }
249
250 beanInitializer.initializeAll(derivedUnitFacadeList, propertyPaths);
251
252 return derivedUnitFacadeList;
253 }
254
255
256 /* (non-Javadoc)
257 * @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)
258 */
259 @Override
260 public <T extends SpecimenOrObservationBase> List<T> listByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
261 Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
262
263 return pageByAssociatedTaxon(type, includeRelationships, associatedTaxon, maxDepth, pageSize, pageNumber, orderHints, propertyPaths).getRecords();
264 }
265
266
267 /* (non-Javadoc)
268 * @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)
269 */
270 @SuppressWarnings("unchecked")
271 @Override
272 public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
273 Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
274
275 Set<Taxon> taxa = new HashSet<Taxon>();
276 Set<Integer> occurrenceIds = new HashSet<Integer>();
277 List<T> occurrences = new ArrayList<T>();
278
279 // Integer limit = PagerUtils.limitFor(pageSize);
280 // Integer start = PagerUtils.startFor(pageSize, pageNumber);
281
282 associatedTaxon = (Taxon) taxonDao.load(associatedTaxon.getUuid());
283
284 if(includeRelationships != null) {
285 taxa = taxonService.listRelatedTaxa(associatedTaxon, includeRelationships, maxDepth, null, null, propertyPaths);
286 }
287
288 taxa.add(associatedTaxon);
289
290 for (Taxon taxon : taxa) {
291 List<T> perTaxonOccurrences = dao.listByAssociatedTaxon(type, taxon, null, null, orderHints, propertyPaths);
292 for (SpecimenOrObservationBase o : perTaxonOccurrences) {
293 occurrenceIds.add(o.getId());
294 }
295 }
296
297 occurrences = (List<T>) dao.listByIds(occurrenceIds, pageSize, pageNumber, orderHints, propertyPaths);
298
299 return new DefaultPagerImpl<T>(pageNumber, occurrenceIds.size(), pageSize, occurrences);
300
301 }
302
303 @Override
304 public Pager<SearchResult<SpecimenOrObservationBase>> findByFullText(
305 Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle boundingBox, List<Language> languages,
306 boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
307 List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {
308
309 LuceneSearch luceneSearch = prepareByFullTextSearch(clazz, queryString, boundingBox, languages, highlightFragments);
310
311 // --- execute search
312 TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
313
314 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
315 idFieldMap.put(CdmBaseType.SPECIMEN_OR_OBSERVATIONBASE, "id");
316
317 // --- initialize taxa, highlight matches ....
318 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
319 @SuppressWarnings("rawtypes")
320 List<SearchResult<SpecimenOrObservationBase>> searchResults = searchResultBuilder.createResultSet(
321 topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
322
323 int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;
324
325 return new DefaultPagerImpl<SearchResult<SpecimenOrObservationBase>>(pageNumber, totalHits, pageSize,
326 searchResults);
327
328 }
329
330
331 /**
332 * @param clazz
333 * @param queryString
334 * @param languages
335 * @param highlightFragments
336 * @return
337 */
338 private LuceneSearch prepareByFullTextSearch(Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle bbox,
339 List<Language> languages, boolean highlightFragments) {
340
341 BooleanQuery finalQuery = new BooleanQuery();
342 BooleanQuery textQuery = new BooleanQuery();
343
344 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, FieldUnit.class);
345 QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(FieldUnit.class);
346
347 // --- criteria
348 luceneSearch.setCdmTypRestriction(clazz);
349 if(queryString != null){
350 textQuery.add(queryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);
351 finalQuery.add(textQuery, Occur.MUST);
352 }
353
354 // --- spacial query
355 if(bbox != null){
356 finalQuery.add(QueryFactory.buildSpatialQueryByRange(bbox, "gatheringEvent.exactLocation.point"), Occur.MUST);
357 }
358
359 luceneSearch.setQuery(finalQuery);
360
361 // --- sorting
362 SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
363 luceneSearch.setSortFields(sortFields);
364
365 if(highlightFragments){
366 luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
367 }
368 return luceneSearch;
369 }
370
371 }