- no change (just updated "last edited" for svn)
[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.Collection;
17 import java.util.HashMap;
18 import java.util.HashSet;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Set;
22 import java.util.UUID;
23
24 import org.apache.log4j.Logger;
25 import org.apache.lucene.index.CorruptIndexException;
26 import org.apache.lucene.queryParser.ParseException;
27 import org.apache.lucene.search.BooleanClause.Occur;
28 import org.apache.lucene.search.BooleanQuery;
29 import org.apache.lucene.search.SortField;
30 import org.hibernate.TransientObjectException;
31 import org.hibernate.search.spatial.impl.Rectangle;
32 import org.springframework.beans.factory.annotation.Autowired;
33 import org.springframework.stereotype.Service;
34 import org.springframework.transaction.annotation.Transactional;
35
36 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade;
37 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeConfigurator;
38 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeNotSupportedException;
39 import eu.etaxonomy.cdm.api.service.molecular.ISequenceService;
40 import eu.etaxonomy.cdm.api.service.pager.Pager;
41 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
42 import eu.etaxonomy.cdm.api.service.search.ILuceneIndexToolProvider;
43 import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;
44 import eu.etaxonomy.cdm.api.service.search.LuceneSearch;
45 import eu.etaxonomy.cdm.api.service.search.LuceneSearch.TopGroupsWithMaxScore;
46 import eu.etaxonomy.cdm.api.service.search.QueryFactory;
47 import eu.etaxonomy.cdm.api.service.search.SearchResult;
48 import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;
49 import eu.etaxonomy.cdm.api.service.util.TaxonRelationshipEdge;
50 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
51 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
52 import eu.etaxonomy.cdm.model.CdmBaseType;
53 import eu.etaxonomy.cdm.model.common.DefinedTerm;
54 import eu.etaxonomy.cdm.model.common.DefinedTermBase;
55 import eu.etaxonomy.cdm.model.common.ICdmBase;
56 import eu.etaxonomy.cdm.model.common.Language;
57 import eu.etaxonomy.cdm.model.common.UuidAndTitleCache;
58 import eu.etaxonomy.cdm.model.description.DescriptionBase;
59 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
60 import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
61 import eu.etaxonomy.cdm.model.location.Country;
62 import eu.etaxonomy.cdm.model.location.NamedArea;
63 import eu.etaxonomy.cdm.model.media.Media;
64 import eu.etaxonomy.cdm.model.molecular.DnaSample;
65 import eu.etaxonomy.cdm.model.molecular.Sequence;
66 import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;
67 import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
68 import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
69 import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
70 import eu.etaxonomy.cdm.model.occurrence.GatheringEvent;
71 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
72 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;
73 import eu.etaxonomy.cdm.model.taxon.Taxon;
74 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
75 import eu.etaxonomy.cdm.persistence.dao.common.IDefinedTermDao;
76 import eu.etaxonomy.cdm.persistence.dao.initializer.AbstractBeanInitializer;
77 import eu.etaxonomy.cdm.persistence.dao.occurrence.IOccurrenceDao;
78 import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
79 import eu.etaxonomy.cdm.persistence.query.OrderHint;
80 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
81
82 /**
83 * @author a.babadshanjan
84 * @created 01.09.2008
85 */
86 @Service
87 @Transactional(readOnly = true)
88 public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObservationBase,IOccurrenceDao> implements IOccurrenceService {
89
90 static private final Logger logger = Logger.getLogger(OccurrenceServiceImpl.class);
91
92 @Autowired
93 private IDefinedTermDao definedTermDao;
94
95 @Autowired
96 private IDescriptionService descriptionService;
97
98 @Autowired
99 private ITaxonService taxonService;
100
101 @Autowired
102 private ITermService termService;
103
104 @Autowired
105 private INameService nameService;
106
107 @Autowired
108 private ISequenceService sequenceService;
109
110 @Autowired
111 private AbstractBeanInitializer beanInitializer;
112
113 @Autowired
114 private ITaxonDao taxonDao;
115
116 @Autowired
117 private ILuceneIndexToolProvider luceneIndexToolProvider;
118
119
120 public OccurrenceServiceImpl() {
121 logger.debug("Load OccurrenceService Bean");
122 }
123
124
125 /* (non-Javadoc)
126 * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
127 */
128 @Override
129 @Transactional(readOnly = false)
130 public void updateTitleCache(Class<? extends SpecimenOrObservationBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<SpecimenOrObservationBase> cacheStrategy, IProgressMonitor monitor) {
131 if (clazz == null){
132 clazz = SpecimenOrObservationBase.class;
133 }
134 super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
135 }
136
137
138 /**
139 * FIXME Candidate for harmonization
140 * move to termService
141 */
142 @Override
143 public Country getCountryByIso(String iso639) {
144 return this.definedTermDao.getCountryByIso(iso639);
145
146 }
147
148 /**
149 * FIXME Candidate for harmonization
150 * move to termService
151 */
152 @Override
153 public List<Country> getCountryByName(String name) {
154 List<? extends DefinedTermBase> terms = this.definedTermDao.findByTitle(Country.class, name, null, null, null, null, null, null) ;
155 List<Country> countries = new ArrayList<Country>();
156 for (int i=0;i<terms.size();i++){
157 countries.add((Country)terms.get(i));
158 }
159 return countries;
160 }
161
162 @Override
163 @Autowired
164 protected void setDao(IOccurrenceDao dao) {
165 this.dao = dao;
166 }
167
168 @Override
169 public Pager<DerivationEvent> getDerivationEvents(SpecimenOrObservationBase occurence, Integer pageSize,Integer pageNumber, List<String> propertyPaths) {
170 Integer numberOfResults = dao.countDerivationEvents(occurence);
171
172 List<DerivationEvent> results = new ArrayList<DerivationEvent>();
173 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
174 results = dao.getDerivationEvents(occurence, pageSize, pageNumber,propertyPaths);
175 }
176
177 return new DefaultPagerImpl<DerivationEvent>(pageNumber, numberOfResults, pageSize, results);
178 }
179
180 /* (non-Javadoc)
181 * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#countDeterminations(eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase, eu.etaxonomy.cdm.model.taxon.TaxonBase)
182 */
183 @Override
184 public int countDeterminations(SpecimenOrObservationBase occurence, TaxonBase taxonbase) {
185 return dao.countDeterminations(occurence, taxonbase);
186 }
187
188 @Override
189 public Pager<DeterminationEvent> getDeterminations(SpecimenOrObservationBase occurrence, TaxonBase taxonBase, Integer pageSize,Integer pageNumber, List<String> propertyPaths) {
190 Integer numberOfResults = dao.countDeterminations(occurrence, taxonBase);
191
192 List<DeterminationEvent> results = new ArrayList<DeterminationEvent>();
193 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
194 results = dao.getDeterminations(occurrence,taxonBase, pageSize, pageNumber, propertyPaths);
195 }
196
197 return new DefaultPagerImpl<DeterminationEvent>(pageNumber, numberOfResults, pageSize, results);
198 }
199
200 @Override
201 public Pager<Media> getMedia(SpecimenOrObservationBase occurence,Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
202 Integer numberOfResults = dao.countMedia(occurence);
203
204 List<Media> results = new ArrayList<Media>();
205 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
206 results = dao.getMedia(occurence, pageSize, pageNumber, propertyPaths);
207 }
208
209 return new DefaultPagerImpl<Media>(pageNumber, numberOfResults, pageSize, results);
210 }
211
212 /* (non-Javadoc)
213 * @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)
214 */
215 @Override
216 public Pager<SpecimenOrObservationBase> list(Class<? extends SpecimenOrObservationBase> type, TaxonBase determinedAs, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
217 Integer numberOfResults = dao.count(type,determinedAs);
218 List<SpecimenOrObservationBase> results = new ArrayList<SpecimenOrObservationBase>();
219 pageNumber = pageNumber == null ? 0 : pageNumber;
220 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
221 Integer start = pageSize == null ? 0 : pageSize * pageNumber;
222 results = dao.list(type,determinedAs, pageSize, start, orderHints,propertyPaths);
223 }
224 return new DefaultPagerImpl<SpecimenOrObservationBase>(pageNumber, numberOfResults, pageSize, results);
225 }
226
227 @Override
228 public List<UuidAndTitleCache<DerivedUnit>> getDerivedUnitUuidAndTitleCache() {
229 return dao.getDerivedUnitUuidAndTitleCache();
230 }
231
232 @Override
233 public List<UuidAndTitleCache<FieldUnit>> getFieldUnitUuidAndTitleCache() {
234 return dao.getFieldUnitUuidAndTitleCache();
235 }
236
237 /* (non-Javadoc)
238 * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#getDerivedUnitFacade(eu.etaxonomy.cdm.model.occurrence.DerivedUnit)
239 */
240 @Override
241 public DerivedUnitFacade getDerivedUnitFacade(DerivedUnit derivedUnit, List<String> propertyPaths) throws DerivedUnitFacadeNotSupportedException {
242 derivedUnit = (DerivedUnit)dao.load(derivedUnit.getUuid(), null);
243 DerivedUnitFacadeConfigurator config = DerivedUnitFacadeConfigurator.NewInstance();
244 config.setThrowExceptionForNonSpecimenPreservationMethodRequest(false);
245 DerivedUnitFacade derivedUnitFacade = DerivedUnitFacade.NewInstance(derivedUnit, config);
246 beanInitializer.initialize(derivedUnitFacade, propertyPaths);
247 return derivedUnitFacade;
248 }
249
250 /* (non-Javadoc)
251 * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#listDerivedUnitFacades(eu.etaxonomy.cdm.model.description.DescriptionBase, java.util.List)
252 */
253 @Override
254 public List<DerivedUnitFacade> listDerivedUnitFacades(
255 DescriptionBase description, List<String> propertyPaths) {
256
257 List<DerivedUnitFacade> derivedUnitFacadeList = new ArrayList<DerivedUnitFacade>();
258 IndividualsAssociation tempIndividualsAssociation;
259 SpecimenOrObservationBase tempSpecimenOrObservationBase;
260 List<DescriptionElementBase> elements = descriptionService.listDescriptionElements(description, null, IndividualsAssociation.class, null, 0, Arrays.asList(new String []{"associatedSpecimenOrObservation"}));
261 for(DescriptionElementBase element : elements){
262 if(element instanceof IndividualsAssociation){
263 tempIndividualsAssociation = (IndividualsAssociation)element;
264 if(tempIndividualsAssociation.getAssociatedSpecimenOrObservation() != null){
265 tempSpecimenOrObservationBase = HibernateProxyHelper.deproxy(tempIndividualsAssociation.getAssociatedSpecimenOrObservation(), SpecimenOrObservationBase.class);
266 if(tempSpecimenOrObservationBase instanceof DerivedUnit){
267 try {
268 derivedUnitFacadeList.add(DerivedUnitFacade.NewInstance((DerivedUnit)tempSpecimenOrObservationBase));
269 } catch (DerivedUnitFacadeNotSupportedException e) {
270 logger.warn(tempIndividualsAssociation.getAssociatedSpecimenOrObservation().getTitleCache() + " : " +e.getMessage());
271 }
272 }
273 }
274
275 }
276 }
277
278 beanInitializer.initializeAll(derivedUnitFacadeList, propertyPaths);
279
280 return derivedUnitFacadeList;
281 }
282
283
284 /* (non-Javadoc)
285 * @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)
286 */
287 @Override
288 public <T extends SpecimenOrObservationBase> List<T> listByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
289 Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
290
291 return pageByAssociatedTaxon(type, includeRelationships, associatedTaxon, maxDepth, pageSize, pageNumber, orderHints, propertyPaths).getRecords();
292 }
293
294
295 /* (non-Javadoc)
296 * @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)
297 */
298 @SuppressWarnings("unchecked")
299 @Override
300 public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
301 Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
302
303 Set<Taxon> taxa = new HashSet<Taxon>();
304 Set<Integer> occurrenceIds = new HashSet<Integer>();
305 List<T> occurrences = new ArrayList<T>();
306
307 // Integer limit = PagerUtils.limitFor(pageSize);
308 // Integer start = PagerUtils.startFor(pageSize, pageNumber);
309
310 associatedTaxon = (Taxon) taxonDao.load(associatedTaxon.getUuid());
311
312 if(includeRelationships != null) {
313 taxa = taxonService.listRelatedTaxa(associatedTaxon, includeRelationships, maxDepth, null, null, propertyPaths);
314 }
315
316 taxa.add(associatedTaxon);
317
318 for (Taxon taxon : taxa) {
319 List<T> perTaxonOccurrences = dao.listByAssociatedTaxon(type, taxon, null, null, orderHints, propertyPaths);
320 for (SpecimenOrObservationBase o : perTaxonOccurrences) {
321 occurrenceIds.add(o.getId());
322 }
323 }
324 occurrences = (List<T>) dao.listByIds(occurrenceIds, pageSize, pageNumber, orderHints, propertyPaths);
325
326 return new DefaultPagerImpl<T>(pageNumber, occurrenceIds.size(), pageSize, occurrences);
327
328 }
329
330 /* (non-Javadoc)
331 * @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)
332 */
333 @SuppressWarnings("unchecked")
334 @Override
335 public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
336 String taxonUUID, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
337
338 UUID uuid = UUID.fromString(taxonUUID);
339 Taxon tax = (Taxon) taxonDao.load(uuid);
340 //TODO REMOVE NULL STATEMENT
341 type=null;
342 return pageByAssociatedTaxon( type,includeRelationships,tax, maxDepth, pageSize, pageNumber, orderHints, propertyPaths );
343
344 }
345
346
347 @Override
348 public Pager<SearchResult<SpecimenOrObservationBase>> findByFullText(
349 Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle boundingBox, List<Language> languages,
350 boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
351 List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {
352
353 LuceneSearch luceneSearch = prepareByFullTextSearch(clazz, queryString, boundingBox, languages, highlightFragments);
354
355 // --- execute search
356 TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
357
358 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
359 idFieldMap.put(CdmBaseType.SPECIMEN_OR_OBSERVATIONBASE, "id");
360
361 // --- initialize taxa, highlight matches ....
362 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
363 @SuppressWarnings("rawtypes")
364 List<SearchResult<SpecimenOrObservationBase>> searchResults = searchResultBuilder.createResultSet(
365 topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
366
367 int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;
368
369 return new DefaultPagerImpl<SearchResult<SpecimenOrObservationBase>>(pageNumber, totalHits, pageSize,
370 searchResults);
371
372 }
373
374
375 /**
376 * @param clazz
377 * @param queryString
378 * @param languages
379 * @param highlightFragments
380 * @return
381 */
382 private LuceneSearch prepareByFullTextSearch(Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle bbox,
383 List<Language> languages, boolean highlightFragments) {
384
385 BooleanQuery finalQuery = new BooleanQuery();
386 BooleanQuery textQuery = new BooleanQuery();
387
388 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, FieldUnit.class);
389 QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(FieldUnit.class);
390
391 // --- criteria
392 luceneSearch.setCdmTypRestriction(clazz);
393 if(queryString != null){
394 textQuery.add(queryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);
395 finalQuery.add(textQuery, Occur.MUST);
396 }
397
398 // --- spacial query
399 if(bbox != null){
400 finalQuery.add(QueryFactory.buildSpatialQueryByRange(bbox, "gatheringEvent.exactLocation.point"), Occur.MUST);
401 }
402
403 luceneSearch.setQuery(finalQuery);
404
405 // --- sorting
406 SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
407 luceneSearch.setSortFields(sortFields);
408
409 if(highlightFragments){
410 luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
411 }
412 return luceneSearch;
413 }
414
415
416 /* (non-Javadoc)
417 * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#getFieldUnits(eu.etaxonomy.cdm.model.occurrence.DerivedUnit)
418 */
419 @Override
420 public Collection<FieldUnit> getFieldUnits(UUID derivedUnitUuid) {
421 //It will search recursively over all {@link DerivationEvent}s and get the "originals" ({@link SpecimenOrObservationBase})
422 //from which this DerivedUnit was derived until all FieldUnits are found.
423
424 //FIXME: use HQL queries to increase performance
425 Collection<FieldUnit> fieldUnits = new ArrayList<FieldUnit>();
426 SpecimenOrObservationBase derivedUnit = load(derivedUnitUuid);
427 if(derivedUnit instanceof DerivedUnit){
428 getFieldUnits((DerivedUnit) derivedUnit, fieldUnits);
429 }
430 return fieldUnits;
431 }
432
433
434 /**
435 * @param original
436 * @param fieldUnits
437 */
438 private void getFieldUnits(DerivedUnit derivedUnit, Collection<FieldUnit> fieldUnits) {
439 Set<SpecimenOrObservationBase> originals = derivedUnit.getOriginals();
440 if(originals!=null && !originals.isEmpty()){
441 for(SpecimenOrObservationBase<?> original:originals){
442 if(original instanceof FieldUnit){
443 fieldUnits.add((FieldUnit) original);
444 }
445 else if(original instanceof DerivedUnit){
446 getFieldUnits((DerivedUnit) original, fieldUnits);
447 }
448 }
449 }
450 }
451
452 /* (non-Javadoc)
453 * @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)
454 */
455 @Override
456 public boolean moveSequence(DnaSample from, DnaSample to, Sequence sequence) {
457 //reload specimens to avoid session conflicts
458 from = (DnaSample) load(from.getUuid());
459 to = (DnaSample) load(to.getUuid());
460 sequence = sequenceService.load(sequence.getUuid());
461
462 if(from==null || to==null || sequence==null){
463 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" +
464 "Operation was move "+sequence+ " from "+from+" to "+to);
465 }
466 from.removeSequence(sequence);
467 saveOrUpdate(from);
468 to.addSequence(sequence);
469 saveOrUpdate(to);
470 return true;
471 }
472
473 /* (non-Javadoc)
474 * @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)
475 */
476 @Override
477 public boolean moveDerivate(SpecimenOrObservationBase<?> from, SpecimenOrObservationBase<?> to, DerivedUnit derivate) {
478 //reload specimens to avoid session conflicts
479 from = load(from.getUuid());
480 to = load(to.getUuid());
481 derivate = (DerivedUnit) load(derivate.getUuid());
482
483 if(from==null || to==null || derivate==null){
484 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" +
485 "Operation was move "+derivate+ " from "+from+" to "+to);
486 }
487
488 SpecimenOrObservationType derivateType = derivate.getRecordBasis();
489 SpecimenOrObservationType toType = to.getRecordBasis();
490 //check if type is a sub derivate type
491 if(toType==SpecimenOrObservationType.FieldUnit //moving to FieldUnit always works
492 || derivateType==SpecimenOrObservationType.Media //moving media always works
493 || (derivateType.isKindOf(toType) && toType!=derivateType)){ //moving only to parent derivate type
494 //remove derivation event from parent specimen of dragged object
495 DerivationEvent eventToRemove = null;
496 for(DerivationEvent event:from.getDerivationEvents()){
497 if(event.getDerivatives().contains(derivate)){
498 eventToRemove = event;
499 break;
500 }
501 }
502 from.removeDerivationEvent(eventToRemove);
503 saveOrUpdate(from);
504 //add new derivation event to target
505 to.addDerivationEvent(DerivationEvent.NewSimpleInstance(to, derivate, eventToRemove==null?null:eventToRemove.getType()));
506 saveOrUpdate(to);
507 return true;
508 }
509 return false;
510 }
511
512 @Override
513 public Collection<ICdmBase> getNonCascadedAssociatedElements(SpecimenOrObservationBase<?> specimen){
514 //potential fields that are not persisted cascadingly
515 /*
516 * SOOB
517 -DescriptionBase
518 -determinations
519 --modifier TERM
520 -kindOfUnit TERM
521 -lifeStage TERM
522 -sex TERM
523
524 FieldUnit
525 -GatheringEvent
526 --Country TERM
527 --CollectingAreas TERM
528
529 DerivedUnit
530 -collection
531 --institute
532 ---types TERM
533 -preservationMethod
534 --medium TERM
535 -storedUnder CDM TaxonNameBase
536 */
537
538 Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<ICdmBase>();
539 //scan SpecimenOrObservationBase
540 for(DeterminationEvent determinationEvent:specimen.getDeterminations()){
541 //modifier
542 if(determinationEvent.getModifier()!=null){
543 nonCascadedCdmEntities.add(determinationEvent.getModifier());
544 }
545 }
546 //kindOfUnit
547 if(specimen.getKindOfUnit()!=null){
548 nonCascadedCdmEntities.add(specimen.getKindOfUnit());
549 }
550 //lifeStage
551 if(specimen.getLifeStage()!=null){
552 nonCascadedCdmEntities.add(specimen.getLifeStage());
553 }
554 //sex
555 if(specimen.getSex()!=null){
556 nonCascadedCdmEntities.add(specimen.getSex());
557 }
558
559 //FieldUnit
560 if(specimen instanceof FieldUnit){
561 FieldUnit fieldUnit = (FieldUnit)specimen;
562 GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent();
563 if(gatheringEvent!=null){
564 //country
565 if(gatheringEvent.getCountry()!=null){
566 nonCascadedCdmEntities.add(gatheringEvent.getCountry());
567 }
568 //collecting areas
569 for (NamedArea namedArea : gatheringEvent.getCollectingAreas()) {
570 nonCascadedCdmEntities.add(namedArea);
571 }
572 }
573 for (DerivationEvent derivationEvent : fieldUnit.getDerivationEvents()) {
574 for (DerivedUnit derivedUnit : derivationEvent.getDerivatives()) {
575 nonCascadedCdmEntities.addAll(getNonCascadedAssociatedElements(derivedUnit));
576 }
577 }
578 }
579
580 //DerivedUnit
581 else if(specimen instanceof DerivedUnit){
582 DerivedUnit derivedUnit = (DerivedUnit)specimen;
583 if(derivedUnit.getCollection()!=null && derivedUnit.getCollection().getInstitute()!=null){
584 for (DefinedTerm type : derivedUnit.getCollection().getInstitute().getTypes()) {
585 nonCascadedCdmEntities.add(type);
586 }
587 }
588 if(derivedUnit.getPreservation()!=null && derivedUnit.getPreservation().getMedium()!=null){
589 nonCascadedCdmEntities.add(derivedUnit.getPreservation().getMedium());
590 }
591 if(derivedUnit.getStoredUnder()!=null){
592 nonCascadedCdmEntities.add(derivedUnit.getStoredUnder());
593 }
594 }
595 return nonCascadedCdmEntities;
596 }
597
598 }