Project

General

Profile

Download (25.3 KB) Statistics
| Branch: | Revision:
1
/**
2
* Copyright (C) 2020 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.app.pesi.merging;
10

    
11
import java.util.HashSet;
12
import java.util.List;
13
import java.util.Optional;
14
import java.util.Set;
15
import java.util.UUID;
16
import java.util.stream.Collectors;
17

    
18
import org.apache.log4j.Logger;
19
import org.springframework.transaction.TransactionStatus;
20

    
21
import eu.etaxonomy.cdm.api.service.DeleteResult;
22
import eu.etaxonomy.cdm.api.service.config.SynonymDeletionConfigurator;
23
import eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator;
24
import eu.etaxonomy.cdm.app.common.CdmDestinations;
25
import eu.etaxonomy.cdm.common.CdmRegEx;
26
import eu.etaxonomy.cdm.common.CdmUtils;
27
import eu.etaxonomy.cdm.database.DbSchemaValidation;
28
import eu.etaxonomy.cdm.database.ICdmDataSource;
29
import eu.etaxonomy.cdm.io.api.application.CdmIoApplicationController;
30
import eu.etaxonomy.cdm.io.common.mapping.out.DbLastActionMapper;
31
import eu.etaxonomy.cdm.io.pesi.erms.ErmsTransformer;
32
import eu.etaxonomy.cdm.io.pesi.out.PesiTransformer;
33
import eu.etaxonomy.cdm.model.common.Annotation;
34
import eu.etaxonomy.cdm.model.common.CdmBase;
35
import eu.etaxonomy.cdm.model.common.Credit;
36
import eu.etaxonomy.cdm.model.common.Extension;
37
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
38
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
39
import eu.etaxonomy.cdm.model.common.Marker;
40
import eu.etaxonomy.cdm.model.description.TaxonDescription;
41
import eu.etaxonomy.cdm.model.name.TaxonName;
42
import eu.etaxonomy.cdm.model.taxon.Synonym;
43
import eu.etaxonomy.cdm.model.taxon.Taxon;
44
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
45
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
46
import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
47
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
48

    
49
/**
50
 * @author a.mueller
51
 * @since 20.01.2020
52
 */
