Do not show empty brackets if no identifier is present for specimen
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / OccurrenceServiceImpl.java
index 559674ce2004feeb28c04e366a72a870a54aea9a..8a56608e9baf1c443a62aca83eca735e48688f67 100644 (file)
@@ -48,6 +48,7 @@ import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase;
 import eu.etaxonomy.cdm.api.service.config.FindOccurrencesConfigurator;
 import eu.etaxonomy.cdm.api.service.config.IIdentifiableEntityServiceConfigurator;
 import eu.etaxonomy.cdm.api.service.config.SpecimenDeleteConfigurator;
+import eu.etaxonomy.cdm.api.service.dto.DNASampleDTO;
 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;
@@ -75,8 +76,6 @@ 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.Language;
 import eu.etaxonomy.cdm.model.description.CategoricalData;
 import eu.etaxonomy.cdm.model.description.DescriptionBase;
@@ -87,6 +86,7 @@ import eu.etaxonomy.cdm.model.description.SpecimenDescription;
 import eu.etaxonomy.cdm.model.description.TaxonDescription;
 import eu.etaxonomy.cdm.model.location.Country;
 import eu.etaxonomy.cdm.model.location.NamedArea;
+import eu.etaxonomy.cdm.model.location.Point;
 import eu.etaxonomy.cdm.model.media.Media;
 import eu.etaxonomy.cdm.model.media.MediaRepresentation;
 import eu.etaxonomy.cdm.model.media.MediaRepresentationPart;
@@ -108,10 +108,12 @@ import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;
 import eu.etaxonomy.cdm.model.taxon.Taxon;
 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
-import eu.etaxonomy.cdm.persistence.dao.common.IDefinedTermDao;
+import eu.etaxonomy.cdm.model.term.DefinedTerm;
+import eu.etaxonomy.cdm.model.term.DefinedTermBase;
 import eu.etaxonomy.cdm.persistence.dao.initializer.AbstractBeanInitializer;
-import eu.etaxonomy.cdm.persistence.dao.molecular.ISingleReadDao;
 import eu.etaxonomy.cdm.persistence.dao.occurrence.IOccurrenceDao;
+import eu.etaxonomy.cdm.persistence.dao.term.IDefinedTermDao;
+import eu.etaxonomy.cdm.persistence.dto.SpecimenNodeWrapper;
 import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
 import eu.etaxonomy.cdm.persistence.query.AssignmentStatus;
 import eu.etaxonomy.cdm.persistence.query.OrderHint;
@@ -120,7 +122,7 @@ import eu.etaxonomy.cdm.strategy.cache.common.IdentifiableEntityDefaultCacheStra
 
 /**
  * @author a.babadshanjan
- * @created 01.09.2008
+ * @since 01.09.2008
  */
 @Service
 @Transactional(readOnly = true)
@@ -146,9 +148,6 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     @Autowired
     private ISequenceService sequenceService;
 
-    @Autowired
-    private ISingleReadDao singleReadDao;
-
     @Autowired
     private AbstractBeanInitializer beanInitializer;
 
@@ -157,6 +156,16 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
 
     private static final String SEPARATOR_STRING = ", ";
 
+    private static final List<String> DERIVED_UNIT_INIT_STRATEGY =  Arrays.asList(new String []{
+            "derivedFrom.derivatives",
+            "derivedFrom.originals",
+            "specimenTypeDesignations.*",
+            "specimenTypeDesignations.citation.*",
+            "specimenTypeDesignations.homotypicalGroup.*",
+            "specimenTypeDesignations.typifiedNames",
+            "collection.$"
+    });
+
     public OccurrenceServiceImpl() {
         logger.debug("Load OccurrenceService Bean");
     }
@@ -164,11 +173,11 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
 
     @Override
     @Transactional(readOnly = false)
