Project

General

Profile

Download (21.6 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2017 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.vaadin.util.converter;
10

    
11
import java.util.ArrayList;
12
import java.util.Collection;
13
import java.util.Collections;
14
import java.util.Comparator;
15
import java.util.HashMap;
16
import java.util.LinkedHashMap;
17
import java.util.LinkedList;
18
import java.util.List;
19
import java.util.Map;
20
import java.util.Optional;
21
import java.util.Set;
22

    
23
import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeCacheStrategy;
24
import eu.etaxonomy.cdm.model.common.CdmBase;
25
import eu.etaxonomy.cdm.model.common.Language;
26
import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
27
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
28
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
29
import eu.etaxonomy.cdm.model.name.TaxonName;
30
import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
31
import eu.etaxonomy.cdm.model.name.TypeDesignationStatusBase;
32
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
33
import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
34
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
35
import eu.etaxonomy.cdm.vaadin.model.EntityReference;
36
import eu.etaxonomy.cdm.vaadin.model.TypedEntityReference;
37
import eu.etaxonomy.cdm.vaadin.view.registration.RegistrationValidationException;
38

    
39
/**
40
 * Converts a collection of TypeDesignations, which should belong to the
41
 * same name of course, into a string representation.
42
 *
43
 * Order of TypeDesignations in the resulting string:
44
 *  Type, Holotype, Lectotype, Epitypes
45
 * @author a.kohlbecker
46
 * @since Mar 10, 2017
47
 *
48
 */
49
public class TypeDesignationConverter {
50

    
51

    
52
    private static final String TYPE_STATUS_SEPARATOR = "; ";
53

    
54
    private static final String TYPE_SEPARATOR = "; ";
55

    
56
    private static final String TYPE_DESIGNATION_SEPARATOR = ", ";
57

    
58
    private Collection<TypeDesignationBase> typeDesignations;
59

    
60
    private int workingSetIdAutoIncrement = 0;
61

    
62
    /**
63
     * Groups the EntityReferences for each of the TypeDesignations by the according TypeDesignationStatus.
64
     * The TypeDesignationStatusBase keys are already ordered by the term order defined in the vocabulary.
65
     */
66
    private LinkedHashMap<TypedEntityReference, TypeDesignationWorkingSet> orderedByTypesByBaseEntity;
67

    
68

    
69
    private EntityReference typifiedName;
70

    
71
    private String finalString = null;
72

    
73
    /**
74
     * @param taxonName
75
     * @throws RegistrationValidationException
76
     *
77
     */
78
    public TypeDesignationConverter(CdmBase containgEntity, Collection<TypeDesignationBase> typeDesignations) throws RegistrationValidationException {
79
        this.typeDesignations = typeDesignations;
80
        Map<TypedEntityReference, TypeDesignationWorkingSet> byBaseEntityByTypeStatus = new HashMap<>();
81
        typeDesignations.forEach(td -> mapTypeDesignation(containgEntity, byBaseEntityByTypeStatus, td));
82
        orderedByTypesByBaseEntity = orderByTypeByBaseEntity(byBaseEntityByTypeStatus);
83
        this.typifiedName = findTypifiedName();
84
    }
85

    
86

    
87
    private void mapTypeDesignation(CdmBase containgEntity, Map<TypedEntityReference, TypeDesignationWorkingSet> byBaseEntityByTypeStatus,
88
            TypeDesignationBase<?> td){
89

    
90
        TypeDesignationStatusBase<?> status = td.getTypeStatus();
91

    
92
        final TypedEntityReference baseEntityReference = baseEntityReference(td);
93

    
94
        EntityReference typeDesignationEntityReference = new EntityReference(td.getId(), stringify(td));
95

    
96
        TypeDesignationWorkingSet typedesignationWorkingSet;
97
        if(!byBaseEntityByTypeStatus.containsKey(baseEntityReference)){
98
            TypedEntityReference containigEntityReference = new TypedEntityReference(containgEntity.getClass(), containgEntity.getId(), containgEntity.toString());
99
            byBaseEntityByTypeStatus.put(baseEntityReference, new TypeDesignationWorkingSet(containigEntityReference, baseEntityReference));
100
        }
101

    
102
        typedesignationWorkingSet = byBaseEntityByTypeStatus.get(baseEntityReference);
103
        typedesignationWorkingSet.insert(status, typeDesignationEntityReference);
104
    }
105

    
106
    /**
107
     * @param td
108
     * @return
109
     */
110
    protected TypedEntityReference baseEntityReference(TypeDesignationBase<?> td) {
111

    
112
        CdmBase baseEntity = null;
113
        String label = "";
114
        if(td  instanceof SpecimenTypeDesignation){
115
            SpecimenTypeDesignation std = (SpecimenTypeDesignation) td;
116
            FieldUnit fu = findFieldUnit(std);
117
            if(fu != null){
118
                baseEntity = fu;
119
                label = fu.getTitleCache();
120
            } else if(((SpecimenTypeDesignation) td).getTypeSpecimen() != null){
121
                baseEntity = ((SpecimenTypeDesignation) td).getTypeSpecimen();
122
                label = ""; // empty label to avoid repeating the DerivedUnit details
123
            }
124
        } else if(td instanceof NameTypeDesignation){
125
            baseEntity = ((NameTypeDesignation)td).getTypeName();
126
            label = "";
127
        }
128
        if(baseEntity == null) {
129
            baseEntity = td;
130
            label = "INCOMPLETE DATA";
131
        }
132

    
133
        TypedEntityReference baseEntityReference = new TypedEntityReference(baseEntity.getClass(), baseEntity.getId(), label);
134

    
135
        return baseEntityReference;
136
    }
137

    
138

    
139
    private LinkedHashMap<TypedEntityReference, TypeDesignationWorkingSet> orderByTypeByBaseEntity(
140
            Map<TypedEntityReference, TypeDesignationWorkingSet> stringsByTypeByBaseEntity){
141

    
142
       // order the FieldUnit TypeName keys
143
       List<TypedEntityReference> baseEntityKeyList = new LinkedList<>(stringsByTypeByBaseEntity.keySet());
144
       Collections.sort(baseEntityKeyList, new Comparator<TypedEntityReference>(){
145
        /**
146
         * Sorts the base entitoes (TypedEntityReference) in nthe following order:
147
         *
148
         * 1. FieldUnits
149
         * 2. DerivedUnit (in case of missing FieldUnit we expect the base type to be DerivedUnit)
150
         * 3. NameType
151
         *
152
         * {@inheritDoc}
153
         */
154
        @Override
155
        public int compare(TypedEntityReference o1, TypedEntityReference o2) {
156
            if(!o1.getType().equals(o2.getType())) {
157
                if(o1.equals(FieldUnit.class) || o2.equals(FieldUnit.class)){
158
                    // FieldUnits first
159
                    return o1.getType().equals(FieldUnit.class) ? -1 : 1;
160
                } else {
161
                    // name types last (in case of missing FieldUnit we expect the base type to be DerivedUnit which comes into the middle)
162
                    return o2.getType().equals(TaxonName.class) ? -1 : 1;
163
                }
164
            } else {
165
                return o1.getLabel().compareTo(o2.getLabel());
166
            }
167
        }});
168

    
169
       // new LinkedHashMap for the ordered FieldUnitOrTypeName keys
170
       LinkedHashMap<TypedEntityReference, TypeDesignationWorkingSet> stringsOrderedbyBaseEntityOrderdByType = new LinkedHashMap<>(stringsByTypeByBaseEntity.size());
171

    
172
       for(TypedEntityReference baseEntityRef : baseEntityKeyList){
173

    
174
           TypeDesignationWorkingSet typeDesignationWorkingSet = stringsByTypeByBaseEntity.get(baseEntityRef);
175
           // order the TypeDesignationStatusBase keys
176
            List<TypeDesignationStatusBase<?>> keyList = new LinkedList<>(typeDesignationWorkingSet.keySet());
177
            Collections.sort(keyList, new Comparator<TypeDesignationStatusBase>() {
178
                @SuppressWarnings("unchecked")
179
                @Override
180
                public int compare(TypeDesignationStatusBase o1, TypeDesignationStatusBase o2) {
181
                    // fix inverted order of cdm terms by -1*
182
                    return -1 * o1.compareTo(o2);
183
                }
184
            });
185
            // new LinkedHashMap for the ordered TypeDesignationStatusBase keys
186
            TypeDesignationWorkingSet orderedStringsByOrderedTypes = new TypeDesignationWorkingSet(typeDesignationWorkingSet.getContainigEntityReference(), baseEntityRef);
187
            orderedStringsByOrderedTypes.setWorkingSetId(typeDesignationWorkingSet.workingSetId); // preserve original workingSetId
188
            keyList.forEach(key -> orderedStringsByOrderedTypes.put(key, typeDesignationWorkingSet.get(key)));
189
            stringsOrderedbyBaseEntityOrderdByType.put(baseEntityRef, orderedStringsByOrderedTypes);
190
       }
191

    
192
        return stringsOrderedbyBaseEntityOrderdByType;
193
    }
194

    
195
    /*
196
    private LinkedHashMap<TypedEntityReference, LinkedHashMap<String, Collection<EntityReference>>> buildOrderedRepresentations(){
197

    
198
        orderedStringsByOrderedTypes.keySet().forEach(
199
                key -> orderedRepresentations.put(
200
                        getTypeDesignationStytusLabel(key),
201
                        orderedStringsByOrderedTypes.get(key))
202
                );
203
        return orderedRepresentations;
204
    }
205
*/
206

    
207
    public TypeDesignationConverter buildString(){
208

    
209
        if(finalString == null){
210

    
211
            finalString = "";
212
            if(getTypifiedNameCache() != null){
213
                finalString += getTypifiedNameCache() + " ";
214
            }
215

    
216
            int typeCount = 0;
217
            for(TypedEntityReference baseEntityRef : orderedByTypesByBaseEntity.keySet()) {
218
                StringBuilder sb = new StringBuilder();
219
                if(typeCount++ > 0){
220
                    sb.append(TYPE_SEPARATOR);
221
                }
222
                boolean isNameTypeDesignation = false;
223
                if(SpecimenOrObservationBase.class.isAssignableFrom(baseEntityRef.getType())){
224
                    sb.append("Type: ");
225
                } else {
226
                    sb.append("NameType: ");
227
                    isNameTypeDesignation = true;
228
                }
229
                if(!baseEntityRef.getLabel().isEmpty()){
230
                    sb.append(baseEntityRef.getLabel()).append(" ");
231
                }
232
                TypeDesignationWorkingSet typeDesignationWorkingSet = orderedByTypesByBaseEntity.get(baseEntityRef);
233
                if(!isNameTypeDesignation ){
234
                    sb.append("(");
235
                }
236
                int typeStatusCount = 0;
237
                for(TypeDesignationStatusBase<?> typeStatus : typeDesignationWorkingSet.keySet()) {
238
                    if(typeStatusCount++  > 0){
239
                        sb.append(TYPE_STATUS_SEPARATOR);
240
                    }
241
                    boolean isPlural = typeDesignationWorkingSet.get(typeStatus).size() > 1;
242
                    sb.append(typeStatus.getLabel());
243
                    if(isPlural){
244
                        sb.append("s: ");
245
                    } else {
246
                        sb.append(", ");
247
                    }
248
                    int typeDesignationCount = 0;
249
                    for(EntityReference typeDesignationEntityReference : typeDesignationWorkingSet.get(typeStatus)) {
250
                        if(typeDesignationCount++  > 0){
251
                            sb.append(TYPE_DESIGNATION_SEPARATOR);
252
                        }
253
                        sb.append(typeDesignationEntityReference.getLabel());
254
                    }
255
                }
256
                if(!isNameTypeDesignation ){
257
                    sb.append(")");
258
                }
259
                typeDesignationWorkingSet.setRepresentation(sb.toString());
260
                finalString += typeDesignationWorkingSet.getRepresentation();
261
            }
262

    
263
        }
264
        return this;
265
    }
266

    
267
    /**
268
     * FIXME use the validation framework validators and to store the validation problems!!!
269
     *
270
     * @return
271
     * @throws RegistrationValidationException
272
     */
273
    private EntityReference findTypifiedName() throws RegistrationValidationException {
274

    
275
        List<String> problems = new ArrayList<>();
276

    
277
        TaxonName typifiedName = null;
278

    
279
        for(TypeDesignationBase<?> typeDesignation : typeDesignations){
280
            typeDesignation.getTypifiedNames();
281
            if(typeDesignation.getTypifiedNames().isEmpty()){
282

    
283
                //TODO instead throw RegistrationValidationException()
284
                problems.add("Missing typifiedName in " + typeDesignation.toString());
285
                continue;
286
            }
287
            if(typeDesignation.getTypifiedNames().size() > 1){
288
              //TODO instead throw RegistrationValidationException()
289
                problems.add("Multiple typifiedName in " + typeDesignation.toString());
290
                continue;
291
            }
292
            if(typifiedName == null){
293
                // remember
294
                typifiedName = typeDesignation.getTypifiedNames().iterator().next();
295
            } else {
296
                // compare
297
                TaxonName otherTypifiedName = typeDesignation.getTypifiedNames().iterator().next();
298
                if(typifiedName.getId() != otherTypifiedName.getId()){
299
                  //TODO instead throw RegistrationValidationException()
300
                    problems.add("Multiple typifiedName in " + typeDesignation.toString());
301
                }
302
            }
303

    
304
        }
305
        if(!problems.isEmpty()){
306
            // FIXME use the validation framework
307
            throw new RegistrationValidationException("Inconsistent type designations", problems);
308
        }
309

    
310
        if(typifiedName != null){
311
            return new EntityReference(typifiedName.getId(), typifiedName.getTitleCache());
312
        }
313
        return null;
314
    }
315

    
316

    
317
    /**
318
     * @return the title cache of the typifying name or <code>null</code>
319
     */
320
    public String getTypifiedNameCache() {
321
        if(typifiedName != null){
322
            return typifiedName.getLabel();
323
        }
324
        return null;
325
    }
326

    
327
    /**
328
     * @return the title cache of the typifying name or <code>null</code>
329
     */
330
    public EntityReference getTypifiedName() {
331

    
332
       return typifiedName;
333
    }
334

    
335
    /**
336
     * @return
337
     */
338
    public Collection<TypeDesignationBase> getTypeDesignations() {
339
        return typeDesignations;
340
    }
341

    
342
    /**
343
     * @param ref
344
     * @return
345
     */
346
    public TypeDesignationBase findTypeDesignation(EntityReference typeDesignationRef) {
347
        for(TypeDesignationBase td : typeDesignations){
348
            if(td.getId() == typeDesignationRef.getId()){
349
                return td;
350
            }
351
        }
352
        // TODO Auto-generated method stub
353
        return null;
354
    }
355

    
356

    
357
    public LinkedHashMap<TypedEntityReference, TypeDesignationWorkingSet> getOrderdTypeDesignationWorkingSets() {
358
        return orderedByTypesByBaseEntity;
359
    }
360

    
361

    
362

    
363
    /**
364
     * @param key
365
     * @return
366
     */
367
    protected String getTypeDesignationStytusLabel(TypeDesignationStatusBase<?> key) {
368
        String typeLable;
369
        if(key.equals( SpecimenTypeDesignationStatus.TYPE())){
370
            typeLable = "Type";
371
        } else {
372
            typeLable = key.getPreferredRepresentation(Language.DEFAULT()).getLabel();
373
        }
374
        return typeLable;
375
    }
376

    
377
    /**
378
     * @param td
379
     * @return
380
     */
381
    private String stringify(TypeDesignationBase td) {
382

    
383
        if(td instanceof NameTypeDesignation){
384
            return stringify((NameTypeDesignation)td);
385
        } else {
386
            return stringify((SpecimenTypeDesignation)td, false);
387
        }
388
    }
389

    
390

    
391
    /**
392
     * @param td
393
     * @return
394
     */
395
    protected String stringify(NameTypeDesignation td) {
396

    
397
        StringBuffer sb = new StringBuffer();
398

    
399
        if(td.getTypeName() != null){
400
            sb.append(td.getTypeName().getTitleCache());
401
        }
402
        if(td.getCitation() != null){
403
            sb.append(" ").append(td.getCitation().getTitleCache());
404
            if(td.getCitationMicroReference() != null){
405
                sb.append(":").append(td.getCitationMicroReference());
406
            }
407
        }
408
        if(td.isNotDesignated()){
409
            sb.append(" not designated");
410
        }
411
        if(td.isRejectedType()){
412
            sb.append(" rejected");
413
        }
414
        if(td.isConservedType()){
415
            sb.append(" conserved");
416
        }
417
        return sb.toString();
418
    }
419

    
420
    /**
421
     * @param td
422
     * @return
423
     */
424
    private String stringify(SpecimenTypeDesignation td, boolean useFullTitleCache) {
425
        String  result = "";
426

    
427
        if(useFullTitleCache){
428
            if(td.getTypeSpecimen() != null){
429
                String nameTitleCache = td.getTypeSpecimen().getTitleCache();
430
                if(getTypifiedNameCache() != null){
431
                    nameTitleCache = nameTitleCache.replace(getTypifiedNameCache(), "");
432
                }
433
                result += nameTitleCache;
434
            }
435
        } else {
436
            if(td.getTypeSpecimen() != null){
437
                DerivedUnit du = td.getTypeSpecimen();
438
                if(du.isProtectedTitleCache()){
439
                    result += du.getTitleCache();
440
                } else {
441
                    DerivedUnitFacadeCacheStrategy cacheStrategy = new DerivedUnitFacadeCacheStrategy();
442
                    result += cacheStrategy.getTitleCache(du, true);
443
                }
444
            }
445
        }
446

    
447
        if(td.getCitation() != null){
448
            result += " " + td.getCitation().getTitleCache();
449
            if(td.getCitationMicroReference() != null){
450
                result += " :" + td.getCitationMicroReference();
451
            }
452
        }
453
        if(td.isNotDesignated()){
454
            result += " not designated";
455
        }
456

    
457
        return result;
458
    }
459

    
460
    /**
461
     * @param td
462
     * @return
463
     * @deprecated
464
     */
465
    @Deprecated
466
    private FieldUnit findFieldUnit(SpecimenTypeDesignation td) {
467

    
468
        DerivedUnit du = td.getTypeSpecimen();
469
        return findFieldUnit(du);
470
    }
471

    
472
    private FieldUnit findFieldUnit(DerivedUnit du) {
473

    
474
        if(du == null || du.getOriginals() == null){
475
            return null;
476
        }
477
        @SuppressWarnings("rawtypes")
478
        Set<SpecimenOrObservationBase> originals = du.getDerivedFrom().getOriginals();
479
        @SuppressWarnings("rawtypes")
480
        Optional<SpecimenOrObservationBase> fieldUnit = originals.stream()
481
                .filter(original -> original instanceof FieldUnit).findFirst();
482
        if (fieldUnit.isPresent()) {
483
            return (FieldUnit) fieldUnit.get();
484
        } else {
485
            for (@SuppressWarnings("rawtypes")
486
            SpecimenOrObservationBase sob : originals) {
487
                if (sob instanceof DerivedUnit) {
488
                    FieldUnit fu = findFieldUnit((DerivedUnit) sob);
489
                    if (fu != null) {
490
                        return fu;
491
                    }
492
                }
493
            }
494
        }
495

    
496
        return null;
497
    }
498

    
499
    public String print() {
500
        return finalString;
501
    }
502

    
503
    /**
504
     * Groups the EntityReferences for TypeDesignations by the according TypeDesignationStatus.
505
     * The TypeDesignationStatusBase keys can be ordered by the term order defined in the vocabulary.
506
     */
507
    public class TypeDesignationWorkingSet extends LinkedHashMap<TypeDesignationStatusBase<?>, Collection<EntityReference>> {
508

    
509
        String workingSetRepresentation = null;
510

    
511
        TypedEntityReference containigEntityReference;
512

    
513
        TypedEntityReference baseEntityReference;
514

    
515
        int workingSetId = workingSetIdAutoIncrement++;
516

    
517
        private static final long serialVersionUID = -1329007606500890729L;
518

    
519
        /**
520
         * @param baseEntityReference
521
         */
522
        public TypeDesignationWorkingSet(TypedEntityReference containigEntityReference, TypedEntityReference baseEntityReference) {
523
            this.containigEntityReference = containigEntityReference;
524
            this.baseEntityReference = baseEntityReference;
525
        }
526

    
527
        public List<EntityReference> getTypeDesignations() {
528
            List<EntityReference> typeDesignations = new ArrayList<>();
529
            this.values().forEach(typeDesignationReferences -> typeDesignationReferences.forEach(td -> typeDesignations.add(td)));
530
            return typeDesignations;
531
        }
532

    
533
        /**
534
         * @param status
535
         * @param typeDesignationEntityReference
536
         */
537
        public void insert(TypeDesignationStatusBase<?> status, EntityReference typeDesignationEntityReference) {
538

    
539
            if(status == null){
540
                status = SpecimenTypeDesignationStatus.TYPE();
541
            }
542
            if(!containsKey(status)){
543
                put(status, new ArrayList<EntityReference>());
544
            }
545
            get(status).add(typeDesignationEntityReference);
546
        }
547

    
548
        /**
549
         * @return the workingSetId
550
         */
551
        public int getWorkingSetId() {
552
            return workingSetId;
553
        }
554

    
555
        /**
556
         * @param workingSetId the workingSetId to set
557
         */
558
        public void setWorkingSetId(int workingSetId) {
559
            this.workingSetId = workingSetId;
560
        }
561

    
562
        public String getRepresentation() {
563
            return workingSetRepresentation;
564
        }
565

    
566
        public void setRepresentation(String representation){
567
            this.workingSetRepresentation = representation;
568
        }
569

    
570
        /**
571
         * A reference to the entity which is the common base entity for all TypeDesignations in this workingset.
572
         * For a {@link SpecimenTypeDesignation} this is usually the {@link FieldUnit} if it is present. Otherwise it can also be
573
         * a {@link DerivedUnit} or something else depending on the specific use case.
574
         *
575
         * @return the baseEntityReference
576
         */
577
        public TypedEntityReference getBaseEntityReference() {
578
            return baseEntityReference;
579
        }
580

    
581
        /**
582
         * A reference to the entity which contains the TypeDesignations bundled in this working set.
583
         * This can be for example a {@link TaxonName} or a {@link Registration} entity.
584
         *
585
         * @return the baseEntityReference
586
         */
587
        public TypedEntityReference getContainigEntityReference() {
588
            return containigEntityReference;
589
        }
590

    
591
        @Override
592
        public String toString(){
593
            if(workingSetRepresentation != null){
594
                return workingSetRepresentation;
595
            } else {
596
                return super.toString();
597
            }
598
        }
599

    
600
    }
601

    
602
}
(4-4/5)