- fully implemented deep delete (child derivates)
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / OccurrenceServiceImpl.java
index e06b998621ac78ce593a65a0218cddd7edc9d879..efcacead215fe937b7a449192ed8ac2f7148b54a 100644 (file)
 \r
 package eu.etaxonomy.cdm.api.service;\r
 \r
+import java.io.IOException;\r
+import java.net.URI;\r
+import java.net.URISyntaxException;\r
 import java.util.ArrayList;\r
 import java.util.Arrays;\r
+import java.util.Collection;\r
+import java.util.HashMap;\r
 import java.util.HashSet;\r
 import java.util.List;\r
+import java.util.Map;\r
+import java.util.Map.Entry;\r
 import java.util.Set;\r
+import java.util.UUID;\r
 \r
 import org.apache.log4j.Logger;\r
+import org.apache.lucene.index.CorruptIndexException;\r
+import org.apache.lucene.queryParser.ParseException;\r
+import org.apache.lucene.search.BooleanClause.Occur;\r
+import org.apache.lucene.search.BooleanQuery;\r
+import org.apache.lucene.search.SortField;\r
+import org.hibernate.TransientObjectException;\r
+import org.hibernate.search.spatial.impl.Rectangle;\r
+import org.joda.time.Partial;\r
 import org.springframework.beans.factory.annotation.Autowired;\r
 import org.springframework.stereotype.Service;\r
-import org.springframework.transaction.annotation.Propagation;\r
 import org.springframework.transaction.annotation.Transactional;\r
 \r
 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade;\r
 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeConfigurator;\r
 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeNotSupportedException;\r
+import eu.etaxonomy.cdm.api.service.DeleteResult.DeleteStatus;\r
+import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase;\r
+import eu.etaxonomy.cdm.api.service.config.SpecimenDeleteConfigurator;\r
+import eu.etaxonomy.cdm.api.service.dto.DerivateHierarchyDTO;\r
+import eu.etaxonomy.cdm.api.service.dto.DerivateHierarchyDTO.ContigFile;\r
+import eu.etaxonomy.cdm.api.service.dto.DerivateHierarchyDTO.MolecularData;\r
+import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;\r
+import eu.etaxonomy.cdm.api.service.molecular.ISequenceService;\r
 import eu.etaxonomy.cdm.api.service.pager.Pager;\r
 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;\r
+import eu.etaxonomy.cdm.api.service.search.ILuceneIndexToolProvider;\r
+import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;\r
+import eu.etaxonomy.cdm.api.service.search.LuceneSearch;\r
+import eu.etaxonomy.cdm.api.service.search.LuceneSearch.TopGroupsWithMaxScore;\r
+import eu.etaxonomy.cdm.api.service.search.QueryFactory;\r
+import eu.etaxonomy.cdm.api.service.search.SearchResult;\r
+import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;\r
 import eu.etaxonomy.cdm.api.service.util.TaxonRelationshipEdge;\r
 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;\r
 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;\r
+import eu.etaxonomy.cdm.model.CdmBaseType;\r
+import eu.etaxonomy.cdm.model.agent.AgentBase;\r
+import eu.etaxonomy.cdm.model.common.CdmBase;\r
+import eu.etaxonomy.cdm.model.common.DefinedTerm;\r
 import eu.etaxonomy.cdm.model.common.DefinedTermBase;\r
+import eu.etaxonomy.cdm.model.common.ICdmBase;\r
+import eu.etaxonomy.cdm.model.common.Language;\r
 import eu.etaxonomy.cdm.model.common.UuidAndTitleCache;\r
 import eu.etaxonomy.cdm.model.description.DescriptionBase;\r
 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;\r
 import eu.etaxonomy.cdm.model.description.IndividualsAssociation;\r
-import eu.etaxonomy.cdm.model.location.WaterbodyOrCountry;\r
+import eu.etaxonomy.cdm.model.description.SpecimenDescription;\r
+import eu.etaxonomy.cdm.model.description.TaxonDescription;\r
+import eu.etaxonomy.cdm.model.location.Country;\r
+import eu.etaxonomy.cdm.model.location.NamedArea;\r
 import eu.etaxonomy.cdm.model.media.Media;\r
+import eu.etaxonomy.cdm.model.media.MediaRepresentation;\r
+import eu.etaxonomy.cdm.model.media.MediaRepresentationPart;\r
+import eu.etaxonomy.cdm.model.media.MediaUtils;\r
+import eu.etaxonomy.cdm.model.molecular.AmplificationResult;\r
+import eu.etaxonomy.cdm.model.molecular.DnaSample;\r
+import eu.etaxonomy.cdm.model.molecular.Sequence;\r
+import eu.etaxonomy.cdm.model.molecular.SingleRead;\r
+import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;\r
+import eu.etaxonomy.cdm.model.name.TaxonNameBase;\r
+import eu.etaxonomy.cdm.model.name.TypeDesignationStatusBase;\r
 import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;\r
-import eu.etaxonomy.cdm.model.occurrence.DerivedUnitBase;\r
+import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;\r
 import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;\r
-import eu.etaxonomy.cdm.model.occurrence.FieldObservation;\r
+import eu.etaxonomy.cdm.model.occurrence.FieldUnit;\r
+import eu.etaxonomy.cdm.model.occurrence.GatheringEvent;\r
+import eu.etaxonomy.cdm.model.occurrence.MediaSpecimen;\r
 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;\r
+import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;\r
 import eu.etaxonomy.cdm.model.taxon.Taxon;\r
 import eu.etaxonomy.cdm.model.taxon.TaxonBase;\r
-import eu.etaxonomy.cdm.persistence.dao.AbstractBeanInitializer;\r
+import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;\r
 import eu.etaxonomy.cdm.persistence.dao.common.IDefinedTermDao;\r
+import eu.etaxonomy.cdm.persistence.dao.initializer.AbstractBeanInitializer;\r
 import eu.etaxonomy.cdm.persistence.dao.occurrence.IOccurrenceDao;\r
-import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;\r
 import eu.etaxonomy.cdm.persistence.query.OrderHint;\r
 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;\r
 \r
@@ -70,12 +122,23 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     @Autowired\r
     private ITaxonService taxonService;\r
 \r
+    @Autowired\r
+    private ITermService termService;\r
+\r
+    @Autowired\r
+    private INameService nameService;\r
+\r
+    @Autowired\r
+    private ISequenceService sequenceService;\r
+\r
     @Autowired\r
     private AbstractBeanInitializer beanInitializer;\r
 \r
     @Autowired\r
