Project

General

Profile

Download (17 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2018 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9
package eu.etaxonomy.cdm.persistence.dao.hibernate.taxonGraph;
10

    
11
import java.util.ArrayList;
12
import java.util.EnumSet;
13
import java.util.HashSet;
14
import java.util.List;
15
import java.util.Set;
16
import java.util.UUID;
17

    
18
import org.apache.log4j.Logger;
19
import org.hibernate.Query;
20
import org.hibernate.Session;
21

    
22
import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
23
import eu.etaxonomy.cdm.model.metadata.CdmPreference;
24
import eu.etaxonomy.cdm.model.name.Rank;
25
import eu.etaxonomy.cdm.model.name.TaxonName;
26
import eu.etaxonomy.cdm.model.reference.Reference;
27
import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
28
import eu.etaxonomy.cdm.model.reference.ReferenceType;
29
import eu.etaxonomy.cdm.model.taxon.Taxon;
30
import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
31
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
32
import eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmPreferenceLookup;
33
import eu.etaxonomy.cdm.persistence.dao.taxonGraph.TaxonGraphException;
34

    
35
/**
36
 * @author a.kohlbecker
37
 * @since Oct 4, 2018
38
 *
39
 */
40
public abstract class AbstractHibernateTaxonGraphProcessor {
41

    
42
    private static final Logger logger = Logger.getLogger(AbstractHibernateTaxonGraphProcessor.class);
43

    
44
    EnumSet<ReferenceType> referenceSectionTypes = EnumSet.of(ReferenceType.Section, ReferenceType.BookSection);
45

    
46
    private Reference secReference = null;
47

    
48
    private TaxonRelationshipType relType = TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN();
49

    
50
    protected TaxonRelationshipType relType() {
51
        if(relType == null){
52
            relType = TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN();
53
        }
54
        return relType;
55
    }
56

    
57

    
58
    /**
59
     * MUST ONLY BE USED IN TESTS
60
     */
61
    @Deprecated
62
    protected UUID secReferenceUUID;
63

    
64

    
65
    /**
66
     * MUST ONLY BE USED IN TESTS
67
     */
68
    @Deprecated
69
    protected void setSecReferenceUUID(UUID uuid){
70
        secReferenceUUID = uuid;
71
    }
72

    
73
    public UUID getSecReferenceUUID(){
74
        if(secReferenceUUID != null){
75
            return secReferenceUUID;
76
        } else {
77
            CdmPreference pref = CdmPreferenceLookup.instance().get(TaxonGraphDaoHibernateImpl.CDM_PREF_KEY_SEC_REF_UUID);
78
            UUID uuid = null;
79
            if(pref != null && pref.getValue() != null){
80
                try {
81
                    uuid = UUID.fromString(pref.getValue());
82
                } catch (Exception e) {
83
                    // TODO is logging only ok?
84
                    logger.error(e);
85
                }
86
            }
87
            if(uuid == null){
88
                logger.error("missing cdm property: " + TaxonGraphDaoHibernateImpl.CDM_PREF_KEY_SEC_REF_UUID.getSubject() + TaxonGraphDaoHibernateImpl.CDM_PREF_KEY_SEC_REF_UUID.getPredicate());
89
            }
90
            return uuid;
91
        }
92
    }
93

    
94
    public Reference secReference(){
95
        if(secReference == null){
96
            Query q = getSession().createQuery("SELECT r FROM Reference r WHERE r.uuid = :uuid");
97
            q.setParameter("uuid", getSecReferenceUUID());
98
            secReference = (Reference) q.uniqueResult();
99
            if(secReference == null){
100
                Reference missingRef = ReferenceFactory.newGeneric();
101
                UUID uuid = getSecReferenceUUID();
102
                if(uuid != null){
103
                    missingRef.setUuid(uuid);
104
                } else {
105
                    throw new RuntimeException("cdm preference " + TaxonGraphDaoHibernateImpl.CDM_PREF_KEY_SEC_REF_UUID.getSubject() + TaxonGraphDaoHibernateImpl.CDM_PREF_KEY_SEC_REF_UUID.getPredicate() + " missing, can not recover");
106
                }
107
                missingRef.setTitle("Autocreated missing reference for cdm property" + TaxonGraphDaoHibernateImpl.CDM_PREF_KEY_SEC_REF_UUID.getSubject() + TaxonGraphDaoHibernateImpl.CDM_PREF_KEY_SEC_REF_UUID.getPredicate());
108
                logger.warn("A reference with " + getSecReferenceUUID() + " does not exist in the database, and thus will be created now with the title "
109
                        + "\"" + missingRef.getTitle() + "\"");
110
                getSession().merge(missingRef);
111
            }
112
        } else {
113
            // make sure the entity is still in the current session
114
            secReference = getSession().load(Reference.class, secReference.getId());
115
        }
116
        return secReference;
117
    }
118

    
119

    
120
    /**
121
     * Create all missing edges from the <code>taxon</code>.
122
     *
123
     * @param taxonName
124
     */
125
    public void updateEdges(Taxon taxon) throws TaxonGraphException {
126

    
127
        Reference conceptReference = conceptReference(taxon.getName().getNomenclaturalReference());
128

    
129
        if(conceptReference != null){
130
            // update edges to higher names
131
            List<TaxonName> relatedHigherNames = relatedHigherNames(taxon.getName());
132
            List<TaxonRelationship> relationsFrom = taxonGraphRelationsFrom(taxon, conceptReference);
133
            List<TaxonName> relatedHigherNamesWithoutRels = new ArrayList<>(relatedHigherNames);
134
            for(TaxonRelationship rel : relationsFrom){
135
                boolean isRelToHigherName = relatedHigherNames.contains(rel.getToTaxon().getName());
136
                if(isRelToHigherName){
137
                    relatedHigherNamesWithoutRels.remove(rel.getToTaxon().getName());
138
                } else {
139
                    taxon.removeTaxonRelation(rel);
140
                }
141
            }
142
            for(TaxonName name : relatedHigherNamesWithoutRels){
143
                Taxon toTaxon = assureSingleTaxon(name);
144
                taxon.addTaxonRelation(toTaxon, relType(), conceptReference, null);
145
            }
146

    
147
            // update edges from lower names
148
            List<TaxonName> relatedLowerNames = relatedLowerNames(taxon.getName());
149
            List<TaxonRelationship> relationsTo = taxonGraphRelationsTo(taxon, null);
150
            List<TaxonName> relatedLowerNamesWithoutRels = new ArrayList<>(relatedLowerNames);
151
            for(TaxonRelationship rel : relationsTo){
152
                boolean isRelFromLowerName = relatedLowerNames.contains(rel.getFromTaxon().getName());
153
                if(isRelFromLowerName){
154
                    relatedLowerNamesWithoutRels.remove(rel.getFromTaxon().getName());
155
                } else {
156
                    taxon.removeTaxonRelation(rel);
157
                }
158
            }
159
            for(TaxonName name : relatedLowerNamesWithoutRels){
160
                Taxon fromTaxon = assureSingleTaxon(name);
161
                fromTaxon.addTaxonRelation(taxon, relType(), conceptReference, null);
162
            }
163
        }
164
    }
165

    
166
    /**
167
     * Remove all edges from the <code>taxon</code> having the <code>conceptReference</code>
168
     *
169
     * @param taxon
170
     * @param oldConceptReference
171
     */
172
    public void removeEdges(Taxon taxon, Reference conceptReference) {
173
        List<TaxonRelationship> relations = taxonGraphRelationsFrom(taxon, conceptReference);
174
        List<TaxonName> relatedHigherNames = relatedHigherNames(taxon.getName());
175
        for(TaxonRelationship rel : relations){
176
            boolean isRelToHigherName = relatedHigherNames.contains(rel.getToTaxon().getName());
177
            if(isRelToHigherName){
178
                taxon.removeTaxonRelation(rel);
179
            }
180
        }
181
    }
182

    
183
    /**
184
     * @param taxon
185
     */
186
    public void updateConceptReferenceInEdges(Taxon taxon, Reference oldNomReference) throws TaxonGraphException {
187

    
188
        Reference conceptReference = conceptReference(taxon.getName().getNomenclaturalReference());
189
        Reference oldConceptReference = conceptReference(oldNomReference);
190

    
191
        if(conceptReference != null && oldConceptReference != null){
192
            // update old with new ref
193
            updateReferenceInEdges(taxon, conceptReference, oldConceptReference);
194
        } else if(conceptReference != null && oldConceptReference == null) {
195
            // create new relations for the name as there are none so far
196
            updateEdges(taxon);
197
        } else if(conceptReference == null && oldConceptReference != null){
198
            // remove all relations
199
            removeEdges(taxon, oldConceptReference);
200
        }
201
    }
202

    
203
    /**
204
     * @param taxon
205
     * @param conceptReference
206
     * @param oldConceptReference
207
     */
208
    protected void updateReferenceInEdges(Taxon taxon, Reference conceptReference, Reference oldConceptReference) {
209
        List<TaxonRelationship> relations = taxonGraphRelationsFrom(taxon, oldConceptReference);
210
        List<TaxonName> relatedHigherNames = relatedHigherNames(taxon.getName());
211
        for(TaxonRelationship rel : relations){
212
            boolean isRelToHigherName = relatedHigherNames.contains(rel.getToTaxon().getName());
213
            if(isRelToHigherName){
214
                rel.setCitation(conceptReference);
215
                getSession().saveOrUpdate(rel);
216
            }
217
        }
218
    }
219

    
220
    abstract public Session getSession();
221

    
222
    public Taxon assureSingleTaxon(TaxonName taxonName) throws TaxonGraphException {
223
        return assureSingleTaxon(taxonName, true);
224
    }
225

    
226
    public Taxon assureSingleTaxon(TaxonName taxonName, boolean createMissing) throws TaxonGraphException {
227

    
228
        UUID secRefUuid = getSecReferenceUUID();
229
        Session session = getSession();
230
        TaxonName taxonNamePersisted = session.load(TaxonName.class, taxonName.getId());
231

    
232
        // filter by secRefUuid
233
        Taxon taxon = null;
234
        Set<Taxon> taxa = new HashSet<>();
235
        for(Taxon t : taxonName.getTaxa()){
236
            if(t.getSec() != null && t.getSec().getUuid().equals(secRefUuid)){
237
                taxa.add(t);
238
            }
239
        }
240

    
241
        if(taxa.size() == 0){
242
            if(createMissing){
243
                if(taxonNamePersisted != null){
244
                    Reference secRef = secReference();
245
                    taxon = Taxon.NewInstance(taxonNamePersisted, secRef);
246
                    session.saveOrUpdate(taxon);
247
                } else {
248
                    throw new TaxonGraphException("Can't create taxon for deleted name: " + taxonName);
249
                }
250
            } else {
251
                if(logger.isDebugEnabled()){
252
                    logger.debug("No taxon found for " + taxonName);
253
                }
254
            }
255
        } else if(taxa.size() == 1){
256
            taxon = taxa.iterator().next();
257
        } else {
258
            throw new TaxonGraphException("A name to be used in a taxon graph must only have one taxon with the default sec reference [secRef uuid: "+ secRefUuid.toString() +"]");
259
        }
260
        return taxon != null ? session.load(Taxon.class, taxon.getId()) : null;
261
    }
262

    
263
    protected Reference conceptReference(Reference nomenclaturalReference) {
264

    
265
        Reference conceptRef = nomenclaturalReference;
266
        if(conceptRef != null){
267
            while(referenceSectionTypes.contains(conceptRef.getType()) && conceptRef.getInReference() != null){
268
                conceptRef = conceptRef.getInReference();
269
            }
270
        }
271
        return conceptRef;
272
    }
273

    
274
    /**
275
     * @param name
276
     * @return
277
     */
278
    protected List<TaxonName> relatedHigherNames(TaxonName name) {
279

    
280
        List<TaxonName> relatedNames = new ArrayList<>();
281

    
282
        if(name.getRank().isSpecies() || name.getRank().isInfraSpecific()){
283
            if(name.getGenusOrUninomial() != null){
284
                List<TaxonName> names = listNamesAtRank(Rank.GENUS(), name.getGenusOrUninomial(), null);
285
                if(names.size() == 0){
286
                    logger.warn("Genus entity with \"" + name.getGenusOrUninomial() + "\" missing");
287
                } else {
288
                    if(names.size() > 1){
289
                        logger.warn("Duplicate genus entities found for \"" + name.getGenusOrUninomial() + "\", will create taxon graph relation to all of them!");
290
                    }
291
                    relatedNames.addAll(names);
292
                }
293
            }
294
        }
295
        if(name.getRank().isInfraSpecific()){
296
            if(name.getGenusOrUninomial() != null && name.getSpecificEpithet() != null){
297
                List<TaxonName> names = listNamesAtRank(Rank.SPECIES(), name.getGenusOrUninomial(), name.getSpecificEpithet());
298
                if(names.size() == 0){
299
                    logger.warn("Species entity with \"" + name.getGenusOrUninomial() + " " + name.getSpecificEpithet() + "\" missing");
300
                } else {
301
                    if(names.size() > 1){
302
                        logger.warn("Duplicate species entities found for \"" + name.getGenusOrUninomial() + " " + name.getSpecificEpithet() + "\", will create taxon graph relation to all of them!");
303
                    }
304
                    relatedNames.addAll(names);
305
                }
306
            }
307
         }
308

    
309
        return relatedNames;
310
    }
311

    
312
    /**
313
     * @param name
314
     * @return
315
     */
316
    protected List<TaxonName> relatedLowerNames(TaxonName name) {
317

    
318
        List<TaxonName> relatedNames = new ArrayList<>();
319

    
320
        if(name.getRank().isGenus()){
321
            if(name.getGenusOrUninomial() != null){
322
                List<TaxonName> names = listNamesAtRank(Rank.SPECIES(), name.getGenusOrUninomial(), null);
323
                if(names.size() == 0){
324
                    logger.debug("No species entity with \"" + name.getGenusOrUninomial() + " *\" found");
325
                } else {
326
                    logger.debug(names.size() + " species entities found with \"" + name.getGenusOrUninomial() + " *\"");
327
                    relatedNames.addAll(names);
328
                }
329
            }
330
        }
331
        if(name.getRank().isSpecies()){
332
            if(name.getGenusOrUninomial() != null && name.getSpecificEpithet() != null){
333
                List<TaxonName> names = listNamesBelowRank(Rank.SPECIES(), name.getGenusOrUninomial(), name.getSpecificEpithet());
334
                if(names.size() == 0){
335
                    logger.warn("No infraspecific entity with \"" + name.getGenusOrUninomial() + " " + name.getSpecificEpithet() + "*\" found");
336
                } else {
337
                    if(names.size() > 1){
338
                        logger.warn(names.size() + " infraspecific entities found with \"" + name.getGenusOrUninomial() + " " + name.getSpecificEpithet() + "*\"found");
339
                    }
340
                    relatedNames.addAll(names);
341
                }
342
            }
343
         }
344

    
345
        return relatedNames;
346
    }
347

    
348

    
349
    /**
350
     * @param taxon
351
     */
352
    protected List<TaxonRelationship> taxonGraphRelationsFrom(Taxon taxon, Reference citation) {
353
        List<TaxonRelationship> relations = getTaxonRelationships(taxon, relType(), citation, TaxonRelationship.Direction.relatedFrom);
354
        return relations;
355
    }
356

    
357
    /**
358
     * @param taxon
359
     */
360
    protected List<TaxonRelationship> taxonGraphRelationsTo(Taxon taxon, Reference citation) {
361
        List<TaxonRelationship> relations = getTaxonRelationships(taxon, relType(), citation, TaxonRelationship.Direction.relatedTo);
362
        return relations;
363
    }
364

    
365
    protected List<TaxonName> listNamesAtRank(Rank rank, String genusOrUninomial, String specificEpithet){
366
        String hql = "SELECT n FROM TaxonName n WHERE n.rank = :rank AND n.genusOrUninomial = :genusOrUninomial";
367
        if(specificEpithet != null){
368
            hql += " AND n.specificEpithet = :specificEpithet";
369
        }
370
        Query q = getSession().createQuery(hql);
371

    
372
        q.setParameter("rank", rank);
373
        q.setParameter("genusOrUninomial", genusOrUninomial);
374
        if(specificEpithet != null){
375
            q.setParameter("specificEpithet", specificEpithet);
376
        }
377

    
378
        List<TaxonName> result = q.list();
379
        return result;
380
    }
381

    
382
    protected List<TaxonName> listNamesBelowRank(Rank rank, String genusOrUninomial, String specificEpithet){
383
        String hql = "SELECT n FROM TaxonName n WHERE n.rank.orderIndex > :rankOrderIndex AND n.genusOrUninomial = :genusOrUninomial";
384
        if(specificEpithet != null){
385
            hql += " AND n.specificEpithet = :specificEpithet";
386
        }
387
        Query q = getSession().createQuery(hql);
388

    
389
        q.setParameter("rankOrderIndex", rank.getOrderIndex());
390
        q.setParameter("genusOrUninomial", genusOrUninomial);
391
        if(specificEpithet != null){
392
            q.setParameter("specificEpithet", specificEpithet);
393
        }
394

    
395
        @SuppressWarnings("unchecked")
396
        List<TaxonName> result = q.list();
397
        return result;
398
    }
399

    
400
    /**
401
     *
402
     * @param relatedTaxon required
403
     * @param type required
404
     * @param citation can be null
405
     * @param direction required
406
     * @return
407
     */
408
    protected List<TaxonRelationship> getTaxonRelationships(Taxon relatedTaxon, TaxonRelationshipType type, Reference citation, Direction direction){
409

    
410
        String hql = "SELECT rel FROM TaxonRelationship rel WHERE rel." + direction + " = :relatedTaxon AND rel.type = :type";
411
        if(citation != null){
412
            hql += " AND rel.citation = :citation";
413
        }
414
        Query q = getSession().createQuery(hql);
415
        q.setParameter("relatedTaxon", relatedTaxon);
416
        q.setParameter("type", type);
417
        if(citation != null){
418
        q.setParameter("citation", citation);
419
        }
420
        @SuppressWarnings("unchecked")
421
        List<TaxonRelationship> rels = q.list();
422
        return rels;
423
    }
424

    
425

    
426
}
(1-1/2)