-    public void updateTitleCache(Class<? extends SpecimenOrObservationBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<SpecimenOrObservationBase> cacheStrategy, IProgressMonitor monitor) {
+    public UpdateResult updateCaches(Class<? extends SpecimenOrObservationBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<SpecimenOrObservationBase> cacheStrategy, IProgressMonitor monitor) {
         if (clazz == null) {
             clazz = SpecimenOrObservationBase.class;
         }
-        super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
+        return super.updateCachesImpl(clazz, stepSize, cacheStrategy, monitor);
     }
 
     /**
@@ -187,7 +196,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<? extends DefinedTermBase> terms = this.definedTermDao.findByTitleWithRestrictions(Country.class, name, null, null, null, null, null, null);
         List<Country> countries = new ArrayList<>();
         for (int i = 0; i < terms.size(); i++) {
             countries.add((Country) terms.get(i));
@@ -203,7 +212,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
 
     @Override
     public Pager<DerivationEvent> getDerivationEvents(SpecimenOrObservationBase occurence, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
-        Integer numberOfResults = dao.countDerivationEvents(occurence);
+        long numberOfResults = dao.countDerivationEvents(occurence);
 
         List<DerivationEvent> results = new ArrayList<>();
         if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
@@ -214,25 +223,26 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     }
 
     @Override
-    public int countDeterminations(SpecimenOrObservationBase occurence, TaxonBase taxonbase) {
+    public long countDeterminations(SpecimenOrObservationBase occurence, TaxonBase taxonbase) {
         return dao.countDeterminations(occurence, taxonbase);
     }
 
     @Override
-    public Pager<DeterminationEvent> getDeterminations(SpecimenOrObservationBase occurrence, TaxonBase taxonBase, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
-        Integer numberOfResults = dao.countDeterminations(occurrence, taxonBase);
+    public Pager<DeterminationEvent> getDeterminations(SpecimenOrObservationBase occurrence, TaxonBase taxonBase,
+            Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
+        long numberOfResults = dao.countDeterminations(occurrence, taxonBase);
 
         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);
         }
 
-        return new DefaultPagerImpl<DeterminationEvent>(pageNumber, numberOfResults, pageSize, results);
+        return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
     }
 
     @Override
     public Pager<Media> getMedia(SpecimenOrObservationBase occurence, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
-        Integer numberOfResults = dao.countMedia(occurence);
+        long numberOfResults = dao.countMedia(occurence);
 
         List<Media> results = new ArrayList<>();
         if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
@@ -275,31 +285,31 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
                 }
             }
         }
-        return new DefaultPagerImpl<Media>(pageNumber, media.size(), pageSize, media);
+        return new DefaultPagerImpl<Media>(pageNumber, Long.valueOf(media.size()), pageSize, media);
     }
 
     @Override
-    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);
+    public Pager<SpecimenOrObservationBase> list(Class<? extends SpecimenOrObservationBase> type, TaxonName determinedAs,
+            Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
+        long numberOfResults = dao.count(type, determinedAs);
+        @SuppressWarnings("rawtypes")
         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;
-            results = dao.list(type, determinedAs, pageSize, start, orderHints, propertyPaths);
+            results = dao.list(type, determinedAs, pageSize, pageNumber, orderHints, propertyPaths);
         }
-        return new DefaultPagerImpl<SpecimenOrObservationBase>(pageNumber, numberOfResults, pageSize, results);
+        return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
     }
 
     @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);
+    public Pager<SpecimenOrObservationBase> list(Class<? extends SpecimenOrObservationBase> type, TaxonBase determinedAs,
+            Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
+        long numberOfResults = dao.count(type, determinedAs);
+        @SuppressWarnings("rawtypes")
         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;
-            results = dao.list(type, determinedAs, pageSize, start, orderHints, propertyPaths);
+            results = dao.list(type, determinedAs, pageSize, pageNumber, orderHints, propertyPaths);
         }
-        return new DefaultPagerImpl<SpecimenOrObservationBase>(pageNumber, numberOfResults, pageSize, results);
+        return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
     }
 
     @Override
@@ -313,18 +323,18 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     }
 
     @Override
-    public DerivedUnitFacade getDerivedUnitFacade(DerivedUnit derivedUnit, List<String> propertyPaths) throws DerivedUnitFacadeNotSupportedException {
+    public DerivedUnitFacade getDerivedUnitFacade(DerivedUnit derivedUnit, List<String> derivedUnitFacadeInitStrategy) throws DerivedUnitFacadeNotSupportedException {
         derivedUnit = (DerivedUnit) dao.load(derivedUnit.getUuid(), null);
         DerivedUnitFacadeConfigurator config = DerivedUnitFacadeConfigurator.NewInstance();
         config.setThrowExceptionForNonSpecimenPreservationMethodRequest(false);
         DerivedUnitFacade derivedUnitFacade = DerivedUnitFacade.NewInstance(derivedUnit, config);
-        beanInitializer.initialize(derivedUnitFacade, propertyPaths);
+        beanInitializer.initialize(derivedUnitFacade, derivedUnitFacadeInitStrategy);
         return derivedUnitFacade;
     }
 
     @Override
     public List<DerivedUnitFacade> listDerivedUnitFacades(
-            DescriptionBase description, List<String> propertyPaths) {
+            DescriptionBase description, List<String> derivedUnitFacadeInitStrategy) {
 
         List<DerivedUnitFacade> derivedUnitFacadeList = new ArrayList<>();
         IndividualsAssociation tempIndividualsAssociation;
@@ -344,7 +354,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
             }
         }
 
-        beanInitializer.initializeAll(derivedUnitFacadeList, propertyPaths);
+        beanInitializer.initializeAll(derivedUnitFacadeList, derivedUnitFacadeInitStrategy);
 
         return derivedUnitFacadeList;
     }
@@ -357,6 +367,12 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
         return pageByAssociatedTaxon(type, includeRelationships, associatedTaxon, maxDepth, pageSize, pageNumber, orderHints, propertyPaths).getRecords();
     }
 
+    @Override
+    public Collection<SpecimenNodeWrapper> listUuidAndTitleCacheByAssociatedTaxon(List<UUID> taxonNodeUuids,
+            Integer limit, Integer start) {
+        return dao.listUuidAndTitleCacheByAssociatedTaxon(taxonNodeUuids, limit, start);
+        }
+
     @Override
     public Collection<SpecimenOrObservationBase> listFieldUnitsByAssociatedTaxon(Taxon associatedTaxon, List<OrderHint> orderHints, List<String> propertyPaths) {
         return pageFieldUnitsByAssociatedTaxon(null, associatedTaxon, null, null, null, null, propertyPaths).getRecords();
@@ -375,24 +391,23 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
         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(), null)) {
+            for (FieldUnit fieldUnit : findFieldUnits(specimen.getUuid(), null)) {
                 fieldUnitUuids.add(fieldUnit.getUuid());
             }
         }
         //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);
+        return new DefaultPagerImpl<>(pageNumber, fieldUnitUuids.size(), pageSize, fieldUnits);
     }
 
     @Override
-    public FieldUnitDTO assembleFieldUnitDTO(FieldUnit fieldUnit, UUID associatedTaxonUuid) {
+    public FieldUnitDTO assembleFieldUnitDTO(FieldUnit fieldUnit) {
 
         if (!getSession().contains(fieldUnit)) {
             fieldUnit = (FieldUnit) load(fieldUnit.getUuid());
         }
-        TaxonBase associatedTaxon = taxonService.load(associatedTaxonUuid);
 
-        FieldUnitDTO fieldUnitDTO = new FieldUnitDTO();
+        FieldUnitDTO fieldUnitDTO = new FieldUnitDTO(fieldUnit);
 
         if (fieldUnit.getGatheringEvent() != null) {
             GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent();
@@ -411,7 +426,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
                 collectionString += (fieldNumber != null ? fieldNumber : "");
                 collectionString.trim();
             }
-            fieldUnitDTO.setCollection(collectionString);
+            fieldUnitDTO.setCollectingString(collectionString);
             // Date
             Partial gatheringDate = gatheringEvent.getGatheringDate();
             String dateString = null;
@@ -424,9 +439,6 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
             fieldUnitDTO.setDate(dateString);
         }
 
-        // Taxon Name
-        fieldUnitDTO.setTaxonName(associatedTaxon.getName().getTitleCache());
-
         // Herbaria map
         Map<eu.etaxonomy.cdm.model.occurrence.Collection, Integer> collectionToCountMap = new HashMap<>();
         // List of accession numbers for citation
@@ -441,21 +453,27 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
                     continue;
                 }
                 // collect accession numbers for citation
-                String mostSignificantIdentifier = getMostSignificantIdentifier(derivedUnit);
-                if (mostSignificantIdentifier != null) {
-                    preservedSpecimenAccessionNumbers.add(mostSignificantIdentifier);
-                }
+                String identifier = getMostSignificantIdentifier(derivedUnit);
                 // collect collections for herbaria column
-                if (derivedUnit.getCollection() != null) {
-                    Integer herbariumCount = collectionToCountMap.get(derivedUnit.getCollection());
+                eu.etaxonomy.cdm.model.occurrence.Collection collection = derivedUnit.getCollection();
+                if (collection != null) {
+                    //combine collection with identifier
+                    if (identifier != null) {
+                        if(collection.getCode()!=null){
+                            identifier = (collection.getCode()!=null?collection.getCode():"[no collection]")+" "+identifier;
+                        }
+                        preservedSpecimenAccessionNumbers.add(identifier);
+                    }
+
+                    Integer herbariumCount = collectionToCountMap.get(collection);
                     if (herbariumCount == null) {
                         herbariumCount = 0;
                     }
-                    collectionToCountMap.put(derivedUnit.getCollection(), herbariumCount + 1);
+                    collectionToCountMap.put(collection, herbariumCount + 1);
                 }
                 if (derivedUnit.getRecordBasis().equals(SpecimenOrObservationType.PreservedSpecimen)) {
                     PreservedSpecimenDTO preservedSpecimenDTO = assemblePreservedSpecimenDTO(derivedUnit, fieldUnitDTO);
-                    fieldUnitDTO.addPreservedSpecimenDTO(preservedSpecimenDTO);
+                    fieldUnitDTO.addDerivate(preservedSpecimenDTO);
                     fieldUnitDTO.setHasCharacterData(fieldUnitDTO.isHasCharacterData() || preservedSpecimenDTO.isHasCharacterData());
                     fieldUnitDTO.setHasDetailImage(fieldUnitDTO.isHasDetailImage() || preservedSpecimenDTO.isHasDetailImage());
                     fieldUnitDTO.setHasDna(fieldUnitDTO.isHasDna() || preservedSpecimenDTO.isHasDna());
@@ -498,7 +516,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
             herbariaString += SEPARATOR_STRING;
         }
         herbariaString = removeTail(herbariaString, SEPARATOR_STRING);
-        fieldUnitDTO.setHerbarium(herbariaString);
+        fieldUnitDTO.setCollection(herbariaString);
 
         return fieldUnitDTO;
     }
@@ -526,7 +544,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
         if (!getSession().contains(derivedUnit)) {
             derivedUnit = (DerivedUnit) load(derivedUnit.getUuid());
         }
-        PreservedSpecimenDTO preservedSpecimenDTO = new PreservedSpecimenDTO();
+        PreservedSpecimenDTO preservedSpecimenDTO = new PreservedSpecimenDTO(derivedUnit);
 
         //specimen identifier
         FormatKey collectionKey = FormatKey.COLLECTION_CODE;
@@ -534,14 +552,19 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
         if (CdmUtils.isBlank(specimenIdentifier)) {
             collectionKey = FormatKey.COLLECTION_NAME;
         }
-        specimenIdentifier = CdmFormatterFactory.format(derivedUnit, new FormatKey[] {
-                collectionKey, FormatKey.SPACE,
-                FormatKey.MOST_SIGNIFICANT_IDENTIFIER, FormatKey.SPACE });
+        if(CdmUtils.isNotBlank(derivedUnit.getMostSignificantIdentifier())){
+            specimenIdentifier = CdmFormatterFactory.format(derivedUnit, new FormatKey[] {
+                    collectionKey, FormatKey.SPACE, FormatKey.OPEN_BRACKET,
+                    FormatKey.MOST_SIGNIFICANT_IDENTIFIER, FormatKey.CLOSE_BRACKET });
+        }
+        if(CdmUtils.isBlank(specimenIdentifier)){
+            specimenIdentifier = derivedUnit.getTitleCache();
+        }
         if(CdmUtils.isBlank(specimenIdentifier)){
             specimenIdentifier = derivedUnit.getUuid().toString();
         }
         preservedSpecimenDTO.setAccessionNumber(specimenIdentifier);
-        preservedSpecimenDTO.setUuid(derivedUnit.getUuid().toString());
+
 
         //preferred stable URI
         preservedSpecimenDTO.setPreferredStableUri(derivedUnit.getPreferredStableUri());
@@ -585,15 +608,12 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
                 fieldUnitDTO.setHasType(true);
             }
             TypeDesignationStatusBase<?> typeStatus = specimenTypeDesignation.getTypeStatus();
-            if (typeStatus != null) {
-                List<String> typedTaxaNames = new ArrayList<>();
-                String label = typeStatus.getLabel();
-                Set<TaxonName> typifiedNames = specimenTypeDesignation.getTypifiedNames();
-                for (TaxonName taxonName : typifiedNames) {
-                    typedTaxaNames.add(taxonName.getFullTitleCache());
-                }
-                preservedSpecimenDTO.addTypes(label, typedTaxaNames);
+            Set<TaxonName> typifiedNames = specimenTypeDesignation.getTypifiedNames();
+            List<String> typedTaxaNames = new ArrayList<>();
+            for (TaxonName taxonName : typifiedNames) {
+                typedTaxaNames.add(taxonName.getTitleCache());
             }
+            preservedSpecimenDTO.addTypes(typeStatus!=null?typeStatus.getLabel():"", typedTaxaNames);
         }
 
         // individuals associations
@@ -735,15 +755,58 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
         return derivedUnits;
     }
 
+    private Set<DerivateDTO> getDerivedUnitDTOsFor(DerivateDTO specimenDto, DerivedUnit specimen, HashMap<UUID, DerivateDTO> alreadyCollectedSpecimen) {
+        Set<DerivateDTO> derivedUnits = new HashSet<>();
+//        load
+        for (DerivationEvent derivationEvent : specimen.getDerivationEvents()) {
+            for (DerivedUnit derivative : derivationEvent.getDerivatives()) {
+                if (!alreadyCollectedSpecimen.containsKey(specimenDto.getUuid())){
+                    PreservedSpecimenDTO dto;
+                    if (derivative instanceof DnaSample) {
+                        dto = new DNASampleDTO(derivative);
+                    } else {
+                        dto = new PreservedSpecimenDTO(derivative);
+                    }
+                    alreadyCollectedSpecimen.put(dto.getUuid(), dto);
+                    dto.addAllDerivates(getDerivedUnitDTOsFor(dto, derivative, alreadyCollectedSpecimen));
+                    derivedUnits.add(dto);
+                }
+            }
+        }
+        return derivedUnits;
+    }
+
+//    private Set<DerivateDTO> getDerivedUnitDTOsFor(DerivateDTO specimenDto, DerivedUnit specimen, HashMap<UUID, DerivateDTO> alreadyCollectedSpecimen) {
+//        Set<DerivateDTO> derivedUnits = new HashSet<>();
+////        load
+//        for (DerivationEvent derivationEvent : specimen.getDerivationEvents()) {
+//            for (DerivedUnit derivative : derivationEvent.getDerivatives()) {
+//                if (!alreadyCollectedSpecimen.containsKey(specimenDto.getUuid())){
+//                    PreservedSpecimenDTO dto;
+//                    if (derivative instanceof DnaSample){
+//                        dto = DNASampleDTO.newInstance(derivative);
+//                    }else{
+//                        dto = PreservedSpecimenDTO.newInstance(derivative);
+//                    }
+//                    alreadyCollectedSpecimen.put(dto.getUuid(), dto);
+//                    dto.addAllDerivates(getDerivedUnitDTOsFor(dto, derivative, alreadyCollectedSpecimen));
+//                    derivedUnits.add(dto);
+//                }
+//            }
+//        }
+//        return derivedUnits;
+//    }
+
 
     @SuppressWarnings("unchecked")
     @Override
-    public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
+    public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includedRelationships,
             Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
 
         Set<Taxon> taxa = new HashSet<>();
         Set<Integer> occurrenceIds = new HashSet<>();
         List<T> occurrences = new ArrayList<>();
+        boolean includeUnpublished = INCLUDE_UNPUBLISHED;
 
         // Integer limit = PagerUtils.limitFor(pageSize);
         // Integer start = PagerUtils.startFor(pageSize, pageNumber);
@@ -752,21 +815,21 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
             associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid());
         }
 
-        if (includeRelationships != null) {
-            taxa = taxonService.listRelatedTaxa(associatedTaxon, includeRelationships, maxDepth, null, null, propertyPaths);
+        if (includedRelationships != null) {
+            taxa = taxonService.listRelatedTaxa(associatedTaxon, includedRelationships, maxDepth, includeUnpublished, null, null, propertyPaths);
         }
 
         taxa.add(associatedTaxon);
 
         for (Taxon taxon : taxa) {
             List<T> perTaxonOccurrences = dao.listByAssociatedTaxon(type, taxon, null, null, orderHints, propertyPaths);
-            for (SpecimenOrObservationBase o : perTaxonOccurrences) {
+            for (SpecimenOrObservationBase<?> o : perTaxonOccurrences) {
                 occurrenceIds.add(o.getId());
             }
         }
-        occurrences = (List<T>) dao.loadList(occurrenceIds, propertyPaths);
+        occurrences = (List<T>) dao.loadList(occurrenceIds, null, propertyPaths);
 
-        return new DefaultPagerImpl<T>(pageNumber, occurrenceIds.size(), pageSize, occurrences);
+        return new DefaultPagerImpl<T>(pageNumber, Long.valueOf(occurrences.size()), pageSize, occurrences);
 
     }
 
@@ -782,6 +845,81 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
 
     }
 
+    @Override
+    public List<FieldUnitDTO> findFieldUnitDTOByAssociatedTaxon(Set<TaxonRelationshipEdge> includedRelationships,
+            UUID associatedTaxonUuid) {
+
+        Set<Taxon> taxa = new HashSet<>();
+        Set<Integer> occurrenceIds = new HashSet<>();
+        List<FieldUnitDTO> fieldUnitDTOs = new ArrayList<>();
+        HashMap<UUID, DerivateDTO> alreadyCollectedSpecimen = new HashMap<>();
+        List<SpecimenOrObservationBase> occurrences = new ArrayList<>();
+        boolean includeUnpublished = INCLUDE_UNPUBLISHED;
+
+        // Integer limit = PagerUtils.limitFor(pageSize);
+        // Integer start = PagerUtils.startFor(pageSize, pageNumber);
+
+        Taxon associatedTaxon = (Taxon) taxonService.load(associatedTaxonUuid);
+
+
+        if (includedRelationships != null) {
+            taxa = taxonService.listRelatedTaxa(associatedTaxon, includedRelationships, null, includeUnpublished, null, null, null);
+        }
+
+        taxa.add(associatedTaxon);
+
+        for (Taxon taxon : taxa) {
+            List<SpecimenOrObservationBase> perTaxonOccurrences = dao.listByAssociatedTaxon(null,taxon, null, null, null, DERIVED_UNIT_INIT_STRATEGY);
+            for (SpecimenOrObservationBase<?> o : perTaxonOccurrences) {
+                if (o.isInstanceOf(DerivedUnit.class)){
+                    DerivedUnit derivedUnit;
+                    DerivateDTO derivedUnitDTO;
+                    if (o.isInstanceOf(DnaSample.class)) {
+                         derivedUnit = HibernateProxyHelper.deproxy(o, DnaSample.class);
+                        derivedUnitDTO = new DNASampleDTO(derivedUnit);
+                    } else {
+                        derivedUnit = HibernateProxyHelper.deproxy(o, DerivedUnit.class);
+                        derivedUnitDTO = new PreservedSpecimenDTO(derivedUnit);
+                    }
+                    if (alreadyCollectedSpecimen.get(derivedUnitDTO.getUuid()) == null){
+                        alreadyCollectedSpecimen.put(derivedUnitDTO.getUuid(), derivedUnitDTO);
+                    }
+                    derivedUnitDTO.addAllDerivates(getDerivedUnitDTOsFor(derivedUnitDTO, derivedUnit, alreadyCollectedSpecimen));
+                    this.findFieldUnitDTO(derivedUnitDTO, fieldUnitDTOs,
+                            alreadyCollectedSpecimen);
+                }
+            }
+
+        }
+
+        return fieldUnitDTOs;
+
+    }
+
+
+
+    @Override
+    public  FieldUnitDTO findByAccessionNumber(
+            String accessionNumberString, List<OrderHint> orderHints,
+            List<String> propertyPaths)  {
+
+        DnaSample dnaSample = dao.findByGeneticAccessionNumber(accessionNumberString, propertyPaths);
+        DerivateDTO derivedUnitDTO;
+        HashMap<UUID, DerivateDTO> alreadyCollectedSpecimen = new HashMap<>();
+        List<FieldUnitDTO> fieldUnitDTOs = new ArrayList<>();
+        if (dnaSample != null){
+            derivedUnitDTO = new DNASampleDTO(dnaSample);
+            alreadyCollectedSpecimen.put(derivedUnitDTO.getUuid(), derivedUnitDTO);
+            derivedUnitDTO.addAllDerivates(getDerivedUnitDTOsFor(derivedUnitDTO, dnaSample, alreadyCollectedSpecimen));
+            FieldUnitDTO fieldUnit = this.findFieldUnitDTO(derivedUnitDTO, fieldUnitDTOs,
+                    alreadyCollectedSpecimen);
+
+            return fieldUnit;
+        }
+        return null;
+
+    }
+
     @Override
     public Pager<SearchResult<SpecimenOrObservationBase>> findByFullText(
             Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle boundingBox, List<Language> languages,
@@ -811,8 +949,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
 
         int totalHits = topDocsResultSet != null ? topDocsResultSet.totalGroupCount : 0;
 
-        return new DefaultPagerImpl<SearchResult<SpecimenOrObservationBase>>(pageNumber, totalHits, pageSize,
-                searchResults);
+        return new DefaultPagerImpl<>(pageNumber, Long.valueOf(totalHits), pageSize, searchResults);
 
     }
 
@@ -851,15 +988,18 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
 
 
     @Override
-    public Collection<FieldUnit> getFieldUnits(UUID derivedUnitUuid, List<String> propertyPaths) {
+    public Collection<FieldUnit> findFieldUnits(UUID derivedUnitUuid, List<String> propertyPaths) {
         //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, propertyPaths);
 //        specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
         Collection<FieldUnit> fieldUnits = new ArrayList<>();
-
+        if (specimen == null){
+            return null;
+        }
         if (specimen.isInstanceOf(FieldUnit.class)) {
             fieldUnits.add(HibernateProxyHelper.deproxy(specimen, FieldUnit.class));
         }
@@ -885,6 +1025,150 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
         return fieldUnits;
     }
 
+
+
+    @Override
+    @Transactional(readOnly=true)
+    public FieldUnitDTO findFieldUnitDTO(DerivateDTO derivedUnitDTO, Collection<FieldUnitDTO> fieldUnits,
+            HashMap<UUID, DerivateDTO> alreadyCollectedSpecimen) {
+        //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.
+        List<SpecimenOrObservationBase> specimens = new ArrayList<>();
+        List<String> propertyPaths = new ArrayList<>();
+
+        propertyPaths.add("descriptions.elements.media.title");
+        propertyPaths.add("kindOfUnit");
+        propertyPaths.add("derivedFrom");
+
+        specimens = dao.findOriginalsForDerivedUnit(derivedUnitDTO.getUuid(), propertyPaths);
+
+        if (specimens.size() > 1){
+            logger.debug("The derived unit with uuid " + derivedUnitDTO.getUuid() + "has more than one orginal");
+        }
+      //  for (SpecimenOrObservationBase specimen: specimens){
+        SpecimenOrObservationBase specimen = null;
+        if (specimens.size() > 0){
+            specimen = specimens.get(0);
+        }else{
+            return null;
+        }
+        FieldUnitDTO fieldUnitDto = null;
+        if (alreadyCollectedSpecimen.get(specimen.getUuid()) != null){
+            alreadyCollectedSpecimen.get(specimen.getUuid()).addDerivate(derivedUnitDTO);
+//            if ( alreadyCollectedSpecimen.get(specimen.getUuid()) instanceof FieldUnitDTO){
+//                ((FieldUnitDTO)alreadyCollectedSpecimen.get(specimen.getUuid())).getTaxonRelatedDerivedUnits().add(derivedUnitDTO.getUuid());
+//            }
+        }else{
+            if (specimen.isInstanceOf(FieldUnit.class)){
+                fieldUnitDto = new FieldUnitDTO((FieldUnit)specimen);
+                fieldUnitDto.addDerivate(derivedUnitDTO);
+                fieldUnits.add(fieldUnitDto);
+
+            }else{
+                DerivateDTO originalDTO;
+                if (specimen instanceof DnaSample){
+                    originalDTO = new DNASampleDTO((DnaSample)specimen);
+                } else {
+                    originalDTO = new PreservedSpecimenDTO((DerivedUnit)specimen);
+                }
+                originalDTO.addDerivate(derivedUnitDTO);
+                fieldUnitDto = findFieldUnitDTO(originalDTO, fieldUnits,
+                        alreadyCollectedSpecimen);
+            }
+
+        }
+      //  }
+        alreadyCollectedSpecimen.put(derivedUnitDTO.getUuid(), derivedUnitDTO);
+//        if (fieldUnitDto != null){
+//            fieldUnitDto.addTaxonRelatedDerivedUnits(derivedUnitDTO);
+//        }
+        return fieldUnitDto;
+
+    }
+
+    @Override
+    @Transactional(readOnly=true)
+    public FieldUnitDTO loadFieldUnitDTO(UUID derivedUnitUuid) {
+
+        FieldUnitDTO fieldUnitDTO = null;
+        DerivateDTO derivedUnitDTO = null;
+
+        Map<UUID, DerivateDTO> cycleDetectionMap = new HashMap<>();
+        SpecimenOrObservationBase derivative = dao.load(derivedUnitUuid);
+        if(derivative != null){
+            derivedUnitDTO = DerivateDTO.newInstance(derivative);
+            while(true){
+                Set<DerivateDTO> originals = originalDTOs(derivedUnitUuid);
+
+                if(originals.isEmpty()){
+                    break;
+                }
+                if (originals.size() > 1){
+                    logger.debug("The derived unit with uuid " + derivedUnitUuid + "has more than one orginal, ignoring all but the first one.");
+                }
+
+                DerivateDTO originalDTO = originals.iterator().next();
+
+                // cycle detection and handling
+                if(cycleDetectionMap.containsKey(originalDTO.getUuid())){
+                    // cycle detected!!!
+                    try {
+                        throw new Exception();
+                    } catch(Exception e){
+                        logger.error("Cycle in derivate graph detected at DerivedUnit with uuid=" + originalDTO.getUuid() , e);
+                    }
+                    // to solve the situation for the output we remove the derivate from the more distant graph node
+                    // by removing it from the derivatives of its original
+                    // but let the derivate to be added to the original which is closer to the FieldUnit (below at originalDTO.addDerivate(derivedUnitDTO);)
+                    for(DerivateDTO seenOriginal: cycleDetectionMap.values()){
+                        for(DerivateDTO derivateDTO : seenOriginal.getDerivates()){
+                            if(derivateDTO.equals(originalDTO)){
+                                seenOriginal.getDerivates().remove(originalDTO);
+                            }
+                        }
+                    }
+                } else {
+                    cycleDetectionMap.put(originalDTO.getUuid(), originalDTO);
+                }
+
+
+                if (originalDTO instanceof FieldUnitDTO){
+                    fieldUnitDTO = (FieldUnitDTO)originalDTO;
+                    if(derivedUnitDTO != null){
+                        fieldUnitDTO.addDerivate(derivedUnitDTO);
+                    }
+                    break;
+                }else{
+                    if (derivedUnitDTO == null){
+                        derivedUnitDTO = originalDTO;
+                    } else {
+                        originalDTO.addDerivate(derivedUnitDTO);
+                        derivedUnitDTO = originalDTO;
+                    }
+                }
+            }
+        }
+        return fieldUnitDTO;
+
+    }
+
+    /**
+     * @param originalDTO
+     * @return
+     */
+    private Set<DerivateDTO> originalDTOs(UUID derivativeUuid) {
+
+        Set<DerivateDTO> dtos = new HashSet<>();
+
+        List<SpecimenOrObservationBase> specimens = dao.findOriginalsForDerivedUnit(derivativeUuid, null);
+        for(SpecimenOrObservationBase sob : specimens){
+            dtos.add(DerivateDTO.newInstance(sob));
+        }
+
+        return dtos;
+    }
+
+
     @Override
     @Transactional(readOnly = false)
     public UpdateResult moveSequence(DnaSample from, DnaSample to, Sequence sequence) {
@@ -1208,37 +1492,55 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
         return dao.listIndividualsAssociations(specimen, limit, start, orderHints, propertyPaths);
     }
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
-    public Collection<TaxonBase<?>> listAssociatedTaxa(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start,
-            List<OrderHint> orderHints, List<String> propertyPaths) {
+    public Collection<TaxonBase<?>> listAssociatedTaxa(SpecimenOrObservationBase<?> specimen, Integer limit,
+            Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
+        return listAssociatedTaxa(specimen, INCLUDE_UNPUBLISHED, limit, start, orderHints, propertyPaths);
+    }
+    @Override
+    public Collection<TaxonBase<?>> listAssociatedTaxa(SpecimenOrObservationBase<?> specimen, boolean includeUnpublished,
+            Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
         Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
 
         //individuals associations
-        associatedTaxa.addAll(listIndividualsAssociationTaxa(specimen, limit, start, orderHints, propertyPaths));
+        associatedTaxa.addAll(listIndividualsAssociationTaxa(specimen, includeUnpublished, limit, start, orderHints, propertyPaths));
         //type designation
         if(specimen.isInstanceOf(DerivedUnit.class)){
-            associatedTaxa.addAll(listTypeDesignationTaxa(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class), limit, start, orderHints, propertyPaths));
+            associatedTaxa.addAll(listTypeDesignationTaxa(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class),
+                  includeUnpublished, limit, start, orderHints, propertyPaths));
         }
         //determinations
-        associatedTaxa.addAll(listDeterminedTaxa(specimen, limit, start, orderHints, propertyPaths));
+        associatedTaxa.addAll(listDeterminedTaxa(specimen, includeUnpublished, limit, start, orderHints, propertyPaths));
 
         return associatedTaxa;
     }
 
 
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Collection<TaxonBase<?>> listDeterminedTaxa(SpecimenOrObservationBase<?> specimen, Integer limit,
+            Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
+        return listDeterminedTaxa(specimen, INCLUDE_UNPUBLISHED, limit, start, orderHints, propertyPaths);
+    }
     @Override