53
public class PesiCommandLineMerge extends PesiMergeBase {
54

    
55
    private static final Logger logger = Logger.getLogger(PesiCommandLineMerge.class);
56

    
57
    static final ICdmDataSource pesiSource = CdmDestinations.cdm_pesi2019_final();
58

    
59
    private CdmIoApplicationController app;
60

    
61
    private void invoke(ICdmDataSource source){
62
        app = CdmIoApplicationController.NewInstance(source, DbSchemaValidation.VALIDATE, false);
63
        doInvoke();
64
    }
65

    
66
    private void doInvoke(){
67
        List<List<String>> fileData = null;
68
        String next = nextMerge(fileData);
69
        while(next.equalsIgnoreCase("m")|| next.equals("f")){
70
            TransactionStatus tx = app.startTransaction();
71
            TaxonInformation taxonInformation;
72
            if (next.equalsIgnoreCase("f")){
73
                if (fileData == null){
74
                    fileData = getFileData();
75
                }else if (fileData.isEmpty()){
76
                    fileData = null;
77
                    next = nextMerge(fileData);
78
                    continue;
79
                }
80
                taxonInformation = readLineFromFile(fileData);
81
                if (taxonInformation == null){
82
                    app.rollbackTransaction(tx);
83
                    nextMerge(fileData);
84
                    continue;
85
                }
86
            }else{
87
                TaxonBase<?>[] taxa = null;
88
                while (taxa == null) {
89
                    taxa = readTaxa();
90
                }
91
                taxonInformation = new TaxonInformation();
92
                taxonInformation.taxon2 = taxa[0];
93
                taxonInformation.taxon1 = taxa[1];
94
            }
95

    
96
            try {
97
                mergeTaxa(tx, taxonInformation);
98
            } catch (Exception e) {
99
                e.printStackTrace();
100
                app.rollbackTransaction(tx);
101
                continue;
102
            }
103
            next = nextMerge(fileData);
104
        }
105
    }
106

    
107
    private boolean mergeTaxa(TransactionStatus tx, TaxonInformation taxonInformation) {
108
        boolean commit = compareTaxa(taxonInformation);
109
        if (commit){
110
            moveTaxonInformation(taxonInformation);
111
        }
112
        if (commit){
113
            app.commitTransaction(tx);
114
            if (taxonInformation.taxonToUse == 1 && taxonInformation.nameToUse == 1){
115
                removeTaxon(taxonInformation.taxon2);
116
            }else if (booleanAnswer("Information moved. Delete old taxon")){
117
                removeTaxon(taxonInformation.taxonToUse == 2 ? taxonInformation.taxon1 : taxonInformation.taxon2);
118
            }
119
        }else{
120
            app.rollbackTransaction(tx);
121
        }
122
        return commit;
123
    }
124

    
125
    private class TaxonInformation{
126
        TaxonBase<?> taxon1;
127
        TaxonBase<?> taxon2;
128
        int taxonToUse = 1;   //
129
        int nameToUse = 1;
130
    }
131

    
132
    /**
133
     * Reads a line from the file, returns it's taxon information and removes
134
     * the line from the input list.
135
     */
136
    private TaxonInformation readLineFromFile(List<List<String>> fileData) {
137
        List<String> line = fileData.get(0);
138
        TaxonInformation taxonInformation = new TaxonInformation();
139
        taxonInformation.taxon1 = taxonByString(line.get(0));
140
        taxonInformation.taxon2 = taxonByString(line.get(1));
141

    
142
        if (taxonInformation.taxon1 == null || taxonInformation.taxon2 == null){
143
            boolean cancel = booleanAnswer("Taxon1 or Taxon2 could not be read from DB! Cancel record");
144
            if (cancel){
145
                fileData.remove(0);
146
            }
147
            return null;
148
        }
149

    
150
        try {
151
            Integer taxonToUse = Integer.valueOf(line.get(2));
152
            if (1 != taxonInformation.taxonToUse){
153
                booleanAnswer("Stay taxon is not '1'");
154
            }
155
            Integer nameToUse = "".equals(line.get(3))? taxonToUse: Integer.valueOf(line.get(3));
156
            if (taxonToUse != 1 && taxonToUse != 2 && taxonToUse != 0){
157
                boolean cancel = booleanAnswer("taxonToUse is not 0, 1 or 2. Cancel record");
158
                if (cancel){
159
                    fileData.remove(0);
160
                }
161
                return null;
162
            }else if (taxonToUse == 0){
163
                logger.warn("Record marked as homonym. No merge: " + taxonInformation.taxon1.getName().getNameCache());
164
                fileData.remove(0);
165
                return null;
166
            }else{
167
                taxonInformation.taxonToUse = taxonToUse;
168
            }
169
            if (nameToUse == null){
170
                nameToUse = taxonToUse;
171
            }
172
            if (nameToUse != 1 && nameToUse != 2){
173
                logger.warn("Name to use has incorrect value: " +  nameToUse);
174
            }else{
175
                taxonInformation.nameToUse = nameToUse;
176
            }
177
        } catch (NumberFormatException e) {
178
            e.printStackTrace();
179
            taxonInformation = null;
180
        }
181
        fileData.remove(0);
182
        return taxonInformation;
183
    }
184

    
185
    private List<List<String>> getFileData() {
186
        List<List<String>> result = null;
187
        while(result == null){
188
            String input = CdmUtils.readInputLine("Path and filename: ");
189
            result = readCsvFile(input);
190
        }
191
        return result;
192
    }
193

    
194
    private String nextMerge(List<List<String>> fileData) {
195
        if (fileData != null){
196
            return "f";
197
        }
198
        do{
199
            String input = CdmUtils.readInputLine("Next input: manual[m], file[f], quit[q]: ");
200
            if (input.matches("[mMfFqQ]")){
201
                return input;
202
            }
203
        }while (true);
204
    }
205

    
206
    private boolean compareTaxa(TaxonInformation taxonInformation) {
207
        TaxonBase<?> removeTaxon = taxonInformation.taxon2;
208
        TaxonBase<?> stayTaxon = taxonInformation.taxon1;
209
        String nc1 = removeTaxon.getName().getNameCache();
210
        String nc2 = stayTaxon.getName().getNameCache();
211

    
212
        String ft1 = removeTaxon.getName().getFullTitleCache();
213
        String ft2 = stayTaxon.getName().getFullTitleCache();
214
        System.out.println("Remove " + getStatusStr(removeTaxon) + ft1);
215
        System.out.println("Stay   " + getStatusStr(stayTaxon) + ft2);
216
        boolean isStandard = taxonInformation.taxonToUse == 1 && taxonInformation.nameToUse == 1;
217
        if (!nc1.equals(nc2)){
218
            return booleanAnswer("Name Cache differs!!! Do you really want to merge???");
219
        }else if (!ft1.equals(ft2)){
220
            return isStandard || booleanAnswer("Full title cache differs! Do you really want to merge anyway");
221
        }else{
222
            return isStandard || booleanAnswer("Same title. Merge");
223
        }
224
    }
225

    
226
    private String getStatusStr(TaxonBase<?> taxon) {
227
        //TODO MAN and Taxon Synonyms
228
        if (taxon.isInstanceOf(Synonym.class)){
229
            return "Syn: ";
230
        }else{
231
            return "Acc: ";
232
        }
233
    }
234

    
235
    private void removeTaxon(TaxonBase<?> taxonBase) {
236
        DeleteResult result;
237
        if (taxonBase.isInstanceOf(Taxon.class)){
238
            Taxon taxonToRemove = CdmBase.deproxy(taxonBase, Taxon.class);
239
            TaxonDeletionConfigurator config = new TaxonDeletionConfigurator();
240
            if (isTaxonSynonym(taxonToRemove)){
241
                result = app.getTaxonService().deleteTaxon(taxonToRemove.getUuid(), config, null);
242
            }else{
243
                TaxonNode nodeToRemove = taxonToRemove.getTaxonNodes().iterator().next();
244
                result = app.getTaxonNodeService().deleteTaxonNode(nodeToRemove.getUuid(), config);
245
            }
246
        }else{
247
            Synonym syn = CdmBase.deproxy(taxonBase, Synonym.class);
248
            SynonymDeletionConfigurator config = new SynonymDeletionConfigurator();
249
            result = app.getTaxonService().deleteSynonym(syn.getUuid(), config);
250
        }
251
        if (!result.isOk()){
252
            System.out.println("Remove taxon was not successful.");
253
        }
254
    }
255

    
256
    private boolean booleanAnswer(String message) {
257
        String answer = "";
258
        while (!(answer.equalsIgnoreCase("y") || answer.equalsIgnoreCase("n"))){
259
            answer = CdmUtils.readInputLine(message + " (y/n)? ");
260
        }
261
        return answer.equalsIgnoreCase("y");
262
    }
263

    
264
    private boolean moveTaxonInformation(TaxonInformation taxonInformation) {
265
        try {
266

    
267
            TaxonBase<?> removeTaxon = CdmBase.deproxy(taxonInformation.taxonToUse == 2 ? taxonInformation.taxon1: taxonInformation.taxon2);
268
            TaxonBase<?> stayTaxon = CdmBase.deproxy(taxonInformation.taxonToUse == 2 ? taxonInformation.taxon2 : taxonInformation.taxon1);
269

    
270
            //mergeTaxa;
271
            mergeSources(removeTaxon, stayTaxon);
272
            mergeAnnotations(removeTaxon, stayTaxon);
273
            mergeMarkers(removeTaxon, stayTaxon);
274
            //TODO for
275
            mergeExtensions(removeTaxon, stayTaxon);
276
            mergeCredits(removeTaxon, stayTaxon);
277
            if (removeTaxon.isInstanceOf(Taxon.class)){
278
                Taxon removeAccTaxon = CdmBase.deproxy(removeTaxon, Taxon.class);
279

    
280
                Taxon stayAccTaxon = accTaxon(stayTaxon);
281
                mergeDescriptions(removeAccTaxon, accTaxon(stayTaxon));
282
                boolean isTaxonSynonym = isTaxonSynonym(removeAccTaxon);
283
                mergeSynonyms(removeAccTaxon, stayAccTaxon, isTaxonSynonym);
284
                mergeChildren(removeAccTaxon, stayAccTaxon, isTaxonSynonym);
285
                //TODO taxon synonym relations
286
                mergeTaxonRelations(removeAccTaxon, stayAccTaxon, isTaxonSynonym);
287
            }
288

    
289
            //mergeNames;
290
            TaxonName removeName;
291
            TaxonName stayName;
292
            if (taxonInformation.nameToUse == taxonInformation.taxonToUse){
293
                removeName = CdmBase.deproxy(removeTaxon.getName());
294
                stayName = CdmBase.deproxy(stayTaxon.getName());
295
            }else{
296
                removeName = CdmBase.deproxy(stayTaxon.getName());
297
                stayName = CdmBase.deproxy(removeTaxon.getName());
298
                stayTaxon.setName(stayName);
299
            }
300
            //TODO unclear if name information should be merged at all
301
            mergeSources(removeName, stayName);
302
            mergeAnnotations(removeName, stayName);
303
            mergeMarkers(removeName, stayName);
304
            mergeExtensions(removeName, stayName);
305
            mergeCredits(removeName, stayName);
306
            mergeNameRelationships(removeName, stayName);
307
            mergeHybridRelationships(removeName, stayName);
308
            mergeNameDescriptions(removeName, stayName);
309

    
310
            if(taxonInformation.taxonToUse == 1 && taxonInformation.nameToUse == 1){
311
                return true;
312
            }else{
313
                return booleanAnswer("Commit moved information");
314
            }
315
        } catch (CloneNotSupportedException e) {
316
            e.printStackTrace();
317
            return false;
318
        }
319
    }
320

    
321
    private boolean isTaxonSynonym(Taxon removeAccTaxon) {
322
        for (TaxonRelationship rel:  removeAccTaxon.getRelationsFromThisTaxon()){
323
            boolean isPseudo = TaxonRelationshipType.pseudoTaxonUuids().contains(rel.getType().getUuid());
324
            if (isPseudo){
325
                return true;
326
            }
327
        }
328
        return false;
329
    }
330

    
331
    private Taxon accTaxon(TaxonBase<?> stayTaxon) {
332
        if (stayTaxon.isInstanceOf(Synonym.class)){
333
            return CdmBase.deproxy(stayTaxon, Synonym.class).getAcceptedTaxon();
334
        }else{
335
            return CdmBase.deproxy(stayTaxon, Taxon.class);
336
        }
337
    }
338

    
339
    private boolean mergeTaxonRelations(Taxon removeTaxon, Taxon stayTaxon, boolean isTaxonSynonym) {
340
        if (isTaxonSynonym){
341
            if (!removeTaxon.getRelationsToThisTaxon().isEmpty()){
342
                logger.warn("taxon synonym has taxon relations to itself. This should not happen. Handle manually.");
343
                return false;
344
            }else{
345
                return true;
346
            }
347
        }
348
        for (TaxonRelationship rel : removeTaxon.getRelationsToThisTaxon()){
349
            System.out.println("Move taxon relationship: " + rel.getType().getTitleCache() + ": " + rel.getFromTaxon().getTitleCache());
350

    
351
            rel.setToTaxon(stayTaxon);
352
//            if (!synonymExists()){
353
//                //TODO homotypical group
354
//                stayTaxon.addSynonym(synonym, synonym.getType());
355
//            }else{
356
//                //TODO merge synonym names
357
//            }
358
        }
359
        if(!removeTaxon.getRelationsFromThisTaxon().isEmpty()){
360
            logger.warn("Taxon-from-relations not yet implemented");
361
        }
362
        return true;
363
    }
364

    
365
    private void mergeNameDescriptions(TaxonName removeName, @SuppressWarnings("unused") TaxonName stayName) {
366
        if(!removeName.getDescriptions().isEmpty()){
367
            logger.warn("Name description exist but merge not yet implemented");
368
        }
369
    }
370

    
371
    private void mergeHybridRelationships(TaxonName removeName, @SuppressWarnings("unused") TaxonName stayName) {
372
        if(!removeName.getHybridChildRelations().isEmpty()){
373
            logger.warn("Hybrid child relation exist but merge not yet implemented");
374
        }
375
        if(!removeName.getHybridParentRelations().isEmpty()){
376
            logger.warn("Hybrid parent relation exist but merge not yet implemented");
377
        }
378
    }
379

    
380
    private void mergeNameRelationships(TaxonName removeName, @SuppressWarnings("unused") TaxonName stayName) {
381
        if(!removeName.getNameRelations().isEmpty()){
382
            logger.warn("Name relations exist but merge not yet implemented");
383
        }
384
    }
385

    
386
    private boolean mergeChildren(Taxon removeTaxon, Taxon stayTaxon, boolean isTaxonSynonym) {
387
        if (isTaxonSynonym){
388
            if (!removeTaxon.getTaxonNodes().isEmpty()){
389
                logger.warn("taxon synonym has taxon node itself. This should not happen. Handle manually.");
390
                return false;
391
            }else{
392
                return true;
393
            }
394
        }
395
        TaxonNode removeNode = removeTaxon.getTaxonNodes().iterator().next();
396
        TaxonNode stayNode = stayTaxon.getTaxonNodes().iterator().next();
397
        Set<UUID> removeNodeChildrenUuids = removeNode.getChildNodes()
398
                .stream().map(tn->tn.getUuid()).collect(Collectors.toSet());
399

    
400
        if(!removeNodeChildrenUuids.isEmpty()){
401
            app.getTaxonNodeService().moveTaxonNodes(removeNodeChildrenUuids,
402
                    stayNode.getUuid(), 0, null);
403
            System.out.println("Child nodes moved: " + removeNodeChildrenUuids.size());
404
        }
405
        return true;
406
    }
407

    
408
    private boolean mergeSynonyms(Taxon removeTaxon, Taxon stayTaxon, boolean isTaxonSynonym) {
409
        if (isTaxonSynonym){
410
            if (!removeTaxon.getSynonyms().isEmpty()){
411
                logger.warn("taxon synonym has synonyms itself. This should not happen. Handle manually.");
412
                return false;
413
            }else{
414
                return true;
415
            }
416
        }
417
        Set<Synonym> synonymsToAdd = new HashSet<>();
418
        for (Synonym synonym : removeTaxon.getSynonyms()){
419
            if (!synonymExists()){
420
                //TODO homotypical group
421
                synonymsToAdd.add(synonym);
422
            }else{
423
                //TODO merge synonym names
424
            }
425
        }
426
        for (Synonym synonym: synonymsToAdd){
427
            stayTaxon.addSynonym(synonym, synonym.getType());
428
        }
429
        return true;
430
    }
431

    
432
    private boolean synonymExists() {
433
        logger.warn("Synonym dulicate check - not yet implemented");
434
        return false;
435
    }
436

    
437
    private void mergeDescriptions(Taxon remove, Taxon stay) {
438
        //TODO handle duplicates for taxon descriptions
439
        for (TaxonDescription description: remove.getDescriptions()){
440
            System.out.println("Move taxon description: " + description.getTitleCache());
441
            stay.addDescription((TaxonDescription)description.clone());
442
        }
443
    }
444

    
445
    private void mergeCredits(IdentifiableEntity<?> removeEntity,
446
            IdentifiableEntity<?> stayEntity) throws CloneNotSupportedException {
447
        String className = removeEntity.getClass().getSimpleName();
448
        for (Credit credit: removeEntity.getCredits()){
449
            System.out.println("Move "+className+" credit: " + credit.toString());
450
            stayEntity.addCredit((Credit)credit.clone());
451
        }
452
    }
453

    
454
    private void mergeExtensions(IdentifiableEntity<?> removeEntity,
455
            IdentifiableEntity<?> stayEntity) throws CloneNotSupportedException {
456

    
457
        String className = removeEntity.getClass().getSimpleName();
458
        for (Extension extension: removeEntity.getExtensions()){
459
            if (!filterExtension(extension, removeEntity, stayEntity)){
460
                System.out.println("Move "+className+" extension: " + extension.getType().getTitleCache() + ": " + extension.getValue());
461

    
462
                IdentifiableEntity<?> thisStayEntity = selectStay(removeEntity, stayEntity, "Extension");
463
                if (thisStayEntity != null){
464
                    thisStayEntity.addExtension((Extension)extension.clone());
465
                }
466
            }
467
        }
468
    }
469

    
470
    private IdentifiableEntity<?> selectStay(IdentifiableEntity<?> removeEntity, IdentifiableEntity<?> stayEntity, String type) {
471
        if(removeEntity.isInstanceOf(Taxon.class) && stayEntity.isInstanceOf(Synonym.class)){
472
            String answer = "";
473
            while(!(answer.matches("[sSaAcC]"))){
474
                answer = CdmUtils.readInputLine(type + ": Stay is Synonym. Move information to [s]ynonym, to [a]ccepted or [c]ancel extension merge?: ");
475
            }
476
            if (answer.equalsIgnoreCase("c")){
477
                return null;
478
            }else if (answer.equalsIgnoreCase("a")){
479
               return accTaxon(CdmBase.deproxy(stayEntity, Synonym.class));
480
            }else{
481
                return stayEntity;
482
            }
483
        }
484
        return stayEntity;
485
    }
486

    
487
    private boolean filterExtension(Extension extension,
488
            @SuppressWarnings("unused") IdentifiableEntity<?> removeEntity,
489
            @SuppressWarnings("unused") IdentifiableEntity<?> stayEntity) {
490
        if (extension.getType().getUuid().equals(ErmsTransformer.uuidExtDisplayName)){
491
            //for merged taxa display name information is not relevant because name is formatted according to "stay" taxon.
492
            return true;
493
        }
494
        return false;
495
    }
496

    
497
    private void mergeMarkers(IdentifiableEntity<?> removeEntity,
498
            IdentifiableEntity<?> stayEntity) throws CloneNotSupportedException {
499
        String className = removeEntity.getClass().getSimpleName();
500
        for (Marker marker: removeEntity.getMarkers()){
501
            if (!filterMarker(marker, removeEntity, stayEntity)){
502
                System.out.println("Move "+className+" marker: " + marker.getMarkerType().getTitleCache() + ": " + marker.getValue());
503
                IdentifiableEntity<?> thisStayEntity = selectStay(removeEntity, stayEntity, "Marker");
504
                if (thisStayEntity != null){
505
                    thisStayEntity.addMarker((Marker)marker.clone());
506
                }
507
            }
508
        }
509
    }
510

    
511
    private void mergeAnnotations(IdentifiableEntity<?> removeEntity,
512
            IdentifiableEntity<?> stayEntity) throws CloneNotSupportedException {
513
        String className = removeEntity.getClass().getSimpleName();
514
        for (Annotation annotation: removeEntity.getAnnotations()){
515
            if (!filterAnnotation(annotation, removeEntity, stayEntity)){
516
                String type = annotation.getAnnotationType() == null? "no type" : annotation.getAnnotationType().getTitleCache();
517
                System.out.println("Move "+className+" note: " + type + ": " + annotation.getText());
518
                handleRemoveAnnotation(annotation, removeEntity, stayEntity);
519
                stayEntity.addAnnotation((Annotation)annotation.clone());
520
            }
521
        }
522
    }
523

    
524
    private void mergeSources(IdentifiableEntity<?> removeEntity,
525
            IdentifiableEntity<?> stayEntity) throws CloneNotSupportedException {
526
        String className = removeEntity.getClass().getSimpleName();
527
        for (IdentifiableSource source: removeEntity.getSources()){
528
            System.out.println("Move "+className+" source: " + source.getType().getMessage() + ": " + source.getCitation().getTitleCache() + "; " + source.getIdInSource() + "/" + source.getIdNamespace());
529
            stayEntity.addSource((IdentifiableSource)source.clone());
530
        }
531
    }
532

    
533
    private boolean filterMarker(Marker marker, @SuppressWarnings("unused") IdentifiableEntity<?> removeEntity,
534
            IdentifiableEntity<?> stayEntity) {
535
        if (isNoLastActionMarker(marker)){
536
            for (Annotation annotation : stayEntity.getAnnotations()){
537
                if (isLastActionDateAnnotation(annotation)){
538
                        return true;
539
                }
540
            }
541
        }
542
        return false;
543
    }
544

    
545
    private boolean isLastActionDateAnnotation(Annotation annotation) {
546
        return annotation.getAnnotationType()!= null
547
                && annotation.getAnnotationType().getUuid().equals(DbLastActionMapper.uuidAnnotationTypeLastActionDate)
548
                && !isBlank(annotation.getText());
549
    }
550

    
551
    private void handleRemoveAnnotation(Annotation annotation,
552
            @SuppressWarnings("unused") IdentifiableEntity<?> removeEntity,
553
            IdentifiableEntity<?> stayEntity) {
554
        if (isLastActionDateAnnotation(annotation)){
555
            Optional<Marker> noLastActionMarker = stayEntity.getMarkers().stream().filter(m->isNoLastActionMarker(m)).findFirst();
556
            if (noLastActionMarker.isPresent()){
557
                stayEntity.removeMarker(noLastActionMarker.get());
558
                System.out.println("  NoLastActionDate annotation removed from 'stay' " + stayEntity.getClass().getSimpleName());
559
            }
560
        }
561
    }
562

    
563
    private boolean isNoLastActionMarker(Marker marker) {
564
        return marker.getMarkerType().getUuid().equals(PesiTransformer.uuidMarkerTypeHasNoLastAction)
565
                && marker.getValue() == true;
566
    }
567

    
568
    @SuppressWarnings("unused")
569
    private boolean filterAnnotation(Annotation annotation, IdentifiableEntity<?> removeEntity, IdentifiableEntity<?> stayEntity) {
570
        return false;
571
    }
572

    
573
    private TaxonBase<?>[] readTaxa() {
574
        TaxonBase<?> taxon1 = readTaxon("Taxon to be removed");
575
        TaxonBase<?> taxon2 = readTaxon("Taxon to stay");
576
        if (taxon1 == null || taxon2 == null){
577
            return null;
578
        }else{
579
            return new TaxonBase<?>[]{taxon1, taxon2};
580
        }
581
    }
582

    
583
    private TaxonBase<?> readTaxon(String message) {
584
        TaxonBase<?> taxon = null;
585
        boolean quit = false;
586
        while (taxon == null && quit == false){
587
            String strTaxon = CdmUtils.readInputLine(message + ": ");
588
            if (strTaxon.equalsIgnoreCase("q")){
589
                quit = true;
590
            }else{
591
                taxon = taxonByString(strTaxon);
592
            }
593
        }
594
        if (taxon == null){
595
            return null;
596
        }else if (taxon.isInstanceOf(Synonym.class)){
597
            return CdmBase.deproxy(taxon, Synonym.class);
598
        }else{
599
            return CdmBase.deproxy(taxon, Taxon.class);
600
        }
601
    }
602

    
603
    /**
604
     * Reads a taxon from database using it's id or uuid as String
605
     */
606
    private TaxonBase<?> taxonByString(String strTaxon) {
607
        TaxonBase<?> taxon = null;
608
        if (strTaxon.matches("\\d{1,10}")){
609
            taxon = app.getTaxonService().find(Integer.valueOf(strTaxon));
610
        }else if (strTaxon.matches(CdmRegEx.UUID_RE)){
611
            taxon = app.getTaxonService().find(UUID.fromString(strTaxon));
612
        }
613
        return taxon;
614
    }
615

    
616

    
617

    
618
    public static void main(String[] args) {
619
        PesiCommandLineMerge merger = new PesiCommandLineMerge();
620
        merger.invoke(pesiSource);
621
        System.exit(0);
622
    }
623
}
(2-2/6)