Project

General

Profile

Download (15.9 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.ReferenceType;
28
import eu.etaxonomy.cdm.model.taxon.Taxon;
29
import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
30
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
31
import eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmPreferenceLookup;
32
import eu.etaxonomy.cdm.persistence.dao.taxonGraph.TaxonGraphException;
33

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

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

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

    
45
    private Reference secReference = null;
46

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

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

    
56

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

    
63

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

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

    
93
    public Reference secReference(){
94
        if(secReference == null){
95
            Query q = getSession().createQuery("SELECT r FROM Reference r WHERE r.uuid = :uuid");
96
            q.setParameter("uuid", getSecReferenceUUID());
97
            secReference = (Reference) q.uniqueResult();
98
        } else {
99
            // make sure the entity is still in the current session
100
            secReference = getSession().load(Reference.class, secReference.getId());
101
        }
102
        return secReference;
103
    }
104

    
105

    
106
    /**
107
     * Create all missing edges from the <code>taxon</code>.
108
     *
109
     * @param taxonName
110
     */
111
    public void updateEdges(Taxon taxon) throws TaxonGraphException {
112

    
113
        Reference conceptReference = conceptReference(taxon.getName().getNomenclaturalReference());
114

    
115
        if(conceptReference != null){
116
            // update edges to higher names
117
            List<TaxonName> relatedHigherNames = relatedHigherNames(taxon.getName());
118
            List<TaxonRelationship> relationsFrom = taxonGraphRelationsFrom(taxon, conceptReference);
119
            List<TaxonName> relatedHigherNamesWithoutRels = new ArrayList<>(relatedHigherNames);
120
            for(TaxonRelationship rel : relationsFrom){
121
                boolean isRelToHigherName = relatedHigherNames.contains(rel.getToTaxon().getName());
122
                if(isRelToHigherName){
123
                    relatedHigherNamesWithoutRels.remove(rel.getToTaxon().getName());
124
                } else {
125
                    taxon.removeTaxonRelation(rel);
126
                }
127
            }
128
            for(TaxonName name : relatedHigherNamesWithoutRels){
129
                Taxon toTaxon = assureSingleTaxon(name);
130
                taxon.addTaxonRelation(toTaxon, relType(), conceptReference, null);
131
            }
132

    
133
            // update edges from lower names
134
            List<TaxonName> relatedLowerNames = relatedLowerNames(taxon.getName());
135
            List<TaxonRelationship> relationsTo = taxonGraphRelationsTo(taxon, null);
136
            List<TaxonName> relatedLowerNamesWithoutRels = new ArrayList<>(relatedLowerNames);
137
            for(TaxonRelationship rel : relationsTo){
138
                boolean isRelFromLowerName = relatedLowerNames.contains(rel.getFromTaxon().getName());
139
                if(isRelFromLowerName){
140
                    relatedLowerNamesWithoutRels.remove(rel.getFromTaxon().getName());
141
                } else {
142
                    taxon.removeTaxonRelation(rel);
143
                }
144
            }
145
            for(TaxonName name : relatedLowerNamesWithoutRels){
146
                Taxon fromTaxon = assureSingleTaxon(name);
147
                fromTaxon.addTaxonRelation(taxon, relType(), conceptReference, null);
148
            }
149
        }
150
    }
151

    
152
    /**
153
     * Remove all edges from the <code>taxon</code> having the <code>conceptReference</code>
154
     *
155
     * @param taxon
156
     * @param oldConceptReference
157
     */
158
    public void removeEdges(Taxon taxon, Reference conceptReference) {
159
        List<TaxonRelationship> relations = taxonGraphRelationsFrom(taxon, conceptReference);
160
        List<TaxonName> relatedHigherNames = relatedHigherNames(taxon.getName());
161
        for(TaxonRelationship rel : relations){
162
            boolean isRelToHigherName = relatedHigherNames.contains(rel.getToTaxon().getName());
163
            if(isRelToHigherName){
164
                taxon.removeTaxonRelation(rel);
165
            }
166
        }
167
    }
168

    
169
    /**
170
     * @param taxon
171
     */
172
    public void updateConceptReferenceInEdges(Taxon taxon, Reference oldNomReference) throws TaxonGraphException {
173

    
174
        Reference conceptReference = conceptReference(taxon.getName().getNomenclaturalReference());
175
        Reference oldConceptReference = conceptReference(oldNomReference);
176

    
177
        if(conceptReference != null && oldConceptReference != null){
178
            // update old with new ref
179
            updateReferenceInEdges(taxon, conceptReference, oldConceptReference);
180
        } else if(conceptReference != null && oldConceptReference == null) {
181
            // create new relations for the name as there are none so far
182
            updateEdges(taxon);
183
        } else if(conceptReference == null && oldConceptReference != null){
184
            // remove all relations
185
            removeEdges(taxon, oldConceptReference);
186
        }
187
    }
188

    
189
    /**
190
     * @param taxon
191
     * @param conceptReference
192
     * @param oldConceptReference
193
     */
194
    protected void updateReferenceInEdges(Taxon taxon, Reference conceptReference, Reference oldConceptReference) {
195
        List<TaxonRelationship> relations = taxonGraphRelationsFrom(taxon, oldConceptReference);
196
        List<TaxonName> relatedHigherNames = relatedHigherNames(taxon.getName());
197
        for(TaxonRelationship rel : relations){
198
            boolean isRelToHigherName = relatedHigherNames.contains(rel.getToTaxon().getName());
199
            if(isRelToHigherName){
200
                rel.setCitation(conceptReference);
201
                getSession().saveOrUpdate(rel);
202
            }
203
        }
204
    }
205

    
206
    abstract public Session getSession();
207

    
208
    public Taxon assureSingleTaxon(TaxonName taxonName) throws TaxonGraphException {
209
        return assureSingleTaxon(taxonName, true);
210
    }
211

    
212
    public Taxon assureSingleTaxon(TaxonName taxonName, boolean createMissing) throws TaxonGraphException {
213

    
214
        UUID secRefUuid = getSecReferenceUUID();
215
        Session session = getSession();
216
        TaxonName taxonNamePersisted = session.load(TaxonName.class, taxonName.getId());
217

    
218
        // filter by secRefUuid
219
        Taxon taxon = null;
220
        Set<Taxon> taxa = new HashSet<>();
221
        for(Taxon t : taxonName.getTaxa()){
222
            if(t.getSec() != null && t.getSec().getUuid().equals(secRefUuid)){
223
                taxa.add(t);
224
            }
225
        }
226

    
227
        if(taxa.size() == 0){
228
            if(createMissing){
229
                if(taxonNamePersisted != null){
230
                    Reference secRef = secReference();
231
                    taxon = Taxon.NewInstance(taxonNamePersisted, secRef);
232
                    session.saveOrUpdate(taxon);
233
                } else {
234
                    throw new TaxonGraphException("Can't create taxon for deleted name: " + taxonName);
235
                }
236
            } else {
237
                if(logger.isDebugEnabled()){
238
                    logger.debug("No taxon found for " + taxonName);
239
                }
240
            }
241
        } else if(taxa.size() == 1){
242
            taxon = taxa.iterator().next();
243
        } else {
244
            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() +"]");
245
        }
246
        return taxon != null ? session.load(Taxon.class, taxon.getId()) : null;
247
    }
