2 * Copyright (C) 2017 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
9 package eu
.etaxonomy
.cdm
.api
.service
.name
;
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
;
20 import java
.util
.Optional
;
22 import java
.util
.UUID
;
24 import org
.apache
.commons
.lang3
.StringUtils
;
25 import org
.hibernate
.search
.hcore
.util
.impl
.HibernateHelper
;
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
;
52 * Manages a collection of {@link TypeDesignationBase TypeDesignations} for the same typified name.
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}.
58 * The TypeDesignationSetManager also provides string representations of the whole ordered set of all
59 * {@link TypeDesignationBase TypeDesignations} and of the TypeDesignationWorkingSets:
61 * <li>{@link #print()}
62 * <li>{@link #getOrderdTypeDesignationWorkingSets()} ... {@link TypeDesignationWorkingSet#getRepresentation()}
64 * Prior using the representations you need to trigger their generation by calling {@link #buildString()}
66 * @author a.kohlbecker
70 public class TypeDesignationSetManager
{
72 enum NameTypeBaseEntityType
{
74 NAME_TYPE_DESIGNATION
,
79 private static final String TYPE_STATUS_SEPARATOR
= "; ";
81 private static final String TYPE_SEPARATOR
= "; ";
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
;
92 private NameTypeBaseEntityType nameTypeBaseEntityType
= NameTypeBaseEntityType
.NAME_TYPE_DESIGNATION
;
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.
98 private LinkedHashMap
<TypedEntityReference
, TypeDesignationWorkingSet
> orderedByTypesByBaseEntity
;
100 private EntityReference typifiedNameRef
;
102 private TaxonName typifiedName
;
104 private String finalString
= null;
106 final NullTypeDesignationStatus NULL_STATUS
= new NullTypeDesignationStatus();
108 private List
<String
> problems
= new ArrayList
<>();
110 private boolean printCitation
= false;
111 private boolean useShortCitation
= false;
113 private List
<TaggedText
> taggedText
;
116 * @param containgEntity
118 * @throws RegistrationValidationException
121 public TypeDesignationSetManager(Collection
<TypeDesignationBase
> typeDesignations
) throws RegistrationValidationException
{
122 if (this.typeDesignations
== null){
123 this.typeDesignations
= new HashMap();
125 for (TypeDesignationBase typeDes
:typeDesignations
){
126 this.typeDesignations
.put(typeDes
.getUuid(), typeDes
);
133 * @param containgEntity
135 * @throws RegistrationValidationException
138 public TypeDesignationSetManager(HomotypicalGroup group
) throws RegistrationValidationException
{
139 if (this.typeDesignations
== null){
140 this.typeDesignations
= new HashMap();
142 for (TypeDesignationBase typeDes
:group
.getTypeDesignations()){
143 this.typeDesignations
.put(typeDes
.getUuid(), typeDes
);
145 //findTypifiedName();
150 * @param typifiedName2
152 public TypeDesignationSetManager(TaxonName typifiedName
) {
153 this.typeDesignations
= new HashMap
<>();
154 this.typifiedNameRef
= new EntityReference(typifiedName
.getUuid(), typifiedName
.getTitleCache());
158 * Add one or more TypeDesignations to the manager. This causes re-grouping and re-ordering
159 * of all managed TypeDesignations.
161 * @param containgEntity
162 * @param typeDesignations
164 public void addTypeDesigations(CdmBase containgEntity
, TypeDesignationBase
... typeDesignations
){
165 for (TypeDesignationBase typeDes
: typeDesignations
){
166 this.typeDesignations
.put(typeDes
.getUuid(), typeDes
);
172 * Groups and orders all managed TypeDesignations.
174 * @param containgEntity
176 protected void mapAndSort() {
178 Map
<TypedEntityReference
, TypeDesignationWorkingSet
> byBaseEntityByTypeStatus
= new HashMap
<>();
180 this.typeDesignations
.values().forEach(td
-> mapTypeDesignation(byBaseEntityByTypeStatus
, td
));
181 orderedByTypesByBaseEntity
= orderByTypeByBaseEntity(byBaseEntityByTypeStatus
);
186 * @param byBaseEntityByTypeStatus
189 private void mapTypeDesignation(Map
<TypedEntityReference
, TypeDesignationWorkingSet
> byBaseEntityByTypeStatus
,
190 TypeDesignationBase
<?
> td
){
192 TypeDesignationStatusBase
<?
> status
= td
.getTypeStatus();
195 final VersionableEntity baseEntity
= baseEntity(td
);
196 final TypedEntityReference
<VersionableEntity
> baseEntityReference
= makeEntityReference(baseEntity
);
198 TypedEntityReference typeDesignationEntityReference
= new TypedEntityReference(
199 HibernateProxyHelper
.deproxy(td
).getClass(),
203 if(!byBaseEntityByTypeStatus
.containsKey(baseEntityReference
)){
204 byBaseEntityByTypeStatus
.put(baseEntityReference
, new TypeDesignationWorkingSet(baseEntity
, baseEntityReference
));
206 byBaseEntityByTypeStatus
.get(baseEntityReference
).insert(status
, typeDesignationEntityReference
);
208 } catch (DataIntegrityException e
){
209 problems
.add(e
.getMessage());
216 * @throws DataIntegrityException
218 protected VersionableEntity
baseEntity(TypeDesignationBase
<?
> td
) throws DataIntegrityException
{
220 VersionableEntity baseEntity
= null;
221 if(td
instanceof SpecimenTypeDesignation
){
222 SpecimenTypeDesignation std
= (SpecimenTypeDesignation
) td
;
223 FieldUnit fu
= findFieldUnit(std
);
226 } else if(((SpecimenTypeDesignation
) td
).getTypeSpecimen() != null){
227 baseEntity
= ((SpecimenTypeDesignation
) td
).getTypeSpecimen();
229 } else if(td
instanceof NameTypeDesignation
){
230 if(nameTypeBaseEntityType
== NameTypeBaseEntityType
.NAME_TYPE_DESIGNATION
){
233 // only other option is TaxonName
234 baseEntity
= ((NameTypeDesignation
)td
).getTypeName();
237 if(baseEntity
== null) {
238 throw new DataIntegrityException("Incomplete TypeDesignation, no type missin in " + td
.toString());
247 protected TypedEntityReference
<VersionableEntity
> makeEntityReference(VersionableEntity baseEntity
) {
249 baseEntity
= (VersionableEntity
) HibernateHelper
.unproxy(baseEntity
);
251 if(baseEntity
instanceof FieldUnit
){
252 label
= ((FieldUnit
)baseEntity
).getTitleCache();
255 TypedEntityReference
<VersionableEntity
> baseEntityReference
= new TypedEntityReference(baseEntity
.getClass(), baseEntity
.getUuid(), label
);
257 return baseEntityReference
;
261 private LinkedHashMap
<TypedEntityReference
, TypeDesignationWorkingSet
> orderByTypeByBaseEntity(
262 Map
<TypedEntityReference
, TypeDesignationWorkingSet
> stringsByTypeByBaseEntity
){
264 // order the FieldUnit TypeName keys
265 List
<TypedEntityReference
> baseEntityKeyList
= new LinkedList
<>(stringsByTypeByBaseEntity
.keySet());
266 Collections
.sort(baseEntityKeyList
, new Comparator
<TypedEntityReference
>(){
268 * Sorts the base entities (TypedEntityReference) in the following order:
271 * 2. DerivedUnit (in case of missing FieldUnit we expect the base type to be DerivedUnit)
277 public int compare(TypedEntityReference o1
, TypedEntityReference o2
) {
279 Class type1
= o1
.getType();
280 Class type2
= o2
.getType();
282 if(!type1
.equals(type2
)) {
283 if(type1
.equals(FieldUnit
.class) || type2
.equals(FieldUnit
.class)){
285 return type1
.equals(FieldUnit
.class) ?
-1 : 1;
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;
291 return o1
.getLabel().compareTo(o2
.getLabel());
295 // new LinkedHashMap for the ordered FieldUnitOrTypeName keys
296 LinkedHashMap
<TypedEntityReference
, TypeDesignationWorkingSet
> stringsOrderedbyBaseEntityOrderdByType
= new LinkedHashMap
<>(stringsByTypeByBaseEntity
.size());
298 for(TypedEntityReference baseEntityRef
: baseEntityKeyList
){
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(),
308 keyList
.forEach(key
-> orderedStringsByOrderedTypes
.put(key
, typeDesignationWorkingSet
.get(key
)));
309 stringsOrderedbyBaseEntityOrderdByType
.put(baseEntityRef
, orderedStringsByOrderedTypes
);
312 return stringsOrderedbyBaseEntityOrderdByType
;
316 private LinkedHashMap<TypedEntityReference, LinkedHashMap<String, Collection<EntityReference>>> buildOrderedRepresentations(){
318 orderedStringsByOrderedTypes.keySet().forEach(
319 key -> orderedRepresentations.put(
320 getTypeDesignationStytusLabel(key),
321 orderedStringsByOrderedTypes.get(key))
323 return orderedRepresentations;
327 public void buildString(){
329 if(finalString
== null){
331 TaggedTextBuilder finalBuilder
= new TaggedTextBuilder();
334 if(getTypifiedNameCache() != null){
335 finalString
+= getTypifiedNameCache() + " ";
336 finalBuilder
.add(TagEnum
.name
, getTypifiedNameCache(), new TypedEntityReference
<>(TaxonName
.class, getTypifiedNameRef().getUuid()));
340 if(orderedByTypesByBaseEntity
!= null){
341 for(TypedEntityReference baseEntityRef
: orderedByTypesByBaseEntity
.keySet()) {
343 TaggedTextBuilder workingsetBuilder
= new TaggedTextBuilder();
345 workingsetBuilder
.add(TagEnum
.separator
, TYPE_SEPARATOR
);
347 boolean isNameTypeDesignation
= false;
348 if(SpecimenOrObservationBase
.class.isAssignableFrom(baseEntityRef
.getType()) ){
349 workingsetBuilder
.add(TagEnum
.label
, "Type:");
351 workingsetBuilder
.add(TagEnum
.label
, "NameType:");
352 isNameTypeDesignation
= true;
354 if(!baseEntityRef
.getLabel().isEmpty()){
355 workingsetBuilder
.add(TagEnum
.specimenOrObservation
, baseEntityRef
.getLabel(), baseEntityRef
);
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
);
364 boolean isPlural
= typeDesignationWorkingSet
.get(typeStatus
).size() > 1;
365 if(!typeStatus
.equals(NULL_STATUS
)) {
367 workingsetBuilder
.add(TagEnum
.label
, typeStatus
.getLabel() + (isPlural ?
"s:" : ","));
371 int typeDesignationCount
= 0;
374 for(TypedEntityReference typeDesignationEntityReference
: typeDesignationWorkingSet
.get(typeStatus
)) {
376 if(typeDesignationCount
++ > 0){
377 workingsetBuilder
.add(TagEnum
.separator
, TYPE_DESIGNATION_SEPARATOR
);
380 workingsetBuilder
.add(TagEnum
.typeDesignation
, typeDesignationEntityReference
.getLabel(), typeDesignationEntityReference
);
385 typeDesignationWorkingSet
.setRepresentation(workingsetBuilder
.toString());
386 finalString
+= typeDesignationWorkingSet
.getRepresentation();
387 finalBuilder
.addAll(workingsetBuilder
);
390 finalString
= finalString
.trim();
391 taggedText
= finalBuilder
.getTaggedText();
395 public void buildStringWithCitation(){
397 if(finalString
== null){
399 TaggedTextBuilder finalBuilder
= new TaggedTextBuilder();
402 if(getTypifiedNameCache() != null){
403 finalString
+= getTypifiedNameCache() + " ";
404 finalBuilder
.add(TagEnum
.name
, getTypifiedNameCache(), new TypedEntityReference
<>(TaxonName
.class, getTypifiedNameRef().getUuid()));
408 if(orderedByTypesByBaseEntity
!= null){
409 for(TypedEntityReference baseEntityRef
: orderedByTypesByBaseEntity
.keySet()) {
411 TaggedTextBuilder workingsetBuilder
= new TaggedTextBuilder();
413 workingsetBuilder
.add(TagEnum
.separator
, TYPE_SEPARATOR
);
415 boolean isNameTypeDesignation
= false;
417 if(!baseEntityRef
.getLabel().isEmpty()){
418 workingsetBuilder
.add(TagEnum
.specimenOrObservation
, baseEntityRef
.getLabel(), baseEntityRef
);
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
);
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:" : ":"));
432 int typeDesignationCount
= 0;
435 for(TypedEntityReference typeDesignationEntityReference
: typeDesignationWorkingSet
.get(typeStatus
)) {
437 if(typeDesignationCount
++ > 0){
438 workingsetBuilder
.add(TagEnum
.separator
, TYPE_DESIGNATION_SEPARATOR
);
441 workingsetBuilder
.add(TagEnum
.typeDesignation
, typeDesignationEntityReference
.getLabel(), typeDesignationEntityReference
);
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);
451 if ((!typeStatus
.equals(NULL_STATUS
)) &&(typeDesignationCount
== typeDesignationWorkingSet
.get(typeStatus
).size())){
452 workingsetBuilder
.add(TagEnum
.separator
, TYPE_STATUS_PARENTHESIS_RIGHT
);
457 typeDesignationWorkingSet
.setRepresentation(workingsetBuilder
.toString());
458 finalString
+= typeDesignationWorkingSet
.getRepresentation();
459 finalBuilder
.addAll(workingsetBuilder
);
462 finalString
= finalString
.trim();
463 taggedText
= finalBuilder
.getTaggedText();
469 * FIXME use the validation framework validators to store the validation problems!!!
472 * @throws RegistrationValidationException
474 private void findTypifiedName() throws RegistrationValidationException
{
476 List
<String
> problems
= new ArrayList
<>();
478 TaxonName typifiedName
= null;
480 for(TypeDesignationBase
<?
> typeDesignation
: typeDesignations
.values()){
481 typeDesignation
.getTypifiedNames();
482 if(typeDesignation
.getTypifiedNames().isEmpty()){
484 //TODO instead throw RegistrationValidationException()
485 problems
.add("Missing typifiedName in " + typeDesignation
.toString());
488 if(typeDesignation
.getTypifiedNames().size() > 1){
489 //TODO instead throw RegistrationValidationException()
490 problems
.add("Multiple typifiedName in " + typeDesignation
.toString());
493 if(typifiedName
== null){
495 typifiedName
= typeDesignation
.getTypifiedNames().iterator().next();
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());
506 if(!problems
.isEmpty()){
507 // FIXME use the validation framework
508 throw new RegistrationValidationException("Inconsistent type designations", problems
);
511 if(typifiedName
!= null){
512 // ON SUCCESS -------------------
513 this.typifiedName
= typifiedName
;
514 this.typifiedNameRef
= new EntityReference(typifiedName
.getUuid(), typifiedName
.getTitleCache());
521 * @return the title cache of the typifying name or <code>null</code>
523 public String
getTypifiedNameCache() {
524 if(typifiedNameRef
!= null){
525 return typifiedNameRef
.getLabel();
531 * @return the title cache of the typifying name or <code>null</code>
533 public EntityReference
getTypifiedNameRef() {
535 return typifiedNameRef
;
541 public Collection
<TypeDesignationBase
> getTypeDesignations() {
542 return typeDesignations
.values();
549 public TypeDesignationBase
findTypeDesignation(EntityReference typeDesignationRef
) {
550 return this.typeDesignations
.get(typeDesignationRef
.getUuid());
554 public LinkedHashMap
<TypedEntityReference
, TypeDesignationWorkingSet
> getOrderdTypeDesignationWorkingSets() {
555 return orderedByTypesByBaseEntity
;
562 private String
stringify(TypeDesignationBase td
) {
564 if(td
instanceof NameTypeDesignation
){
565 return stringify((NameTypeDesignation
)td
);
567 return stringify((SpecimenTypeDesignation
)td
, false);
576 protected String
stringify(NameTypeDesignation td
) {
578 StringBuffer sb
= new StringBuffer();
580 if(td
.getTypeName() != null){
581 sb
.append(td
.getTypeName().getTitleCache());
583 if(td
.getCitation() != null){
584 sb
.append(" ").append(td
.getCitation().getTitleCache());
585 if(td
.getCitationMicroReference() != null){
586 sb
.append(":").append(td
.getCitationMicroReference());
589 if(td
.isNotDesignated()){
590 sb
.append(" not designated");
592 if(td
.isRejectedType()){
593 sb
.append(" rejected");
595 if(td
.isConservedType()){
596 sb
.append(" conserved");
598 return sb
.toString();
605 private String
stringify(SpecimenTypeDesignation td
, boolean useFullTitleCache
) {
608 if(useFullTitleCache
){
609 if(td
.getTypeSpecimen() != null){
610 String nameTitleCache
= td
.getTypeSpecimen().getTitleCache();
611 if(getTypifiedNameCache() != null){
612 nameTitleCache
= nameTitleCache
.replace(getTypifiedNameCache(), "");
614 result
+= nameTitleCache
;
617 if(td
.getTypeSpecimen() != null){
618 DerivedUnit du
= td
.getTypeSpecimen();
619 if(du
.isProtectedTitleCache()){
620 result
+= du
.getTitleCache();
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
;
635 if(!typeSpecimenTitle
.isEmpty() && !referenceStr
.isEmpty()){
636 typeSpecimenTitle
+= " in ";
638 typeSpecimenTitle
+= referenceStr
+ " ";
642 DerivedUnitFacadeCacheStrategy cacheStrategy
= new DerivedUnitFacadeCacheStrategy();
643 typeSpecimenTitle
+= cacheStrategy
.getTitleCache(du
, true);
647 result
+= (isMediaSpecimen ?
"[icon] " : "") + typeSpecimenTitle
.trim();
652 if(isPrintCitation() && td
.getCitation() != null){
653 Reference citation
= HibernateProxyHelper
.deproxy(td
.getCitation(), Reference
.class);
654 if(citation
.getAbbrevTitle() != null){
656 result
+= " " + citation
.getAbbrevTitle();
658 result
+= " " + citation
.getTitleCache();
660 if(td
.getCitationMicroReference() != null){
661 result
+= " :" + td
.getCitationMicroReference();
664 if(td
.isNotDesignated()){
665 result
+= " not designated";
677 private FieldUnit
findFieldUnit(SpecimenTypeDesignation td
) {
679 DerivedUnit du
= td
.getTypeSpecimen();
680 return findFieldUnit(du
);
683 private FieldUnit
findFieldUnit(DerivedUnit du
) {
685 if(du
== null || du
.getOriginals() == null){
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();
696 for (@SuppressWarnings("rawtypes")
697 SpecimenOrObservationBase sob
: originals
) {
698 if (sob
instanceof DerivedUnit
) {
699 FieldUnit fu
= findFieldUnit((DerivedUnit
) sob
);
710 public String
print() {
715 public List
<TaggedText
> toTaggedText() {
720 public List
<TaggedText
> toTaggedTextWithCitation() {
721 buildStringWithCitation();
727 * @return the printCitation
729 public boolean isPrintCitation() {
730 return printCitation
;
734 * @param printCitation the printCitation to set
736 public void setPrintCitation(boolean printCitation
) {
737 this.printCitation
= printCitation
;
741 * @return the typifiedName
743 public TaxonName
getTypifiedName() {
747 public void setNameTypeBaseEntityType(NameTypeBaseEntityType nameTypeBaseEntityType
){
748 this.nameTypeBaseEntityType
= nameTypeBaseEntityType
;
751 public NameTypeBaseEntityType
getNameTypeBaseEntityType(){
752 return nameTypeBaseEntityType
;
755 public boolean isUseShortCitation() {
756 return useShortCitation
;
759 public void setUseShortCitation(boolean useShortCitation
) {
760 this.useShortCitation
= useShortCitation
;
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.
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.
771 * A workingset can be referenced by the <code>baseEntityReference</code>.
773 public class TypeDesignationWorkingSet
extends LinkedHashMap
<TypeDesignationStatusBase
<?
>, Collection
<TypedEntityReference
>> {
775 private static final long serialVersionUID
= -1329007606500890729L;
777 String workingSetRepresentation
= null;
779 TypedEntityReference
<VersionableEntity
> baseEntityReference
;
781 VersionableEntity baseEntity
;
783 List
<DerivedUnit
> derivedUnits
= null;
786 * @param baseEntityReference
788 public TypeDesignationWorkingSet(VersionableEntity baseEntity
, TypedEntityReference
<VersionableEntity
> baseEntityReference
) {
789 this.baseEntity
= baseEntity
;
790 this.baseEntityReference
= baseEntityReference
;
796 public VersionableEntity
getBaseEntity() {
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
;
808 * @param typeDesignationEntityReference
810 public void insert(TypeDesignationStatusBase
<?
> status
, TypedEntityReference typeDesignationEntityReference
) {
813 status
= NULL_STATUS
;
815 if(!containsKey(status
)){
816 put(status
, new ArrayList
<>());
818 get(status
).add(typeDesignationEntityReference
);
822 public String
getRepresentation() {
823 return workingSetRepresentation
;
826 public void setRepresentation(String representation
){
827 this.workingSetRepresentation
= representation
;
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.
835 * @return the baseEntityReference
837 public TypedEntityReference
<VersionableEntity
> getBaseEntityReference() {
838 return baseEntityReference
;
842 public String
toString(){
843 if(workingSetRepresentation
!= null){
844 return workingSetRepresentation
;
846 return super.toString();
853 public boolean isSpecimenTypeDesigationWorkingSet() {
854 return SpecimenOrObservationBase
.class.isAssignableFrom(baseEntityReference
.getType());
857 public TypeDesignationWorkingSetType
getWorkingsetType() {
858 return isSpecimenTypeDesigationWorkingSet() ? TypeDesignationWorkingSetType
.SPECIMEN_TYPE_DESIGNATION_WORKINGSET
: TypeDesignationWorkingSetType
.NAME_TYPE_DESIGNATION_WORKINGSET
;
863 public enum TypeDesignationWorkingSetType
{
864 SPECIMEN_TYPE_DESIGNATION_WORKINGSET
,
865 NAME_TYPE_DESIGNATION_WORKINGSET
,
868 @SuppressWarnings({ "deprecation", "serial" })
869 class NullTypeDesignationStatus
extends TypeDesignationStatusBase
<NullTypeDesignationStatus
>{
875 public void resetTerms() {
884 protected void setDefaultTerms(TermVocabulary
<NullTypeDesignationStatus
> termVocabulary
) {
890 class DataIntegrityException
extends Exception
{
892 private static final long serialVersionUID
= 1464726696296824905L;
897 public DataIntegrityException(String string
) {