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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
615

    
616

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