-    public Collection<TaxonBase<?>> listDeterminedTaxa(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start,
+    public Collection<TaxonBase<?>> listDeterminedTaxa(SpecimenOrObservationBase<?> specimen, boolean includeUnpublished, 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(taxonService.load(determinationEvent.getTaxon().getUuid(), propertyPaths));
+                    associatedTaxa.add(taxonService.load(determinationEvent.getTaxon().getUuid(), includeUnpublished, propertyPaths));
                 }
                 if(determinationEvent.getTaxonName()!=null){
                     Collection<TaxonBase> taxonBases = determinationEvent.getTaxonName().getTaxonBases();
                     for (TaxonBase taxonBase : taxonBases) {
-                        associatedTaxa.add(taxonService.load(taxonBase.getUuid(), propertyPaths));
+                        associatedTaxa.add(taxonService.load(taxonBase.getUuid(), includeUnpublished, propertyPaths));
                     }
                 }
             }
@@ -1246,9 +1548,17 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
         return associatedTaxa;
     }
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
     public Collection<TaxonBase<?>> listTypeDesignationTaxa(DerivedUnit specimen, Integer limit, Integer start,
             List<OrderHint> orderHints, List<String> propertyPaths) {
+        return listTypeDesignationTaxa(specimen, INCLUDE_UNPUBLISHED, limit, start, orderHints, propertyPaths);
+    }
+    @Override
+    public Collection<TaxonBase<?>> listTypeDesignationTaxa(DerivedUnit specimen, boolean includeUnpublished,
+            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)){
@@ -1256,7 +1566,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
                 for (TaxonName taxonName : typifiedNames) {
                     Set<Taxon> taxa = taxonName.getTaxa();
                     for (Taxon taxon : taxa) {
-                        associatedTaxa.add(taxonService.load(taxon.getUuid(), propertyPaths));
+                        associatedTaxa.add(taxonService.load(taxon.getUuid(), includeUnpublished, propertyPaths));
                     }
                 }
             }
