X-Git-Url: https://dev.e-taxonomy.eu/gitweb/cdmlib.git/blobdiff_plain/4794837a93289f3b8e8256f6a94577c5278ba666..ad80fdc31885697feed8d22235415bedff69c30b:/cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/OccurrenceServiceImpl.java diff --git a/cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/OccurrenceServiceImpl.java b/cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/OccurrenceServiceImpl.java index 3bf117560f..2cd090df46 100644 --- a/cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/OccurrenceServiceImpl.java +++ b/cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/OccurrenceServiceImpl.java @@ -10,42 +10,72 @@ package eu.etaxonomy.cdm.api.service; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; +import java.util.UUID; import org.apache.log4j.Logger; +import org.apache.lucene.index.CorruptIndexException; +import org.apache.lucene.queryParser.ParseException; +import org.apache.lucene.search.BooleanClause.Occur; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.SortField; +import org.hibernate.TransientObjectException; +import org.hibernate.search.spatial.impl.Rectangle; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade; import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeConfigurator; import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeNotSupportedException; +import eu.etaxonomy.cdm.api.service.dto.DerivateHierarchyDTO; +import eu.etaxonomy.cdm.api.service.molecular.ISequenceService; import eu.etaxonomy.cdm.api.service.pager.Pager; import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl; +import eu.etaxonomy.cdm.api.service.search.ILuceneIndexToolProvider; +import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder; +import eu.etaxonomy.cdm.api.service.search.LuceneSearch; +import eu.etaxonomy.cdm.api.service.search.LuceneSearch.TopGroupsWithMaxScore; +import eu.etaxonomy.cdm.api.service.search.QueryFactory; +import eu.etaxonomy.cdm.api.service.search.SearchResult; +import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder; import eu.etaxonomy.cdm.api.service.util.TaxonRelationshipEdge; import eu.etaxonomy.cdm.common.monitor.IProgressMonitor; import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper; +import eu.etaxonomy.cdm.model.CdmBaseType; import eu.etaxonomy.cdm.model.common.DefinedTermBase; +import eu.etaxonomy.cdm.model.common.Language; import eu.etaxonomy.cdm.model.common.UuidAndTitleCache; import eu.etaxonomy.cdm.model.description.DescriptionBase; import eu.etaxonomy.cdm.model.description.DescriptionElementBase; import eu.etaxonomy.cdm.model.description.IndividualsAssociation; -import eu.etaxonomy.cdm.model.location.WaterbodyOrCountry; +import eu.etaxonomy.cdm.model.location.Country; import eu.etaxonomy.cdm.model.media.Media; +import eu.etaxonomy.cdm.model.media.MediaRepresentation; +import eu.etaxonomy.cdm.model.media.MediaRepresentationPart; +import eu.etaxonomy.cdm.model.molecular.DnaSample; +import eu.etaxonomy.cdm.model.molecular.Sequence; import eu.etaxonomy.cdm.model.occurrence.DerivationEvent; -import eu.etaxonomy.cdm.model.occurrence.DerivedUnitBase; +import eu.etaxonomy.cdm.model.occurrence.DerivedUnit; import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent; -import eu.etaxonomy.cdm.model.occurrence.FieldObservation; +import eu.etaxonomy.cdm.model.occurrence.FieldUnit; +import eu.etaxonomy.cdm.model.occurrence.GatheringEvent; +import eu.etaxonomy.cdm.model.occurrence.MediaSpecimen; import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase; +import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType; import eu.etaxonomy.cdm.model.taxon.Taxon; import eu.etaxonomy.cdm.model.taxon.TaxonBase; -import eu.etaxonomy.cdm.persistence.dao.AbstractBeanInitializer; import eu.etaxonomy.cdm.persistence.dao.common.IDefinedTermDao; +import eu.etaxonomy.cdm.persistence.dao.initializer.AbstractBeanInitializer; import eu.etaxonomy.cdm.persistence.dao.occurrence.IOccurrenceDao; import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao; import eu.etaxonomy.cdm.persistence.query.OrderHint; @@ -56,7 +86,7 @@ import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy; * @created 01.09.2008 */ @Service -@Transactional(propagation = Propagation.SUPPORTS, readOnly = true) +@Transactional(readOnly = true) public class OccurrenceServiceImpl extends IdentifiableServiceBase implements IOccurrenceService { static private final Logger logger = Logger.getLogger(OccurrenceServiceImpl.class); @@ -70,12 +100,17 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase getWaterbodyOrCountryByName(String name) { - List terms = this.definedTermDao.findByTitle(WaterbodyOrCountry.class, name, null, null, null, null, null, null) ; - List countries = new ArrayList(); + public List getCountryByName(String name) { + List terms = this.definedTermDao.findByTitle(Country.class, name, null, null, null, null, null, null) ; + List countries = new ArrayList(); for (int i=0;i(pageNumber, numberOfResults, pageSize, results); } + /* (non-Javadoc) + * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#countDeterminations(eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase, eu.etaxonomy.cdm.model.taxon.TaxonBase) + */ + @Override + public int countDeterminations(SpecimenOrObservationBase occurence, TaxonBase taxonbase) { + return dao.countDeterminations(occurence, taxonbase); + } + @Override public Pager getDeterminations(SpecimenOrObservationBase occurrence, TaxonBase taxonBase, Integer pageSize,Integer pageNumber, List propertyPaths) { Integer numberOfResults = dao.countDeterminations(occurrence, taxonBase); @@ -178,21 +221,21 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase> getDerivedUnitBaseUuidAndTitleCache() { - return dao.getDerivedUnitBaseUuidAndTitleCache(); + public List> getDerivedUnitUuidAndTitleCache() { + return dao.getDerivedUnitUuidAndTitleCache(); } @Override - public List> getFieldObservationUuidAndTitleCache() { - return dao.getFieldObservationUuidAndTitleCache(); + public List> getFieldUnitUuidAndTitleCache() { + return dao.getFieldUnitUuidAndTitleCache(); } /* (non-Javadoc) - * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#getDerivedUnitFacade(eu.etaxonomy.cdm.model.occurrence.DerivedUnitBase) + * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#getDerivedUnitFacade(eu.etaxonomy.cdm.model.occurrence.DerivedUnit) */ @Override - public DerivedUnitFacade getDerivedUnitFacade(DerivedUnitBase derivedUnit, List propertyPaths) throws DerivedUnitFacadeNotSupportedException { - derivedUnit = (DerivedUnitBase)dao.load(derivedUnit.getUuid(), null); + public DerivedUnitFacade getDerivedUnitFacade(DerivedUnit derivedUnit, List propertyPaths) throws DerivedUnitFacadeNotSupportedException { + derivedUnit = (DerivedUnit)dao.load(derivedUnit.getUuid(), null); DerivedUnitFacadeConfigurator config = DerivedUnitFacadeConfigurator.NewInstance(); config.setThrowExceptionForNonSpecimenPreservationMethodRequest(false); DerivedUnitFacade derivedUnitFacade = DerivedUnitFacade.NewInstance(derivedUnit, config); @@ -216,9 +259,9 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase listFieldUnitsByAssociatedTaxon(Set includeRelationships, + Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List orderHints, List propertyPaths) { + + if(!getSession().contains(associatedTaxon)){ + associatedTaxon = (Taxon) taxonDao.load(associatedTaxon.getUuid()); + } + + Set fieldUnits = new HashSet(); + + List records = pageByAssociatedTaxon(null, includeRelationships, associatedTaxon, maxDepth, pageSize, pageNumber, orderHints, propertyPaths).getRecords(); + for(SpecimenOrObservationBase specimen:records){ + fieldUnits.addAll(getFieldUnits(specimen.getUuid())); + } + return fieldUnits; + } + + @Override + public Collection listDerivateHierarchyDTOsByAssociatedTaxon(Set includeRelationships, + Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List orderHints, List propertyPaths) { + + if(!getSession().contains(associatedTaxon)){ + associatedTaxon = (Taxon) taxonDao.load(associatedTaxon.getUuid()); + } + + Collection fieldUnits = listFieldUnitsByAssociatedTaxon(includeRelationships, associatedTaxon, maxDepth, pageSize, pageNumber, orderHints, propertyPaths); + + Collection derivateHierarchyDTOs = new ArrayList(); + for(FieldUnit fieldUnit:fieldUnits){ + derivateHierarchyDTOs.add(assembleDerivateHierarchyDTO(fieldUnit, associatedTaxon)); + } + return derivateHierarchyDTOs; + } + + private DerivateHierarchyDTO assembleDerivateHierarchyDTO(FieldUnit fieldUnit, Taxon associatedTaxon){ + + DerivateHierarchyDTO dto = new DerivateHierarchyDTO(); + // TaxonNameBase name = associatedTaxon.getName(); + // name = HibernateProxyHelper.deproxy(name, TaxonNameBase.class); + // dto.setType(!name.getTypeDesignations().isEmpty()); + + if(fieldUnit.getGatheringEvent()!=null){ + GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent(); + //Country + dto.setCountry(gatheringEvent.getCountry()!=null?gatheringEvent.getCountry().getDescription():""); + //Collection + dto.setCollection((gatheringEvent.getCollector()!=null?gatheringEvent.getCollector():"") + fieldUnit.getFieldNumber()!=null?fieldUnit.getFieldNumber():""); + //Date + dto.setDate(gatheringEvent.getGatheringDate()!=null?gatheringEvent.getGatheringDate().toString():""); + } + + //Taxon Name + dto.setTaxonName(associatedTaxon.getName().getFullTitleCache()); + + + Collection derivedUnits = new ArrayList(); + getDerivedUnitsFor(fieldUnit, derivedUnits); + //Herbaria map + Map collectionToCountMap = new HashMap(); + + //iterate over sub derivates + for (DerivedUnit derivedUnit : derivedUnits) { + //assemble molecular data + if(derivedUnit instanceof DnaSample){//.getRecordBasis()==SpecimenOrObservationType.DnaSample){ + dto.setHasDna(true); + + DnaSample dna = (DnaSample)derivedUnit; + if(dna.getBankNumber()!=null){ + dto.addMolecularData(dna.getBankNumber()); + } + } + //assemble media data + else if(derivedUnit instanceof MediaSpecimen){ + + MediaSpecimen media = (MediaSpecimen)derivedUnit; + String mediaUriString = getMediaUriString(media); + if(media.getKindOfUnit()!=null){ + if(media.getKindOfUnit().getUuid().equals(UUID.fromString("acda15be-c0e2-4ea8-8783-b9b0c4ad7f03"))){ + dto.setHasSpecimenScan(true); + if(mediaUriString!=null){ + dto.addSpecimenScan(mediaUriString); + } + } + else if(media.getKindOfUnit().getUuid().equals(UUID.fromString("31eb8d02-bf5d-437c-bcc6-87a626445f34"))){ + dto.setHasDetailImage(true); + if(mediaUriString!=null){ + dto.addDetailImage(mediaUriString); + } + } + } + } + //assemble preserved specimen data + else if(derivedUnit.getRecordBasis()==SpecimenOrObservationType.PreservedSpecimen){ + eu.etaxonomy.cdm.model.occurrence.Collection collection = derivedUnit.getCollection(); + if(collection!=null){ + Integer count = collectionToCountMap.get(collection); + if(count==null){ + count = 1; + } + else{ + count++; + } + collectionToCountMap.put(collection, count); + } + } + } + //assemble herbaria string + String herbariaString = ""; + final String herbariaSeparator = ", "; + for(Entry e:collectionToCountMap.entrySet()){ + eu.etaxonomy.cdm.model.occurrence.Collection collection = e.getKey(); + if(collection.getCode()!=null){ + herbariaString += collection.getCode(); + } + if(e.getValue()>1){ + herbariaString += "("+e.getValue()+")"; + } + herbariaString += herbariaSeparator; + } + if(herbariaString.endsWith(herbariaSeparator)){ + herbariaString = herbariaString.substring(0, herbariaString.length()-herbariaSeparator.length()); + } + dto.setHerbarium(herbariaString); + return dto; + } + + private String getMediaUriString(MediaSpecimen mediaSpecimen){ + if(!getSession().contains(mediaSpecimen)){ + mediaSpecimen = (MediaSpecimen) load(mediaSpecimen.getUuid()); + } + String mediaUri = null; + Collection mediaRepresentations = mediaSpecimen.getMediaSpecimen().getRepresentations(); + if(mediaRepresentations!=null && !mediaRepresentations.isEmpty()){ + Collection mediaRepresentationParts = mediaRepresentations.iterator().next().getParts(); + if(mediaRepresentationParts!=null && !mediaRepresentationParts.isEmpty()){ + MediaRepresentationPart part = mediaRepresentationParts.iterator().next(); + if(part.getUri()!=null){ + mediaUri = part.getUri().toASCIIString(); + } + } + } + return mediaUri; + } + + private void getDerivedUnitsFor(SpecimenOrObservationBase specimen, Collection derivedUnits){ + for(DerivationEvent derivationEvent:specimen.getDerivationEvents()){ + for(DerivedUnit derivative:derivationEvent.getDerivatives()){ + derivedUnits.add(derivative); + getDerivedUnitsFor(derivative, derivedUnits); + } + } + } + /* (non-Javadoc) * @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) @@ -260,7 +459,9 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase) dao.listByIds(occurrenceIds, pageSize, pageNumber, orderHints, propertyPaths); return new DefaultPagerImpl(pageNumber, occurrenceIds.size(), pageSize, occurrences); } + /* (non-Javadoc) + * @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) + */ + @SuppressWarnings("unchecked") + @Override + public Pager pageByAssociatedTaxon(Class type, Set includeRelationships, + String taxonUUID, Integer maxDepth, Integer pageSize, Integer pageNumber, List orderHints, List propertyPaths) { + + UUID uuid = UUID.fromString(taxonUUID); + Taxon tax = (Taxon) taxonDao.load(uuid); + //TODO REMOVE NULL STATEMENT +type=null; + return pageByAssociatedTaxon( type,includeRelationships,tax, maxDepth, pageSize, pageNumber, orderHints, propertyPaths ); + + } + + + @Override + public Pager> findByFullText( + Class clazz, String queryString, Rectangle boundingBox, List languages, + boolean highlightFragments, Integer pageSize, Integer pageNumber, List orderHints, + List propertyPaths) throws CorruptIndexException, IOException, ParseException { + + LuceneSearch luceneSearch = prepareByFullTextSearch(clazz, queryString, boundingBox, languages, highlightFragments); + + // --- execute search + TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber); + + Map idFieldMap = new HashMap(); + idFieldMap.put(CdmBaseType.SPECIMEN_OR_OBSERVATIONBASE, "id"); + + // --- initialize taxa, highlight matches .... + ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery()); + @SuppressWarnings("rawtypes") + List> searchResults = searchResultBuilder.createResultSet( + topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths); + + int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0; + + return new DefaultPagerImpl>(pageNumber, totalHits, pageSize, + searchResults); + + } + + + /** + * @param clazz + * @param queryString + * @param languages + * @param highlightFragments + * @return + */ + private LuceneSearch prepareByFullTextSearch(Class clazz, String queryString, Rectangle bbox, + List languages, boolean highlightFragments) { + + BooleanQuery finalQuery = new BooleanQuery(); + BooleanQuery textQuery = new BooleanQuery(); + + LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, FieldUnit.class); + QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(FieldUnit.class); + + // --- criteria + luceneSearch.setCdmTypRestriction(clazz); + if(queryString != null){ + textQuery.add(queryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD); + finalQuery.add(textQuery, Occur.MUST); + } + + // --- spacial query + if(bbox != null){ + finalQuery.add(QueryFactory.buildSpatialQueryByRange(bbox, "gatheringEvent.exactLocation.point"), Occur.MUST); + } + + luceneSearch.setQuery(finalQuery); + + // --- sorting + SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)}; + luceneSearch.setSortFields(sortFields); + + if(highlightFragments){ + luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray()); + } + return luceneSearch; + } + + + /* (non-Javadoc) + * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#getFieldUnits(eu.etaxonomy.cdm.model.occurrence.DerivedUnit) + */ + @Override + public Collection getFieldUnits(UUID derivedUnitUuid) { + //It will search recursively over all {@link DerivationEvent}s and get the "originals" ({@link SpecimenOrObservationBase}) + //from which this DerivedUnit was derived until all FieldUnits are found. + + //FIXME: use HQL queries to increase performance + SpecimenOrObservationBase specimen = load(derivedUnitUuid); +// specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class); + Collection fieldUnits = new ArrayList(); + + if(specimen instanceof FieldUnit){ + fieldUnits.add((FieldUnit) specimen); + } + else if(specimen instanceof DerivedUnit){ + getFieldUnits((DerivedUnit) specimen, fieldUnits); + } + return fieldUnits; + } + + + /** + * @param original + * @param fieldUnits + */ + private void getFieldUnits(DerivedUnit derivedUnit, Collection fieldUnits) { + Set originals = derivedUnit.getOriginals(); + if(originals!=null && !originals.isEmpty()){ + for(SpecimenOrObservationBase original:originals){ + if(original instanceof FieldUnit){ + fieldUnits.add((FieldUnit) original); + } + else if(original instanceof DerivedUnit){ + getFieldUnits((DerivedUnit) original, fieldUnits); + } + } + } + } + + /* (non-Javadoc) + * @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) + */ + @Override + public boolean moveSequence(DnaSample from, DnaSample to, Sequence sequence) { + //reload specimens to avoid session conflicts + from = (DnaSample) load(from.getUuid()); + to = (DnaSample) load(to.getUuid()); + sequence = sequenceService.load(sequence.getUuid()); + + if(from==null || to==null || sequence==null){ + 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" + + "Operation was move "+sequence+ " from "+from+" to "+to); + } + from.removeSequence(sequence); + saveOrUpdate(from); + to.addSequence(sequence); + saveOrUpdate(to); + return true; + } + + /* (non-Javadoc) + * @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) + */ + @Override + public boolean moveDerivate(SpecimenOrObservationBase from, SpecimenOrObservationBase to, DerivedUnit derivate) { + //reload specimens to avoid session conflicts + from = load(from.getUuid()); + to = load(to.getUuid()); + derivate = (DerivedUnit) load(derivate.getUuid()); + + if(from==null || to==null || derivate==null){ + 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" + + "Operation was move "+derivate+ " from "+from+" to "+to); + } + + SpecimenOrObservationType derivateType = derivate.getRecordBasis(); + SpecimenOrObservationType toType = to.getRecordBasis(); + //check if type is a sub derivate type + if(toType==SpecimenOrObservationType.FieldUnit //moving to FieldUnit always works + || derivateType==SpecimenOrObservationType.Media //moving media always works + || (derivateType.isKindOf(toType) && toType!=derivateType)){ //moving only to parent derivate type + //remove derivation event from parent specimen of dragged object + DerivationEvent eventToRemove = null; + for(DerivationEvent event:from.getDerivationEvents()){ + if(event.getDerivatives().contains(derivate)){ + eventToRemove = event; + break; + } + } + from.removeDerivationEvent(eventToRemove); + saveOrUpdate(from); + //add new derivation event to target + to.addDerivationEvent(DerivationEvent.NewSimpleInstance(to, derivate, eventToRemove==null?null:eventToRemove.getType())); + saveOrUpdate(to); + return true; + } + return false; + } + }