ref #6668 Include deleted derivatives recursively into DeleteResult
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / OccurrenceServiceImpl.java
index 67c28bf49f8405c5286997af021d6643a44f5a25..e2f1411fc00f2ccea3d6a2f049c854d953b82cc0 100644 (file)
@@ -1,4 +1,3 @@
-// $Id$
 /**
  * Copyright (C) 2007 EDIT
  * European Distributed Institute of Taxonomy
@@ -19,6 +18,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -26,7 +26,6 @@ import java.util.Set;
 import java.util.UUID;
 
 import org.apache.log4j.Logger;
-import org.apache.lucene.index.CorruptIndexException;
 import org.apache.lucene.queryparser.classic.ParseException;
 import org.apache.lucene.search.BooleanClause.Occur;
 import org.apache.lucene.search.BooleanQuery.Builder;
@@ -47,11 +46,13 @@ import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeNotSupportedException;
 import eu.etaxonomy.cdm.api.service.UpdateResult.Status;
 import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase;
 import eu.etaxonomy.cdm.api.service.config.FindOccurrencesConfigurator;
+import eu.etaxonomy.cdm.api.service.config.FindOccurrencesConfigurator.AssignmentStatus;
 import eu.etaxonomy.cdm.api.service.config.IIdentifiableEntityServiceConfigurator;
 import eu.etaxonomy.cdm.api.service.config.SpecimenDeleteConfigurator;
 import eu.etaxonomy.cdm.api.service.dto.DerivateDTO;
 import eu.etaxonomy.cdm.api.service.dto.DerivateDataDTO;
 import eu.etaxonomy.cdm.api.service.dto.DerivateDataDTO.ContigFile;
+import eu.etaxonomy.cdm.api.service.dto.DerivateDataDTO.Link;
 import eu.etaxonomy.cdm.api.service.dto.DerivateDataDTO.MolecularData;
 import eu.etaxonomy.cdm.api.service.dto.FieldUnitDTO;
 import eu.etaxonomy.cdm.api.service.dto.PreservedSpecimenDTO;
@@ -61,12 +62,16 @@ 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.LuceneParseException;
 import eu.etaxonomy.cdm.api.service.search.LuceneSearch;
 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.CdmUtils;
 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
+import eu.etaxonomy.cdm.format.CdmFormatterFactory;
+import eu.etaxonomy.cdm.format.ICdmFormatter.FormatKey;
 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
 import eu.etaxonomy.cdm.model.CdmBaseType;
 import eu.etaxonomy.cdm.model.agent.AgentBase;
@@ -93,7 +98,7 @@ 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.SpecimenTypeDesignation;
-import eu.etaxonomy.cdm.model.name.TaxonNameBase;
+import eu.etaxonomy.cdm.model.name.TaxonName;
 import eu.etaxonomy.cdm.model.name.TypeDesignationStatusBase;
 import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;
 import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
@@ -112,6 +117,7 @@ import eu.etaxonomy.cdm.persistence.dao.occurrence.IOccurrenceDao;
 import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
 import eu.etaxonomy.cdm.persistence.query.OrderHint;
 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
+import eu.etaxonomy.cdm.strategy.cache.common.IdentifiableEntityDefaultCacheStrategy;
 
 /**
  * @author a.babadshanjan
@@ -132,6 +138,9 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     @Autowired
     private INameService nameService;
 
+    @Autowired
+    private IEventBaseService eventService;
+
     @Autowired
     private ITaxonService taxonService;
 
@@ -180,7 +189,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     @Override
     public List<Country> getCountryByName(String name) {
         List<? extends DefinedTermBase> terms = this.definedTermDao.findByTitle(Country.class, name, null, null, null, null, null, null);
-        List<Country> countries = new ArrayList<Country>();
+        List<Country> countries = new ArrayList<>();
         for (int i = 0; i < terms.size(); i++) {
             countries.add((Country) terms.get(i));
         }
@@ -197,7 +206,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     public Pager<DerivationEvent> getDerivationEvents(SpecimenOrObservationBase occurence, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
         Integer numberOfResults = dao.countDerivationEvents(occurence);
 
-        List<DerivationEvent> results = new ArrayList<DerivationEvent>();
+        List<DerivationEvent> results = new ArrayList<>();
         if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
             results = dao.getDerivationEvents(occurence, pageSize, pageNumber, propertyPaths);
         }
@@ -214,7 +223,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     public Pager<DeterminationEvent> getDeterminations(SpecimenOrObservationBase occurrence, TaxonBase taxonBase, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
         Integer numberOfResults = dao.countDeterminations(occurrence, taxonBase);
 
-        List<DeterminationEvent> results = new ArrayList<DeterminationEvent>();
+        List<DeterminationEvent> results = new ArrayList<>();
         if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
             results = dao.getDeterminations(occurrence, taxonBase, pageSize, pageNumber, propertyPaths);
         }
@@ -226,7 +235,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     public Pager<Media> getMedia(SpecimenOrObservationBase occurence, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
         Integer numberOfResults = dao.countMedia(occurence);
 
-        List<Media> results = new ArrayList<Media>();
+        List<Media> results = new ArrayList<>();
         if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
             results = dao.getMedia(occurence, pageSize, pageNumber, propertyPaths);
         }
@@ -237,7 +246,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     @Override
     public Pager<Media> getMediainHierarchy(SpecimenOrObservationBase rootOccurence, Integer pageSize,
             Integer pageNumber, List<String> propertyPaths) {
-        List<Media> media = new ArrayList<Media>();
+        List<Media> media = new ArrayList<>();
         //media specimens
         if(rootOccurence.isInstanceOf(MediaSpecimen.class)){
             MediaSpecimen mediaSpecimen = HibernateProxyHelper.deproxy(rootOccurence, MediaSpecimen.class);
@@ -249,7 +258,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
             Set<Sequence> sequences = dnaSample.getSequences();
             //we do show only those gelPhotos which lead to a consensus sequence
             for (Sequence sequence : sequences) {
-                Set<Media> dnaRelatedMedia = new HashSet<Media>();
+                Set<Media> dnaRelatedMedia = new HashSet<>();
                 for (SingleRead singleRead : sequence.getSingleReads()){
                     AmplificationResult amplification = singleRead.getAmplificationResult();
                     dnaRelatedMedia.add(amplification.getGelPhoto());
@@ -271,9 +280,9 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     }
 
     @Override
-    public Pager<SpecimenOrObservationBase> list(Class<? extends SpecimenOrObservationBase> type, TaxonNameBase determinedAs, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
+    public Pager<SpecimenOrObservationBase> list(Class<? extends SpecimenOrObservationBase> type, TaxonName determinedAs, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
         Integer numberOfResults = dao.count(type, determinedAs);
-        List<SpecimenOrObservationBase> results = new ArrayList<SpecimenOrObservationBase>();
+        List<SpecimenOrObservationBase> results = new ArrayList<>();
         pageNumber = pageNumber == null ? 0 : pageNumber;
         if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
             Integer start = pageSize == null ? 0 : pageSize * pageNumber;
@@ -285,7 +294,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     @Override
     public Pager<SpecimenOrObservationBase> list(Class<? extends SpecimenOrObservationBase> type, TaxonBase determinedAs, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
         Integer numberOfResults = dao.count(type, determinedAs);
-        List<SpecimenOrObservationBase> results = new ArrayList<SpecimenOrObservationBase>();
+        List<SpecimenOrObservationBase> results = new ArrayList<>();
         pageNumber = pageNumber == null ? 0 : pageNumber;
         if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
             Integer start = pageSize == null ? 0 : pageSize * pageNumber;
@@ -295,8 +304,8 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     }
 
     @Override
-    public List<UuidAndTitleCache<DerivedUnit>> getDerivedUnitUuidAndTitleCache() {
-        return dao.getDerivedUnitUuidAndTitleCache();
+    public List<UuidAndTitleCache<DerivedUnit>> getDerivedUnitUuidAndTitleCache(Integer limit, String pattern) {
+        return dao.getDerivedUnitUuidAndTitleCache(limit, pattern);
     }
 
     @Override
@@ -318,24 +327,21 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     public List<DerivedUnitFacade> listDerivedUnitFacades(
             DescriptionBase description, List<String> propertyPaths) {
 
-        List<DerivedUnitFacade> derivedUnitFacadeList = new ArrayList<DerivedUnitFacade>();
+        List<DerivedUnitFacade> derivedUnitFacadeList = new ArrayList<>();
         IndividualsAssociation tempIndividualsAssociation;
         SpecimenOrObservationBase tempSpecimenOrObservationBase;
-        List<DescriptionElementBase> elements = descriptionService.listDescriptionElements(description, null, IndividualsAssociation.class, null, 0, Arrays.asList(new String []{"associatedSpecimenOrObservation"}));
-        for (DescriptionElementBase element : elements) {
-            if (element.isInstanceOf(IndividualsAssociation.class)) {
-                tempIndividualsAssociation = HibernateProxyHelper.deproxy(element, IndividualsAssociation.class);
-                if (tempIndividualsAssociation.getAssociatedSpecimenOrObservation() != null) {
-                    tempSpecimenOrObservationBase = HibernateProxyHelper.deproxy(tempIndividualsAssociation.getAssociatedSpecimenOrObservation(), SpecimenOrObservationBase.class);
-                    if (tempSpecimenOrObservationBase.isInstanceOf(DerivedUnit.class)) {
-                        try {
-                            derivedUnitFacadeList.add(DerivedUnitFacade.NewInstance(HibernateProxyHelper.deproxy(tempSpecimenOrObservationBase, DerivedUnit.class)));
-                        } catch (DerivedUnitFacadeNotSupportedException e) {
-                            logger.warn(tempIndividualsAssociation.getAssociatedSpecimenOrObservation().getTitleCache() + " : " + e.getMessage());
-                        }
+        List<IndividualsAssociation> elements = descriptionService.listDescriptionElements(description, null, IndividualsAssociation.class, null, 0, Arrays.asList(new String []{"associatedSpecimenOrObservation"}));
+        for (IndividualsAssociation element : elements) {
+            tempIndividualsAssociation = HibernateProxyHelper.deproxy(element, IndividualsAssociation.class);
+            if (tempIndividualsAssociation.getAssociatedSpecimenOrObservation() != null) {
+                tempSpecimenOrObservationBase = HibernateProxyHelper.deproxy(tempIndividualsAssociation.getAssociatedSpecimenOrObservation(), SpecimenOrObservationBase.class);
+                if (tempSpecimenOrObservationBase.isInstanceOf(DerivedUnit.class)) {
+                    try {
+                        derivedUnitFacadeList.add(DerivedUnitFacade.NewInstance(HibernateProxyHelper.deproxy(tempSpecimenOrObservationBase, DerivedUnit.class)));
+                    } catch (DerivedUnitFacadeNotSupportedException e) {
+                        logger.warn(tempIndividualsAssociation.getAssociatedSpecimenOrObservation().getTitleCache() + " : " + e.getMessage());
                     }
                 }
-
             }
         }
 
@@ -367,16 +373,16 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
         }
 
         // gather the IDs of all relevant field units
-        Set<Integer> fieldUnitIds = new HashSet<Integer>();
+        Set<UUID> fieldUnitUuids = new HashSet<>();
         List<SpecimenOrObservationBase> records = listByAssociatedTaxon(null, includeRelationships, associatedTaxon, maxDepth, null, null, orderHints, propertyPaths);
         for (SpecimenOrObservationBase<?> specimen : records) {
             for (FieldUnit fieldUnit : getFieldUnits(specimen.getUuid())) {
-                fieldUnitIds.add(fieldUnit.getId());
+                fieldUnitUuids.add(fieldUnit.getUuid());
             }
         }
-        //dao.listByIds() does the paging of the field units. Passing the field units directly to the Pager would not work
-        List<SpecimenOrObservationBase> fieldUnits = dao.listByIds(fieldUnitIds, pageSize, pageNumber, orderHints, propertyPaths);
-        return new DefaultPagerImpl<SpecimenOrObservationBase>(pageNumber, fieldUnitIds.size(), pageSize, fieldUnits);
+        //dao.list() does the paging of the field units. Passing the field units directly to the Pager would not work
+        List<SpecimenOrObservationBase> fieldUnits = dao.list(fieldUnitUuids, pageSize, pageNumber, orderHints, propertyPaths);
+        return new DefaultPagerImpl<SpecimenOrObservationBase>(pageNumber, fieldUnitUuids.size(), pageSize, fieldUnits);
     }
 
     @Override
@@ -393,7 +399,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
             GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent();
             // Country
             NamedArea country = gatheringEvent.getCountry();
-            fieldUnitDTO.setCountry(country != null ? country.getDescription() : null);
+            fieldUnitDTO.setCountry(country != null ? country.getLabel() : null);
             // Collection
             AgentBase collector = gatheringEvent.getCollector();
             String fieldNumber = fieldUnit.getFieldNumber();
@@ -411,7 +417,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
             Partial gatheringDate = gatheringEvent.getGatheringDate();
             String dateString = null;
             if (gatheringDate != null) {
-                gatheringDate.toString();
+                dateString = gatheringDate.toString();
             }
             else if(gatheringEvent.getTimeperiod()!=null && gatheringEvent.getTimeperiod().getFreeText()!=null){
                 dateString = gatheringEvent.getTimeperiod().getFreeText();
@@ -423,9 +429,9 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
         fieldUnitDTO.setTaxonName(associatedTaxon.getName().getTitleCache());
 
         // Herbaria map
-        Map<eu.etaxonomy.cdm.model.occurrence.Collection, Integer> collectionToCountMap = new HashMap<eu.etaxonomy.cdm.model.occurrence.Collection, Integer>();
+        Map<eu.etaxonomy.cdm.model.occurrence.Collection, Integer> collectionToCountMap = new HashMap<>();
         // List of accession numbers for citation
-        List<String> preservedSpecimenAccessionNumbers = new ArrayList<String>();
+        List<String> preservedSpecimenAccessionNumbers = new ArrayList<>();
 
         // assemble preserved specimen DTOs
         Set<DerivationEvent> derivationEvents = fieldUnit.getDerivationEvents();
@@ -463,6 +469,11 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
 
         // assemble citation
         String citation = fieldUnit.getTitleCache();
+        if((CdmUtils.isBlank(citation) || citation.equals(IdentifiableEntityDefaultCacheStrategy.TITLE_CACHE_GENERATION_NOT_IMPLEMENTED))
+                && !fieldUnit.isProtectedTitleCache()){
+            fieldUnit.setTitleCache(null);
+            citation = fieldUnit.getTitleCache();
+        }
         if (!preservedSpecimenAccessionNumbers.isEmpty()) {
             citation += " (";
             for (String accessionNumber : preservedSpecimenAccessionNumbers) {
@@ -518,18 +529,24 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
         }
         PreservedSpecimenDTO preservedSpecimenDTO = new PreservedSpecimenDTO();
 
-        // check identifiers in priority order accNo>barCode>catalogNumber
-        if (derivedUnit.getAccessionNumber() != null && !derivedUnit.getAccessionNumber().isEmpty()) {
-            preservedSpecimenDTO.setAccessionNumber(derivedUnit.getAccessionNumber());
+        //specimen identifier
+        FormatKey collectionKey = FormatKey.COLLECTION_CODE;
+        String specimenIdentifier = CdmFormatterFactory.format(derivedUnit, collectionKey);
+        if (CdmUtils.isBlank(specimenIdentifier)) {
+            collectionKey = FormatKey.COLLECTION_NAME;
         }
-        else if(derivedUnit.getBarcode()!=null && !derivedUnit.getBarcode().isEmpty()){
-            preservedSpecimenDTO.setAccessionNumber(derivedUnit.getBarcode());
-        }
-        else if(derivedUnit.getCatalogNumber()!=null && !derivedUnit.getCatalogNumber().isEmpty()){
-            preservedSpecimenDTO.setAccessionNumber(derivedUnit.getCatalogNumber());
+        specimenIdentifier = CdmFormatterFactory.format(derivedUnit, new FormatKey[] {
+                collectionKey, FormatKey.SPACE,
+                FormatKey.MOST_SIGNIFICANT_IDENTIFIER, FormatKey.SPACE });
+        if(CdmUtils.isBlank(specimenIdentifier)){
+            specimenIdentifier = derivedUnit.getUuid().toString();
         }
+        preservedSpecimenDTO.setAccessionNumber(specimenIdentifier);
         preservedSpecimenDTO.setUuid(derivedUnit.getUuid().toString());
 
+        //preferred stable URI
+        preservedSpecimenDTO.setPreferredStableUri(derivedUnit.getPreferredStableUri());
+
         // citation
         Collection<FieldUnit> fieldUnits = getFieldUnits(derivedUnit);
         if (fieldUnits.size() == 1) {
@@ -548,7 +565,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
         }
         for (DescriptionElementBase descriptionElementBase : characterDataForSpecimen) {
             String character = descriptionElementBase.getFeature().getLabel();
-            ArrayList<Language> languages = new ArrayList<Language>(Collections.singleton(Language.DEFAULT()));
+            ArrayList<Language> languages = new ArrayList<>(Collections.singleton(Language.DEFAULT()));
             if (descriptionElementBase instanceof QuantitativeData) {
                 QuantitativeData quantitativeData = (QuantitativeData) descriptionElementBase;
                 DefaultQuantitativeDescriptionBuilder builder = new DefaultQuantitativeDescriptionBuilder();
@@ -570,11 +587,11 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
             }
             TypeDesignationStatusBase<?> typeStatus = specimenTypeDesignation.getTypeStatus();
             if (typeStatus != null) {
-                List<String> typedTaxaNames = new ArrayList<String>();
+                List<String> typedTaxaNames = new ArrayList<>();
                 String label = typeStatus.getLabel();
-                Set<TaxonNameBase> typifiedNames = specimenTypeDesignation.getTypifiedNames();
-                for (TaxonNameBase taxonNameBase : typifiedNames) {
-                    typedTaxaNames.add(taxonNameBase.getFullTitleCache());
+                Set<TaxonName> typifiedNames = specimenTypeDesignation.getTypifiedNames();
+                for (TaxonName taxonName : typifiedNames) {
+                    typedTaxaNames.add(taxonName.getFullTitleCache());
                 }
                 preservedSpecimenDTO.addTypes(label, typedTaxaNames);
             }
@@ -587,8 +604,8 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
                 if (individualsAssociation.getInDescription().isInstanceOf(TaxonDescription.class)) {
                     TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(individualsAssociation.getInDescription(), TaxonDescription.class);
                     Taxon taxon = taxonDescription.getTaxon();
-                    if (taxon != null && taxon.getName() != null) {
-                        preservedSpecimenDTO.addAssociatedTaxon(taxon.getName().getTitleCache());
+                    if (taxon != null) {
+                        preservedSpecimenDTO.addAssociatedTaxon(taxon);
                     }
                 }
             }
@@ -622,18 +639,22 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
                             logger.error("Could not create BOLD URI", e1);
                         }
                         final DefinedTerm dnaMarker = sequence.getDnaMarker();
-                        MolecularData molecularData = derivateDataDTO.addProviderLink(boldUri != null ? boldUri : null, dnaMarker != null ? dnaMarker.getLabel() : "[no marker]");
+                        Link providerLink = null;
+                        if(boldUri!=null && dnaMarker!=null){
+                               providerLink = new DerivateDataDTO.Link(boldUri, dnaMarker.getLabel());
+                        }
+                        MolecularData molecularData = derivateDataDTO.addProviderLink(providerLink);
 
                         //contig file
                         ContigFile contigFile = null;
                         if (sequence.getContigFile() != null) {
                             MediaRepresentationPart contigMediaRepresentationPart = MediaUtils.getFirstMediaRepresentationPart(sequence.getContigFile());
                             if (contigMediaRepresentationPart != null) {
-                                contigFile = molecularData.addContigFile(contigMediaRepresentationPart.getUri(), "contig");
+                                contigFile = molecularData.addContigFile(new Link(contigMediaRepresentationPart.getUri(), "contig"));
                             }
                         }
-                        if(contigFile==null){
-                            contigFile = molecularData.addContigFile(null, "[no contig]");
+                        else{
+                               contigFile = molecularData.addContigFile(null);
                         }
                         // primer files
                         if (sequence.getSingleReads() != null) {
@@ -652,27 +673,29 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
             else if (childDerivate.isInstanceOf(MediaSpecimen.class)) {
                 MediaSpecimen media = HibernateProxyHelper.deproxy(childDerivate, MediaSpecimen.class);
 
-                String mediaUriString = getMediaUriString(media);
+                URI mediaUri = getMediaUri(media);
                 if (media.getKindOfUnit() != null) {
                     // specimen scan
-                    if (media.getKindOfUnit().getUuid().equals(UUID.fromString("acda15be-c0e2-4ea8-8783-b9b0c4ad7f03"))) {
+                    if (media.getKindOfUnit().getUuid().equals(DefinedTerm.uuidSpecimenScan)) {
                         derivateDataDTO.addSpecimenScanUuid(media.getMediaSpecimen().getUuid());
                         derivateDTO.setHasSpecimenScan(true);
                         String imageLinkText = "scan";
                         if (derivateDTO instanceof PreservedSpecimenDTO && ((PreservedSpecimenDTO) derivateDTO).getAccessionNumber() != null) {
                             imageLinkText = ((PreservedSpecimenDTO) derivateDTO).getAccessionNumber();
                         }
-                        derivateDataDTO.addSpecimenScan(mediaUriString == null ? "" : mediaUriString, imageLinkText);
+                        derivateDataDTO.addSpecimenScan(mediaUri, imageLinkText);
                     }
                     // detail image
-                    else if (media.getKindOfUnit().getUuid().equals(UUID.fromString("31eb8d02-bf5d-437c-bcc6-87a626445f34"))) {
+                    else if (media.getKindOfUnit().getUuid().equals(DefinedTerm.uuidDetailImage)) {
                         derivateDataDTO.addDetailImageUuid(media.getMediaSpecimen().getUuid());
                         derivateDTO.setHasDetailImage(true);
-                        String motif = "";
-                        if (media.getMediaSpecimen() != null && media.getMediaSpecimen().getTitle() != null) {
-                            motif = media.getMediaSpecimen().getTitle().getText();
+                        String motif = "detail image";
+                        if (media.getMediaSpecimen()!=null){
+                               if(CdmUtils.isNotBlank(media.getMediaSpecimen().getTitleCache())) {
+                                       motif = media.getMediaSpecimen().getTitleCache();
+                               }
                         }
-                        derivateDataDTO.addDetailImage(mediaUriString == null ? "" : mediaUriString, motif != null ? motif : "[no motif]");
+                        derivateDataDTO.addDetailImage(mediaUri, motif);
                     }
                 }
             }
@@ -687,15 +710,15 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
         return string;
     }
 
-    private String getMediaUriString(MediaSpecimen mediaSpecimen) {
-        String mediaUri = null;
+    private URI getMediaUri(MediaSpecimen mediaSpecimen) {
+        URI mediaUri = null;
         Collection<MediaRepresentation> mediaRepresentations = mediaSpecimen.getMediaSpecimen().getRepresentations();
         if (mediaRepresentations != null && !mediaRepresentations.isEmpty()) {
             Collection<MediaRepresentationPart> mediaRepresentationParts = mediaRepresentations.iterator().next().getParts();
             if (mediaRepresentationParts != null && !mediaRepresentationParts.isEmpty()) {
                 MediaRepresentationPart part = mediaRepresentationParts.iterator().next();
                 if (part.getUri() != null) {
-                    mediaUri = part.getUri().toASCIIString();
+                    mediaUri = part.getUri();
                 }
             }
         }
@@ -703,7 +726,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     }
 
     private Collection<DerivedUnit> getDerivedUnitsFor(SpecimenOrObservationBase<?> specimen) {
-        Collection<DerivedUnit> derivedUnits = new ArrayList<DerivedUnit>();
+        Collection<DerivedUnit> derivedUnits = new ArrayList<>();
         for (DerivationEvent derivationEvent : specimen.getDerivationEvents()) {
             for (DerivedUnit derivative : derivationEvent.getDerivatives()) {
                 derivedUnits.add(derivative);
@@ -719,9 +742,9 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
             Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
 
-        Set<Taxon> taxa = new HashSet<Taxon>();
-        Set<Integer> occurrenceIds = new HashSet<Integer>();
-        List<T> occurrences = new ArrayList<T>();
+        Set<Taxon> taxa = new HashSet<>();
+        Set<Integer> occurrenceIds = new HashSet<>();
+        List<T> occurrences = new ArrayList<>();
 
         // Integer limit = PagerUtils.limitFor(pageSize);
         // Integer start = PagerUtils.startFor(pageSize, pageNumber);
@@ -742,7 +765,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
                 occurrenceIds.add(o.getId());
             }
         }
-        occurrences = (List<T>) dao.listByIds(occurrenceIds, pageSize, pageNumber, orderHints, propertyPaths);
+        occurrences = (List<T>) dao.loadList(occurrenceIds, propertyPaths);
 
         return new DefaultPagerImpl<T>(pageNumber, occurrenceIds.size(), pageSize, occurrences);
 
@@ -764,14 +787,21 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     public Pager<SearchResult<SpecimenOrObservationBase>> findByFullText(
             Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle boundingBox, List<Language> languages,
             boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
-            List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {
+            List<String> propertyPaths) throws IOException, LuceneParseException {
 
         LuceneSearch luceneSearch = prepareByFullTextSearch(clazz, queryString, boundingBox, languages, highlightFragments);
 
         // --- execute search
-        TopGroups<BytesRef> topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
+        TopGroups<BytesRef> topDocsResultSet;
+        try {
+            topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
+        } catch (ParseException e) {
+            LuceneParseException parseException = new LuceneParseException(e.getMessage());
+            parseException.setStackTrace(e.getStackTrace());
+            throw parseException;
+        }
 
-        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
+        Map<CdmBaseType, String> idFieldMap = new HashMap<>();
         idFieldMap.put(CdmBaseType.SPECIMEN_OR_OBSERVATIONBASE, "id");
 
         // --- initialize taxa, highlight matches ....
@@ -829,7 +859,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
         // FIXME: use HQL queries to increase performance
         SpecimenOrObservationBase<?> specimen = load(derivedUnitUuid);
 //        specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
-        Collection<FieldUnit> fieldUnits = new ArrayList<FieldUnit>();
+        Collection<FieldUnit> fieldUnits = new ArrayList<>();
 
         if (specimen.isInstanceOf(FieldUnit.class)) {
             fieldUnits.add(HibernateProxyHelper.deproxy(specimen, FieldUnit.class));
@@ -841,7 +871,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     }
 
     private Collection<FieldUnit> getFieldUnits(DerivedUnit derivedUnit) {
-        Collection<FieldUnit> fieldUnits = new HashSet<FieldUnit>();
+        Collection<FieldUnit> fieldUnits = new HashSet<>();
         Set<SpecimenOrObservationBase> originals = derivedUnit.getOriginals();
         if (originals != null && !originals.isEmpty()) {
             for (SpecimenOrObservationBase<?> original : originals) {
@@ -978,10 +1008,10 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
         ---types TERM
         -preservationMethod
         --medium TERM
-        -storedUnder CDM TaxonNameBase
+        -storedUnder CDM TaxonName
          */
 
