Project

General

Profile

Download (20 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 TypeDesignations by the according TypeDesignationStatus.
62
     * The TypeDesignationStatusBase keys are already ordered by the term order defined in the vocabulary.
63
     */
64
    private LinkedHashMap<TypedEntityReference, TypeDesignationWorkingSet> orderedByTypesByBaseEntity;
65

    
66

    
67
    private EntityReference typifiedName;
68

    
69
    private String finalString = null;
70

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

    
84

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

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

    
90
        final TypedEntityReference baseEntityReference = baseEntityReference(td);
91

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

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

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

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

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

    
131
        TypedEntityReference baseEntityReference = new TypedEntityReference(baseEntity.getClass(), baseEntity.getId(), label);
132

    
133
        return baseEntityReference;
134
    }
135

    
136

    
137
    private LinkedHashMap<TypedEntityReference, TypeDesignationWorkingSet> orderByTypeByBaseEntity(
138
            Map<TypedEntityReference, TypeDesignationWorkingSet> stringsByTypeByBaseEntity){
139

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

    
151
       // new LinkedHashMap for the ordered FieldUnitOrTypeName keys
152
       LinkedHashMap<TypedEntityReference, TypeDesignationWorkingSet> stringsOrderedbyBaseEntityOrderdByType = new LinkedHashMap<>(stringsByTypeByBaseEntity.size());
153

    
154
       for(TypedEntityReference baseEntityRef : baseEntityKeyList){
155

    
156
           TypeDesignationWorkingSet typeDesignationWorkingSet = stringsByTypeByBaseEntity.get(baseEntityRef);
157
           // order the TypeDesignationStatusBase keys
158
            List<TypeDesignationStatusBase<?>> keyList = new LinkedList<>(typeDesignationWorkingSet.keySet());
159
            Collections.sort(keyList, new Comparator<TypeDesignationStatusBase>() {
160
                @Override
161
                public int compare(TypeDesignationStatusBase o1, TypeDesignationStatusBase o2) {
162
                    // fix inverted order of cdm terms by -1*
163
                    return -1 * o1.compareTo(o2);
164
                }
165
            });
166
            // new LinkedHashMap for the ordered TypeDesignationStatusBase keys
167
            TypeDesignationWorkingSet orderedStringsByOrderedTypes = new TypeDesignationWorkingSet(typeDesignationWorkingSet.getContainigEntityReference(), baseEntityRef);
168
            keyList.forEach(key -> orderedStringsByOrderedTypes.put(key, typeDesignationWorkingSet.get(key)));
169
            stringsOrderedbyBaseEntityOrderdByType.put(baseEntityRef, orderedStringsByOrderedTypes);
170
       }
171

    
172
        return stringsOrderedbyBaseEntityOrderdByType;
173
    }
174

    
175
    /*
176
    private LinkedHashMap<TypedEntityReference, LinkedHashMap<String, Collection<EntityReference>>> buildOrderedRepresentations(){
177

    
178
        orderedStringsByOrderedTypes.keySet().forEach(
179
                key -> orderedRepresentations.put(
180
                        getTypeDesignationStytusLabel(key),
181
                        orderedStringsByOrderedTypes.get(key))
182
                );
183
        return orderedRepresentations;
184
    }
185
*/
186

    
187
    public TypeDesignationConverter buildString(){
188

    
189
        if(finalString == null){
190

    
191
            finalString = "";
192
            if(getTypifiedNameCache() != null){
193
                finalString += getTypifiedNameCache() + " ";
194
            }
195

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

    
243
        }
244
        return this;
245
    }
246

    
247
    /**
248
     * FIXME use the validation framework validators and to store the validation problems!!!
249
     *
250
     * @return
251
     * @throws RegistrationValidationException
252
     */