248

    
249
    protected Reference conceptReference(Reference nomenclaturalReference) {
250

    
251
        Reference conceptRef = nomenclaturalReference;
252
        if(conceptRef != null){
253
            while(referenceSectionTypes.contains(conceptRef.getType()) && conceptRef.getInReference() != null){
254
                conceptRef = conceptRef.getInReference();
255
            }
256
        }
257
        return conceptRef;
258
    }
259

    
260
    /**
261
     * @param name
262
     * @return
263
     */
264
    protected List<TaxonName> relatedHigherNames(TaxonName name) {
265

    
266
        List<TaxonName> relatedNames = new ArrayList<>();
267

    
268
        if(name.getRank().isSpecies() || name.getRank().isInfraSpecific()){
269
            if(name.getGenusOrUninomial() != null){
270
                List<TaxonName> names = listNamesAtRank(Rank.GENUS(), name.getGenusOrUninomial(), null);
271
                if(names.size() == 0){
272
                    logger.warn("Genus entity with \"" + name.getGenusOrUninomial() + "\" missing");
273
                } else {
274
                    if(names.size() > 1){
275
                        logger.warn("Duplicate genus entities found for \"" + name.getGenusOrUninomial() + "\", will create taxon graph relation to all of them!");
276
                    }
277
                    relatedNames.addAll(names);
278
                }
279
            }
280
        }
281
        if(name.getRank().isInfraSpecific()){
282
            if(name.getGenusOrUninomial() != null && name.getSpecificEpithet() != null){
283
                List<TaxonName> names = listNamesAtRank(Rank.SPECIES(), name.getGenusOrUninomial(), name.getSpecificEpithet());
284
                if(names.size() == 0){
285
                    logger.warn("Species entity with \"" + name.getGenusOrUninomial() + " " + name.getSpecificEpithet() + "\" missing");
286
                } else {
287
                    if(names.size() > 1){
288
                        logger.warn("Duplicate species entities found for \"" + name.getGenusOrUninomial() + " " + name.getSpecificEpithet() + "\", will create taxon graph relation to all of them!");
289
                    }
290
                    relatedNames.addAll(names);
291
                }
292
            }
293
         }
294

    
295
        return relatedNames;
296
    }
297

    
298
    /**
299
     * @param name
300
     * @return
301
     */