@@ -1264,15 +1574,24 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
         return associatedTaxa;
     }
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
-    public Collection<TaxonBase<?>> listIndividualsAssociationTaxa(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start,
-            List<OrderHint> orderHints, List<String> propertyPaths) {
+    public Collection<TaxonBase<?>> listIndividualsAssociationTaxa(SpecimenOrObservationBase<?> specimen, Integer limit,
+            Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
+        return listIndividualsAssociationTaxa(specimen, INCLUDE_UNPUBLISHED, limit, start, orderHints, propertyPaths);
+    }
+
+    @Override
+    public Collection<TaxonBase<?>> listIndividualsAssociationTaxa(SpecimenOrObservationBase<?> specimen, boolean includeUnpublished,
+            Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
         Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
         for (IndividualsAssociation individualsAssociation : listIndividualsAssociations(specimen, null, null, null, null)) {
             if(individualsAssociation.getInDescription().isInstanceOf(TaxonDescription.class)){
                 TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(individualsAssociation.getInDescription(), TaxonDescription.class);
                 if(taxonDescription.getTaxon()!=null){
-                    associatedTaxa.add(taxonService.load(taxonDescription.getTaxon().getUuid(), propertyPaths));
+                    associatedTaxa.add(taxonService.load(taxonDescription.getTaxon().getUuid(), includeUnpublished, propertyPaths));
                 }
             }
         }
@@ -1333,12 +1652,12 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
 
 
     @Override
-    public Integer countByTitle(IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config){
+    public long 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());
+                TaxonBase<?> taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
                 if(taxonBase.isInstanceOf(Taxon.class)){
                     taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
                 }
@@ -1355,7 +1674,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
             if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens() || !occurrenceConfig.getAssignmentStatus().equals(AssignmentStatus.ALL_SPECIMENS)){
                 List<SpecimenOrObservationBase> occurrences = new ArrayList<>();
                 occurrences.addAll(dao.findOccurrences(occurrenceConfig.getClazz(),
-                        occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
+                        occurrenceConfig.getTitleSearchStringSqlized(), occurrenceConfig.getSignificantIdentifier(),
                         occurrenceConfig.getSpecimenType(), taxon, taxonName, occurrenceConfig.getMatchMode(), null, null,
                         occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths()));
                 occurrences = filterOccurencesByAssignmentAndHierarchy(occurrenceConfig, occurrences, taxon, taxonName);
@@ -1363,24 +1682,71 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
             }
 
             return dao.countOccurrences(occurrenceConfig.getClazz(),
-                    occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
+                    occurrenceConfig.getTitleSearchStringSqlized(), occurrenceConfig.getSignificantIdentifier(),
                     occurrenceConfig.getSpecimenType(), taxon, taxonName, occurrenceConfig.getMatchMode(), null, null,
                     occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths());
         }
         else{
-            return dao.countByTitle(config.getTitleSearchString());
+            return super.countByTitle(config);
         }
     }
 
     @Override