253
    private EntityReference findTypifiedName() throws RegistrationValidationException {
254

    
255
        List<String> problems = new ArrayList<>();
256

    
257
        TaxonName typifiedName = null;
258

    
259
        for(TypeDesignationBase<?> typeDesignation : typeDesignations){
260
            typeDesignation.getTypifiedNames();
261
            if(typeDesignation.getTypifiedNames().isEmpty()){
262

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

    
284
        }
285
        if(!problems.isEmpty()){
286
            // FIXME use the validation framework
287
            throw new RegistrationValidationException("Inconsistent type designations", problems);
288
        }
289

    
290
        if(typifiedName != null){
291
            return new EntityReference(typifiedName.getId(), typifiedName.getTitleCache());
292
        }
293
        return null;
294
    }
295

    
296

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

    
307
    /**
308
     * @return the title cache of the typifying name or <code>null</code>
309
     */
310
    public EntityReference getTypifiedName() {
311

    
312
       return typifiedName;
313
    }
314

    
315
    /**
316
     * @return
317
     */
318
    public Collection<TypeDesignationBase> getTypeDesignations() {
319
        return typeDesignations;
320
    }
321

    
322
    public LinkedHashMap<TypedEntityReference, TypeDesignationWorkingSet> getOrderdTypeDesignationWorkingSets() {
323
        return orderedByTypesByBaseEntity;
324
    }
325

    
326

    
327

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

    
342
    /**
343
     * @param td
344
     * @return
345
     */
346
    private String stringify(TypeDesignationBase td) {
347

    
348
        if(td instanceof NameTypeDesignation){
349
            return stringify((NameTypeDesignation)td);
350
        } else {
351
            return stringify((SpecimenTypeDesignation)td, false);
352
        }
353
    }
354

    
355

    
356
    /**
357
     * @param td
358
     * @return
359
     */
360
    protected String stringify(NameTypeDesignation td) {
361

    
362
        StringBuffer sb = new StringBuffer();
363

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

    
385
    /**
386
     * @param td
387
     * @return
388
     */
389
    private String stringify(SpecimenTypeDesignation td, boolean useFullTitleCache) {
390
        String  result = "";
391

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

    
412
        if(td.getCitation() != null){
413
            result += " " + td.getCitation().getTitleCache();
414
            if(td.getCitationMicroReference() != null){
415
                result += " :" + td.getCitationMicroReference();
416
            }
417
        }
418
        if(td.isNotDesignated()){
419
            result += " not designated";
420
        }
421

    
422
        return result;
423
    }
424

    
425
    /**
426
     * @param td
427
     * @return
428
     * @deprecated
429
     */
430
    @Deprecated
431
    private FieldUnit findFieldUnit(SpecimenTypeDesignation td) {
432

    
433
        DerivedUnit du = td.getTypeSpecimen();
434
        return findFieldUnit(du);
435
    }
436

    
437
    private FieldUnit findFieldUnit(DerivedUnit du) {
438

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

    
461
        return null;
462
    }
463

    
464
    public String print() {
465
        return finalString;
466
    }
467

    
468
    /**
469
     * Groups the EntityReferences for TypeDesignations by the according TypeDesignationStatus.
470
     * The TypeDesignationStatusBase keys can be ordered by the term order defined in the vocabulary.
471
     */
472
    public class TypeDesignationWorkingSet extends LinkedHashMap<TypeDesignationStatusBase<?>, Collection<EntityReference>> {
473

    
474
        String workingSetRepresentation = null;
475

    
476
        TypedEntityReference containigEntityReference;
477

    
478
        TypedEntityReference baseEntityReference;
479

    
480
        private static final long serialVersionUID = -1329007606500890729L;
481

    
482
        /**
483
         * @param baseEntityReference
484
         */
485
        public TypeDesignationWorkingSet(TypedEntityReference containigEntityReference, TypedEntityReference baseEntityReference) {
486
            this.containigEntityReference = containigEntityReference;
487
            this.baseEntityReference = baseEntityReference;
488
        }
489

    
490
        public List<EntityReference> getTypeDesignations() {
491
            List<EntityReference> typeDesignations = new ArrayList<>();
492
            this.values().forEach(typeDesignationReferences -> typeDesignationReferences.forEach(td -> typeDesignations.add(td)));
493
            return typeDesignations;
494
        }
495

    
496
        /**
497
         * @param status
498
         * @param typeDesignationEntityReference
499
         */
500
        public void insert(TypeDesignationStatusBase<?> status, EntityReference typeDesignationEntityReference) {
501

    
502
            if(status == null){
503
                status = SpecimenTypeDesignationStatus.TYPE();
504
            }
505
            if(!containsKey(status)){
506
                put(status, new ArrayList<EntityReference>());
507
            }
508
            get(status).add(typeDesignationEntityReference);
509
        }
510

    
511
        public String getRepresentation() {
512
            return workingSetRepresentation;
513
        }
514

    
515
        public void setRepresentation(String representation){
516
            this.workingSetRepresentation = representation;
517
        }
518

    
519
        /**
520
         * A reference to the entity which is the common base entity for all TypeDesignations in this workingset.
521
         * For a {@link SpecimenTypeDesignation} this is usually the {@link FieldUnit} if it is present. Otherwise it can also be
522
         * a {@link DerivedUnit} or something else depending on the specific use case.
523
         *
524
         * @return the baseEntityReference
525
         */
526
        public TypedEntityReference getBaseEntityReference() {
527
            return baseEntityReference;
528
        }
529

    
530
        /**
531
         * A reference to the entity which contains the TypeDesignations bundled in this working set.
532
         * This can be for example a {@link TaxonName} or a {@link Registration} entity.
533
         *
534
         * @return the baseEntityReference
535
         */
536
        public TypedEntityReference getContainigEntityReference() {
537
            return containigEntityReference;
538
        }
539

    
540
        @Override
541
        public String toString(){
542
            if(workingSetRepresentation != null){
543
                return workingSetRepresentation;
544
            } else {
545
                return super.toString();
546
            }
547
        }
548

    
549
    }
550

    
551
}
(4-4/5)