fix TypeDesignationSetManager, use values of map of typeDesignations instead of map...
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / name / TypeDesignationSetManager.java
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.api.service.name;
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 import java.util.UUID;
23
24 import org.apache.commons.lang3.StringUtils;
25 import org.hibernate.search.hcore.util.impl.HibernateHelper;
26
27 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeCacheStrategy;
28 import eu.etaxonomy.cdm.api.service.exception.RegistrationValidationException;
29 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
30 import eu.etaxonomy.cdm.model.common.CdmBase;
31 import eu.etaxonomy.cdm.model.common.IdentifiableSource;
32 import eu.etaxonomy.cdm.model.common.TermVocabulary;
33 import eu.etaxonomy.cdm.model.common.VersionableEntity;
34 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
35 import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
36 import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
37 import eu.etaxonomy.cdm.model.name.TaxonName;
38 import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
39 import eu.etaxonomy.cdm.model.name.TypeDesignationStatusBase;
40 import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
41 import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
42 import eu.etaxonomy.cdm.model.occurrence.MediaSpecimen;
43 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
44 import eu.etaxonomy.cdm.model.reference.Reference;
45 import eu.etaxonomy.cdm.ref.EntityReference;
46 import eu.etaxonomy.cdm.ref.TypedEntityReference;
47 import eu.etaxonomy.cdm.strategy.cache.TagEnum;
48 import eu.etaxonomy.cdm.strategy.cache.TaggedText;
49 import eu.etaxonomy.cdm.strategy.cache.TaggedTextBuilder;
50 import eu.etaxonomy.cdm.strategy.cache.reference.DefaultReferenceCacheStrategy;
51 /**
52 * Manages a collection of {@link TypeDesignationBase TypeDesignations} for the same typified name.
53 *
54 * Type designations are ordered by the base type which is a {@link TaxonName} for {@link NameTypeDesignation NameTypeDesignations} or
55 * in case of {@link SpecimenTypeDesignation SpecimenTypeDesignations} the associate {@link FieldUnit} or the {@link DerivedUnit}
56 * if the former is missing. The type designations per base type are furthermore ordered by the {@link TypeDesignationStatusBase}.
57 *
58 * The TypeDesignationSetManager also provides string representations of the whole ordered set of all
59 * {@link TypeDesignationBase TypeDesignations} and of the TypeDesignationWorkingSets:
60 * <ul>
61 * <li>{@link #print()}
62 * <li>{@link #getOrderdTypeDesignationWorkingSets()} ... {@link TypeDesignationWorkingSet#getRepresentation()}
63 * </ul>
64 * Prior using the representations you need to trigger their generation by calling {@link #buildString()}
65 *
66 * @author a.kohlbecker
67 * @since Mar 10, 2017
68 *
69 */
70 public class TypeDesignationSetManager {
71
72 enum NameTypeBaseEntityType{
73
74 NAME_TYPE_DESIGNATION,
75 TYPE_NAME;
76
77 }
78
79 private static final String TYPE_STATUS_SEPARATOR = "; ";
80
81 private static final String TYPE_SEPARATOR = "; ";
82
83 private static final String TYPE_DESIGNATION_SEPARATOR = ", ";
84 private static final String TYPE_STATUS_SEPARATOR_WITHCITATION = ": ";
85 private static final String TYPE_STATUS_PARENTHESIS_LEFT = " (";
86 private static final String TYPE_STATUS_PARENTHESIS_RIGHT = ")";
87 private static final String REFERENCE_PARENTHESIS_RIGHT = "]";
88 private static final String REFERENCE_PARENTHESIS_LEFT = " [";
89 private static final String REFERENCE_FIDE = "fide ";
90 private Map<UUID,TypeDesignationBase> typeDesignations;
91
92 private NameTypeBaseEntityType nameTypeBaseEntityType = NameTypeBaseEntityType.NAME_TYPE_DESIGNATION;
93
94 /**
95 * Groups the EntityReferences for each of the TypeDesignations by the according TypeDesignationStatus.
96 * The TypeDesignationStatusBase keys are already ordered by the term order defined in the vocabulary.
97 */
98 private LinkedHashMap<TypedEntityReference, TypeDesignationWorkingSet> orderedByTypesByBaseEntity;
99
100 private EntityReference typifiedNameRef;
101
102 private TaxonName typifiedName;
103
104 private String finalString = null;
105
106 final NullTypeDesignationStatus NULL_STATUS = new NullTypeDesignationStatus();
107
108 private List<String> problems = new ArrayList<>();
109
110 private boolean printCitation = false;
111 private boolean useShortCitation = false;
112
113 private List<TaggedText> taggedText;
114
115 /**
116 * @param containgEntity
117 * @param taxonName
118 * @throws RegistrationValidationException
119 *
120 */
121 public TypeDesignationSetManager(Collection<TypeDesignationBase> typeDesignations) throws RegistrationValidationException {
122 if (this.typeDesignations == null){
123 this.typeDesignations = new HashMap();
124 }
125 for (TypeDesignationBase typeDes:typeDesignations){
126 this.typeDesignations.put(typeDes.getUuid(), typeDes);
127 }
128 findTypifiedName();
129 mapAndSort();
130 }
131
132 /**
133 * @param containgEntity
134 * @param taxonName
135 * @throws RegistrationValidationException
136 *
137 */
138 public TypeDesignationSetManager(HomotypicalGroup group) throws RegistrationValidationException {
139 if (this.typeDesignations == null){
140 this.typeDesignations = new HashMap();
141 }
142 for (TypeDesignationBase typeDes:group.getTypeDesignations()){
143 this.typeDesignations.put(typeDes.getUuid(), typeDes);
144 }
145 //findTypifiedName();
146 mapAndSort();
147 }
148
149 /**
150 * @param typifiedName2
151 */
152 public TypeDesignationSetManager(TaxonName typifiedName) {
153 this.typeDesignations = new HashMap<>();
154 this.typifiedNameRef = new EntityReference(typifiedName.getUuid(), typifiedName.getTitleCache());
155 }
156
157 /**
158 * Add one or more TypeDesignations to the manager. This causes re-grouping and re-ordering
159 * of all managed TypeDesignations.
160 *
161 * @param containgEntity
162 * @param typeDesignations
163 */
164 public void addTypeDesigations(CdmBase containgEntity, TypeDesignationBase ... typeDesignations){
165 for (TypeDesignationBase typeDes: typeDesignations){
166 this.typeDesignations.put(typeDes.getUuid(), typeDes);
167 }
168 mapAndSort();
169 }
170
171 /**
172 * Groups and orders all managed TypeDesignations.
173 *
174 * @param containgEntity
175 */
176 protected void mapAndSort() {
177 finalString = null;
178 Map<TypedEntityReference, TypeDesignationWorkingSet> byBaseEntityByTypeStatus = new HashMap<>();
179
180 this.typeDesignations.values().forEach(td -> mapTypeDesignation(byBaseEntityByTypeStatus, td));
181 orderedByTypesByBaseEntity = orderByTypeByBaseEntity(byBaseEntityByTypeStatus);
182 }
183
184
185 /**
186 * @param byBaseEntityByTypeStatus
187 * @param td
188 */
189 private void mapTypeDesignation(Map<TypedEntityReference, TypeDesignationWorkingSet> byBaseEntityByTypeStatus,
190 TypeDesignationBase<?> td){
191
192 TypeDesignationStatusBase<?> status = td.getTypeStatus();
193
194 try {
195 final VersionableEntity baseEntity = baseEntity(td);
196 final TypedEntityReference<VersionableEntity> baseEntityReference = makeEntityReference(baseEntity);
197
198 TypedEntityReference typeDesignationEntityReference = new TypedEntityReference(
199 HibernateProxyHelper.deproxy(td).getClass(),
200 td.getUuid(),
201 stringify(td));
202
203 if(!byBaseEntityByTypeStatus.containsKey(baseEntityReference)){
204 byBaseEntityByTypeStatus.put(baseEntityReference, new TypeDesignationWorkingSet(baseEntity, baseEntityReference));
205 }
206 byBaseEntityByTypeStatus.get(baseEntityReference).insert(status, typeDesignationEntityReference);
207
208 } catch (DataIntegrityException e){
209 problems.add(e.getMessage());
210 }
211 }
212
213 /**
214 * @param td
215 * @return
216 * @throws DataIntegrityException
217 */
218 protected VersionableEntity baseEntity(TypeDesignationBase<?> td) throws DataIntegrityException {
219
220 VersionableEntity baseEntity = null;
221 if(td instanceof SpecimenTypeDesignation){
222 SpecimenTypeDesignation std = (SpecimenTypeDesignation) td;
223 FieldUnit fu = findFieldUnit(std);
224 if(fu != null){
225 baseEntity = fu;
226 } else if(((SpecimenTypeDesignation) td).getTypeSpecimen() != null){
227 baseEntity = ((SpecimenTypeDesignation) td).getTypeSpecimen();
228 }
229 } else if(td instanceof NameTypeDesignation){
230 if(nameTypeBaseEntityType == NameTypeBaseEntityType.NAME_TYPE_DESIGNATION){
231 baseEntity = td;
232 } else {
233 // only other option is TaxonName
234 baseEntity = ((NameTypeDesignation)td).getTypeName();
235 }
236 }
237 if(baseEntity == null) {
238 throw new DataIntegrityException("Incomplete TypeDesignation, no type missin in " + td.toString());
239 }
240 return baseEntity;
241 }
242
243 /**
244 * @param td
245 * @return
246 */
247 protected TypedEntityReference<VersionableEntity> makeEntityReference(VersionableEntity baseEntity) {
248
249 baseEntity = (VersionableEntity) HibernateHelper.unproxy(baseEntity);
250 String label = "";
251 if(baseEntity instanceof FieldUnit){
252 label = ((FieldUnit)baseEntity).getTitleCache();
253 }
254
255 TypedEntityReference<VersionableEntity> baseEntityReference = new TypedEntityReference(baseEntity.getClass(), baseEntity.getUuid(), label);
256
257 return baseEntityReference;
258 }
259
260
261 private LinkedHashMap<TypedEntityReference, TypeDesignationWorkingSet> orderByTypeByBaseEntity(
262 Map<TypedEntityReference, TypeDesignationWorkingSet> stringsByTypeByBaseEntity){
263
264 // order the FieldUnit TypeName keys
265 List<TypedEntityReference> baseEntityKeyList = new LinkedList<>(stringsByTypeByBaseEntity.keySet());
266 Collections.sort(baseEntityKeyList, new Comparator<TypedEntityReference>(){
267 /**
268 * Sorts the base entities (TypedEntityReference) in the following order:
269 *
270 * 1. FieldUnits
271 * 2. DerivedUnit (in case of missing FieldUnit we expect the base type to be DerivedUnit)
272 * 3. NameType
273 *
274 * {@inheritDoc}
275 */
276 @Override
277 public int compare(TypedEntityReference o1, TypedEntityReference o2) {
278
279 Class type1 = o1.getType();
280 Class type2 = o2.getType();
281
282 if(!type1.equals(type2)) {
283 if(type1.equals(FieldUnit.class) || type2.equals(FieldUnit.class)){
284 // FieldUnits first
285 return type1.equals(FieldUnit.class) ? -1 : 1;
286 } else {
287 // name types last (in case of missing FieldUnit we expect the base type to be DerivedUnit which comes into the middle)
288 return type2.equals(TaxonName.class) || type2.equals(NameTypeDesignation.class) ? -1 : 1;
289 }
290 } else {
291 return o1.getLabel().compareTo(o2.getLabel());
292 }
293 }});
294
295 // new LinkedHashMap for the ordered FieldUnitOrTypeName keys
296 LinkedHashMap<TypedEntityReference, TypeDesignationWorkingSet> stringsOrderedbyBaseEntityOrderdByType = new LinkedHashMap<>(stringsByTypeByBaseEntity.size());
297
298 for(TypedEntityReference baseEntityRef : baseEntityKeyList){
299
300 TypeDesignationWorkingSet typeDesignationWorkingSet = stringsByTypeByBaseEntity.get(baseEntityRef);
301 // order the TypeDesignationStatusBase keys
302 List<TypeDesignationStatusBase<?>> keyList = new LinkedList<>(typeDesignationWorkingSet.keySet());
303 Collections.sort(keyList, new TypeDesignationStatusComparator());
304 // new LinkedHashMap for the ordered TypeDesignationStatusBase keys
305 TypeDesignationWorkingSet orderedStringsByOrderedTypes = new TypeDesignationWorkingSet(
306 typeDesignationWorkingSet.getBaseEntity(),
307 baseEntityRef);
308 keyList.forEach(key -> orderedStringsByOrderedTypes.put(key, typeDesignationWorkingSet.get(key)));
309 stringsOrderedbyBaseEntityOrderdByType.put(baseEntityRef, orderedStringsByOrderedTypes);
310 }
311
312 return stringsOrderedbyBaseEntityOrderdByType;
313 }
314
315 /*
316 private LinkedHashMap<TypedEntityReference, LinkedHashMap<String, Collection<EntityReference>>> buildOrderedRepresentations(){
317
318 orderedStringsByOrderedTypes.keySet().forEach(
319 key -> orderedRepresentations.put(
320 getTypeDesignationStytusLabel(key),
321 orderedStringsByOrderedTypes.get(key))
322 );
323 return orderedRepresentations;
324 }
325 */
326
327 public void buildString(){
328
329 if(finalString == null){
330
331 TaggedTextBuilder finalBuilder = new TaggedTextBuilder();
332 finalString = "";
333
334 if(getTypifiedNameCache() != null){
335 finalString += getTypifiedNameCache() + " ";
336 finalBuilder.add(TagEnum.name, getTypifiedNameCache(), new TypedEntityReference<>(TaxonName.class, getTypifiedNameRef().getUuid()));
337 }
338
339 int typeCount = 0;
340 if(orderedByTypesByBaseEntity != null){
341 for(TypedEntityReference baseEntityRef : orderedByTypesByBaseEntity.keySet()) {
342
343 TaggedTextBuilder workingsetBuilder = new TaggedTextBuilder();
344 if(typeCount++ > 0){
345 workingsetBuilder.add(TagEnum.separator, TYPE_SEPARATOR);
346 }
347 boolean isNameTypeDesignation = false;
348 if(SpecimenOrObservationBase.class.isAssignableFrom(baseEntityRef.getType()) ){
349 workingsetBuilder.add(TagEnum.label, "Type:");
350 } else{
351 workingsetBuilder.add(TagEnum.label, "NameType:");
352 isNameTypeDesignation = true;
353 }
354 if(!baseEntityRef.getLabel().isEmpty()){
355 workingsetBuilder.add(TagEnum.specimenOrObservation, baseEntityRef.getLabel(), baseEntityRef);
356 }
357 TypeDesignationWorkingSet typeDesignationWorkingSet = orderedByTypesByBaseEntity.get(baseEntityRef);
358 int typeStatusCount = 0;
359 for(TypeDesignationStatusBase<?> typeStatus : typeDesignationWorkingSet.keySet()) {
360 if(typeStatusCount++ > 0){
361 workingsetBuilder.add(TagEnum.separator, TYPE_STATUS_SEPARATOR);
362
363 }
364 boolean isPlural = typeDesignationWorkingSet.get(typeStatus).size() > 1;
365 if(!typeStatus.equals(NULL_STATUS)) {
366
367 workingsetBuilder.add(TagEnum.label, typeStatus.getLabel() + (isPlural ? "s:" : ","));
368 }
369
370
371 int typeDesignationCount = 0;
372
373
374 for(TypedEntityReference typeDesignationEntityReference : typeDesignationWorkingSet.get(typeStatus)) {
375
376 if(typeDesignationCount++ > 0){
377 workingsetBuilder.add(TagEnum.separator, TYPE_DESIGNATION_SEPARATOR);
378 }
379
380 workingsetBuilder.add(TagEnum.typeDesignation, typeDesignationEntityReference.getLabel(), typeDesignationEntityReference);
381
382 }
383
384 }
385 typeDesignationWorkingSet.setRepresentation(workingsetBuilder.toString());
386 finalString += typeDesignationWorkingSet.getRepresentation();
387 finalBuilder.addAll(workingsetBuilder);
388 }
389 }
390 finalString = finalString.trim();
391 taggedText = finalBuilder.getTaggedText();
392 }
393 }
394
395 public void buildStringWithCitation(){
396
397 if(finalString == null){
398
399 TaggedTextBuilder finalBuilder = new TaggedTextBuilder();
400 finalString = "";
401
402 if(getTypifiedNameCache() != null){
403 finalString += getTypifiedNameCache() + " ";
404 finalBuilder.add(TagEnum.name, getTypifiedNameCache(), new TypedEntityReference<>(TaxonName.class, getTypifiedNameRef().getUuid()));
405 }
406
407 int typeCount = 0;
408 if(orderedByTypesByBaseEntity != null){
409 for(TypedEntityReference baseEntityRef : orderedByTypesByBaseEntity.keySet()) {
410
411 TaggedTextBuilder workingsetBuilder = new TaggedTextBuilder();
412 if(typeCount++ > 0){
413 workingsetBuilder.add(TagEnum.separator, TYPE_SEPARATOR);
414 }
415 boolean isNameTypeDesignation = false;
416
417 if(!baseEntityRef.getLabel().isEmpty()){
418 workingsetBuilder.add(TagEnum.specimenOrObservation, baseEntityRef.getLabel(), baseEntityRef);
419 }
420 TypeDesignationWorkingSet typeDesignationWorkingSet = orderedByTypesByBaseEntity.get(baseEntityRef);
421 int typeStatusCount = 0;
422 for(TypeDesignationStatusBase<?> typeStatus : typeDesignationWorkingSet.keySet()) {
423 if(typeStatusCount++ > 0){
424 workingsetBuilder.add(TagEnum.separator, TYPE_STATUS_SEPARATOR);
425
426 }
427 boolean isPlural = typeDesignationWorkingSet.get(typeStatus).size() > 1;
428 if(!typeStatus.equals(NULL_STATUS)) {
429 workingsetBuilder.add(TagEnum.separator, TYPE_STATUS_PARENTHESIS_LEFT);
430 workingsetBuilder.add(TagEnum.label, typeStatus.getLabel() + (isPlural ? "s:" : ":"));
431 }
432 int typeDesignationCount = 0;
433
434
435 for(TypedEntityReference typeDesignationEntityReference : typeDesignationWorkingSet.get(typeStatus)) {
436
437 if(typeDesignationCount++ > 0){
438 workingsetBuilder.add(TagEnum.separator, TYPE_DESIGNATION_SEPARATOR);
439 }
440
441 workingsetBuilder.add(TagEnum.typeDesignation, typeDesignationEntityReference.getLabel(), typeDesignationEntityReference);
442
443 TypeDesignationBase typeDes = typeDesignations.get(typeDesignationEntityReference.getUuid());
444 if (typeDes.getCitation() != null){
445 // workingsetBuilder.add(TagEnum.separator, REFERENCE_PARENTHESIS_LEFT);
446 String shortCitation = ((DefaultReferenceCacheStrategy)typeDes.getCitation().getCacheStrategy()).createShortCitation(typeDes.getCitation());
447 workingsetBuilder.add(TagEnum.reference, shortCitation, typeDesignationEntityReference);
448 //workingsetBuilder.add(TagEnum.separator, REFERENCE_PARENTHESIS_RIGHT);
449 }
450
451 if ((!typeStatus.equals(NULL_STATUS)) &&(typeDesignationCount == typeDesignationWorkingSet.get(typeStatus).size())){
452 workingsetBuilder.add(TagEnum.separator, TYPE_STATUS_PARENTHESIS_RIGHT);
453 }
454 }
455
456 }
457 typeDesignationWorkingSet.setRepresentation(workingsetBuilder.toString());
458 finalString += typeDesignationWorkingSet.getRepresentation();
459 finalBuilder.addAll(workingsetBuilder);
460 }
461 }
462 finalString = finalString.trim();
463 taggedText = finalBuilder.getTaggedText();
464 }
465 }
466
467
468 /**
469 * FIXME use the validation framework validators to store the validation problems!!!
470 *
471 * @return
472 * @throws RegistrationValidationException
473 */
474 private void findTypifiedName() throws RegistrationValidationException {
475
476 List<String> problems = new ArrayList<>();
477
478 TaxonName typifiedName = null;
479
480 for(TypeDesignationBase<?> typeDesignation : typeDesignations.values()){
481 typeDesignation.getTypifiedNames();
482 if(typeDesignation.getTypifiedNames().isEmpty()){
483
484 //TODO instead throw RegistrationValidationException()
485 problems.add("Missing typifiedName in " + typeDesignation.toString());
486 continue;
487 }
488 if(typeDesignation.getTypifiedNames().size() > 1){
489 //TODO instead throw RegistrationValidationException()
490 problems.add("Multiple typifiedName in " + typeDesignation.toString());
491 continue;
492 }
493 if(typifiedName == null){
494 // remember
495 typifiedName = typeDesignation.getTypifiedNames().iterator().next();
496 } else {
497 // compare
498 TaxonName otherTypifiedName = typeDesignation.getTypifiedNames().iterator().next();
499 if(!typifiedName.getUuid().equals(otherTypifiedName.getUuid())){
500 //TODO instead throw RegistrationValidationException()
501 problems.add("Multiple typifiedName in " + typeDesignation.toString());
502 }
503 }
504
505 }
506 if(!problems.isEmpty()){
507 // FIXME use the validation framework
508 throw new RegistrationValidationException("Inconsistent type designations", problems);
509 }
510
511 if(typifiedName != null){
512 // ON SUCCESS -------------------
513 this.typifiedName = typifiedName;
514 this.typifiedNameRef = new EntityReference(typifiedName.getUuid(), typifiedName.getTitleCache());
515
516 }
517 }
518
519
520 /**
521 * @return the title cache of the typifying name or <code>null</code>
522 */
523 public String getTypifiedNameCache() {
524 if(typifiedNameRef != null){
525 return typifiedNameRef.getLabel();
526 }
527 return null;
528 }
529
530 /**
531 * @return the title cache of the typifying name or <code>null</code>
532 */
533 public EntityReference getTypifiedNameRef() {
534
535 return typifiedNameRef;
536 }
537
538 /**
539 * @return
540 */
541 public Collection<TypeDesignationBase> getTypeDesignations() {
542 return typeDesignations.values();
543 }
544
545 /**
546 * @param ref
547 * @return
548 */
549 public TypeDesignationBase findTypeDesignation(EntityReference typeDesignationRef) {
550 return this.typeDesignations.get(typeDesignationRef.getUuid());
551 }
552
553
554 public LinkedHashMap<TypedEntityReference, TypeDesignationWorkingSet> getOrderdTypeDesignationWorkingSets() {
555 return orderedByTypesByBaseEntity;
556 }
557
558 /**
559 * @param td
560 * @return
561 */
562 private String stringify(TypeDesignationBase td) {
563
564 if(td instanceof NameTypeDesignation){
565 return stringify((NameTypeDesignation)td);
566 } else {
567 return stringify((SpecimenTypeDesignation)td, false);
568 }
569 }
570
571
572 /**
573 * @param td
574 * @return
575 */
576 protected String stringify(NameTypeDesignation td) {
577
578 StringBuffer sb = new StringBuffer();
579
580 if(td.getTypeName() != null){
581 sb.append(td.getTypeName().getTitleCache());
582 }
583 if(td.getCitation() != null){
584 sb.append(" ").append(td.getCitation().getTitleCache());
585 if(td.getCitationMicroReference() != null){
586 sb.append(":").append(td.getCitationMicroReference());
587 }
588 }
589 if(td.isNotDesignated()){
590 sb.append(" not designated");
591 }
592 if(td.isRejectedType()){
593 sb.append(" rejected");
594 }
595 if(td.isConservedType()){
596 sb.append(" conserved");
597 }
598 return sb.toString();
599 }
600
601 /**
602 * @param td
603 * @return
604 */
605 private String stringify(SpecimenTypeDesignation td, boolean useFullTitleCache) {
606 String result = "";
607
608 if(useFullTitleCache){
609 if(td.getTypeSpecimen() != null){
610 String nameTitleCache = td.getTypeSpecimen().getTitleCache();
611 if(getTypifiedNameCache() != null){
612 nameTitleCache = nameTitleCache.replace(getTypifiedNameCache(), "");
613 }
614 result += nameTitleCache;
615 }
616 } else {
617 if(td.getTypeSpecimen() != null){
618 DerivedUnit du = td.getTypeSpecimen();
619 if(du.isProtectedTitleCache()){
620 result += du.getTitleCache();
621 } else {
622 du = HibernateProxyHelper.deproxy(du);
623 boolean isMediaSpecimen = du instanceof MediaSpecimen;
624 String typeSpecimenTitle = "";
625 if(isMediaSpecimen && HibernateProxyHelper.deproxyOrNull(du.getCollection()) == null) {
626 // special case of an published image which is not covered by the DerivedUnitFacadeCacheStrategy
627 MediaSpecimen msp = (MediaSpecimen)du;
628 if(msp.getMediaSpecimen() != null){
629 for(IdentifiableSource source : msp.getMediaSpecimen().getSources()){
630 String refDetailStr = source.getCitationMicroReference();
631 String referenceStr = source.getCitation() == null? "": source.getCitation().getTitleCache();
632 if(StringUtils.isNotBlank(source.getCitationMicroReference())){
633 typeSpecimenTitle += refDetailStr;
634 }
635 if(!typeSpecimenTitle.isEmpty() && !referenceStr.isEmpty()){
636 typeSpecimenTitle += " in ";
637 }
638 typeSpecimenTitle += referenceStr + " ";
639 }
640 }
641 } else {
642 DerivedUnitFacadeCacheStrategy cacheStrategy = new DerivedUnitFacadeCacheStrategy();
643 typeSpecimenTitle += cacheStrategy.getTitleCache(du, true);
644
645 }
646
647 result += (isMediaSpecimen ? "[icon] " : "") + typeSpecimenTitle.trim();
648 }
649 }
650 }
651
652 if(isPrintCitation() && td.getCitation() != null){
653 Reference citation = HibernateProxyHelper.deproxy(td.getCitation(), Reference.class);
654 if(citation.getAbbrevTitle() != null){
655
656 result += " " + citation.getAbbrevTitle();
657 } else {
658 result += " " + citation.getTitleCache();
659 }
660 if(td.getCitationMicroReference() != null){
661 result += " :" + td.getCitationMicroReference();
662 }
663 }
664 if(td.isNotDesignated()){
665 result += " not designated";
666 }
667
668 return result;
669 }
670
671 /**
672 * @param td
673 * @return
674 * @deprecated
675 */
676 @Deprecated
677 private FieldUnit findFieldUnit(SpecimenTypeDesignation td) {
678
679 DerivedUnit du = td.getTypeSpecimen();
680 return findFieldUnit(du);
681 }
682
683 private FieldUnit findFieldUnit(DerivedUnit du) {
684
685 if(du == null || du.getOriginals() == null){
686 return null;
687 }
688 @SuppressWarnings("rawtypes")
689 Set<SpecimenOrObservationBase> originals = du.getDerivedFrom().getOriginals();
690 @SuppressWarnings("rawtypes")
691 Optional<SpecimenOrObservationBase> fieldUnit = originals.stream()
692 .filter(original -> original instanceof FieldUnit).findFirst();
693 if (fieldUnit.isPresent()) {
694 return (FieldUnit) fieldUnit.get();
695 } else {
696 for (@SuppressWarnings("rawtypes")
697 SpecimenOrObservationBase sob : originals) {
698 if (sob instanceof DerivedUnit) {
699 FieldUnit fu = findFieldUnit((DerivedUnit) sob);
700 if (fu != null) {
701 return fu;
702 }
703 }
704 }
705 }
706
707 return null;
708 }
709
710 public String print() {
711 buildString();
712 return finalString;
713 }
714
715 public List<TaggedText> toTaggedText() {
716 buildString();
717 return taggedText;
718 }
719
720 public List<TaggedText> toTaggedTextWithCitation() {
721 buildStringWithCitation();
722 return taggedText;
723 }
724
725
726 /**
727 * @return the printCitation
728 */
729 public boolean isPrintCitation() {
730 return printCitation;
731 }
732
733 /**
734 * @param printCitation the printCitation to set
735 */
736 public void setPrintCitation(boolean printCitation) {
737 this.printCitation = printCitation;
738 }
739
740 /**
741 * @return the typifiedName
742 */
743 public TaxonName getTypifiedName() {
744 return typifiedName;
745 }
746
747 public void setNameTypeBaseEntityType(NameTypeBaseEntityType nameTypeBaseEntityType){
748 this.nameTypeBaseEntityType = nameTypeBaseEntityType;
749 }
750
751 public NameTypeBaseEntityType getNameTypeBaseEntityType(){
752 return nameTypeBaseEntityType;
753 }
754
755 public boolean isUseShortCitation() {
756 return useShortCitation;
757 }
758
759 public void setUseShortCitation(boolean useShortCitation) {
760 this.useShortCitation = useShortCitation;
761 }
762
763 /**
764 * TypeDesignations which refer to the same FieldUnit (SpecimenTypeDesignation) or TaxonName
765 * (NameTypeDesignation) form a working set. The <code>TypeDesignationWorkingSet</code> internally
766 * works with EnityReferences to the actual TypeDesignations.
767 *
768 * The EntityReferences for TypeDesignations are grouped by the according TypeDesignationStatus.
769 * The TypeDesignationStatusBase keys can be ordered by the term order defined in the vocabulary.
770 *
771 * A workingset can be referenced by the <code>baseEntityReference</code>.
772 */
773 public class TypeDesignationWorkingSet extends LinkedHashMap<TypeDesignationStatusBase<?>, Collection<TypedEntityReference>> {
774
775 private static final long serialVersionUID = -1329007606500890729L;
776
777 String workingSetRepresentation = null;
778
779 TypedEntityReference<VersionableEntity> baseEntityReference;
780
781 VersionableEntity baseEntity;
782
783 List<DerivedUnit> derivedUnits = null;
784
785 /**
786 * @param baseEntityReference
787 */
788 public TypeDesignationWorkingSet(VersionableEntity baseEntity, TypedEntityReference<VersionableEntity> baseEntityReference) {
789 this.baseEntity = baseEntity;
790 this.baseEntityReference = baseEntityReference;
791 }
792
793 /**
794 * @return
795 */
796 public VersionableEntity getBaseEntity() {
797 return baseEntity;
798 }
799
800 public List<TypedEntityReference> getTypeDesignations() {
801 List<TypedEntityReference> typeDesignations = new ArrayList<>();
802 this.values().forEach(typeDesignationReferences -> typeDesignationReferences.forEach(td -> typeDesignations.add(td)));
803 return typeDesignations;
804 }
805
806 /**
807 * @param status
808 * @param typeDesignationEntityReference
809 */
810 public void insert(TypeDesignationStatusBase<?> status, TypedEntityReference typeDesignationEntityReference) {
811
812 if(status == null){
813 status = NULL_STATUS;
814 }
815 if(!containsKey(status)){
816 put(status, new ArrayList<>());
817 }
818 get(status).add(typeDesignationEntityReference);
819 }
820
821
822 public String getRepresentation() {
823 return workingSetRepresentation;
824 }
825
826 public void setRepresentation(String representation){
827 this.workingSetRepresentation = representation;
828 }
829
830 /**
831 * A reference to the entity which is the common base entity for all TypeDesignations in this workingset.
832 * For a {@link SpecimenTypeDesignation} this is usually the {@link FieldUnit} if it is present. Otherwise it can also be
833 * a {@link DerivedUnit} or something else depending on the specific use case.
834 *
835 * @return the baseEntityReference
836 */
837 public TypedEntityReference<VersionableEntity> getBaseEntityReference() {
838 return baseEntityReference;
839 }
840
841 @Override
842 public String toString(){
843 if(workingSetRepresentation != null){
844 return workingSetRepresentation;
845 } else {
846 return super.toString();
847 }
848 }
849
850 /**
851 * @return
852 */
853 public boolean isSpecimenTypeDesigationWorkingSet() {
854 return SpecimenOrObservationBase.class.isAssignableFrom(baseEntityReference.getType());
855 }
856
857 public TypeDesignationWorkingSetType getWorkingsetType() {
858 return isSpecimenTypeDesigationWorkingSet() ? TypeDesignationWorkingSetType.SPECIMEN_TYPE_DESIGNATION_WORKINGSET : TypeDesignationWorkingSetType.NAME_TYPE_DESIGNATION_WORKINGSET;
859 }
860
861 }
862
863 public enum TypeDesignationWorkingSetType {
864 SPECIMEN_TYPE_DESIGNATION_WORKINGSET,
865 NAME_TYPE_DESIGNATION_WORKINGSET,
866 }
867
868 @SuppressWarnings({ "deprecation", "serial" })
869 class NullTypeDesignationStatus extends TypeDesignationStatusBase<NullTypeDesignationStatus>{
870
871 /**
872 * {@inheritDoc}
873 */
874 @Override
875 public void resetTerms() {
876 // empty
877
878 }
879
880 /**
881 * {@inheritDoc}
882 */
883 @Override
884 protected void setDefaultTerms(TermVocabulary<NullTypeDesignationStatus> termVocabulary) {
885 // empty
886 }
887
888 }
889
890 class DataIntegrityException extends Exception {
891
892 private static final long serialVersionUID = 1464726696296824905L;
893
894 /**
895 * @param string
896 */
897 public DataIntegrityException(String string) {
898 super(string);
899 }
900 }
901 }