-        Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<ICdmBase>();
+        Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<>();
 
         //Choose the correct entry point to traverse the graph (FieldUnit or DerivedUnit)
 
@@ -1047,7 +1077,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
 
     private Collection<ICdmBase> getSpecimenOrObservationNonCascadedAssociatedElements(
             SpecimenOrObservationBase<?> specimen) {
-        Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<ICdmBase>();
+        Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<>();
         // scan SpecimenOrObservationBase
         for (DeterminationEvent determinationEvent : specimen.getDeterminations()) {
             // modifier
@@ -1071,32 +1101,34 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     }
 
     @Override
-    public DeleteResult isDeletable(SpecimenOrObservationBase specimen, DeleteConfiguratorBase config) {
+    public DeleteResult isDeletable(UUID specimenUuid, DeleteConfiguratorBase config) {
         DeleteResult deleteResult = new DeleteResult();
+        SpecimenOrObservationBase specimen = this.load(specimenUuid);
         SpecimenDeleteConfigurator specimenDeleteConfigurator = (SpecimenDeleteConfigurator) config;
 
         // check elements found by super method
-        Set<CdmBase> relatedObjects = super.isDeletable(specimen, config).getRelatedObjects();
+        Set<CdmBase> relatedObjects = super.isDeletable(specimenUuid, config).getRelatedObjects();
         for (CdmBase cdmBase : relatedObjects) {
             // check for type designation
             if (cdmBase.isInstanceOf(SpecimenTypeDesignation.class) && !specimenDeleteConfigurator.isDeleteFromTypeDesignation()) {
                 deleteResult.setAbort();
-                deleteResult.addException(new ReferencedObjectUndeletableException("Specimen is a type specimen."));
+                deleteResult.addException(new ReferencedObjectUndeletableException("Specimen or obeservation is a type specimen."));
                 deleteResult.addRelatedObject(cdmBase);
                 break;
             }
             // check for IndividualsAssociations
             else if (cdmBase.isInstanceOf(IndividualsAssociation.class) && !specimenDeleteConfigurator.isDeleteFromIndividualsAssociation()) {
                 deleteResult.setAbort();
-                deleteResult.addException(new ReferencedObjectUndeletableException("Specimen is still associated via IndividualsAssociations"));
+                deleteResult.addException(new ReferencedObjectUndeletableException("Specimen or obeservation is still associated via IndividualsAssociations"));
                 deleteResult.addRelatedObject(cdmBase);
                 break;
             }
-            // check for specimen/taxon description
-            else if((cdmBase.isInstanceOf(SpecimenDescription.class) || cdmBase.isInstanceOf(TaxonDescription.class))
+            // check for taxon description
+            else if(cdmBase.isInstanceOf(TaxonDescription.class)
+                    && HibernateProxyHelper.deproxy(cdmBase, TaxonDescription.class).getDescribedSpecimenOrObservation().equals(specimen)
                     && !specimenDeleteConfigurator.isDeleteFromDescription()){
                 deleteResult.setAbort();
-                deleteResult.addException(new ReferencedObjectUndeletableException("Specimen is still used in a Description."));
+                deleteResult.addException(new ReferencedObjectUndeletableException("Specimen or obeservation is still used as \"Described Specimen\" in a taxon description."));
                 deleteResult.addRelatedObject(cdmBase);
                 break;
             }
@@ -1113,7 +1145,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
                     if(!specimenDeleteConfigurator.isDeleteChildren()){
                         //if children should not be deleted then it is undeletable
                         deleteResult.setAbort();
-                        deleteResult.addException(new ReferencedObjectUndeletableException("Derivative still has child derivatives."));
+                        deleteResult.addException(new ReferencedObjectUndeletableException("Specimen or obeservation still has child derivatives."));
                         deleteResult.addRelatedObject(cdmBase);
                         break;
                     }
@@ -1122,7 +1154,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
                         Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
                         DeleteResult childResult = new DeleteResult();
                         for (DerivedUnit derivedUnit : derivatives) {
-                            childResult.includeResult(isDeletable(derivedUnit, specimenDeleteConfigurator));
+                            childResult.includeResult(isDeletable(derivedUnit.getUuid(), specimenDeleteConfigurator));
                         }
                         if (!childResult.isOk()) {
                             deleteResult.setAbort();
@@ -1134,14 +1166,18 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
                 }
             }
             // check for amplification
-            else if (cdmBase.isInstanceOf(AmplificationResult.class) && !specimenDeleteConfigurator.isDeleteMolecularData()) {
+            else if (cdmBase.isInstanceOf(AmplificationResult.class)
+                    && !specimenDeleteConfigurator.isDeleteMolecularData()
+                    && !specimenDeleteConfigurator.isDeleteChildren()) {
                 deleteResult.setAbort();
                 deleteResult.addException(new ReferencedObjectUndeletableException("DnaSample is used in amplification results."));
                 deleteResult.addRelatedObject(cdmBase);
                 break;
             }
             // check for sequence
-            else if (cdmBase.isInstanceOf(Sequence.class) && !specimenDeleteConfigurator.isDeleteMolecularData()) {
+            else if (cdmBase.isInstanceOf(Sequence.class)
+                    && !specimenDeleteConfigurator.isDeleteMolecularData()
+                    && !specimenDeleteConfigurator.isDeleteChildren()) {
                 deleteResult.setAbort();
                 deleteResult.addException(new ReferencedObjectUndeletableException("DnaSample is used in sequences."));
                 deleteResult.addRelatedObject(cdmBase);
@@ -1158,32 +1194,44 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     /**
      * {@inheritDoc}
      */
+    @Transactional(readOnly = false)
     @Override
     public DeleteResult delete(UUID specimenUuid, SpecimenDeleteConfigurator config) {
         return delete(load(specimenUuid), config);
     }
 
+
+    @Transactional(readOnly = false)
     @Override
     public DeleteResult delete(SpecimenOrObservationBase<?> specimen, SpecimenDeleteConfigurator config) {
         specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
 
+        DeleteResult deleteResult = isDeletable(specimen.getUuid(), config);
+        if (!deleteResult.isOk()) {
+            return deleteResult;
+        }
+
         if (config.isDeleteChildren()) {
             Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
             //clone to avoid concurrent modification
             //can happen if the child is deleted and deleted its own derivedFrom event
-            Set<DerivationEvent> derivationEventsClone = new HashSet<DerivationEvent>(derivationEvents);
+            Set<DerivationEvent> derivationEventsClone = new HashSet<>(derivationEvents);
             for (DerivationEvent derivationEvent : derivationEventsClone) {
                 Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
-                for (DerivedUnit derivedUnit : derivatives) {
-                    delete(derivedUnit, config);
+                Iterator<DerivedUnit> it = derivatives.iterator();
+                Set<DerivedUnit> derivativesToDelete = new HashSet<>();
+                while (it.hasNext()) {
+                    DerivedUnit unit = it.next();
+                    derivativesToDelete.add(unit);
+                }
+                for (DerivedUnit unit:derivativesToDelete){
+                    deleteResult.includeResult(delete(unit, config));
                 }
             }
         }
 
-        DeleteResult deleteResult = isDeletable(specimen, config);
-        if (!deleteResult.isOk()) {
-            return deleteResult;
-        }
+
+
 
         // check related objects
         Set<CdmBase> relatedObjects = deleteResult.getRelatedObjects();
@@ -1193,10 +1241,10 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
             if (relatedObject.isInstanceOf(SpecimenTypeDesignation.class)) {
                 SpecimenTypeDesignation designation = HibernateProxyHelper.deproxy(relatedObject, SpecimenTypeDesignation.class);
                 designation.setTypeSpecimen(null);
-                List<TaxonNameBase> typifiedNames = new ArrayList<TaxonNameBase>();
+                List<TaxonName> typifiedNames = new ArrayList<>();
                 typifiedNames.addAll(designation.getTypifiedNames());
-                for (TaxonNameBase taxonNameBase : typifiedNames) {
-                    taxonNameBase.removeTypeDesignation(designation);
+                for (TaxonName taxonName : typifiedNames) {
+                    taxonName.removeTypeDesignation(designation);
                 }
             }
             // delete IndividualsAssociation
@@ -1205,23 +1253,20 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
                 association.setAssociatedSpecimenOrObservation(null);
                 association.getInDescription().removeElement(association);
             }
-            // check for taxon description
+            // check for "described specimen" (deprecated)
             if (relatedObject.isInstanceOf(TaxonDescription.class)) {
-                TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(relatedObject, TaxonDescription.class);
-                taxonDescription.setDescribedSpecimenOrObservation(null);
+                TaxonDescription description = HibernateProxyHelper.deproxy(relatedObject, TaxonDescription.class);
+                description.setDescribedSpecimenOrObservation(null);
             }
             // check for specimen description
             if (relatedObject.isInstanceOf(SpecimenDescription.class)) {
                 SpecimenDescription specimenDescription = HibernateProxyHelper.deproxy(relatedObject, SpecimenDescription.class);
-                // check if specimen is "described" specimen
-                if (specimenDescription.getDescribedSpecimenOrObservation().equals(specimen)) {
-                    specimenDescription.setDescribedSpecimenOrObservation(null);
-                }
+                specimenDescription.setDescribedSpecimenOrObservation(null);
                 // check if description is a description of the given specimen
                 if (specimen.getDescriptions().contains(specimenDescription)) {
                     specimen.removeDescription(specimenDescription);
                 }
-                DeleteResult descriptionDelete = descriptionService.isDeletable(specimenDescription, null);
+                DeleteResult descriptionDelete = descriptionService.isDeletable(specimenDescription.getUuid(), null);
                 if (descriptionDelete.isOk()){
                     descriptionService.delete(specimenDescription);
                 }
@@ -1248,6 +1293,8 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
                             specimenOrObservationBase.removeDerivationEvent(derivationEvent);
                             deleteResult.addUpdatedObject(specimenOrObservationBase);
                         }
+                        // if derivationEvent has no derivates anymore, delete it
+                        eventService.delete(derivationEvent);
                     }
                 }
                 else{
@@ -1255,8 +1302,20 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
                 }
             }
         }
+        if (specimen instanceof FieldUnit){
+            FieldUnit fieldUnit = HibernateProxyHelper.deproxy(specimen, FieldUnit.class);
+            GatheringEvent event = fieldUnit.getGatheringEvent();
+            fieldUnit.setGatheringEvent(null);
+            if (event != null){
+                DeleteResult result = eventService.isDeletable(event.getUuid(), null);
+                if (result.isOk()){
+                    eventService.delete(event);
+                }
+            }
 
+        }
         deleteResult.includeResult(delete(specimen));
+
         return deleteResult;
     }
 
@@ -1265,46 +1324,68 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
         return dao.listIndividualsAssociations(specimen, null, null, null, null);
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public Collection<TaxonBase<?>> listAssociatedTaxa(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start,
             List<OrderHint> orderHints, List<String> propertyPaths) {
-        Collection<TaxonBase<?>> associatedTaxa = new HashSet<TaxonBase<?>>();
+        Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
 
         //individuals associations
-        for (IndividualsAssociation individualsAssociation : listIndividualsAssociations(specimen, limit, start, orderHints, propertyPaths)) {
-            if(individualsAssociation.getInDescription().isInstanceOf(TaxonDescription.class)){
-                TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(individualsAssociation.getInDescription(), TaxonDescription.class);
-                if(taxonDescription.getTaxon()!=null){
-                    associatedTaxa.add(taxonDescription.getTaxon());
-                }
-            }
-        }
+        associatedTaxa.addAll(listIndividualsAssociationTaxa(specimen, limit, start, orderHints, propertyPaths));
         //type designation
-        Collection<SpecimenTypeDesignation> typeDesignations = new HashSet<SpecimenTypeDesignation>();
-        for (SpecimenTypeDesignation typeDesignation : listTypeDesignations(specimen, limit, start, orderHints, propertyPaths)) {
-            if(typeDesignation.getTypeSpecimen().equals(specimen)){
-                Set<TaxonNameBase> typifiedNames = typeDesignation.getTypifiedNames();
-                for (TaxonNameBase taxonNameBase : typifiedNames) {
-                    associatedTaxa.addAll(taxonNameBase.getTaxa());
-                }
-            }
+        if(specimen.isInstanceOf(DerivedUnit.class)){
+            associatedTaxa.addAll(listTypeDesignationTaxa(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class), limit, start, orderHints, propertyPaths));
         }
         //determinations
-        Collection<DeterminationEvent> determinationEvents = new HashSet<DeterminationEvent>();
+        associatedTaxa.addAll(listDeterminedTaxa(specimen, limit, start, orderHints, propertyPaths));
+
+        return associatedTaxa;
+    }
+
+
+    @Override
+    public Collection<TaxonBase<?>> listDeterminedTaxa(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start,
+            List<OrderHint> orderHints, List<String> propertyPaths) {
+        Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
         for (DeterminationEvent determinationEvent : listDeterminationEvents(specimen, limit, start, orderHints, propertyPaths)) {
             if(determinationEvent.getIdentifiedUnit().equals(specimen)){
                 if(determinationEvent.getTaxon()!=null){
                     associatedTaxa.add(determinationEvent.getTaxon());
                 }
                 if(determinationEvent.getTaxonName()!=null){
-                    associatedTaxa.addAll(determinationEvent.getTaxonName().getTaxonBases());
+                    associatedTaxa.addAll((Collection)determinationEvent.getTaxonName().getTaxonBases());
                 }
             }
         }
+        return associatedTaxa;
+    }
 
+    @Override
+    public Collection<TaxonBase<?>> listTypeDesignationTaxa(DerivedUnit specimen, Integer limit, Integer start,
+            List<OrderHint> orderHints, List<String> propertyPaths) {
+        Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
+        for (SpecimenTypeDesignation typeDesignation : listTypeDesignations(specimen, limit, start, orderHints, propertyPaths)) {
+            if(typeDesignation.getTypeSpecimen().equals(specimen)){
+                Set<TaxonName> typifiedNames = typeDesignation.getTypifiedNames();
+                for (TaxonName taxonName : typifiedNames) {
+                    associatedTaxa.addAll(taxonName.getTaxa());
+                }
+            }
+        }
+        return associatedTaxa;
+    }
+
+    @Override
+    public Collection<TaxonBase<?>> listIndividualsAssociationTaxa(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start,
+            List<OrderHint> orderHints, List<String> propertyPaths) {
+        Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
+        for (IndividualsAssociation individualsAssociation : listIndividualsAssociations(specimen, limit, start, orderHints, propertyPaths)) {
+            if(individualsAssociation.getInDescription().isInstanceOf(TaxonDescription.class)){
+                TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(individualsAssociation.getInDescription(), TaxonDescription.class);
+                if(taxonDescription.getTaxon()!=null){
+                    associatedTaxa.add(taxonDescription.getTaxon());
+                }
+            }
+        }
         return associatedTaxa;
     }
 
@@ -1315,7 +1396,19 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     }
 
     @Override
-    public Collection<SpecimenTypeDesignation> listTypeDesignations(SpecimenOrObservationBase<?> specimen,
+    public Map<DerivedUnit, Collection<SpecimenTypeDesignation>> listTypeDesignations(
+            Collection<DerivedUnit> specimens, Integer limit, Integer start,
+            List<OrderHint> orderHints, List<String> propertyPaths) {
+        Map<DerivedUnit, Collection<SpecimenTypeDesignation>> typeDesignationMap = new HashMap<>();
+        for (DerivedUnit specimen : specimens) {
+            Collection<SpecimenTypeDesignation> typeDesignations = listTypeDesignations(specimen, limit, start, orderHints, propertyPaths);
+            typeDesignationMap.put(specimen, typeDesignations);
+        }
+        return typeDesignationMap;
+    }
+
+    @Override
+    public Collection<SpecimenTypeDesignation> listTypeDesignations(DerivedUnit specimen,
             Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
         return dao.listTypeDesignations(specimen, limit, start, orderHints, propertyPaths);
     }
@@ -1333,7 +1426,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
         if (specimen != null) {
             return specimen.characterData();
         }else{
-            return new ArrayList<DescriptionElementBase>();
+            return new ArrayList<>();
         }
     }
 
@@ -1349,12 +1442,38 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     }
 
 
+    @Override
+    public Integer countByTitle(IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config){
+        if (config instanceof FindOccurrencesConfigurator) {
+            FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
+            Taxon taxon = null;
+            if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
+                TaxonBase taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
+                if(taxonBase.isInstanceOf(Taxon.class)){
+                    taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
+                }
+            }
+            TaxonName taxonName = null;
+            if(occurrenceConfig.getAssociatedTaxonNameUuid()!=null){
+                taxonName = nameService.load(occurrenceConfig.getAssociatedTaxonNameUuid());
+            }
+            return dao.countOccurrences(occurrenceConfig.getClazz(),
+                    occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
+                    occurrenceConfig.getSpecimenType(), taxon, taxonName, occurrenceConfig.getMatchMode(), null, null,
+                    occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths());
+        }
+        else{
+            return dao.countByTitle(config.getTitleSearchString());
+        }
+
+    }
+
     @Override
     public Pager<SpecimenOrObservationBase> findByTitle(
             IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config) {
         if (config instanceof FindOccurrencesConfigurator) {
             FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
-            List<SpecimenOrObservationBase> occurrences = new ArrayList<SpecimenOrObservationBase>();
+            List<SpecimenOrObservationBase> occurrences = new ArrayList<>();
             Taxon taxon = null;
             if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
                 TaxonBase taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
@@ -1362,7 +1481,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
                     taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
                 }
             }
-            TaxonNameBase taxonName = null;
+            TaxonName taxonName = null;
             if(occurrenceConfig.getAssociatedTaxonNameUuid()!=null){
                 taxonName = nameService.load(occurrenceConfig.getAssociatedTaxonNameUuid());
             }
@@ -1370,9 +1489,28 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
                     occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
                     occurrenceConfig.getSpecimenType(), taxon, taxonName, occurrenceConfig.getMatchMode(), null, null,
                     occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths()));