-    private ITaxonDao taxonDao;\r
+    private ILuceneIndexToolProvider luceneIndexToolProvider;\r
 \r
+    @Autowired\r
+    private ICdmGenericDao genericDao;\r
 \r
 \r
     public OccurrenceServiceImpl() {\r
@@ -101,7 +164,7 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
      * move to termService\r
      */\r
     @Override\r
-    public WaterbodyOrCountry getCountryByIso(String iso639) {\r
+    public Country getCountryByIso(String iso639) {\r
         return this.definedTermDao.getCountryByIso(iso639);\r
 \r
     }\r
@@ -111,11 +174,11 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
      * move to termService\r
      */\r
     @Override\r
-    public List<WaterbodyOrCountry> getWaterbodyOrCountryByName(String name) {\r
-        List<? extends DefinedTermBase> terms = this.definedTermDao.findByTitle(WaterbodyOrCountry.class, name, null, null, null, null, null, null) ;\r
-        List<WaterbodyOrCountry> countries = new ArrayList<WaterbodyOrCountry>();\r
+    public List<Country> getCountryByName(String name) {\r
+        List<? extends DefinedTermBase> terms = this.definedTermDao.findByTitle(Country.class, name, null, null, null, null, null, null) ;\r
+        List<Country> countries = new ArrayList<Country>();\r
         for (int i=0;i<terms.size();i++){\r
-            countries.add((WaterbodyOrCountry)terms.get(i));\r
+            countries.add((Country)terms.get(i));\r
         }\r
         return countries;\r
     }\r
@@ -138,6 +201,14 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
         return new DefaultPagerImpl<DerivationEvent>(pageNumber, numberOfResults, pageSize, results);\r
     }\r
 \r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#countDeterminations(eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase, eu.etaxonomy.cdm.model.taxon.TaxonBase)\r
+     */\r
+    @Override\r
+    public int countDeterminations(SpecimenOrObservationBase occurence, TaxonBase taxonbase) {\r
+        return dao.countDeterminations(occurence, taxonbase);\r
+    }\r
+\r
     @Override\r
     public Pager<DeterminationEvent> getDeterminations(SpecimenOrObservationBase occurrence, TaxonBase taxonBase, Integer pageSize,Integer pageNumber, List<String> propertyPaths) {\r
         Integer numberOfResults = dao.countDeterminations(occurrence, taxonBase);\r
@@ -178,21 +249,21 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
     }\r
 \r
     @Override\r
-    public List<UuidAndTitleCache<DerivedUnitBase>> getDerivedUnitBaseUuidAndTitleCache() {\r
-        return dao.getDerivedUnitBaseUuidAndTitleCache();\r
+    public List<UuidAndTitleCache<DerivedUnit>> getDerivedUnitUuidAndTitleCache() {\r
+        return dao.getDerivedUnitUuidAndTitleCache();\r
     }\r
 \r
     @Override\r
-    public List<UuidAndTitleCache<FieldObservation>> getFieldObservationUuidAndTitleCache() {\r
-        return dao.getFieldObservationUuidAndTitleCache();\r
+    public List<UuidAndTitleCache<FieldUnit>> getFieldUnitUuidAndTitleCache() {\r
+        return dao.getFieldUnitUuidAndTitleCache();\r
     }\r
 \r
     /* (non-Javadoc)\r
-     * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#getDerivedUnitFacade(eu.etaxonomy.cdm.model.occurrence.DerivedUnitBase)\r
+     * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#getDerivedUnitFacade(eu.etaxonomy.cdm.model.occurrence.DerivedUnit)\r
      */\r
     @Override\r
-    public DerivedUnitFacade getDerivedUnitFacade(DerivedUnitBase derivedUnit, List<String> propertyPaths) throws DerivedUnitFacadeNotSupportedException {\r
-        derivedUnit = (DerivedUnitBase<?>)dao.load(derivedUnit.getUuid(), null);\r
+    public DerivedUnitFacade getDerivedUnitFacade(DerivedUnit derivedUnit, List<String> propertyPaths) throws DerivedUnitFacadeNotSupportedException {\r
+        derivedUnit = (DerivedUnit)dao.load(derivedUnit.getUuid(), null);\r
         DerivedUnitFacadeConfigurator config = DerivedUnitFacadeConfigurator.NewInstance();\r
         config.setThrowExceptionForNonSpecimenPreservationMethodRequest(false);\r
         DerivedUnitFacade derivedUnitFacade = DerivedUnitFacade.NewInstance(derivedUnit, config);\r
@@ -216,9 +287,9 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
                 tempIndividualsAssociation = (IndividualsAssociation)element;\r
                 if(tempIndividualsAssociation.getAssociatedSpecimenOrObservation() != null){\r
                     tempSpecimenOrObservationBase = HibernateProxyHelper.deproxy(tempIndividualsAssociation.getAssociatedSpecimenOrObservation(), SpecimenOrObservationBase.class);\r
-                    if(tempSpecimenOrObservationBase instanceof DerivedUnitBase){\r
+                    if(tempSpecimenOrObservationBase instanceof DerivedUnit){\r
                         try {\r
-                            derivedUnitFacadeList.add(DerivedUnitFacade.NewInstance((DerivedUnitBase)tempSpecimenOrObservationBase));\r
+                            derivedUnitFacadeList.add(DerivedUnitFacade.NewInstance((DerivedUnit)tempSpecimenOrObservationBase));\r
                         } catch (DerivedUnitFacadeNotSupportedException e) {\r
                             logger.warn(tempIndividualsAssociation.getAssociatedSpecimenOrObservation().getTitleCache() + " : " +e.getMessage());\r
                         }\r
@@ -244,6 +315,260 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
         return pageByAssociatedTaxon(type, includeRelationships, associatedTaxon, maxDepth, pageSize, pageNumber, orderHints, propertyPaths).getRecords();\r
     }\r
 \r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#listByAnyAssociation(java.lang.Class, java.util.Set, eu.etaxonomy.cdm.model.taxon.Taxon, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)\r
+     */\r
+    @Override\r
+    public Collection<FieldUnit> listFieldUnitsByAssociatedTaxon(Set<TaxonRelationshipEdge> includeRelationships,\r
+            Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
+\r
+        if(!getSession().contains(associatedTaxon)){\r
+            associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid());\r
+        }\r
+\r
+        Set<FieldUnit> fieldUnits = new HashSet<FieldUnit>();\r
+\r
+        List<SpecimenOrObservationBase> records = pageByAssociatedTaxon(null, includeRelationships, associatedTaxon, maxDepth, pageSize, pageNumber, orderHints, propertyPaths).getRecords();\r
+        for(SpecimenOrObservationBase<?> specimen:records){\r
+            fieldUnits.addAll(getFieldUnits(specimen.getUuid()));\r
+        }\r
+        return fieldUnits;\r
+    }\r
+\r
+    @Override\r
+    public DerivateHierarchyDTO assembleDerivateHierarchyDTO(FieldUnit fieldUnit, UUID associatedTaxonUuid){\r
+\r
+        if(!getSession().contains(fieldUnit)){\r
+            fieldUnit = (FieldUnit) load(fieldUnit.getUuid());\r
+        }\r
+        TaxonBase associatedTaxon = taxonService.load(associatedTaxonUuid);\r
+\r
+        DerivateHierarchyDTO dto = new DerivateHierarchyDTO();\r
+        Map<UUID, TypeDesignationStatusBase> typeSpecimenUUIDtoTypeDesignationStatus = new HashMap<UUID, TypeDesignationStatusBase>();\r
+\r
+        //gather types for this taxon name\r
+        TaxonNameBase<?,?> name = associatedTaxon.getName();\r
+        Set<?> typeDesignations = name.getSpecimenTypeDesignations();\r
+        for (Object object : typeDesignations) {\r
+            if(object instanceof SpecimenTypeDesignation){\r
+                SpecimenTypeDesignation specimenTypeDesignation = (SpecimenTypeDesignation)object;\r
+                DerivedUnit typeSpecimen = specimenTypeDesignation.getTypeSpecimen();\r
+                final TypeDesignationStatusBase typeStatus = specimenTypeDesignation.getTypeStatus();\r
+                typeSpecimenUUIDtoTypeDesignationStatus.put(typeSpecimen.getUuid(), typeStatus);\r
+            }\r
+        }\r
+\r
+        if(fieldUnit.getGatheringEvent()!=null){\r
+            GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent();\r
+            //Country\r
+            final NamedArea country = gatheringEvent.getCountry();\r
+            dto.setCountry(country!=null?country.getDescription():"");\r
+            //Collection\r
+            final AgentBase collector = gatheringEvent.getCollector();\r
+            final String fieldNumber = fieldUnit.getFieldNumber();\r
+            dto.setCollection(((collector!=null?collector:"") + " " + (fieldNumber!=null?fieldNumber:"")).trim());\r
+            //Date\r
+            final Partial gatheringDate = gatheringEvent.getGatheringDate();\r
+            dto.setDate(gatheringDate!=null?gatheringDate.toString():"");\r
+        }\r
+\r
+        //Taxon Name\r
+        dto.setTaxonName(associatedTaxon.getName().getFullTitleCache());\r
+\r
+\r
+        Collection<DerivedUnit> derivedUnits = new ArrayList<DerivedUnit>();\r
+        getDerivedUnitsFor(fieldUnit, derivedUnits);\r
+\r
+        //Herbaria map\r
+        Map<eu.etaxonomy.cdm.model.occurrence.Collection, Integer> collectionToCountMap = new HashMap<eu.etaxonomy.cdm.model.occurrence.Collection, Integer>();\r
+        //List of accession numbers for citation\r
+        List<String> preservedSpecimenAccessionNumbers = new ArrayList<String>();\r
+\r
+        //iterate over sub derivates\r
+        for (DerivedUnit derivedUnit : derivedUnits) {\r
+            //current accession number\r
+            String currentAccessionNumber = derivedUnit.getAccessionNumber()!=null?derivedUnit.getAccessionNumber():"";\r
+            //current herbarium\r
+            String currentHerbarium = "";\r
+            eu.etaxonomy.cdm.model.occurrence.Collection collection = derivedUnit.getCollection();\r
+            if(collection!=null){\r
+                currentHerbarium = collection.getCode()!=null?collection.getCode():"";\r
+                //count herbaria\r
+                Integer count = collectionToCountMap.get(collection);\r
+                if(count==null){\r
+                    count = 1;\r
+                }\r
+                else{\r
+                    count++;\r
+                }\r
+                collectionToCountMap.put(collection, count);\r
+            }\r
+            //check if derived unit is a type\r
+            if(typeSpecimenUUIDtoTypeDesignationStatus.keySet().contains(derivedUnit.getUuid())){\r
+                dto.setHasType(true);\r
+                TypeDesignationStatusBase typeDesignationStatus = typeSpecimenUUIDtoTypeDesignationStatus.get(derivedUnit.getUuid());\r
+                String typeStatus = typeDesignationStatus.getLabel();\r
+                dto.addTypes(typeStatus, currentAccessionNumber);\r
+            }\r
+            //assemble molecular data\r
+            //pattern: DNAMarker [contig1, primer1_1, primer1_2, ...][contig2, primer2_1, ...]...\r
+            if(derivedUnit instanceof DnaSample){\r
+                if(derivedUnit.getRecordBasis()==SpecimenOrObservationType.TissueSample){\r
+                    //TODO implement TissueSample assembly for web service\r
+                }\r
+                if(derivedUnit.getRecordBasis()==SpecimenOrObservationType.DnaSample){\r
+\r
+                    DnaSample dna = (DnaSample)derivedUnit;\r
+                    if(!dna.getSequences().isEmpty()){\r
+                        dto.setHasDna(true);\r
+                    }\r
+                    for(Sequence sequence:dna.getSequences()){\r
+                        URI boldUri = null;\r
+                        try {\r
+                            boldUri = sequence.getBoldUri();\r
+                        } catch (URISyntaxException e1) {\r
+                            logger.error("Could not create BOLD URI", e1);\r
+                        }\r
+                        final DefinedTerm dnaMarker = sequence.getDnaMarker();\r
+                        MolecularData molecularData = dto.addProviderLink(boldUri!=null?boldUri:null,dnaMarker!=null?dnaMarker.getLabel():"[no marker]");\r
+\r
+                        //contig file FIXME show primer although contig not present?\r
+                        if(sequence.getContigFile()!=null){\r
+                            MediaRepresentationPart contigMediaRepresentationPart = MediaUtils.getFirstMediaRepresentationPart(sequence.getContigFile());\r
+                            if(contigMediaRepresentationPart!=null){\r
+                                ContigFile contigFile = molecularData.addContigFile(contigMediaRepresentationPart.getUri(), "contig");\r
+                                //primer files\r
+                                if(sequence.getSingleReads()!=null){\r
+                                    for (SingleRead singleRead : sequence.getSingleReads()) {\r
+                                        MediaRepresentationPart pherogramMediaRepresentationPart = MediaUtils.getFirstMediaRepresentationPart(singleRead.getPherogram());\r
+                                        if(pherogramMediaRepresentationPart!=null){\r
+                                            contigFile.addPrimerLink(pherogramMediaRepresentationPart.getUri(), "primer");\r
+                                        }\r
+                                    }\r
+                                }\r
+                            }\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+            //assemble media data\r
+            else if(derivedUnit instanceof MediaSpecimen){\r
+\r
+                MediaSpecimen media = (MediaSpecimen)derivedUnit;\r
+                String mediaUriString = getMediaUriString(media);\r
+                if(media.getKindOfUnit()!=null){\r
+                    //specimen scan\r
+                    if(media.getKindOfUnit().getUuid().equals(UUID.fromString("acda15be-c0e2-4ea8-8783-b9b0c4ad7f03"))){\r
+                        dto.setHasSpecimenScan(true);\r
+                            final String imageLinkText = currentHerbarium+" "+currentAccessionNumber;\r
+                            dto.addSpecimenScan(mediaUriString==null?"":mediaUriString, !imageLinkText.equals(" ")?imageLinkText:"[no accession]");\r
+                    }\r
+                    //detail image\r
+                    else if(media.getKindOfUnit().getUuid().equals(UUID.fromString("31eb8d02-bf5d-437c-bcc6-87a626445f34"))){\r
+                        dto.setHasDetailImage(true);\r
+                        String motif = "";\r
+                        if(media.getMediaSpecimen()!=null && media.getMediaSpecimen().getTitle()!=null){\r
+                            motif = media.getMediaSpecimen().getTitle().getText();\r
+                        }\r
+                        dto.addDetailImage(mediaUriString==null?"":mediaUriString, motif!=null?motif:"[no motif]");\r
+                    }\r
+                }\r
+            }\r
+            //assemble preserved specimen data\r
+            else if(derivedUnit.getRecordBasis()==SpecimenOrObservationType.PreservedSpecimen){\r
+                if(!currentAccessionNumber.isEmpty()){\r
+                    preservedSpecimenAccessionNumbers.add(currentAccessionNumber);\r
+                }\r
+            }\r
+        }\r
+\r
+        final String separator = ", ";\r
+        //assemble citation\r
+        String citation = "";\r
+        citation += !dto.getCountry().isEmpty()?dto.getCountry()+separator:"";\r
+        if(fieldUnit.getGatheringEvent()!=null){\r
+            if(fieldUnit.getGatheringEvent().getLocality()!=null){\r
+                citation += fieldUnit.getGatheringEvent().getLocality().getText();\r
+                citation += separator;\r
+            }\r
+            if(fieldUnit.getGatheringEvent().getExactLocation()!=null\r
+                    && fieldUnit.getGatheringEvent().getExactLocation().getLatitude()!=null\r
+                    && fieldUnit.getGatheringEvent().getExactLocation().getLongitude()!=null){\r
+                citation += fieldUnit.getGatheringEvent().getExactLocation().getLatitude().toString();\r
+                citation += separator;\r
+                citation += fieldUnit.getGatheringEvent().getExactLocation().getLongitude().toString();\r
+                citation += separator;\r
+            }\r
+        }\r
+        citation += !dto.getCollection().isEmpty()?dto.getCollection():"";\r
+        if(!preservedSpecimenAccessionNumbers.isEmpty()){\r
+            citation += " (";\r
+            for(String accessionNumber:preservedSpecimenAccessionNumbers){\r
+                if(!accessionNumber.isEmpty()){\r
+                    citation += accessionNumber+separator;\r
+                }\r
+            }\r
+            citation = removeTail(citation, separator);\r
+            citation += ")";\r
+        }\r
+        citation = removeTail(citation, separator);\r
+        dto.setCitation(citation);\r
+\r
+        //assemble herbaria string\r
+        String herbariaString = "";\r
+        for(Entry<eu.etaxonomy.cdm.model.occurrence.Collection, Integer> e:collectionToCountMap.entrySet()){\r
+            eu.etaxonomy.cdm.model.occurrence.Collection collection = e.getKey();\r
+            if(collection.getCode()!=null){\r
+                herbariaString += collection.getCode();\r
+            }\r
+            if(e.getValue()>1){\r
+                herbariaString += "("+e.getValue()+")";\r
+            }\r
+            herbariaString += separator;\r
+        }\r
+        herbariaString = removeTail(herbariaString, separator);\r
+        dto.setHerbarium(herbariaString);\r
+\r
+        return dto;\r
+    }\r
+\r
+\r
+    /**\r
+     * @param string\r
+     * @param tail\r
+     * @return\r
+     */\r
+    private String removeTail(String string, final String tail) {\r
+        if(string.endsWith(tail)){\r
+            string = string.substring(0, string.length()-tail.length());\r
+        }\r
+        return string;\r
+    }\r
+\r
+    private String getMediaUriString(MediaSpecimen mediaSpecimen){\r
+        String mediaUri = null;\r
+        Collection<MediaRepresentation> mediaRepresentations = mediaSpecimen.getMediaSpecimen().getRepresentations();\r
+        if(mediaRepresentations!=null && !mediaRepresentations.isEmpty()){\r
+            Collection<MediaRepresentationPart> mediaRepresentationParts = mediaRepresentations.iterator().next().getParts();\r
+            if(mediaRepresentationParts!=null && !mediaRepresentationParts.isEmpty()){\r
+                MediaRepresentationPart part = mediaRepresentationParts.iterator().next();\r
+                if(part.getUri()!=null){\r
+                    mediaUri = part.getUri().toASCIIString();\r
+                }\r
+            }\r
+        }\r
+        return mediaUri;\r
+    }\r
+\r
+    private void getDerivedUnitsFor(SpecimenOrObservationBase<?> specimen, Collection<DerivedUnit> derivedUnits){\r
+        for(DerivationEvent derivationEvent:specimen.getDerivationEvents()){\r
+            for(DerivedUnit derivative:derivationEvent.getDerivatives()){\r
+                derivedUnits.add(derivative);\r
+                getDerivedUnitsFor(derivative, derivedUnits);\r
+            }\r
+        }\r
+    }\r
+\r
 \r
     /* (non-Javadoc)\r
      * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#pageByAssociatedTaxon(java.lang.Class, java.util.Set, eu.etaxonomy.cdm.model.taxon.Taxon, java.lang.Integer, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)\r
@@ -260,7 +585,9 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
 //        Integer limit = PagerUtils.limitFor(pageSize);\r
 //        Integer start = PagerUtils.startFor(pageSize, pageNumber);\r
 \r
-        associatedTaxon = (Taxon) taxonDao.load(associatedTaxon.getUuid());\r
+        if(!getSession().contains(associatedTaxon)){\r
+            associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid());\r
+        }\r
 \r
         if(includeRelationships != null) {\r
             taxa = taxonService.listRelatedTaxa(associatedTaxon, includeRelationships, maxDepth, null, null, propertyPaths);\r
@@ -274,11 +601,552 @@ public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObs
                 occurrenceIds.add(o.getId());\r
             }\r
         }\r
-\r
         occurrences = (List<T>) dao.listByIds(occurrenceIds, pageSize, pageNumber, orderHints, propertyPaths);\r
 \r
         return new DefaultPagerImpl<T>(pageNumber, occurrenceIds.size(), pageSize, occurrences);\r
 \r
     }\r
 \r
+\r
+    @Override\r
+    public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,\r
+            String taxonUUID, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
+\r
+        UUID uuid = UUID.fromString(taxonUUID);\r
+        Taxon tax = (Taxon) taxonService.load(uuid);\r
+       //TODO REMOVE NULL STATEMENT\r
+        type=null;\r
+        return pageByAssociatedTaxon( type,includeRelationships,tax, maxDepth, pageSize, pageNumber, orderHints, propertyPaths );\r
+\r
+    }\r
+\r
+\r
+    @Override\r
+    public Pager<SearchResult<SpecimenOrObservationBase>> findByFullText(\r
+            Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle boundingBox, List<Language> languages,\r
+            boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,\r
+            List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {\r
+\r
+        LuceneSearch luceneSearch = prepareByFullTextSearch(clazz, queryString, boundingBox, languages, highlightFragments);\r
+\r
+        // --- execute search\r
+        TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);\r
+\r
+        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();\r
+        idFieldMap.put(CdmBaseType.SPECIMEN_OR_OBSERVATIONBASE, "id");\r
+\r
+        // --- initialize taxa, highlight matches ....\r
+        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());\r
+        @SuppressWarnings("rawtypes")\r
+        List<SearchResult<SpecimenOrObservationBase>> searchResults = searchResultBuilder.createResultSet(\r
+                topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);\r
+\r
+        int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;\r
+\r
+        return new DefaultPagerImpl<SearchResult<SpecimenOrObservationBase>>(pageNumber, totalHits, pageSize,\r
+                searchResults);\r
+\r
+    }\r
+\r
+\r
+    /**\r
+     * @param clazz\r
+     * @param queryString\r
+     * @param languages\r
+     * @param highlightFragments\r
+     * @return\r
+     */\r
+    private LuceneSearch prepareByFullTextSearch(Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle bbox,\r
+            List<Language> languages, boolean highlightFragments) {\r
+\r
+        BooleanQuery finalQuery = new BooleanQuery();\r
+        BooleanQuery textQuery = new BooleanQuery();\r
+\r
+        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, FieldUnit.class);\r
+        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(FieldUnit.class);\r
+\r
+        // --- criteria\r
+        luceneSearch.setCdmTypRestriction(clazz);\r
+        if(queryString != null){\r
+            textQuery.add(queryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);\r
+            finalQuery.add(textQuery, Occur.MUST);\r
+        }\r
+\r
+        // --- spacial query\r
+        if(bbox != null){\r
+            finalQuery.add(QueryFactory.buildSpatialQueryByRange(bbox, "gatheringEvent.exactLocation.point"), Occur.MUST);\r
+        }\r
+\r
+        luceneSearch.setQuery(finalQuery);\r
+\r
+        // --- sorting\r
+        SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};\r
+        luceneSearch.setSortFields(sortFields);\r
+\r
+        if(highlightFragments){\r
+            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());\r
+        }\r
+        return luceneSearch;\r
+    }\r
+\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#getFieldUnits(eu.etaxonomy.cdm.model.occurrence.DerivedUnit)\r
+     */\r
+    @Override\r
+    public Collection<FieldUnit> getFieldUnits(UUID derivedUnitUuid) {\r
+        //It will search recursively over all {@link DerivationEvent}s and get the "originals" ({@link SpecimenOrObservationBase})\r
+        //from which this DerivedUnit was derived until all FieldUnits are found.\r
+\r
+        //FIXME: use HQL queries to increase performance\r
+        SpecimenOrObservationBase<?> specimen = load(derivedUnitUuid);\r
+//        specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);\r
+        Collection<FieldUnit> fieldUnits = new ArrayList<FieldUnit>();\r
+\r
+        if(specimen instanceof FieldUnit){\r
+            fieldUnits.add((FieldUnit) specimen);\r
+        }\r
+        else if(specimen instanceof DerivedUnit){\r
+            getFieldUnits((DerivedUnit) specimen, fieldUnits);\r
+        }\r
+        return fieldUnits;\r
+    }\r
+\r
+\r
+    /**\r
+     * @param original\r
+     * @param fieldUnits\r
+     */\r
+    private void getFieldUnits(DerivedUnit derivedUnit, Collection<FieldUnit> fieldUnits) {\r
+        Set<SpecimenOrObservationBase> originals = derivedUnit.getOriginals();\r
+        if(originals!=null && !originals.isEmpty()){\r
+            for(SpecimenOrObservationBase<?> original:originals){\r
+                if(original instanceof FieldUnit){\r
+                    fieldUnits.add((FieldUnit) original);\r
+                }\r
+                else if(original instanceof DerivedUnit){\r
+                    getFieldUnits((DerivedUnit) original, fieldUnits);\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#moveSequence(eu.etaxonomy.cdm.model.molecular.DnaSample, eu.etaxonomy.cdm.model.molecular.DnaSample, eu.etaxonomy.cdm.model.molecular.Sequence)\r
+     */\r
+    @Override\r
+    public boolean moveSequence(DnaSample from, DnaSample to, Sequence sequence) {\r
+        //reload specimens to avoid session conflicts\r
+        from = (DnaSample) load(from.getUuid());\r
+        to = (DnaSample) load(to.getUuid());\r
+        sequence = sequenceService.load(sequence.getUuid());\r
+\r
+        if(from==null || to==null || sequence==null){\r
+            throw new TransientObjectException("One of the CDM entities has not been saved to the data base yet. Moving only works for persisted/saved CDM entities.\n" +\r
+                    "Operation was move "+sequence+ " from "+from+" to "+to);\r
+        }\r
+        from.removeSequence(sequence);\r
+        saveOrUpdate(from);\r
+        to.addSequence(sequence);\r
+        saveOrUpdate(to);\r
+        return true;\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#moveDerivate(eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase, eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase, eu.etaxonomy.cdm.model.occurrence.DerivedUnit)\r
+     */\r
+    @Override\r
+    public boolean moveDerivate(SpecimenOrObservationBase<?> from, SpecimenOrObservationBase<?> to, DerivedUnit derivate) {\r
+        //reload specimens to avoid session conflicts\r
+        from = load(from.getUuid());\r
+        to = load(to.getUuid());\r
+        derivate = (DerivedUnit) load(derivate.getUuid());\r
+\r
+        if(from==null || to==null || derivate==null){\r
+            throw new TransientObjectException("One of the CDM entities has not been saved to the data base yet. Moving only works for persisted/saved CDM entities.\n" +\r
+                       "Operation was move "+derivate+ " from "+from+" to "+to);\r
+        }\r
+\r
+        SpecimenOrObservationType derivateType = derivate.getRecordBasis();\r
+        SpecimenOrObservationType toType = to.getRecordBasis();\r
+        //check if type is a sub derivate type\r
+        if(toType==SpecimenOrObservationType.FieldUnit //moving to FieldUnit always works\r
+                || derivateType==SpecimenOrObservationType.Media //moving media always works\r
+                || (derivateType.isKindOf(toType) && toType!=derivateType)){ //moving only to parent derivate type\r
+            //remove derivation event from parent specimen of dragged object\r
+            DerivationEvent eventToRemove = null;\r
+            for(DerivationEvent event:from.getDerivationEvents()){\r
+                if(event.getDerivatives().contains(derivate)){\r
+                    eventToRemove = event;\r
+                    break;\r
+                }\r
+            }\r
+            from.removeDerivationEvent(eventToRemove);\r
+            saveOrUpdate(from);\r
+            //add new derivation event to target\r
+            DerivationEvent derivedFromNewOriginalEvent = DerivationEvent.NewSimpleInstance(to, derivate, eventToRemove==null?null:eventToRemove.getType());\r
+            to.addDerivationEvent(derivedFromNewOriginalEvent);\r
+            derivate.setDerivedFrom(derivedFromNewOriginalEvent);\r
+            saveOrUpdate(to);\r
+            return true;\r
+        }\r
+        return false;\r
+    }\r
+\r
+    @Override\r
+    public Collection<ICdmBase> getNonCascadedAssociatedElements(SpecimenOrObservationBase<?> specimen){\r
+        //potential fields that are not persisted cascadingly\r
+        /*\r
+         * SOOB\r
+        -DescriptionBase\r
+        -determinations\r
+        --modifier TERM\r
+        -kindOfUnit TERM\r
+        -lifeStage TERM\r
+        -sex TERM\r
+\r
+        FieldUnit\r
+        -GatheringEvent\r
+        --Country TERM\r
+        --CollectingAreas TERM\r
+\r
+        DerivedUnit\r
+        -collection\r
+        --institute\r
+        ---types TERM\r
+        -preservationMethod\r
+        --medium TERM\r
+        -storedUnder CDM TaxonNameBase\r
+        */\r
+\r
+        Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<ICdmBase>();\r
+\r
+        //Choose the correct entry point to traverse the graph (FieldUnit or DerivedUnit)\r
+\r
+        //FieldUnit\r
+        if(specimen instanceof FieldUnit){\r
+            nonCascadedCdmEntities.addAll(getFieldUnitNonCascadedAssociatedElements((FieldUnit)specimen));\r
+        }\r
+        //DerivedUnit\r
+        else if(specimen instanceof DerivedUnit){\r
+            DerivedUnit derivedUnit = (DerivedUnit)specimen;\r
+            if(derivedUnit.getDerivedFrom()!=null){\r
+                Collection<FieldUnit> fieldUnits = new ArrayList<FieldUnit>();\r
+                getFieldUnits(derivedUnit, fieldUnits);\r
+                for(FieldUnit fieldUnit:fieldUnits){\r
+                    nonCascadedCdmEntities.addAll(getFieldUnitNonCascadedAssociatedElements(fieldUnit));\r
+                }\r
+            }\r
+        }\r
+        return nonCascadedCdmEntities;\r
+    }\r
+\r
+    private Collection<ICdmBase> getFieldUnitNonCascadedAssociatedElements(FieldUnit fieldUnit){\r
+        //get non cascaded element on SpecimenOrObservationBase level\r
+        Collection<ICdmBase> nonCascadedCdmEntities = getSpecimenOrObservationNonCascadedAssociatedElements(fieldUnit);\r
+\r
+        //get FieldUnit specific elements\r
+        GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent();\r
+        if(gatheringEvent!=null){\r
+            //country\r
+            if(gatheringEvent.getCountry()!=null){\r
+                nonCascadedCdmEntities.add(gatheringEvent.getCountry());\r
+            }\r
+            //collecting areas\r
+            for (NamedArea namedArea : gatheringEvent.getCollectingAreas()) {\r
+                nonCascadedCdmEntities.add(namedArea);\r
+            }\r
+        }\r
+        for (DerivationEvent derivationEvent : fieldUnit.getDerivationEvents()) {\r
+            for (DerivedUnit derivedUnit : derivationEvent.getDerivatives()) {\r
+                nonCascadedCdmEntities.addAll(getDerivedUnitNonCascadedAssociatedElements(derivedUnit));\r
+            }\r
+        }\r
+        return nonCascadedCdmEntities;\r
+    }\r
+\r
+    private Collection<ICdmBase> getDerivedUnitNonCascadedAssociatedElements(DerivedUnit derivedUnit){\r
+        //get non cascaded element on SpecimenOrObservationBase level\r
+        Collection<ICdmBase> nonCascadedCdmEntities = getSpecimenOrObservationNonCascadedAssociatedElements(derivedUnit);\r
+\r
+        //get DerivedUnit specific elements\r
+        if(derivedUnit.getCollection()!=null && derivedUnit.getCollection().getInstitute()!=null){\r
+            for (DefinedTerm type : derivedUnit.getCollection().getInstitute().getTypes()) {\r
+                nonCascadedCdmEntities.add(type);\r
+            }\r
+        }\r
+        if(derivedUnit.getPreservation()!=null && derivedUnit.getPreservation().getMedium()!=null){\r
+            nonCascadedCdmEntities.add(derivedUnit.getPreservation().getMedium());\r
+        }\r
+        if(derivedUnit.getStoredUnder()!=null){\r
+            nonCascadedCdmEntities.add(derivedUnit.getStoredUnder());\r
+        }\r
+        return nonCascadedCdmEntities;\r
+    }\r
+\r
+    /**\r
+     * @param specimen\r
+     * @return\r
+     */\r
+    private Collection<ICdmBase> getSpecimenOrObservationNonCascadedAssociatedElements(\r
+            SpecimenOrObservationBase<?> specimen) {\r
+        Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<ICdmBase>();\r
+        //scan SpecimenOrObservationBase\r
+        for(DeterminationEvent determinationEvent:specimen.getDeterminations()){\r
+            //modifier\r
+            if(determinationEvent.getModifier()!=null){\r
+                nonCascadedCdmEntities.add(determinationEvent.getModifier());\r
+            }\r
+        }\r
+        //kindOfUnit\r
+        if(specimen.getKindOfUnit()!=null){\r
+            nonCascadedCdmEntities.add(specimen.getKindOfUnit());\r
+        }\r
+        //lifeStage\r
+        if(specimen.getLifeStage()!=null){\r
+            nonCascadedCdmEntities.add(specimen.getLifeStage());\r
+        }\r
+        //sex\r
+        if(specimen.getSex()!=null){\r
+            nonCascadedCdmEntities.add(specimen.getSex());\r
+        }\r
+        return nonCascadedCdmEntities;\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.VersionableServiceBase#isDeletable(eu.etaxonomy.cdm.model.common.VersionableEntity, eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase)\r
+     */\r
+    @Override\r
+    public DeleteResult isDeletable(SpecimenOrObservationBase specimen, DeleteConfiguratorBase config) {\r
+        DeleteResult deleteResult = super.isDeletable(specimen, config);\r
+        SpecimenDeleteConfigurator specimenDeleteConfigurator = (SpecimenDeleteConfigurator)config;\r
+\r
+        //check elements found by super method\r
+        Set<CdmBase> relatedObjects = deleteResult.getRelatedObjects();\r
+        boolean isDeletable = true;\r
+        for (CdmBase cdmBase : relatedObjects) {\r
+            //check for type designation\r
+            if(cdmBase.isInstanceOf(SpecimenTypeDesignation.class) && !specimenDeleteConfigurator.isDeleteFromTypeDesignation()){\r
+                isDeletable = false;\r
+                deleteResult.addException(new ReferencedObjectUndeletableException("Specimen is a type specimen."));\r
+                break;\r
+            }\r
+            //check for IndividualsAssociations\r
+            else if(cdmBase.isInstanceOf(IndividualsAssociation.class) && !specimenDeleteConfigurator.isDeleteFromIndividualsAssociation()){\r
+                isDeletable = false;\r
+                deleteResult.addException(new ReferencedObjectUndeletableException("Specimen is still associated via IndividualsAssociations"));\r
+                break;\r
+            }\r
+            //check for specimen/taxon description\r
+            else if((cdmBase.isInstanceOf(SpecimenDescription.class) || cdmBase.isInstanceOf(TaxonDescription.class))\r
+                    && !specimenDeleteConfigurator.isDeleteFromDescription()){\r
+                isDeletable = false;\r
+                deleteResult.addException(new ReferencedObjectUndeletableException("Specimen is still used in a Description."));\r
+                break;\r
+            }\r
+            //check for children and parents (derivation events)\r
+            else if(cdmBase.isInstanceOf(DerivationEvent.class)){\r
+                DerivationEvent derivationEvent = HibernateProxyHelper.deproxy(cdmBase, DerivationEvent.class);\r
+                //check if derivation event is derivedFrom event (parent -> child)\r
+                if(derivationEvent.getDerivatives().contains(specimen)){\r
+                    //if it is then the specimen is still deletable\r
+                    continue;\r
+                }\r
+                else if(!specimenDeleteConfigurator.isDeleteChildren()){\r
+                    //if not and children should not be deleted then it is undeletable\r
+                    isDeletable = false;\r
+                    deleteResult.addException(new ReferencedObjectUndeletableException("Derivate still has child derivates."));\r
+                    break;\r
+                }\r
+                else{\r
+                    //check all children if they can be deleted\r
+                    Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();\r
+                    DeleteResult childResult = new DeleteResult();\r
+                    for (DerivedUnit derivedUnit : derivatives) {\r
+                        childResult.includeResult(isDeletable(derivedUnit, specimenDeleteConfigurator));\r
+                    }\r
+                    if(!childResult.isOk()){\r
+                        isDeletable = false;\r
+                        deleteResult.includeResult(childResult);\r
+                        break;\r
+                    }\r
+                }\r
+            }\r
+            //check for amplification\r
+            else if(cdmBase.isInstanceOf(AmplificationResult.class) && !specimenDeleteConfigurator.isDeleteMolecularData()){\r
+                isDeletable = false;\r
+                deleteResult.addException(new ReferencedObjectUndeletableException("DnaSample is used in amplification results."));\r
+                break;\r
+            }\r
+            //check for sequence\r
+            else if(cdmBase.isInstanceOf(Sequence.class) && !specimenDeleteConfigurator.isDeleteMolecularData()){\r
+                isDeletable = false;\r
+                deleteResult.addException(new ReferencedObjectUndeletableException("DnaSample is used in sequences."));\r
+                break;\r
+            }\r
+        }\r
+        if(isDeletable){\r
+            //set to OK when config allows to delete related objects\r
+            deleteResult.setStatus(DeleteStatus.OK);\r
+        }\r
+\r
+        return deleteResult;\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#delete(eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase, eu.etaxonomy.cdm.api.service.config.SpecimenDeleteConfigurator)\r
+     */\r
+    @Override\r
+    public DeleteResult delete(SpecimenOrObservationBase<?> specimen, SpecimenDeleteConfigurator config) {\r
+        specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);\r
+\r
+        DeleteResult deleteResult = isDeletable(specimen, config);\r
+        if(!deleteResult.isOk()){\r
+            return deleteResult;\r
+        }\r
+\r
+        //check related objects\r
+        Set<CdmBase> relatedObjects = deleteResult.getRelatedObjects();\r
+        for (CdmBase relatedObject : relatedObjects) {\r
+            //check for TypeDesignations\r
+            if(relatedObject.isInstanceOf(SpecimenTypeDesignation.class)){\r
+                SpecimenTypeDesignation designation = HibernateProxyHelper.deproxy(relatedObject, SpecimenTypeDesignation.class);\r
+                designation.setTypeSpecimen(null);\r
+                Set<TaxonNameBase> typifiedNames = designation.getTypifiedNames();\r
+                for (TaxonNameBase taxonNameBase : typifiedNames) {\r
+                    taxonNameBase.removeTypeDesignation(designation);\r
+                }\r
+            }\r
+            //delete IndividualsAssociation\r
+            if(relatedObject.isInstanceOf(IndividualsAssociation.class)){\r
+                IndividualsAssociation assciation = HibernateProxyHelper.deproxy(relatedObject, IndividualsAssociation.class);\r
+                assciation.setAssociatedSpecimenOrObservation(null);\r
+                assciation.getInDescription().removeElement(assciation);\r
+            }\r
+            //check for taxon description\r
+            if(relatedObject.isInstanceOf(TaxonDescription.class)){\r
+                TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(relatedObject, TaxonDescription.class);\r
+                taxonDescription.setDescribedSpecimenOrObservation(null);\r
+            }\r
+            //check for specimen description\r
+            if(relatedObject.isInstanceOf(SpecimenDescription.class)){\r
+                SpecimenDescription specimenDescription = HibernateProxyHelper.deproxy(relatedObject, SpecimenDescription.class);\r
+                //check if specimen is "described" specimen\r
+                if(specimenDescription.getDescribedSpecimenOrObservation().equals(specimen)){\r
+                    specimenDescription.setDescribedSpecimenOrObservation(null);\r
+                }\r
+                //check if description is a description of the given specimen\r
+                if(specimen.getDescriptions().contains(specimenDescription)){\r
+                    specimen.removeDescription(specimenDescription);\r
+                }\r
+            }\r
+            //check for amplification\r
+            if(relatedObject.isInstanceOf(AmplificationResult.class)){\r
+                AmplificationResult amplificationResult = HibernateProxyHelper.deproxy(relatedObject, AmplificationResult.class);\r
+                amplificationResult.getDnaSample().removeAmplificationResult(amplificationResult);\r
+            }\r
+            //check for sequence\r
+            if(relatedObject.isInstanceOf(Sequence.class)){\r
+                Sequence sequence = HibernateProxyHelper.deproxy(relatedObject, Sequence.class);\r
+                sequence.getDnaSample().removeSequence(sequence);\r
+            }\r
+            //check for children and parents (derivation events)\r
+            if(relatedObject.isInstanceOf(DerivationEvent.class)){\r
+                DerivationEvent derivationEvent = HibernateProxyHelper.deproxy(relatedObject, DerivationEvent.class);\r
+                //parent derivation event (derivedFrom)\r
+                if(derivationEvent.getDerivatives().contains(specimen) && specimen.isInstanceOf(DerivedUnit.class)){\r
+                    derivationEvent.removeDerivative(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class));\r
+                    if(derivationEvent.getDerivatives().isEmpty()){\r
+                        Set<SpecimenOrObservationBase> originals = derivationEvent.getOriginals();\r
+                        for (SpecimenOrObservationBase specimenOrObservationBase : originals) {\r
+                            specimenOrObservationBase.removeDerivationEvent(derivationEvent);\r
+                        }\r
+                    }\r
+                }\r
+                //child derivation events\r
+                else{\r
+                    deleteResult.includeResult(deepDelete(specimen, config));\r
+                }\r
+            }\r
+        }\r
+\r
+        deleteResult.includeResult(delete(specimen));\r
+        return deleteResult;\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#deleteDerivateHierarchy(eu.etaxonomy.cdm.model.common.ICdmBase)\r
+     */\r
+    @Override\r
+    public DeleteResult deleteDerivateHierarchy(CdmBase from, SpecimenDeleteConfigurator config) {\r
+        DeleteResult deleteResult = new DeleteResult();\r
+        if(from.isInstanceOf(Sequence.class)){\r
+            if(!config.isDeleteMolecularData()){\r
+                deleteResult.setAbort();\r
+                deleteResult.addException(new ReferencedObjectUndeletableException("deleting molecur data is not allowed in config"));\r
+                return deleteResult;\r
+            }\r
+            Sequence sequence = HibernateProxyHelper.deproxy(from, Sequence.class);\r
+            sequence.getDnaSample().removeSequence(sequence);\r
+            deleteResult = sequenceService.delete(sequence);\r
+        }\r
+        else if(from.isInstanceOf(SingleRead.class))  {\r
+            if(!config.isDeleteMolecularData()){\r
+                deleteResult.setAbort();\r
+                deleteResult.addException(new ReferencedObjectUndeletableException("deleting molecur data is not allowed in config"));\r
+                return deleteResult;\r
+            }\r
+            SingleRead singleRead = HibernateProxyHelper.deproxy(from, SingleRead.class);\r
+            singleRead.getAmplificationResult().removeSingleRead(singleRead);\r
+            deleteResult.setStatus(DeleteStatus.OK);\r
+        }\r
+        else if(from.isInstanceOf(SpecimenOrObservationBase.class))  {\r
+            deleteResult = deepDelete(HibernateProxyHelper.deproxy(from, SpecimenOrObservationBase.class), config);\r
+        }\r
+        return deleteResult;\r
+    }\r
+\r
+    private DeleteResult deepDelete(SpecimenOrObservationBase<?> entity, SpecimenDeleteConfigurator config){\r
+        DeleteResult deleteResult = isDeletable(entity, config);\r
+        if(!deleteResult.isOk()){\r
+            return deleteResult;\r
+        }\r
+        Set<DerivationEvent> derivationEvents = entity.getDerivationEvents();\r
+        for (DerivationEvent derivationEvent : derivationEvents) {\r
+            Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();\r
+            for (DerivedUnit derivedUnit : derivatives) {\r
+                deleteResult.includeResult(deepDelete(derivedUnit, config));\r
+            }\r
+        }\r
+        deleteResult.includeResult(delete(entity, config));\r
+        return deleteResult;\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#listAssociatedTaxa(eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase)\r
+     */\r
+    @Override\r
+    public Collection<IndividualsAssociation> listIndividualsAssociations(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {\r
+        return dao.listIndividualsAssociations(specimen, null, null, null, null);\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#listTypeDesignations(eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)\r
+     */\r
+    @Override\r
+    public Collection<SpecimenTypeDesignation> listTypeDesignations(SpecimenOrObservationBase<?> specimen,\r
+            Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {\r
+        return dao.listTypeDesignations(specimen, limit, start, orderHints, propertyPaths);\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.IOccurrenceService#listDescriptionsWithDescriptionSpecimen(eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)\r
+     */\r
+    @Override\r
+    public Collection<DescriptionBase<?>> listDescriptionsWithDescriptionSpecimen(\r
+            SpecimenOrObservationBase<?> specimen, Integer limit, Integer start, List<OrderHint> orderHints,\r
+            List<String> propertyPaths) {\r
+        return dao.listDescriptionsWithDescriptionSpecimen(specimen, limit, start, orderHints, propertyPaths);\r
+    }\r
+\r
 }\r