Project

General

Profile

Download (22.1 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2007 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.api.service;
10

    
11
import java.util.ArrayList;
12
import java.util.HashMap;
13
import java.util.HashSet;
14
import java.util.List;
15
import java.util.Map;
16
import java.util.Map.Entry;
17
import java.util.Set;
18
import java.util.UUID;
19

    
20
import org.apache.logging.log4j.LogManager;
21
import org.apache.logging.log4j.Logger;
22
import org.springframework.beans.factory.annotation.Autowired;
23
import org.springframework.stereotype.Service;
24
import org.springframework.transaction.annotation.Transactional;
25

    
26
import eu.etaxonomy.cdm.api.service.config.NodeDeletionConfigurator.ChildHandling;
27
import eu.etaxonomy.cdm.api.service.config.TermNodeDeletionConfigurator;
28
import eu.etaxonomy.cdm.api.service.exception.DataChangeNoRollbackException;
29
import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
30
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
31
import eu.etaxonomy.cdm.model.common.CdmBase;
32
import eu.etaxonomy.cdm.model.description.Character;
33
import eu.etaxonomy.cdm.model.description.Feature;
34
import eu.etaxonomy.cdm.model.description.FeatureState;
35
import eu.etaxonomy.cdm.model.description.MeasurementUnit;
36
import eu.etaxonomy.cdm.model.description.State;
37
import eu.etaxonomy.cdm.model.description.StatisticalMeasure;
38
import eu.etaxonomy.cdm.model.term.DefinedTerm;
39
import eu.etaxonomy.cdm.model.term.DefinedTermBase;
40
import eu.etaxonomy.cdm.model.term.Representation;
41
import eu.etaxonomy.cdm.model.term.TermNode;
42
import eu.etaxonomy.cdm.model.term.TermTree;
43
import eu.etaxonomy.cdm.model.term.TermType;
44
import eu.etaxonomy.cdm.model.term.TermVocabulary;
45
import eu.etaxonomy.cdm.persistence.dao.term.ITermNodeDao;
46
import eu.etaxonomy.cdm.persistence.dto.CharacterDto;
47
import eu.etaxonomy.cdm.persistence.dto.CharacterNodeDto;
48
import eu.etaxonomy.cdm.persistence.dto.FeatureStateDto;
49
import eu.etaxonomy.cdm.persistence.dto.MergeResult;
50
import eu.etaxonomy.cdm.persistence.dto.TermDto;
51
import eu.etaxonomy.cdm.persistence.dto.TermNodeDto;
52
import eu.etaxonomy.cdm.persistence.dto.TermVocabularyDto;
53
import eu.etaxonomy.cdm.persistence.query.OrderHint;
54

    
55
/**
56
 * @author a.mueller
57
 * @since Jul 22, 2019
58
 */
59
@Service
60
@Transactional(readOnly = false)
61
public class TermNodeServiceImpl
62
        extends VersionableServiceBase<TermNode, ITermNodeDao>
63
        implements ITermNodeService {
64

    
65
    @SuppressWarnings("unused")
66
    private static final Logger logger = LogManager.getLogger(TermNodeServiceImpl.class);
67

    
68
	@Override
69
    @Autowired
70
	protected void setDao(ITermNodeDao dao) {
71
		this.dao = dao;
72
	}
73

    
74
	@Autowired
75
    private ITermService termService;
76

    
77
	@Autowired
78
	private IVocabularyService vocabularyService;
79

    
80
	@Override
81
    public List<TermNode> list(TermType termType, Integer limit, Integer start,
82
	        List<OrderHint> orderHints, List<String> propertyPaths){
83
	    return dao.list(termType, limit, start, orderHints, propertyPaths);
84
	}
85

    
86
	@Override
87
	@Transactional(readOnly = false)
88
	public <T extends DefinedTermBase<?>> DeleteResult deleteNode(UUID nodeUuid, TermNodeDeletionConfigurator config) {
89
	    DeleteResult result = new DeleteResult();
90
        @SuppressWarnings("unchecked")
91
        TermNode<T> node = CdmBase.deproxy(dao.load(nodeUuid));
92
	    result = isDeletable(nodeUuid, config);
93
	    if (result.isOk()){
94
	        TermNode<T> parent = node.getParent();
95
            parent = CdmBase.deproxy(parent);
96
            List<TermNode<T>> children = new ArrayList<>(node.getChildNodes());
97

    
98
	        if (config.getChildHandling().equals(ChildHandling.DELETE)){
99

    
100
	            for (TermNode<?> child: children){
101
	                deleteNode(child.getUuid(), config);
102
	               // node.removeChild(child);
103
	            }
104
	            if (parent != null){
105
	                parent.removeChild(node);
106
	            }
107
	        } else{
108
	            if (parent != null){
109
	                parent.removeChild(node);
110
	                for (TermNode<T> child: children){
111
	                    node.removeChild(child);
112
	                    parent.addChild(child);
113
	                }
114
	            }else{
115
	                 result.setAbort();
116
	                 result.addException(new ReferencedObjectUndeletableException("The root node can not be deleted without its child nodes"));
117
	                 return result;
118
	             }
119
	         }
120

    
121
	         dao.delete(node);
122
	         result.addDeletedObject(node);
123
	         if(parent!=null){
124
	             result.addUpdatedObject(parent);
125
	         }
126
	         if (config.isDeleteElement()){
127
	             DefinedTermBase<?> term = node.getTerm();
128
                 termService.delete(term.getUuid());
129
                 result.addDeletedObject(term);
130
             }
131
	     }
132
	     return result;
133
	 }
134

    
135
	 @Override
136
     public UpdateResult createChildNode(UUID parentNodeUuid, DefinedTermBase term, UUID vocabularyUuid){
137
	     TermVocabulary vocabulary = vocabularyService.load(vocabularyUuid);
138

    
139
	     vocabulary.addTerm(term);
140
	     vocabularyService.save(vocabulary);
141
	     return addChildNode(parentNodeUuid, term.getUuid());
142
	 }
143

    
144
     @Override
145
     public UpdateResult addChildNode(UUID nodeUUID, UUID termChildUuid){
146
         return addChildNode(nodeUUID, termChildUuid, 0);
147
     }
148

    
149
	 @Override
150
	 public UpdateResult addChildNode(UUID nodeUUID, UUID termChildUuid, int position){
151
	     UpdateResult result = new UpdateResult();
152

    
153
	     TermNode node = load(nodeUUID);
154
	     if (node == null){
155
	         result.setError();
156
             result.addException(new Exception("The parent node does not exist."));
157
             return result;
158
	     }
159
	     DefinedTermBase child = HibernateProxyHelper.deproxy(termService.load(termChildUuid), DefinedTermBase.class);
160

    
161
	     if(node.getGraph() != null && !node.getGraph().isAllowDuplicates() && node.getGraph().getDistinctTerms().contains(child)){
162
	         result.setError();
163
	         result.addException(new Exception("This term tree does not allow duplicate terms."));
164
	         return result;
165
	     }
166

    
167
	     TermNode childNode;
168
         if(position<0) {
169
             childNode = node.addChild(child);
170
         } else {
171
             childNode = node.addChild(child, position);
172
         }
173
         save(childNode);
174
         result.addUpdatedObject(node);
175
         result.setCdmEntity(childNode);
176
         return result;
177
     }
178

    
179
	@Override
180
	public DeleteResult isDeletable(UUID nodeUuid, TermNodeDeletionConfigurator config){
181
	    TermNode<?> node = load(nodeUuid);
182
	    DeleteResult result = new DeleteResult();
183
        if (node == null){
184
            result.addException(new DataChangeNoRollbackException("The object is not available anymore."));
185
            result.setAbort();
186
            return result;
187
        }
188
	    Set<CdmBase> references = commonService.getReferencingObjectsForDeletion(node);
189
	    for (CdmBase ref:references){
190
	        if (ref instanceof TermNode){
191
	            break;
192
	        }
193
	        if (ref instanceof TermTree){
194
	            TermTree<?> refTree = HibernateProxyHelper.deproxy(ref, TermTree.class);
195
	            if (node.getGraph().equals((refTree))){
196
	                break;
197
	            }
198
	        }
199
	        result.setAbort();
200
	        result.addException(new ReferencedObjectUndeletableException("The featureNode is referenced by " + ref.getUserFriendlyDescription() +" with id " +ref.getId()));
201
	    }
202
	    return result;
203
	}
204

    
205
    @Override
206
    public UpdateResult moveNode(UUID movedNodeUuid, UUID targetNodeUuid, int position) {
207
        UpdateResult result = new UpdateResult();
208
        List<String> propertyPaths = new ArrayList<>();
209
        propertyPaths.add("parent");
210
        propertyPaths.add("parent.children");
211
        propertyPaths.add("children");
212
        TermNode<?> test = load(movedNodeUuid, propertyPaths);
213
        TermNode movedNode = CdmBase.deproxy(load(movedNodeUuid, propertyPaths), TermNode.class);
214
        TermNode<?> targetNode = CdmBase.deproxy(load(targetNodeUuid, propertyPaths));
215
        TermNode<?> parent = CdmBase.deproxy(movedNode.getParent());
216
        parent.removeChild(movedNode);
217
        if(position < 0){
218
            targetNode.addChild(movedNode);
219
        }
220
        else{
221
            targetNode.addChild(movedNode, position);
222
        }
223
        result.addUpdatedObject(targetNode);
224
        result.addUpdatedObject(parent);
225
        result.setCdmEntity(movedNode);
226
        return result;
227
    }
228

    
229
    @Override
230
    public UpdateResult moveNode(UUID movedNodeUuid, UUID targetNodeUuid) {
231
        return moveNode(movedNodeUuid, targetNodeUuid, -1);
232
    }
233

    
234
    @Override
235
    public UpdateResult saveTermNodeDtoList(List<TermNodeDto> dtos){
236
        UpdateResult result = new UpdateResult();
237
        List<UUID> uuids = new ArrayList<>();
238
        dtos.stream().forEach(dto -> uuids.add(dto.getUuid()));
239
        List<TermNode> nodes = dao.list(uuids, null, 0, null, null);
240
        //check all attributes for changes and adapt
241
        for (TermNode<?> node: nodes){
242
            for (TermNodeDto dto: dtos){
243

    
244
                if (dto.getUuid().equals(node.getUuid())){
245
    //                only node changes, everything else will be handled by the operations/service methods
246
                    updateFeatureStates(node, dto, true);
247
                    updateFeatureStates(node, dto, false);
248

    
249
                }
250
                MergeResult<TermNode> mergeResult = dao.merge(node, true);
251
                result.addUpdatedObject(mergeResult.getMergedEntity());
252
            }
253
        }
254
        return result;
255
    }
256

    
257
    private void updateFeatureStates(TermNode<?> node, TermNodeDto dto, boolean inApplicable) {
258
        Map<FeatureState, FeatureStateDto> changeState = new HashMap<>();
259
        Set<FeatureStateDto> newStates = new HashSet<>();
260
        Set<FeatureState> deleteState = new HashSet<>();
261
        boolean stillExist = false;
262
        Set<FeatureState> setToUpdate = null;
263
        Set<FeatureStateDto> setForUpdate = null;
264
        if (inApplicable){
265
            setToUpdate = node.getInapplicableIf();
266
            setForUpdate = dto.getInapplicableIf();
267
        }else{
268
            setToUpdate = node.getOnlyApplicableIf();
269
            setForUpdate = dto.getOnlyApplicableIf();
270
        }
271
        for (FeatureState featureState: setToUpdate){
272
            stillExist = false;
273
            for (FeatureStateDto featureStateDto: setForUpdate){
274
                if (featureStateDto.getUuid() != null && featureStateDto.getUuid().equals(featureState.getUuid())){
275
                    stillExist = true;
276
                    if (featureStateDto.getFeature().getUuid().equals(featureState.getFeature().getUuid()) && featureStateDto.getState().getUuid().equals(featureState.getState().getUuid())){
277
                        //do nothing
278
                    }else{
279
                        changeState.put(featureState, featureStateDto);
280
                    }
281
                    break;
282
                }
283
            }
284
            if (!stillExist){
285
                deleteState.add(featureState);
286
            }
287
        }
288

    
289
        for (FeatureStateDto featureStateDto: setForUpdate){
290
            stillExist = false;
291
            if (featureStateDto.getUuid() == null){
292
                newStates.add(featureStateDto);
293
            }else{
294
                for (FeatureState featureState: setToUpdate){
295
                    if (featureStateDto.getUuid() != null && featureStateDto.getUuid().equals(featureState.getUuid())){
296
                        stillExist = true;
297
                        break;
298
                    }
299
                }
300
                if (!stillExist){
301
                    newStates.add(featureStateDto);
302
                }
303
            }
304
        }
305
        if (inApplicable){
306
            node.getInapplicableIf().removeAll(deleteState);
307
        }else{
308
            node.getOnlyApplicableIf().removeAll(deleteState);
309
        }
310
        for (Entry<FeatureState, FeatureStateDto> change: changeState.entrySet()){
311
            if (!change.getKey().getFeature().getUuid().equals(change.getValue().getFeature().getUuid())){
312
                DefinedTermBase<?> term = termService.load(change.getValue().getFeature().getUuid());
313
                if (term instanceof Feature){
314
                    Feature feature = HibernateProxyHelper.deproxy(term, Feature.class);
315
                    change.getKey().setFeature(feature);
316
                }
317

    
318
            }
319
            if (!change.getKey().getState().getUuid().equals(change.getValue().getState().getUuid())){
320
                DefinedTermBase<?> term = termService.load(change.getValue().getState().getUuid());
321
                if (term instanceof State){
322
                    State state = HibernateProxyHelper.deproxy(term, State.class);
323
                    change.getKey().setState(state);
324
                }
325

    
326
            }
327
            if (inApplicable){
328
                node.getInapplicableIf().add(change.getKey());
329
            }else{
330
                node.getOnlyApplicableIf().add(change.getKey());
331
            }
332
        }
333
        for (FeatureStateDto stateDto: newStates){
334
            Feature feature = null;
335
            State state = null;
336
            DefinedTermBase<?> term = termService.find(stateDto.getFeature().getUuid());
337
            term = HibernateProxyHelper.deproxy(term);
338
            if (term instanceof Character){
339
                feature = HibernateProxyHelper.deproxy(term, Character.class);
340
            }
341
            DefinedTermBase<?> termState = termService.load(stateDto.getState().getUuid());
342
            if (termState instanceof State){
343
                state = HibernateProxyHelper.deproxy(termState, State.class);
344
            }
345
            FeatureState newState = FeatureState.NewInstance(feature, state);
346
            if (inApplicable){
347
                node.getInapplicableIf().add(newState);
348
            }else{
349
                node.getOnlyApplicableIf().add(newState);
350
            }
351
        }
352
    }
353

    
354
    @Override
355
    public UpdateResult saveCharacterNodeDtoList(List<CharacterNodeDto> dtos){
356
        MergeResult<TermNode> mergeResult;
357
        UpdateResult result = new UpdateResult();
358
        List<UUID> nodeUuids = new ArrayList<>();
359

    
360
        dtos.stream().forEach(dto -> nodeUuids.add(dto.getUuid()));
361
        List<TermNode> nodes = dao.list(nodeUuids, null, 0, null, null);
362
        //check all attributes for changes and adapt
363
        for (TermNode<Character> node: nodes){
364
            for (CharacterNodeDto dto: dtos){
365
    //            TermNodeDto dto = dtoIterator.next();
366
                if (dto.getUuid().equals(node.getUuid())){
367
                    updateFeatureStates(node, dto, true);
368
                    updateFeatureStates(node, dto, false);
369
//                    if (!dto.getInapplicableIf().equals(node.getInapplicableIf())){
370
//                        node.getInapplicableIf().clear();
371
//                        node.getInapplicableIf().addAll(dto.getInapplicableIf());
372
//                    }
373
//                    if (!dto.getOnlyApplicableIf().equals(node.getOnlyApplicableIf())){
374
//                        node.getOnlyApplicableIf().clear();
375
//                        node.getOnlyApplicableIf().addAll(dto.getOnlyApplicableIf());
376
//                    }
377

    
378
                    Character character = null;
379
                    CharacterDto characterDto = (CharacterDto) dto.getTerm();
380
                    character = HibernateProxyHelper.deproxy(node.getTerm(), Character.class);
381
                    if (characterDto.getRatioTo() != null){
382
                        TermNode ratioToStructure = this.load(characterDto.getRatioTo().getUuid());
383
                        character.setRatioToStructure(ratioToStructure);
384
                    }else{
385
                        character.setRatioToStructure(null);
386
                    }
387

    
388
                    //supportsXXX
389
                    //TODO add all other supportsXXX (6 are missing)
390
                    character.setSupportsCategoricalData(characterDto.isSupportsCategoricalData());
391
                    character.setSupportsQuantitativeData(characterDto.isSupportsQuantitativeData());
392

    
393
                    //availableForXXX
394
                    character.setAvailableForTaxon(characterDto.isAvailableForTaxon());
395
                    character.setAvailableForOccurrence(characterDto.isAvailableForOccurrence());
396
                    character.setAvailableForTaxonName(characterDto.isAvailableForTaxonName());
397

    
398
//                  representations
399
                    for (Representation rep: dto.getTerm().getRepresentations()){
400
                        Representation oldRep = character.getRepresentation(rep.getLanguage());
401
                        if (oldRep == null){
402
                            oldRep = Representation.NewInstance(null, null, null, rep.getLanguage());
403
                            character.addRepresentation(oldRep);
404
                        }
405
                        oldRep.setLabel(rep.getLabel());
406
                        oldRep.setAbbreviatedLabel(rep.getAbbreviatedLabel());
407
                        oldRep.setText(rep.getText());
408
                        oldRep.setPlural(rep.getPlural());
409
                    }
410
                    Set<Representation> deleteRepresentations = new HashSet<>();
411
                    if (character.getRepresentations().size() > dto.getTerm().getRepresentations().size()){
412
                        for (Representation rep: character.getRepresentations()){
413
                            if(dto.getTerm().getRepresentation(rep.getLanguage()) == null){
414
                                deleteRepresentations.add(rep);
415
                            }
416
                        }
417
                    }
418

    
419
                    if (!deleteRepresentations.isEmpty()){
420
                        for (Representation rep: deleteRepresentations){
421
                            character.removeRepresentation(rep);
422
                        }
423
                    }
424

    
425
//                  structural modifier
426
                    if (characterDto.getStructureModifier() != null){
427
                        DefinedTerm structureModifier = (DefinedTerm) termService.load(characterDto.getStructureModifier().getUuid());
428
                        character.setStructureModifier(structureModifier);
429
                    }else{
430
                        character.setStructureModifier(null);
431
                    }
432
//                  recommended measurement units
433
                    character.getRecommendedMeasurementUnits().clear();
434
                    List<UUID> uuids = new ArrayList<>();
435
                    for (TermDto termDto: characterDto.getRecommendedMeasurementUnits()){
436
                        uuids.add(termDto.getUuid());
437
                    }
438
                    List<DefinedTermBase> terms;
439
                    if (!uuids.isEmpty()){
440
                        terms = termService.load(uuids, null);
441
                        Set<MeasurementUnit> measurementUnits = new HashSet<>();
442
                        for (DefinedTermBase<?> term: terms){
443
                            if (term instanceof MeasurementUnit){
444
                                measurementUnits.add((MeasurementUnit)term);
445
                            }
446
                        }
447
                        character.getRecommendedMeasurementUnits().addAll(measurementUnits);
448
                    }
449
//                  statistical measures
450
                    character.getRecommendedStatisticalMeasures().clear();
451
                    uuids = new ArrayList<>();
452
                    for (TermDto termDto: characterDto.getRecommendedStatisticalMeasures()){
453
                        uuids.add(termDto.getUuid());
454
                    }
455
                    if (!uuids.isEmpty()){
456
                        terms = termService.load(uuids, null);
457
                        Set<StatisticalMeasure> statisticalMeasures = new HashSet<>();
458
                        for (DefinedTermBase<?> term: terms){
459
                            if (term instanceof StatisticalMeasure){
460
                                statisticalMeasures.add((StatisticalMeasure)term);
461
                            }
462
                        }
463
                        character.getRecommendedStatisticalMeasures().addAll(statisticalMeasures);
464
                    }
465

    
466
//                  recommended mod. vocabularies
467
                    character.getRecommendedModifierEnumeration().clear();
468
                    uuids = new ArrayList<>();
469
                    for (TermVocabularyDto termDto: characterDto.getRecommendedModifierEnumeration()){
470
                        uuids.add(termDto.getUuid());
471
                    }
472
                    List<TermVocabulary> termVocs;
473
                    if (!uuids.isEmpty()){
474
                        termVocs = vocabularyService.load(uuids, null);
475
                        for (TermVocabulary<DefinedTerm> voc: termVocs){
476
                            character.addRecommendedModifierEnumeration(voc);
477
                        }
478
                    }
479

    
480
//                  supported state vocabularies
481
                    character.getSupportedCategoricalEnumerations().clear();
482
                    uuids = new ArrayList<>();
483
                    for (TermVocabularyDto termDto: characterDto.getSupportedCategoricalEnumerations()){
484
                        uuids.add(termDto.getUuid());
485
                    }
486
                    if (!uuids.isEmpty()){
487
                        termVocs = vocabularyService.load(uuids, null);
488

    
489
                        for (TermVocabulary voc: termVocs){
490
                            character.addSupportedCategoricalEnumeration(voc);
491
                        }
492
                    }
493
                    node.setTerm(character);
494
                    mergeResult = dao.merge(node, true);
495
                    result.addUpdatedObject(mergeResult.getMergedEntity());
496
                }
497
            }
498
        }
499
        return result;
500
    }
501

    
502
    @Override
503
    public UpdateResult saveNewCharacterNodeDtoMap(Map<Character, CharacterNodeDto> dtos, UUID vocabularyUuid){
504
        UpdateResult result = new UpdateResult();
505
        UpdateResult resultLocal = new UpdateResult();
506
        for (Entry<Character, CharacterNodeDto> dtoEntry: dtos.entrySet()){
507
            resultLocal = createChildNode(dtoEntry.getValue().getParentUuid(), dtoEntry.getKey(), vocabularyUuid);
508
            dtoEntry.getValue().setUuid(resultLocal.getCdmEntity().getUuid());
509
            result.includeResult(resultLocal);
510
        }
511
        List<CharacterNodeDto> dtoList = new ArrayList<>(dtos.values());
512
        result.includeResult(saveCharacterNodeDtoList(dtoList));
513
        return result;
514
    }
515
}
(88-88/95)