+            //filter out (un-)assigned specimens
+            if(taxon==null && taxonName==null){
+                AssignmentStatus assignmentStatus = occurrenceConfig.getAssignmentStatus();
+                List<SpecimenOrObservationBase<?>> specimenWithAssociations = new ArrayList<>();
+                if(!assignmentStatus.equals(AssignmentStatus.ALL_SPECIMENS)){
+                    for (SpecimenOrObservationBase specimenOrObservationBase : occurrences) {
+                        Collection<TaxonBase<?>> associatedTaxa = listAssociatedTaxa(specimenOrObservationBase, null, null, null, null);
+                        if(!associatedTaxa.isEmpty()){
+                            specimenWithAssociations.add(specimenOrObservationBase);
+                        }
+                    }
+                }
+                if(assignmentStatus.equals(AssignmentStatus.UNASSIGNED_SPECIMENS)){
+                    occurrences.removeAll(specimenWithAssociations);
+                }
+                if(assignmentStatus.equals(AssignmentStatus.ASSIGNED_SPECIMENS)){
+                    occurrences = new ArrayList<>(specimenWithAssociations);
+                }
+            }
             // indirectly associated specimens
-            List<SpecimenOrObservationBase> indirectlyAssociatedOccurrences = new ArrayList<SpecimenOrObservationBase>(occurrences);
             if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens()){
+                List<SpecimenOrObservationBase> indirectlyAssociatedOccurrences = new ArrayList<>(occurrences);
                 for (SpecimenOrObservationBase specimen : occurrences) {
                     List<SpecimenOrObservationBase<?>> allHierarchyDerivates = getAllHierarchyDerivatives(specimen);
                     for (SpecimenOrObservationBase<?> specimenOrObservationBase : allHierarchyDerivates) {
@@ -1391,7 +1529,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
 
     @Override
     public List<SpecimenOrObservationBase<?>> getAllHierarchyDerivatives(SpecimenOrObservationBase<?> specimen){
-        List<SpecimenOrObservationBase<?>> allHierarchyDerivatives = new ArrayList<SpecimenOrObservationBase<?>>();
+        List<SpecimenOrObservationBase<?>> allHierarchyDerivatives = new ArrayList<>();
         Collection<FieldUnit> fieldUnits = getFieldUnits(specimen.getUuid());
         if(fieldUnits.isEmpty()){
             allHierarchyDerivatives.add(specimen);
@@ -1406,15 +1544,23 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
         return allHierarchyDerivatives;
     }
 
+    @Override
+    public List<DerivedUnit> getAllChildDerivatives(UUID specimenUuid){
+        return getAllChildDerivatives(load(specimenUuid));
+    }
+
     @Override
     public List<DerivedUnit> getAllChildDerivatives(SpecimenOrObservationBase<?> specimen){
-        List<DerivedUnit> childDerivate = new ArrayList<DerivedUnit>();
+        if (specimen == null){
+            return null;
+        }
+        List<DerivedUnit> childDerivate = new ArrayList<>();
         Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
         for (DerivationEvent derivationEvent : derivationEvents) {
             Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
             for (DerivedUnit derivedUnit : derivatives) {
                 childDerivate.add(derivedUnit);
-                childDerivate.addAll(getAllChildDerivatives(derivedUnit));
+                childDerivate.addAll(getAllChildDerivatives(derivedUnit.getUuid()));
             }
         }
         return childDerivate;
@@ -1422,29 +1568,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
 
     @Override
     public int countOccurrences(IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config){
-        if (config instanceof FindOccurrencesConfigurator) {
-            FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
-            Taxon taxon = null;
-            if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
-                TaxonBase taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
-                if(taxonBase.isInstanceOf(Taxon.class)){
-                    taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
-                }
-            }
-            TaxonNameBase taxonName = null;
-            if(occurrenceConfig.getAssociatedTaxonNameUuid()!=null){
-                taxonName = nameService.load(occurrenceConfig.getAssociatedTaxonNameUuid());
-            }
-            // indirectly associated specimens
-            if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens()){
-                return findByTitle(config).getRecords().size();
-            }
-            return dao.countOccurrences(occurrenceConfig.getClazz(), occurrenceConfig.getTitleSearchString(),
-                    occurrenceConfig.getSignificantIdentifier(), occurrenceConfig.getSpecimenType(), taxon, taxonName,
-                    occurrenceConfig.getMatchMode(), null, null, occurrenceConfig.getOrderHints(),
-                    occurrenceConfig.getPropertyPaths());
-        }
-        return super.countByTitle(config);
+        return countByTitle(config);
     }
 
 }