Project

General

Profile

Download (19.4 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
    /**
61
     * Groups the EntityReferences for each of the TypeDesignatinos by the according TypeDesignationStatus.
62
     * The TypeDesignationStatusBase keys are already ordered by the term order defined in the vocabulary.
63
     */
64
    private LinkedHashMap<TypedEntityReference, Map<TypeDesignationStatusBase<?>, Collection<EntityReference>>> orderedByTypesByBaseEntity;
65

    
66
    private EntityReference typifiedName;
67

    
68
    private String finalString = null;
69

    
70
    /**
71
     * @param taxonName
72
     * @throws RegistrationValidationException
73
     *
74
     */
75
    public TypeDesignationConverter(Collection<TypeDesignationBase> typeDesignations) throws RegistrationValidationException {
76
        this.typeDesignations = typeDesignations;
77
        Map<TypedEntityReference, Map<TypeDesignationStatusBase<?>, Collection<EntityReference>>> byBaseEntityByTypeStatus = new HashMap<>();
78
        typeDesignations.forEach(td -> mapTypeDesignation(byBaseEntityByTypeStatus, td));
79
        orderedByTypesByBaseEntity = orderByTypeByBaseEntity(byBaseEntityByTypeStatus);
80
        this.typifiedName = findTypifiedName();
81
    }
82

    
83

    
84
    private void mapTypeDesignation(Map<TypedEntityReference, Map<TypeDesignationStatusBase<?>, Collection<EntityReference>>> byBaseEntityByTypeStatus,
85
            TypeDesignationBase<?> td){
86

    
87
        TypeDesignationStatusBase<?> status = td.getTypeStatus();
88

    
89
        final TypedEntityReference baseEntityReference = baseEntityReference(td);
90

    
91
        EntityReference typeDesignationEntityReference = new EntityReference(td.getId(), stringify(td));
92

    
93
        Map<TypeDesignationStatusBase<?>, Collection<EntityReference>> stringsOrderdByType;
94
        if(!byBaseEntityByTypeStatus.containsKey(baseEntityReference)){
95
            byBaseEntityByTypeStatus.put(baseEntityReference, new LinkedHashMap<>());
96
        }
97

    
98
        stringsOrderdByType = byBaseEntityByTypeStatus.get(baseEntityReference);
99

    
100
        // the cdm ordered term bases are ordered inverse, fixing this for here
101
        if(status == null){
102
            status = SpecimenTypeDesignationStatus.TYPE();
103
        }
104
        if(!stringsOrderdByType.containsKey(status)){
105
            stringsOrderdByType.put(status, new ArrayList<EntityReference>());
106
        }
107
        stringsOrderdByType.get(status).add(typeDesignationEntityReference);
108
    }
109

    
110
    /**
111
     * @param td
112
     * @return
113
     */
114
    protected TypedEntityReference baseEntityReference(TypeDesignationBase<?> td) {
115

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

    
137
        TypedEntityReference baseEntityReference = new TypedEntityReference(baseEntity.getClass(), baseEntity.getId(), label);
138

    
139
        return baseEntityReference;
140
    }
141

    
142

    
143
    private LinkedHashMap<TypedEntityReference, Map<TypeDesignationStatusBase<?>, Collection<EntityReference>>> orderByTypeByBaseEntity(
144
            Map<TypedEntityReference, Map<TypeDesignationStatusBase<?>, Collection<EntityReference>>> stringsByTypeByBaseEntity){
145

    
146
       // order the FieldUnit TypeName keys
147
       List<TypedEntityReference> baseEntityKeyList = new LinkedList<>(stringsByTypeByBaseEntity.keySet());
148
       Collections.sort(baseEntityKeyList, new Comparator<TypedEntityReference>(){
149
        @Override
150
        public int compare(TypedEntityReference o1, TypedEntityReference o2) {
151
            if(!o1.getType().equals(o2.getType())) {
152
                return o1.getType().equals(FieldUnit.class) ? -1 : 1;
153
            }
154
            return o1.getLabel().compareTo(o2.getLabel());
155
        }});
156

    
157
       // new LinkedHashMap for the ordered FieldUnitOrTypeName keys
158
       LinkedHashMap<TypedEntityReference, Map<TypeDesignationStatusBase<?>, Collection<EntityReference>>> stringsOrderedbyBaseEntityOrderdByType = new LinkedHashMap<>(stringsByTypeByBaseEntity.size());
159

    
160
       for(TypedEntityReference baseEntityRef : baseEntityKeyList){
161

    
162
           Map<TypeDesignationStatusBase<?>, Collection<EntityReference>> stringsByType = stringsByTypeByBaseEntity.get(baseEntityRef);
163
           // order the TypeDesignationStatusBase keys
164
            List<TypeDesignationStatusBase<?>> keyList = new LinkedList<>(stringsByType.keySet());
165
            Collections.sort(keyList, new Comparator<TypeDesignationStatusBase>() {
166
                @Override
167
                public int compare(TypeDesignationStatusBase o1, TypeDesignationStatusBase o2) {
168
                    // fix inverted order of cdm terms by -1*
169
                    return -1 * o1.compareTo(o2);
170
                }
171
            });
172
            // new LinkedHashMap for the ordered TypeDesignationStatusBase keys
173
            LinkedHashMap<TypeDesignationStatusBase<?>, Collection<EntityReference>> orderedStringsByOrderedTypes = new LinkedHashMap<>();
174
            keyList.forEach(key -> orderedStringsByOrderedTypes.put(key, stringsByType.get(key)));
175
            stringsOrderedbyBaseEntityOrderdByType.put(baseEntityRef, orderedStringsByOrderedTypes);
176
       }
177

    
178
        return stringsOrderedbyBaseEntityOrderdByType;
179
    }
180

    
181
    /*
182
    private LinkedHashMap<TypedEntityReference, LinkedHashMap<String, Collection<EntityReference>>> buildOrderedRepresentations(){
183

    
184
        orderedStringsByOrderedTypes.keySet().forEach(
185
                key -> orderedRepresentations.put(
186
                        getTypeDesignationStytusLabel(key),
187
                        orderedStringsByOrderedTypes.get(key))
188
                );
189
        return orderedRepresentations;
190
    }
191
*/
192

    
193
    public TypeDesignationConverter buildString(){
194

    
195
        if(finalString == null){
196
            StringBuilder sb = new StringBuilder();
197

    
198
            if(getTypifiedNameCache() != null){
199
                sb.append(getTypifiedNameCache()).append(" ");
200
            }
201

    
202
            int typeCount = 0;
203
            for(TypedEntityReference baseEntityRef : orderedByTypesByBaseEntity.keySet()) {
204
                if(typeCount++ > 0){
205
                    sb.append(TYPE_SEPARATOR);
206
                }
207
                boolean isNameTypeDesignation = false;
208
                if(SpecimenOrObservationBase.class.isAssignableFrom(baseEntityRef.getType())){
209
                    sb.append("Type: ");
210
                } else {
211
                    sb.append("NameType: ");
212
                    isNameTypeDesignation = true;
213
                }
214
                if(!baseEntityRef.getLabel().isEmpty()){
215
                    sb.append(baseEntityRef.getLabel()).append(" ");
216
                }
217
                Map<TypeDesignationStatusBase<?>, Collection<EntityReference>> orderedRepresentations = orderedByTypesByBaseEntity.get(baseEntityRef);
218
                if(!isNameTypeDesignation ){
219
                    sb.append("(");
220
                }
221
                int typeStatusCount = 0;
222
                for(TypeDesignationStatusBase<?> typeStatus : orderedRepresentations.keySet()) {
223
                    if(typeStatusCount++  > 0){
224
                        sb.append(TYPE_STATUS_SEPARATOR);
225
                    }
226
                    boolean isPlural = orderedRepresentations.get(typeStatus).size() > 1;
227
                    sb.append(typeStatus.getLabel());
228
                    if(isPlural){
229
                        sb.append("s: ");
230
                    } else {
231
                        sb.append(", ");
232
                    }
233
                    int typeDesignationCount = 0;
234
                    for(EntityReference typeDesignationEntityReference : orderedRepresentations.get(typeStatus)) {
235
                        if(typeDesignationCount++  > 0){
236
                            sb.append(TYPE_DESIGNATION_SEPARATOR);
237
                        }
238
                        sb.append(typeDesignationEntityReference.getLabel());
239
                    };
240
                };
241
                if(!isNameTypeDesignation ){
242
                    sb.append(")");
243
                }
244
            };
245

    
246
            finalString  = sb.toString();
247
        }
248
        return this;
249
    }
250

    
251
    /**
252
     * FIXME use the validation framework validators and to store the validation problems!!!
253
     *
254
     * @return
255
     * @throws RegistrationValidationException
256
     */
257
    private EntityReference findTypifiedName() throws RegistrationValidationException {
258

    
259
        List<String> problems = new ArrayList<>();
260

    
261
        TaxonName typifiedName = null;
262

    
263
        for(TypeDesignationBase<?> typeDesignation : typeDesignations){
264
            typeDesignation.getTypifiedNames();
265
            if(typeDesignation.getTypifiedNames().isEmpty()){
266

    
267
                //TODO instead throw RegistrationValidationException()
268
                problems.add("Missing typifiedName in " + typeDesignation.toString());
269
                continue;
270
            }
271
            if(typeDesignation.getTypifiedNames().size() > 1){
272
              //TODO instead throw RegistrationValidationException()
273
                problems.add("Multiple typifiedName in " + typeDesignation.toString());
274
                continue;
275
            }
276
            if(typifiedName == null){
277
                // remember
278
                typifiedName = typeDesignation.getTypifiedNames().iterator().next();
279
            } else {
280
                // compare
281
                TaxonName otherTypifiedName = typeDesignation.getTypifiedNames().iterator().next();
282
                if(typifiedName.getId() != otherTypifiedName.getId()){
283
                  //TODO instead throw RegistrationValidationException()
284
                    problems.add("Multiple typifiedName in " + typeDesignation.toString());
285
                }
286
            }
287

    
288
        }
289
        if(!problems.isEmpty()){
290
            // FIXME use the validation framework
291
            throw new RegistrationValidationException("Inconsistent type designations", problems);
292
        }
293

    
294
        if(typifiedName != null){
295
            return new EntityReference(typifiedName.getId(), typifiedName.getTitleCache());
296
        }
297
        return null;
298
    }
299

    
300

    
301
    /**
302
     * @return the title cache of the typifying name or <code>null</code>
303
     */
304
    public String getTypifiedNameCache() {
305
        if(typifiedName != null){
306
            return typifiedName.getLabel();
307
        }
308
        return null;
309
    }
310

    
311
    /**
312
     * @return the title cache of the typifying name or <code>null</code>
313
     */
314
    public EntityReference getTypifiedName() {
315

    
316
       return typifiedName;
317
    }
318

    
319
    /**
320
     * @return
321
     */
322
    public Collection<TypeDesignationBase> getTypeDesignations() {
323
        return typeDesignations;
324
    }
325

    
326
    public LinkedHashMap<TypedEntityReference, Map<TypeDesignationStatusBase<?>, Collection<EntityReference>>> getOrderedTypeDesignations() {
327
        return orderedByTypesByBaseEntity;
328
    }
329

    
330

    
331

    
332
    /**
333
     * @param key
334
     * @return
335
     */
336
    protected String getTypeDesignationStytusLabel(TypeDesignationStatusBase<?> key) {
337
        String typeLable;
338
        if(key.equals( SpecimenTypeDesignationStatus.TYPE())){
339
            typeLable = "Type";
340
        } else {
341
            typeLable = key.getPreferredRepresentation(Language.DEFAULT()).getLabel();
342
        }
343
        return typeLable;
344
    }
345

    
346
    /**
347
     * @param td
348
     * @return
349
     */
350
    private String stringify(TypeDesignationBase td) {
351

    
352
        if(td instanceof NameTypeDesignation){
353
            return stringify((NameTypeDesignation)td);
354
        } else {
355
            return stringify((SpecimenTypeDesignation)td, false);
356
        }
357
    }
358

    
359

    
360
    /**
361
     * @param td
362
     * @return
363
     */
364
    protected String stringify(NameTypeDesignation td) {
365

    
366
        StringBuffer sb = new StringBuffer();
367

    
368
        if(td.getTypeName() != null){
369
            sb.append(td.getTypeName().getTitleCache());
370
        }
371
        if(td.getCitation() != null){
372
            sb.append(" ").append(td.getCitation().getTitleCache());
373
            if(td.getCitationMicroReference() != null){
374
                sb.append(":").append(td.getCitationMicroReference());
375
            }
376
        }
377
        if(td.isNotDesignated()){
378
            sb.append(" not designated");
379
        }
380
        if(td.isRejectedType()){
381
            sb.append(" rejected");
382
        }
383
        if(td.isConservedType()){
384
            sb.append(" conserved");
385
        }
386
        return sb.toString();
387
    }
388

    
389
    /**
390
     * @param td
391
     * @return
392
     */
393
    private String stringify(SpecimenTypeDesignation td, boolean useFullTitleCache) {
394
        String  result = "";
395

    
396
        if(useFullTitleCache){
397
            if(td.getTypeSpecimen() != null){
398
                String nameTitleCache = td.getTypeSpecimen().getTitleCache();
399
                if(getTypifiedNameCache() != null){
400
                    nameTitleCache = nameTitleCache.replace(getTypifiedNameCache(), "");
401
                }
402
                result += nameTitleCache;
403
            }
404
        } else {
405
            if(td.getTypeSpecimen() != null){
406
                DerivedUnit du = td.getTypeSpecimen();
407
                if(du.isProtectedTitleCache()){
408
                    result += du.getTitleCache();
409
                } else {
410
                    DerivedUnitFacadeCacheStrategy cacheStrategy = new DerivedUnitFacadeCacheStrategy();
411
                    result += cacheStrategy.getTitleCache(du);
412
                }
413
            }
414
        }
415

    
416
        if(td.getCitation() != null){
417
            result += " " + td.getCitation().getTitleCache();
418
            if(td.getCitationMicroReference() != null){
419
                result += " :" + td.getCitationMicroReference();
420
            }
421
        }
422
        if(td.isNotDesignated()){
423
            result += " not designated";
424
        }
425

    
426
        return result;
427
    }
428

    
429
    /**
430
     * @param td
431
     * @return
432
     * @deprecated
433
     */
434
    @Deprecated
435
    private FieldUnit findFieldUnit(SpecimenTypeDesignation td) {
436

    
437
        DerivedUnit du = td.getTypeSpecimen();
438
        return findFieldUnit(du);
439
    }
440

    
441
    private FieldUnit findFieldUnit(DerivedUnit du) {
442

    
443
        if(du == null || du.getOriginals() == null){
444
            return null;
445
        }
446
        @SuppressWarnings("rawtypes")
447
        Set<SpecimenOrObservationBase> originals = du.getDerivedFrom().getOriginals();
448
        @SuppressWarnings("rawtypes")
449
        Optional<SpecimenOrObservationBase> fieldUnit = originals.stream()
450
                .filter(original -> original instanceof FieldUnit).findFirst();
451
        if (fieldUnit.isPresent()) {
452
            return (FieldUnit) fieldUnit.get();
453
        } else {
454
            for (@SuppressWarnings("rawtypes")
455
            SpecimenOrObservationBase sob : originals) {
456
                if (sob instanceof DerivedUnit) {
457
                    FieldUnit fu = findFieldUnit((DerivedUnit) sob);
458
                    if (fu != null) {
459
                        return fu;
460
                    }
461
                }
462
            }
463
        }
464

    
465
        return null;
466
    }
467

    
468

    
469
    public String print() {
470
        return finalString;
471
    }
472

    
473
/**
474
 *
475
 * @author a.kohlbecker
476
 * @since Jun 12, 2017
477
 *
478
 */
479
    public class FieldUnitOrTypeName {
480

    
481
        FieldUnit fieldUnit = null;
482

    
483
        TaxonName typeName = null;
484

    
485
        /**
486
         * @param fieldUnit
487
         * @param typeName
488
         */
489
        private FieldUnitOrTypeName(FieldUnit fieldUnit, TaxonName typeName) {
490
            this.fieldUnit = fieldUnit;
491
            this.typeName = typeName;
492
            if(fieldUnit != null && typeName != null){
493
                throw new RuntimeException("FieldUnitOrTypeName must not contain two non null fields");
494
            }
495

    
496
            if(fieldUnit == null && typeName == null){
497
                throw new NullPointerException("FieldUnitOrTypeName must not contain two null fields");
498
            }
499
        }
500

    
501
        /**
502
         * @return the fieldUnit
503
         */
504
        public FieldUnit getFieldUnit() {
505
            return fieldUnit;
506
        }
507

    
508
        /**
509
         * @return the typeName
510
         */
511
        public TaxonName getTypeName() {
512
            return typeName;
513
        }
514

    
515
        /**
516
         * @return the fieldUnit
517
         */
518
        public boolean isFieldUnit() {
519
            return fieldUnit != null;
520
        }
521

    
522
        /**
523
         * @return the typeName
524
         */
525
        public boolean isTypeName() {
526
            return typeName != null;
527
        }
528

    
529
        public int getEntitiyId() {
530
            if(isFieldUnit()){
531
                return fieldUnit.getId();
532
            } else {
533
                return typeName.getId();
534
            }
535
        }
536

    
537
        /**
538
         * @return
539
         */
540
        public String getTypeLabel() {
541
            if(isFieldUnit()){
542
                return "Type";
543
            } else {
544
                return "NameType";
545
            }
546
        }
547

    
548
        /**
549
         * @return
550
         */
551
        public String getTitleCache() {
552
            if(isFieldUnit()){
553
                return fieldUnit.getTitleCache();
554
            } else {
555
                return typeName.getTitleCache();
556
            }
557
        }
558

    
559
        public boolean matches(FieldUnit fieldUnit, TaxonName typeName){
560
            boolean fuMatch = this.fieldUnit == null && fieldUnit == null || this.fieldUnit.equals(fieldUnit);
561
            boolean nameMatch = this.typeName == null && typeName == null || this.typeName.equals(typeName);
562
            return fuMatch && nameMatch;
563
        }
564

    
565

    
566

    
567

    
568
    }
569
}
(4-4/5)