X-Git-Url: https://dev.e-taxonomy.eu/gitweb/cdmlib.git/blobdiff_plain/2ad617ca0cc98d70a6e93c6512f923f20fe16cd7..a7f43cf4c361fc3288a5b1a6c50bd002b56d9629:/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 4176c997c9..74b2b6cb5d 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,52 +10,97 @@ package eu.etaxonomy.cdm.api.service; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashSet; 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.hibernate.Hibernate; +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.joda.time.Partial; 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.DeleteResult.DeleteStatus; +import eu.etaxonomy.cdm.api.service.config.SpecimenDeleteConfigurator; +import eu.etaxonomy.cdm.api.service.dto.DerivateHierarchyDTO; +import eu.etaxonomy.cdm.api.service.dto.DerivateHierarchyDTO.ContigFile; +import eu.etaxonomy.cdm.api.service.dto.DerivateHierarchyDTO.MolecularData; +import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException; +import eu.etaxonomy.cdm.api.service.molecular.ISequenceService; import eu.etaxonomy.cdm.api.service.pager.Pager; -import eu.etaxonomy.cdm.api.service.pager.PagerUtils; 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.agent.AgentBase; +import eu.etaxonomy.cdm.model.common.CdmBase; +import eu.etaxonomy.cdm.model.common.DefinedTerm; import eu.etaxonomy.cdm.model.common.DefinedTermBase; -import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction; +import eu.etaxonomy.cdm.model.common.ICdmBase; +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.DescriptionElementSource; import eu.etaxonomy.cdm.model.description.IndividualsAssociation; import eu.etaxonomy.cdm.model.description.TaxonDescription; -import eu.etaxonomy.cdm.model.location.WaterbodyOrCountry; +import eu.etaxonomy.cdm.model.location.Country; +import eu.etaxonomy.cdm.model.location.NamedArea; import eu.etaxonomy.cdm.model.media.Media; -import eu.etaxonomy.cdm.model.name.HomotypicalGroup; +import eu.etaxonomy.cdm.model.media.MediaRepresentation; +import eu.etaxonomy.cdm.model.media.MediaRepresentationPart; +import eu.etaxonomy.cdm.model.media.MediaUtils; +import eu.etaxonomy.cdm.model.molecular.DnaSample; +import eu.etaxonomy.cdm.model.molecular.Sequence; +import eu.etaxonomy.cdm.model.molecular.SingleRead; +import eu.etaxonomy.cdm.model.name.NameTypeDesignation; +import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation; import eu.etaxonomy.cdm.model.name.TaxonNameBase; +import eu.etaxonomy.cdm.model.name.TypeDesignationBase; +import eu.etaxonomy.cdm.model.name.TypeDesignationStatusBase; 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.model.taxon.TaxonRelationship; -import eu.etaxonomy.cdm.persistence.dao.AbstractBeanInitializer; +import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao; import eu.etaxonomy.cdm.persistence.dao.common.IDefinedTermDao; -import eu.etaxonomy.cdm.persistence.dao.description.IDescriptionDao; +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; import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy; @@ -64,7 +109,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); @@ -78,12 +123,23 @@ 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(); + @Override + 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 getDerivationEvents(SpecimenOrObservationBase occurence, Integer pageSize,Integer pageNumber, List propertyPaths) { Integer numberOfResults = dao.countDerivationEvents(occurence); @@ -142,6 +202,15 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase(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); @@ -153,6 +222,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase(pageNumber, numberOfResults, pageSize, results); } + @Override public Pager getMedia(SpecimenOrObservationBase occurence,Integer pageSize, Integer pageNumber, List propertyPaths) { Integer numberOfResults = dao.countMedia(occurence); @@ -167,6 +237,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase list(Class type, TaxonBase determinedAs, Integer pageSize, Integer pageNumber, List orderHints, List propertyPaths) { Integer numberOfResults = dao.count(type,determinedAs); List results = new ArrayList(); @@ -179,21 +250,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); @@ -217,9 +288,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) taxonService.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 DerivateHierarchyDTO assembleDerivateHierarchyDTO(FieldUnit fieldUnit, UUID associatedTaxonUuid){ + + if(!getSession().contains(fieldUnit)){ + fieldUnit = (FieldUnit) load(fieldUnit.getUuid()); + } + TaxonBase associatedTaxon = taxonService.load(associatedTaxonUuid); + + DerivateHierarchyDTO dto = new DerivateHierarchyDTO(); + Map typeSpecimenUUIDtoTypeDesignationStatus = new HashMap(); + + //gather types for this taxon name + TaxonNameBase name = associatedTaxon.getName(); + Set typeDesignations = name.getSpecimenTypeDesignations(); + for (Object object : typeDesignations) { + if(object instanceof SpecimenTypeDesignation){ + SpecimenTypeDesignation specimenTypeDesignation = (SpecimenTypeDesignation)object; + DerivedUnit typeSpecimen = specimenTypeDesignation.getTypeSpecimen(); + final TypeDesignationStatusBase typeStatus = specimenTypeDesignation.getTypeStatus(); + typeSpecimenUUIDtoTypeDesignationStatus.put(typeSpecimen.getUuid(), typeStatus); + } + } + + if(fieldUnit.getGatheringEvent()!=null){ + GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent(); + //Country + final NamedArea country = gatheringEvent.getCountry(); + dto.setCountry(country!=null?country.getDescription():""); + //Collection + final AgentBase collector = gatheringEvent.getCollector(); + final String fieldNumber = fieldUnit.getFieldNumber(); + dto.setCollection(((collector!=null?collector:"") + " " + (fieldNumber!=null?fieldNumber:"")).trim()); + //Date + final Partial gatheringDate = gatheringEvent.getGatheringDate(); + dto.setDate(gatheringDate!=null?gatheringDate.toString():""); + } + + //Taxon Name + dto.setTaxonName(associatedTaxon.getName().getFullTitleCache()); + + + Collection derivedUnits = new ArrayList(); + getDerivedUnitsFor(fieldUnit, derivedUnits); + + //Herbaria map + Map collectionToCountMap = new HashMap(); + //List of accession numbers for citation + List preservedSpecimenAccessionNumbers = new ArrayList(); + + //iterate over sub derivates + for (DerivedUnit derivedUnit : derivedUnits) { + //current accession number + String currentAccessionNumber = derivedUnit.getAccessionNumber()!=null?derivedUnit.getAccessionNumber():""; + //current herbarium + String currentHerbarium = ""; + eu.etaxonomy.cdm.model.occurrence.Collection collection = derivedUnit.getCollection(); + if(collection!=null){ + currentHerbarium = collection.getCode()!=null?collection.getCode():""; + //count herbaria + Integer count = collectionToCountMap.get(collection); + if(count==null){ + count = 1; + } + else{ + count++; + } + collectionToCountMap.put(collection, count); + } + //check if derived unit is a type + if(typeSpecimenUUIDtoTypeDesignationStatus.keySet().contains(derivedUnit.getUuid())){ + dto.setHasType(true); + TypeDesignationStatusBase typeDesignationStatus = typeSpecimenUUIDtoTypeDesignationStatus.get(derivedUnit.getUuid()); + String typeStatus = typeDesignationStatus.getLabel(); + dto.addTypes(typeStatus, currentAccessionNumber); + } + //assemble molecular data + //pattern: DNAMarker [contig1, primer1_1, primer1_2, ...][contig2, primer2_1, ...]... + if(derivedUnit instanceof DnaSample){ + if(derivedUnit.getRecordBasis()==SpecimenOrObservationType.TissueSample){ + //TODO implement TissueSample assembly for web service + } + if(derivedUnit.getRecordBasis()==SpecimenOrObservationType.DnaSample){ + + DnaSample dna = (DnaSample)derivedUnit; + if(!dna.getSequences().isEmpty()){ + dto.setHasDna(true); + } + for(Sequence sequence:dna.getSequences()){ + URI boldUri = null; + try { + boldUri = sequence.getBoldUri(); + } catch (URISyntaxException e1) { + logger.error("Could not create BOLD URI", e1); + } + final DefinedTerm dnaMarker = sequence.getDnaMarker(); + MolecularData molecularData = dto.addProviderLink(boldUri!=null?boldUri:null,dnaMarker!=null?dnaMarker.getLabel():"[no marker]"); + + //contig file FIXME show primer although contig not present? + if(sequence.getContigFile()!=null){ + MediaRepresentationPart contigMediaRepresentationPart = MediaUtils.getFirstMediaRepresentationPart(sequence.getContigFile()); + if(contigMediaRepresentationPart!=null){ + ContigFile contigFile = molecularData.addContigFile(contigMediaRepresentationPart.getUri(), "contig"); + //primer files + if(sequence.getSingleReads()!=null){ + for (SingleRead singleRead : sequence.getSingleReads()) { + MediaRepresentationPart pherogramMediaRepresentationPart = MediaUtils.getFirstMediaRepresentationPart(singleRead.getPherogram()); + if(pherogramMediaRepresentationPart!=null){ + contigFile.addPrimerLink(pherogramMediaRepresentationPart.getUri(), "primer"); + } + } + } + } + } + } + } + } + //assemble media data + else if(derivedUnit instanceof MediaSpecimen){ + + MediaSpecimen media = (MediaSpecimen)derivedUnit; + String mediaUriString = getMediaUriString(media); + if(media.getKindOfUnit()!=null){ + //specimen scan + if(media.getKindOfUnit().getUuid().equals(UUID.fromString("acda15be-c0e2-4ea8-8783-b9b0c4ad7f03"))){ + dto.setHasSpecimenScan(true); + final String imageLinkText = currentHerbarium+" "+currentAccessionNumber; + dto.addSpecimenScan(mediaUriString==null?"":mediaUriString, !imageLinkText.equals(" ")?imageLinkText:"[no accession]"); + } + //detail image + else if(media.getKindOfUnit().getUuid().equals(UUID.fromString("31eb8d02-bf5d-437c-bcc6-87a626445f34"))){ + dto.setHasDetailImage(true); + String motif = ""; + if(media.getMediaSpecimen()!=null && media.getMediaSpecimen().getTitle()!=null){ + motif = media.getMediaSpecimen().getTitle().getText(); + } + dto.addDetailImage(mediaUriString==null?"":mediaUriString, motif!=null?motif:"[no motif]"); + } + } + } + //assemble preserved specimen data + else if(derivedUnit.getRecordBasis()==SpecimenOrObservationType.PreservedSpecimen){ + if(!currentAccessionNumber.isEmpty()){ + preservedSpecimenAccessionNumbers.add(currentAccessionNumber); + } + } + } + + final String separator = ", "; + //assemble citation + String citation = ""; + citation += !dto.getCountry().isEmpty()?dto.getCountry()+separator:""; + if(fieldUnit.getGatheringEvent()!=null){ + if(fieldUnit.getGatheringEvent().getLocality()!=null){ + citation += fieldUnit.getGatheringEvent().getLocality().getText(); + citation += separator; + } + if(fieldUnit.getGatheringEvent().getExactLocation()!=null + && fieldUnit.getGatheringEvent().getExactLocation().getLatitude()!=null + && fieldUnit.getGatheringEvent().getExactLocation().getLongitude()!=null){ + citation += fieldUnit.getGatheringEvent().getExactLocation().getLatitude().toString(); + citation += separator; + citation += fieldUnit.getGatheringEvent().getExactLocation().getLongitude().toString(); + citation += separator; + } + } + citation += !dto.getCollection().isEmpty()?dto.getCollection():""; + if(!preservedSpecimenAccessionNumbers.isEmpty()){ + citation += " ("; + for(String accessionNumber:preservedSpecimenAccessionNumbers){ + if(!accessionNumber.isEmpty()){ + citation += accessionNumber+separator; + } + } + citation = removeTail(citation, separator); + citation += ")"; + } + citation = removeTail(citation, separator); + dto.setCitation(citation); + + //assemble herbaria string + String herbariaString = ""; + 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 += separator; + } + herbariaString = removeTail(herbariaString, separator); + dto.setHerbarium(herbariaString); + + return dto; + } + + + /** + * @param string + * @param tail + * @return + */ + private String removeTail(String string, final String tail) { + if(string.endsWith(tail)){ + string = string.substring(0, string.length()-tail.length()); + } + return string; + } + + private String getMediaUriString(MediaSpecimen mediaSpecimen){ + 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) @@ -254,34 +579,508 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase Pager pageByAssociatedTaxon(Class type, Set includeRelationships, Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List orderHints, List propertyPaths) { - List taxa = new ArrayList(); + Set taxa = new HashSet(); Set occurrenceIds = new HashSet(); List occurrences = new ArrayList(); - Integer limit = PagerUtils.limitFor(pageSize); - Integer start = PagerUtils.startFor(pageSize, pageNumber); - - - associatedTaxon = (Taxon) taxonDao.load(associatedTaxon.getUuid()); +// Integer limit = PagerUtils.limitFor(pageSize); +// Integer start = PagerUtils.startFor(pageSize, pageNumber); + if(!getSession().contains(associatedTaxon)){ + associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid()); + } if(includeRelationships != null) { - taxa = taxonService.listRelatedTaxa(associatedTaxon, includeRelationships, maxDepth, pageSize, pageSize, propertyPaths); + taxa = taxonService.listRelatedTaxa(associatedTaxon, includeRelationships, maxDepth, null, null, propertyPaths); } taxa.add(associatedTaxon); for (Taxon taxon : taxa) { - List perTaxonOccurrences = dao.listByAssociatedTaxon(type, taxon, limit, start, orderHints, propertyPaths); + List perTaxonOccurrences = dao.listByAssociatedTaxon(type, taxon, null, null, orderHints, propertyPaths); for (SpecimenOrObservationBase o : perTaxonOccurrences) { occurrenceIds.add(o.getId()); } } - - occurrences = (List) dao.listByIds(occurrenceIds, null, null, orderHints, propertyPaths); + occurrences = (List) dao.listByIds(occurrenceIds, pageSize, pageNumber, orderHints, propertyPaths); return new DefaultPagerImpl(pageNumber, occurrenceIds.size(), pageSize, occurrences); } + + @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) taxonService.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 + DerivationEvent derivedFromNewOriginalEvent = DerivationEvent.NewSimpleInstance(to, derivate, eventToRemove==null?null:eventToRemove.getType()); + to.addDerivationEvent(derivedFromNewOriginalEvent); + derivate.setDerivedFrom(derivedFromNewOriginalEvent); + saveOrUpdate(to); + return true; + } + return false; + } + + @Override + public Collection getNonCascadedAssociatedElements(SpecimenOrObservationBase specimen){ + //potential fields that are not persisted cascadingly + /* + * SOOB + -DescriptionBase + -determinations + --modifier TERM + -kindOfUnit TERM + -lifeStage TERM + -sex TERM + + FieldUnit + -GatheringEvent + --Country TERM + --CollectingAreas TERM + + DerivedUnit + -collection + --institute + ---types TERM + -preservationMethod + --medium TERM + -storedUnder CDM TaxonNameBase + */ + + Collection nonCascadedCdmEntities = new HashSet(); + + //Choose the correct entry point to traverse the graph (FieldUnit or DerivedUnit) + + //FieldUnit + if(specimen instanceof FieldUnit){ + nonCascadedCdmEntities.addAll(getFieldUnitNonCascadedAssociatedElements((FieldUnit)specimen)); + } + //DerivedUnit + else if(specimen instanceof DerivedUnit){ + DerivedUnit derivedUnit = (DerivedUnit)specimen; + if(derivedUnit.getDerivedFrom()!=null){ + Collection fieldUnits = new ArrayList(); + getFieldUnits(derivedUnit, fieldUnits); + for(FieldUnit fieldUnit:fieldUnits){ + nonCascadedCdmEntities.addAll(getFieldUnitNonCascadedAssociatedElements(fieldUnit)); + } + } + } + return nonCascadedCdmEntities; + } + + private Collection getFieldUnitNonCascadedAssociatedElements(FieldUnit fieldUnit){ + //get non cascaded element on SpecimenOrObservationBase level + Collection nonCascadedCdmEntities = getSpecimenOrObservationNonCascadedAssociatedElements(fieldUnit); + + //get FieldUnit specific elements + GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent(); + if(gatheringEvent!=null){ + //country + if(gatheringEvent.getCountry()!=null){ + nonCascadedCdmEntities.add(gatheringEvent.getCountry()); + } + //collecting areas + for (NamedArea namedArea : gatheringEvent.getCollectingAreas()) { + nonCascadedCdmEntities.add(namedArea); + } + } + for (DerivationEvent derivationEvent : fieldUnit.getDerivationEvents()) { + for (DerivedUnit derivedUnit : derivationEvent.getDerivatives()) { + nonCascadedCdmEntities.addAll(getDerivedUnitNonCascadedAssociatedElements(derivedUnit)); + } + } + return nonCascadedCdmEntities; + } + + private Collection getDerivedUnitNonCascadedAssociatedElements(DerivedUnit derivedUnit){ + //get non cascaded element on SpecimenOrObservationBase level + Collection nonCascadedCdmEntities = getSpecimenOrObservationNonCascadedAssociatedElements(derivedUnit); + + //get DerivedUnit specific elements + if(derivedUnit.getCollection()!=null && derivedUnit.getCollection().getInstitute()!=null){ + for (DefinedTerm type : derivedUnit.getCollection().getInstitute().getTypes()) { + nonCascadedCdmEntities.add(type); + } + } + if(derivedUnit.getPreservation()!=null && derivedUnit.getPreservation().getMedium()!=null){ + nonCascadedCdmEntities.add(derivedUnit.getPreservation().getMedium()); + } + if(derivedUnit.getStoredUnder()!=null){ + nonCascadedCdmEntities.add(derivedUnit.getStoredUnder()); + } + return nonCascadedCdmEntities; + } + + /** + * @param specimen + * @return + */ + private Collection getSpecimenOrObservationNonCascadedAssociatedElements( + SpecimenOrObservationBase specimen) { + Collection nonCascadedCdmEntities = new HashSet(); + //scan SpecimenOrObservationBase + for(DeterminationEvent determinationEvent:specimen.getDeterminations()){ + //modifier + if(determinationEvent.getModifier()!=null){ + nonCascadedCdmEntities.add(determinationEvent.getModifier()); + } + } + //kindOfUnit + if(specimen.getKindOfUnit()!=null){ + nonCascadedCdmEntities.add(specimen.getKindOfUnit()); + } + //lifeStage + if(specimen.getLifeStage()!=null){ + nonCascadedCdmEntities.add(specimen.getLifeStage()); + } + //sex + if(specimen.getSex()!=null){ + nonCascadedCdmEntities.add(specimen.getSex()); + } + return nonCascadedCdmEntities; + } + + + /* (non-Javadoc) + * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#delete(eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase, eu.etaxonomy.cdm.api.service.config.SpecimenDeleteConfigurator) + */ + @Override + public DeleteResult delete(SpecimenOrObservationBase specimen, SpecimenDeleteConfigurator config) { + DeleteResult deleteResult = new DeleteResult(); + //check for derivation events + Set derivationEvents = specimen.getDerivationEvents(); + for (DerivationEvent derivationEvent : derivationEvents) { + if(!derivationEvent.getDerivatives().isEmpty()){ + deleteResult.setAbort(); + deleteResult.addException(new ReferencedObjectUndeletableException("Derivate with children cannot be deleted.")); + return deleteResult; + } + } + //check for original (parent derivate) + if(specimen instanceof DerivedUnit){ + DerivationEvent derivedFromEvent = ((DerivedUnit) specimen).getDerivedFrom(); + if(derivedFromEvent!=null){ + derivedFromEvent.removeDerivative((DerivedUnit) specimen); + } + } + //check for IndividualsAssociation (e.g. in TaxonDescriptions) + Collection> associatedTaxa = listAssociatedTaxa(specimen, null, null, null, null); + if(!associatedTaxa.isEmpty()){ + if(config.isDeleteFromIndividualsAssociation()){ + for (TaxonBase taxonBase : associatedTaxa) { + if(taxonBase instanceof Taxon){ + Set descriptions = ((Taxon) taxonBase).getDescriptions(); + for (TaxonDescription taxonDescription : descriptions) { + Set elements = taxonDescription.getElements(); + for (DescriptionElementBase descriptionElementBase : elements) { + if(descriptionElementBase instanceof IndividualsAssociation){ + IndividualsAssociation individualsAssociation = (IndividualsAssociation) descriptionElementBase; + if(individualsAssociation.getAssociatedSpecimenOrObservation().equals(specimen)){ + individualsAssociation.setAssociatedSpecimenOrObservation(null); + } + } + } + } + } + } + } + else{ + deleteResult.addRelatedObjects(new HashSet(associatedTaxa)); + deleteResult.setAbort(); + deleteResult.addException(new ReferencedObjectUndeletableException("Specimen is still associated with taxa.")); + return deleteResult; + } + } + //check for TypeDesignations + Collection> typedTaxa = listTypedTaxa(specimen, null, null, null, null); + if(!typedTaxa.isEmpty()){ + if(config.isdeleteFromTypeDesignation()){ + for (TaxonBase taxonBase : typedTaxa) { + if(taxonBase.getName()!=null){ + Set typeDesignations = taxonBase.getName().getTypeDesignations(); + for (TypeDesignationBase typeDesignationBase : typeDesignations) { + if(typeDesignationBase instanceof SpecimenTypeDesignation){ + ((SpecimenTypeDesignation) typeDesignationBase).setTypeSpecimen(null); + } + } + } + } + } + else{ + deleteResult.addRelatedObjects(new HashSet(typedTaxa)); + deleteResult.setAbort(); + deleteResult.addException(new ReferencedObjectUndeletableException("Specimen is a type specimen.")); + return deleteResult; + } + } + if(!config.isDeleteChildren()){ + Set referencingObjects = genericDao.getReferencingObjects(specimen); + for (CdmBase referencingObject : referencingObjects){ + //DerivedUnit?.storedUnder + if (referencingObject.isInstanceOf(DerivedUnit.class)){ + String message = "Name can't be deleted as it is used as derivedUnit#storedUnder by %s. Remove 'stored under' prior to deleting this name"; + message = String.format(message, CdmBase.deproxy(referencingObject, DerivedUnit.class).getTitleCache()); + } + //DescriptionElementSource#nameUsedInSource + if (referencingObject.isInstanceOf(DescriptionElementSource.class)){ + String message = "Name can't be deleted as it is used as descriptionElementSource#nameUsedInSource"; + } + //NameTypeDesignation#typeName + if (referencingObject.isInstanceOf(NameTypeDesignation.class)){ + String message = "Name can't be deleted as it is used as a name type in a NameTypeDesignation"; + } + } + deleteResult = delete(specimen); + } + else{ + //TODO implement deep delete + } + return deleteResult; + } + + /* (non-Javadoc) + * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#deleteDerivateHierarchy(eu.etaxonomy.cdm.model.common.ICdmBase) + */ + @Override + public DeleteResult deleteDerivateHierarchy(ICdmBase from, SpecimenDeleteConfigurator config) { + DeleteResult deleteResult = new DeleteResult(); + if(from instanceof Sequence){ + Sequence sequence = (Sequence)from; + sequence.getDnaSample().removeSequence(sequence); + deleteResult.setStatus(DeleteStatus.OK); + } + else if(from instanceof SpecimenOrObservationBase) { + deleteResult = delete((SpecimenOrObservationBase) from, config); + } + return deleteResult; + } + + private Set collectEntitiesToDelete(ICdmBase entity){ + Set entitiesToDelete = new LinkedHashSet(); + + if(entity instanceof SpecimenOrObservationBase){ + SpecimenOrObservationBase specimen = (SpecimenOrObservationBase) entity; + if(entity instanceof DerivedUnit){ + DerivedUnit derivedUnit = (DerivedUnit)entity; + DerivationEvent derivedFrom = derivedUnit.getDerivedFrom(); + Set originals = derivedFrom.getOriginals(); + for (SpecimenOrObservationBase original: originals) { + original.removeDerivationEvent(derivedFrom); +// saveOrUpdate(original); + } + } + if(entity instanceof DnaSample && ((DnaSample) entity).getRecordBasis()==SpecimenOrObservationType.DnaSample){ + DnaSample dnaSample = (DnaSample)entity; + for (Sequence sequence : dnaSample.getSequences()) { + entitiesToDelete.addAll(collectEntitiesToDelete(sequence)); + dnaSample.removeSequence(sequence); +// saveOrUpdate(dnaSample); + } + } + Set derivationEvents = specimen.getDerivationEvents(); + for (DerivationEvent derivationEvent : derivationEvents) { + for (DerivedUnit derivedUnit : derivationEvent.getDerivatives()) { + entitiesToDelete.addAll(collectEntitiesToDelete(derivedUnit)); + } + } + } + else if(entity instanceof Sequence){ + Sequence sequence = (Sequence)entity; + for (SingleRead singleRead : sequence.getSingleReads()) { + entitiesToDelete.addAll(collectEntitiesToDelete(singleRead)); + sequence.removeSingleRead(singleRead); + } +// sequenceService.saveOrUpdate(sequence); + } + entitiesToDelete.add(entity); + return entitiesToDelete; + } + + /* (non-Javadoc) + * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#listAssociatedTaxa(eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase) + */ + @Override + public Collection> listAssociatedTaxa(SpecimenOrObservationBase specimen, Integer limit, Integer start, List orderHints, List propertyPaths) { + return dao.listAssociatedTaxa(specimen, null, null, null, null); + } + + @Override + public Collection> listTypedTaxa(SpecimenOrObservationBase specimen, Integer limit, Integer start, List orderHints, List propertyPaths) { + return dao.listTypedTaxa(specimen, limit, start, orderHints, propertyPaths); + } + }