302
    protected List<TaxonName> relatedLowerNames(TaxonName name) {
303

    
304
        List<TaxonName> relatedNames = new ArrayList<>();
305

    
306
        if(name.getRank().isGenus()){
307
            if(name.getGenusOrUninomial() != null){
308
                List<TaxonName> names = listNamesAtRank(Rank.SPECIES(), name.getGenusOrUninomial(), null);
309
                if(names.size() == 0){
310
                    logger.debug("No species entity with \"" + name.getGenusOrUninomial() + " *\" found");
311
                } else {
312
                    logger.debug(names.size() + " species entities found with \"" + name.getGenusOrUninomial() + " *\"");
313
                    relatedNames.addAll(names);
314
                }
315
            }
316
        }
317
        if(name.getRank().isSpecies()){
318
            if(name.getGenusOrUninomial() != null && name.getSpecificEpithet() != null){
319
                List<TaxonName> names = listNamesBelowRank(Rank.SPECIES(), name.getGenusOrUninomial(), name.getSpecificEpithet());
320
                if(names.size() == 0){
321
                    logger.warn("No infraspecific entity with \"" + name.getGenusOrUninomial() + " " + name.getSpecificEpithet() + "*\" found");
322
                } else {
323
                    if(names.size() > 1){
324
                        logger.warn(names.size() + " infraspecific entities found with \"" + name.getGenusOrUninomial() + " " + name.getSpecificEpithet() + "*\"found");
325
                    }
326
                    relatedNames.addAll(names);
327
                }
328
            }
329
         }
330

    
331
        return relatedNames;
332
    }
333

    
334

    
335
    /**
336
     * @param taxon
337
     */
338
    protected List<TaxonRelationship> taxonGraphRelationsFrom(Taxon taxon, Reference citation) {
339
        List<TaxonRelationship> relations = getTaxonRelationships(taxon, relType(), citation, TaxonRelationship.Direction.relatedFrom);
340
        return relations;
341
    }
342

    
343
    /**
344
     * @param taxon
345
     */
346
    protected List<TaxonRelationship> taxonGraphRelationsTo(Taxon taxon, Reference citation) {
347
        List<TaxonRelationship> relations = getTaxonRelationships(taxon, relType(), citation, TaxonRelationship.Direction.relatedTo);
348
        return relations;
349
    }
350

    
351
    protected List<TaxonName> listNamesAtRank(Rank rank, String genusOrUninomial, String specificEpithet){
352
        String hql = "SELECT n FROM TaxonName n WHERE n.rank = :rank AND n.genusOrUninomial = :genusOrUninomial";
353
        if(specificEpithet != null){
354
            hql += " AND n.specificEpithet = :specificEpithet";
355
        }
356
        Query q = getSession().createQuery(hql);
357

    
358
        q.setParameter("rank", rank);
359
        q.setParameter("genusOrUninomial", genusOrUninomial);
360
        if(specificEpithet != null){
361
            q.setParameter("specificEpithet", specificEpithet);
362
        }
363

    
364
        List<TaxonName> result = q.list();
365
        return result;
366
    }
367

    
368
    protected List<TaxonName> listNamesBelowRank(Rank rank, String genusOrUninomial, String specificEpithet){
369
        String hql = "SELECT n FROM TaxonName n WHERE n.rank.orderIndex > :rankOrderIndex AND n.genusOrUninomial = :genusOrUninomial";
370
        if(specificEpithet != null){
371
            hql += " AND n.specificEpithet = :specificEpithet";
372
        }
373
        Query q = getSession().createQuery(hql);
374

    
375
        q.setParameter("rankOrderIndex", rank.getOrderIndex());
376
        q.setParameter("genusOrUninomial", genusOrUninomial);
377
        if(specificEpithet != null){
378
            q.setParameter("specificEpithet", specificEpithet);
379
        }
380

    
381
        @SuppressWarnings("unchecked")
382
        List<TaxonName> result = q.list();
383
        return result;
384
    }
385

    
386
    /**
387
     *
388
     * @param relatedTaxon required
389
     * @param type required
390
     * @param citation can be null
391
     * @param direction required
392
     * @return
393
     */
394
    protected List<TaxonRelationship> getTaxonRelationships(Taxon relatedTaxon, TaxonRelationshipType type, Reference citation, Direction direction){
395

    
396
        String hql = "SELECT rel FROM TaxonRelationship rel WHERE rel." + direction + " = :relatedTaxon AND rel.type = :type";
397
        if(citation != null){
398
            hql += " AND rel.citation = :citation";
399
        }
400
        Query q = getSession().createQuery(hql);
401
        q.setParameter("relatedTaxon", relatedTaxon);
402
        q.setParameter("type", type);
403
        if(citation != null){
404
        q.setParameter("citation", citation);
405
        }
406
        @SuppressWarnings("unchecked")
407
        List<TaxonRelationship> rels = q.list();
408
        return rels;
409
    }
410

    
411

    
412
}
(1-1/2)