-    public Pager<SpecimenOrObservationBase> findByTitle(
-            IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config) {
+    public Pager<UuidAndTitleCache<SpecimenOrObservationBase>> findByTitleUuidAndTitleCache(
+            FindOccurrencesConfigurator config){
+        List<UuidAndTitleCache<SpecimenOrObservationBase>> occurrences = new ArrayList<>();
+        Taxon taxon = null;
+        if(config.getAssociatedTaxonUuid()!=null){
+            TaxonBase<?> taxonBase = taxonService.load(config.getAssociatedTaxonUuid());
+            if(taxonBase.isInstanceOf(Taxon.class)){
+                taxon = CdmBase.deproxy(taxonBase, Taxon.class);
+            }
+        }
+        TaxonName taxonName = null;
+        if(config.getAssociatedTaxonNameUuid()!=null){
+            taxonName = nameService.load(config.getAssociatedTaxonNameUuid());
+        }
+        occurrences.addAll(dao.findOccurrencesUuidAndTitleCache(config.getClazz(),
+                config.getTitleSearchString(), config.getSignificantIdentifier(),
+                config.getSpecimenType(), taxon, taxonName, config.getMatchMode(), null, null,
+                config.getOrderHints()));
+
+        return new DefaultPagerImpl<>(config.getPageNumber(), occurrences.size(), config.getPageSize(), occurrences);
+    }
+
+    @Override
+    public List<PreservedSpecimenDTO> findByTitlePreservedSpecimenDTO(FindOccurrencesConfigurator config) {
+        Taxon taxon = null;
+        if(config.getAssociatedTaxonUuid()!=null){
+            TaxonBase<?> taxonBase = taxonService.load(config.getAssociatedTaxonUuid());
+            if(taxonBase.isInstanceOf(Taxon.class)){
+                taxon = CdmBase.deproxy(taxonBase, Taxon.class);
+            }
+        }
+        TaxonName taxonName = null;
+        if(config.getAssociatedTaxonNameUuid()!=null){
+            taxonName = nameService.load(config.getAssociatedTaxonNameUuid());
+        }
+        List<DerivedUnit> occurrences = new ArrayList<>();
+        occurrences.addAll(dao.findOccurrences(DerivedUnit.class,
+                config.getTitleSearchString(), config.getSignificantIdentifier(),
+                config.getSpecimenType(), taxon, taxonName, config.getMatchMode(), null, null,
+                config.getOrderHints(), null));
+
+        List<PreservedSpecimenDTO> dtos = new ArrayList<>();
+        occurrences.forEach(derivedUnit->dtos.add(assemblePreservedSpecimenDTO(derivedUnit)));
+        return dtos;
+    }
+
+    @Override
+    public <S extends SpecimenOrObservationBase> Pager<S> findByTitle(
+            IIdentifiableEntityServiceConfigurator<S> config) {
         if (config instanceof FindOccurrencesConfigurator) {
             FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
             List<SpecimenOrObservationBase> occurrences = new ArrayList<>();
             Taxon taxon = null;
             if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
-                TaxonBase taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
+                TaxonBase<?> taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
                 if(taxonBase.isInstanceOf(Taxon.class)){
                     taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
                 }
@@ -1389,13 +1755,14 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
             if(occurrenceConfig.getAssociatedTaxonNameUuid()!=null){
                 taxonName = nameService.load(occurrenceConfig.getAssociatedTaxonNameUuid());
             }
-            occurrences.addAll(dao.findOccurrences(occurrenceConfig.getClazz(),
+            List<? extends SpecimenOrObservationBase> foundOccurrences = dao.findOccurrences(occurrenceConfig.getClazz(),
                     occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
                     occurrenceConfig.getSpecimenType(), taxon, taxonName, occurrenceConfig.getMatchMode(), null, null,
-                    occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths()));
+                    occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths());
+            occurrences.addAll(foundOccurrences);
             occurrences = filterOccurencesByAssignmentAndHierarchy(occurrenceConfig, occurrences, taxon, taxonName);
 
-            return new DefaultPagerImpl<SpecimenOrObservationBase>(config.getPageNumber(), occurrences.size(), config.getPageSize(), occurrences);
+            return new DefaultPagerImpl<>(config.getPageNumber(), occurrences.size(), config.getPageSize(), (List<S>)occurrences);
         }
         return super.findByTitle(config);
     }
@@ -1406,10 +1773,12 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
         //filter out (un-)assigned specimens
         if(taxon==null && taxonName==null){
             AssignmentStatus assignmentStatus = occurrenceConfig.getAssignmentStatus();
-            List<SpecimenOrObservationBase<?>> specimenWithAssociations = new ArrayList<>();
+            List<SpecimenOrObservationBase> specimenWithAssociations = new ArrayList<>();
             if(!assignmentStatus.equals(AssignmentStatus.ALL_SPECIMENS)){
                 for (SpecimenOrObservationBase specimenOrObservationBase : occurrences) {
-                    Collection<TaxonBase<?>> associatedTaxa = listAssociatedTaxa(specimenOrObservationBase, null, null, null, null);
+                    boolean includeUnpublished = true;  //TODO not sure if this is correct, maybe we have to propagate publish flag to higher methods.
+                    Collection<TaxonBase<?>> associatedTaxa = listAssociatedTaxa(specimenOrObservationBase,
+                            includeUnpublished, null, null, null, null);
                     if(!associatedTaxa.isEmpty()){
                         specimenWithAssociations.add(specimenOrObservationBase);
                     }
@@ -1425,7 +1794,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
         // indirectly associated specimens
         if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens()){
             List<SpecimenOrObservationBase> indirectlyAssociatedOccurrences = new ArrayList<>(occurrences);
-            for (SpecimenOrObservationBase specimen : occurrences) {
+            for (SpecimenOrObservationBase<?> specimen : occurrences) {
                 List<SpecimenOrObservationBase<?>> allHierarchyDerivates = getAllHierarchyDerivatives(specimen);
                 for (SpecimenOrObservationBase<?> specimenOrObservationBase : allHierarchyDerivates) {
                     if(!occurrences.contains(specimenOrObservationBase)){
@@ -1441,7 +1810,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     @Override
     public List<SpecimenOrObservationBase<?>> getAllHierarchyDerivatives(SpecimenOrObservationBase<?> specimen){
         List<SpecimenOrObservationBase<?>> allHierarchyDerivatives = new ArrayList<>();
-        Collection<FieldUnit> fieldUnits = getFieldUnits(specimen.getUuid(), null);
+        Collection<FieldUnit> fieldUnits = findFieldUnits(specimen.getUuid(), null);
         if(fieldUnits.isEmpty()){
             allHierarchyDerivatives.add(specimen);
             allHierarchyDerivatives.addAll(getAllChildDerivatives(specimen));
@@ -1478,7 +1847,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     }
 
     @Override
-    public int countOccurrences(IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config){
+    public long countOccurrences(IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config){
         return countByTitle(config);
     }
 
@@ -1486,8 +1855,17 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
      * {@inheritDoc}
      */
     @Override
-    public List<FieldUnit> getFieldUnitsForGatheringEvent(UUID gatheringEventUuid) {
-        return dao.getFieldUnitsForGatheringEvent(gatheringEventUuid, null, null, null, null);
+    public List<FieldUnit> findFieldUnitsForGatheringEvent(UUID gatheringEventUuid) {
+        return dao.findFieldUnitsForGatheringEvent(gatheringEventUuid, null, null, null, null);
     }
 
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public List<Point> findPointsForFieldUnitList(List<UUID> fieldUnitUuids) {
+
+        return dao.findPointsForFieldUnitList(fieldUnitUuids);
+    }
 }