cleanup
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / format / taxon / TaxonRelationshipFormatter.java
index f098184891d1edd49263c4c8c493f29cb48bd3ae..d4671949f5da5eb71e71401eab75ffc52303b4bb 100644 (file)
@@ -11,31 +11,36 @@ package eu.etaxonomy.cdm.format.taxon;
 import java.util.ArrayList;
 import java.util.List;
 
-import org.codehaus.plexus.util.StringUtils;
+import org.apache.commons.lang3.StringUtils;
 
-import eu.etaxonomy.cdm.common.CdmUtils;
+import eu.etaxonomy.cdm.common.UTF8;
 import eu.etaxonomy.cdm.model.agent.Person;
 import eu.etaxonomy.cdm.model.agent.Team;
 import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
 import eu.etaxonomy.cdm.model.common.CdmBase;
 import eu.etaxonomy.cdm.model.common.Language;
-import eu.etaxonomy.cdm.model.common.Representation;
 import eu.etaxonomy.cdm.model.name.TaxonName;
 import eu.etaxonomy.cdm.model.reference.Reference;
 import eu.etaxonomy.cdm.model.taxon.Taxon;
 import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
 import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
+import eu.etaxonomy.cdm.model.term.Representation;
 import eu.etaxonomy.cdm.ref.TypedEntityReference;
 import eu.etaxonomy.cdm.strategy.cache.TagEnum;
 import eu.etaxonomy.cdm.strategy.cache.TaggedText;
+import eu.etaxonomy.cdm.strategy.cache.TaggedTextBuilder;
+import eu.etaxonomy.cdm.strategy.cache.agent.PersonDefaultCacheStrategy;
+import eu.etaxonomy.cdm.strategy.cache.agent.TeamDefaultCacheStrategy;
 
 /**
+ * Formatter for TaxonRelationships.
+ *
  * @author a.mueller
  * @since 13.08.2018
- *
  */
 public class TaxonRelationshipFormatter {
 
+    private static final String DOUBTFUL_TAXON_MARKER = "?" + UTF8.NARROW_NO_BREAK;
     private static final String REL_SEC = ", rel. sec. ";
     private static final String ERR_SEC = ", err. sec. ";
     private static final String SYN_SEC = ", syn. sec. ";
@@ -46,106 +51,150 @@ public class TaxonRelationshipFormatter {
     private static final String AUCT = "auct.";
     private static final String SENSU_SEPARATOR = " sensu ";
     private static final String SEC_SEPARATOR = " sec. ";
+    private static final String PRO_PARTE_SEPARATOR = ", ";
     private static final String DETAIL_SEPARATOR = ": ";
     private static final String INVERT_SYMBOL = "<-"; //TODO
     private static final String UNDEFINED_SYMBOL = "??";  //TODO
 
-    public List<TaggedText> getTaggedText(TaxonRelationship taxonRelationship, boolean reverse, List<Language> languages) {
+    private static TaxonRelationshipFormatter instance;
+
+// ************************* FACTORY ************************/
+
+    public static TaxonRelationshipFormatter NewInstance(){
+        return new TaxonRelationshipFormatter();
+    }
+
+    public static TaxonRelationshipFormatter INSTANCE(){
+        if (instance == null){
+            instance = NewInstance();
+        }
+        return instance;
+    }
+
+// ******************* CONSTRUCTOR ************************/
+
+    private TaxonRelationshipFormatter(){}
+
+// ********************** METHODS ***************************/
+
+    public List<TaggedText> getTaggedText(TaxonRelationship taxonRelationship, boolean inverse, List<Language> languages) {
+        return getTaggedText(taxonRelationship, inverse, languages, false);
+    }
+
+    public List<TaggedText> getTaggedText(TaxonRelationship taxonRelationship, boolean inverse,
+            List<Language> languages, boolean withoutName) {
 
         if (taxonRelationship == null){
             return null;
         }
 
         TaxonRelationshipType type = taxonRelationship.getType();
-        boolean isMisapplied = type == null ? false : type.isAnyMisappliedName() && reverse;
+        boolean isMisapplied = (type == null ? false : type.isMisappliedName() && inverse);
         boolean isSynonym = type == null? false : type.isAnySynonym();
 
-        Taxon relatedTaxon = reverse? taxonRelationship.getFromTaxon()
+        Taxon relatedTaxon = inverse? taxonRelationship.getFromTaxon()
                 : taxonRelationship.getToTaxon();
 
         if (relatedTaxon == null){
             return null;
         }
+
+        String doubtfulTaxonStr = relatedTaxon.isDoubtful() ? DOUBTFUL_TAXON_MARKER : "";
+        String doubtfulRelationStr = taxonRelationship.isDoubtful() ? "?" : "";
+
         TaxonName name = relatedTaxon.getName();
 
-        List<TaggedText> tags = new ArrayList<>();
+        TaggedTextBuilder builder = new TaggedTextBuilder();
 
         //rel symbol
-        String symbol = getSymbol(type, reverse, languages);
-        tags.add(TaggedText.NewInstance(TagEnum.symbol, symbol));
-
-        //whitespace
-        tags.add(TaggedText.NewWhitespaceInstance());
+        String symbol = doubtfulRelationStr + getSymbol(type, inverse, languages);
+        builder.add(TagEnum.symbol, symbol);
 
         //name
-        if (isMisapplied){
-            //starting quote
-            String startQuote = QUOTE_START;
-            tags.add(TaggedText.NewSeparatorInstance(startQuote));
-
-            //name cache
-            List<TaggedText> nameCacheTags = getNameCacheTags(name);
-            tags.addAll(nameCacheTags);
-
-            //end quote
-            String endQuote = QUOTE_END;
-            tags.add(TaggedText.NewSeparatorInstance(endQuote));
+        if (!withoutName){
+            if (isMisapplied){
+                //starting quote
+                String startQuote = " " + doubtfulTaxonStr + QUOTE_START;
+                builder.addSeparator(startQuote);
+
+                //name cache
+                List<TaggedText> nameCacheTags = getNameCacheTags(name);
+                builder.addAll(nameCacheTags);
+
+                //end quote
+                String endQuote = QUOTE_END;
+                builder.add(TagEnum.postSeparator, endQuote);
+            }else{
+                builder.addSeparator(" " + doubtfulTaxonStr);
+                //name full title cache
+                List<TaggedText> nameCacheTags = getNameTitleCacheTags(name);
+                builder.addAll(nameCacheTags);
+            }
         }else{
-            //name title cache
-            //TODO fullTitle?
-            List<TaggedText> nameCacheTags = getNameTitleCacheTags(name);
-            tags.addAll(nameCacheTags);
+            if (isNotBlank(doubtfulTaxonStr)){
+                builder.addSeparator(" " + doubtfulTaxonStr);
+            }
         }
 
-
-        //sensu (+ Separatoren?)
+        //sec/sensu (+ Separatoren?)
         if (isNotBlank(relatedTaxon.getAppendedPhrase())){
-            tags.add(TaggedText.NewWhitespaceInstance());
-            tags.add(TaggedText.NewInstance(TagEnum.appendedPhrase, relatedTaxon.getAppendedPhrase()));
+            builder.addWhitespace();
+            builder.add(TagEnum.appendedPhrase, relatedTaxon.getAppendedPhrase());
         }
-        List<TaggedText> secTags = getSensuTags(relatedTaxon.getSec(), relatedTaxon.getSecMicroReference(), isMisapplied);
+        List<TaggedText> secTags = getReferenceTags(relatedTaxon.getSec(), relatedTaxon.getSecMicroReference(),
+               /* isMisapplied,*/ false);
         if (!secTags.isEmpty()) {
-            tags.add(TaggedText.NewSeparatorInstance(isMisapplied? SENSU_SEPARATOR : SEC_SEPARATOR));
-            tags.addAll(secTags);
+            builder.addSeparator(isMisapplied? SENSU_SEPARATOR : SEC_SEPARATOR);
+            builder.addAll(secTags);
         }else if (isBlank(relatedTaxon.getAppendedPhrase())) {
             if (isMisapplied){
-                tags.add(TaggedText.NewWhitespaceInstance());
+                builder.addWhitespace();
                 //TODO type unclear sensuReference(?)
-                tags.add(TaggedText.NewInstance(TagEnum.authors, AUCT));
+                builder.add(TagEnum.appendedPhrase, AUCT);
             }else{
-                tags.add(TaggedText.NewSeparatorInstance(SEC_SEPARATOR + UNKNOWN_SEC));
+                builder.addSeparator(SEC_SEPARATOR + UNKNOWN_SEC);
             }
         }
 
 //        //, non author
         if (isMisapplied && name != null){
-            if (name.getCombinationAuthorship() != null){
-                tags.add(TaggedText.NewSeparatorInstance(NON_SEPARATOR));
-                //TODO add nom. ref. author tags
-            }else if (isNotBlank(name.getAuthorshipCache())){
-                tags.add(TaggedText.NewSeparatorInstance(NON_SEPARATOR));
-                tags.add(TaggedText.NewInstance(TagEnum.authors, name.getAuthorshipCache().trim()));
+            if (isNotBlank(name.getAuthorshipCache())){
+                builder.addSeparator(NON_SEPARATOR);
+                builder.add(TagEnum.authors, name.getAuthorshipCache().trim());
+            }
+        }
+
+        //p.p.
+        if (isMisapplied) {
+            if (isProParteMAN(type, inverse)) {
+                builder.addSeparator(PRO_PARTE_SEPARATOR);
+                symbol = "p.p.";
+                builder.add(TagEnum.symbol, symbol);
+            } else if (isPartialMAN(type, inverse)) {
+                builder.addSeparator(PRO_PARTE_SEPARATOR);
+                symbol = "part.";
+                builder.add(TagEnum.symbol, symbol);
             }
         }
 
-        //TODO tagEnum for relSec?
-        List<TaggedText> relSecTags = getSensuTags /*getCitationTags*/(taxonRelationship.getCitation(),
-                taxonRelationship.getCitationMicroReference(), false);
+        //rel sec
+        List<TaggedText> relSecTags = getReferenceTags(taxonRelationship.getCitation(),
+                taxonRelationship.getCitationMicroReference(),true);
         if (!relSecTags.isEmpty()){
-            TaggedText relSecSeparatorToag = TaggedText.NewSeparatorInstance(isSynonym ? SYN_SEC : isMisapplied ? ERR_SEC : REL_SEC);
-            tags.add(relSecSeparatorToag);
-            tags.addAll(relSecTags);
+            builder.addSeparator(isSynonym ? SYN_SEC : isMisapplied ? ERR_SEC : REL_SEC);
+            builder.addAll(relSecTags);
         }
 
-        return tags;
+        return builder.getTaggedText();
     }
 
-    private List<TaggedText> getSensuTags(Reference ref, String detail, boolean isSensu) {
+    private List<TaggedText> getReferenceTags(Reference ref, String detail, /*boolean isSensu,*/ boolean isRelation) {
         List<TaggedText> result = new ArrayList<>();
         String secRef;
 
         if (ref != null){
-            TeamOrPersonBase<?> author = ref.getAuthorship();
+            TeamOrPersonBase<?> author = CdmBase.deproxy(ref.getAuthorship());
+
             //TODO distinguish linked and unlinked usage,
             // if reference is not linked short citation should only be used
             //   if both author and year exists, also initials should be added in this case
@@ -153,12 +202,11 @@ public class TaxonRelationshipFormatter {
             if (ref.isProtectedTitleCache() == false &&
                     author != null &&
                     isNotBlank(author.getTitleCache())){
-                //TODO move to authorFormatter
-                String familyNames = getFamilyNames(author);
-                if (isNotBlank(familyNames)){
-                    secRef = familyNames;
+                if (author.isInstanceOf(Person.class)){
+                    secRef = PersonDefaultCacheStrategy.INSTANCE().getFamilyTitle((Person)author);
                 }else{
-                    secRef = ref.getAuthorship().getTitleCache();
+                    //#9624
+                    secRef = TeamDefaultCacheStrategy.INSTANCE_ET_AL_2().getFamilyTitle((Team)author);
                 }
                 if (isNotBlank(ref.getYear())){
                    secRef += " " + ref.getYear();
@@ -166,136 +214,69 @@ public class TaxonRelationshipFormatter {
             }else{
                 secRef = ref.getTitleCache();
             }
-            TagEnum secType = isSensu? TagEnum.sensuReference : TagEnum.secReference;
+            TagEnum secType = /*isSensu? TagEnum.sensuReference : */ isRelation? TagEnum.relSecReference : TagEnum.secReference;
             TaggedText refTag = TaggedText.NewInstance(secType, secRef);
-            refTag.setEntityReference(new TypedEntityReference<>(CdmBase.deproxy(ref.getClass()), ref.getUuid(), secRef));
+            refTag.setEntityReference(new TypedEntityReference<>(CdmBase.deproxy(ref).getClass(), ref.getUuid()));
             result.add(refTag);
         }
         if (isNotBlank(detail)){
             result.add(TaggedText.NewSeparatorInstance(DETAIL_SEPARATOR));
-            //TODO do we need a sensu micro reference??
-            TagEnum detailType = isSensu? TagEnum.sensuMicroReference : TagEnum.secMicroReference;
+            TagEnum detailType = /*isSensu? TagEnum.sensuMicroReference : */ isRelation? TagEnum.relSecMicroReference :TagEnum.secMicroReference;
             TaggedText microTag = TaggedText.NewInstance(detailType, detail);
             result.add(microTag);
         }
         return result;
     }
 
-    /**
-     * @param author
-     * @return
-     */
-    private String getFamilyNames(TeamOrPersonBase<?> author) {
-        if (author.isInstanceOf(Person.class)){
-            Person person = CdmBase.deproxy(author, Person.class);
-            return isNotBlank(person.getFamilyName())? person.getFamilyName() : null;
-        }else{
-            Team team = CdmBase.deproxy(author, Team.class);
-            String result = null;
-            int n = team.getTeamMembers().size();
-            int index = 0;
-            if (team.isHasMoreMembers()){
-                n++;
-            }
-            for (Person member : team.getTeamMembers()){
-                String name = isNotBlank(member.getFamilyName())? member.getFamilyName(): member.getTitleCache();
-                String separator = index < n ? ", " : " & ";
-                result = CdmUtils.concat(separator, result, name);
-                index++;
-            }
-            if (team.isHasMoreMembers()){
-                //TODO or et al.???
-                result += " & al.";
-            }
-            return result;
-        }
-    }
-
-
-    /**
-     * @param sec
-     * @param secMicroReference
-     * @return
-     */
-    private List<TaggedText> getCitationTags(Reference ref, String secMicroReference) {
-        List<TaggedText> result = new ArrayList<>();
-        String secRef;
-
-        if (ref != null){
-            //copied from TaxonBaseDefaultCacheStrategy
-            if (ref.isProtectedTitleCache() == false &&
-                    ref.getCacheStrategy() != null &&
-                    ref.getAuthorship() != null &&
-                    isNotBlank(ref.getAuthorship().getTitleCache()) &&
-                    isNotBlank(ref.getYear())){
-                secRef = ref.getCacheStrategy().getCitation(ref);
-            }else{
-                secRef = ref.getTitleCache();
-            }
-            //TODO do we need a sensuReference type?
-            TaggedText refTag = TaggedText.NewInstance(TagEnum.secReference, secRef);
-            refTag.setEntityReference(new TypedEntityReference<>(ref.getClass(), ref.getUuid(), secRef));
-            result.add(refTag);
-        }
-        if (isNotBlank(secMicroReference)){
-            result.add(TaggedText.NewSeparatorInstance(DETAIL_SEPARATOR));
-            TaggedText microTag = TaggedText.NewInstance(TagEnum.secMicroReference, secMicroReference);
-            result.add(microTag);
-        }
-        return result;
-    }
-
-
-    /**
-     * @param name
-     * @return
-     */
     private List<TaggedText> getNameCacheTags(TaxonName name) {
-        List<TaggedText> result = name.getCacheStrategy().getTaggedName(name);
+        List<TaggedText> result = name.cacheStrategy().getTaggedName(name);
         return result;
     }
 
     private List<TaggedText> getNameTitleCacheTags(TaxonName name) {
 
         //TODO full title?
-        List<TaggedText> result = name.getCacheStrategy().getTaggedTitle(name);
+        List<TaggedText> result = name.cacheStrategy().getTaggedFullTitle(name);
         return result;
     }
 
-
     /**
      * @param type the taxon relationship type
-     * @param reverse is the relationship used reverse
+     * @param inverse is the relationship used inverse
      * @param languages list of preferred languages
      * @return the symbol for the taxon relationship
      */
-    private String getSymbol(TaxonRelationshipType type, boolean reverse, List<Language> languages) {
+    private String getSymbol(TaxonRelationshipType type, boolean inverse, List<Language> languages) {
         if (type == null){
             return UNDEFINED_SYMBOL;
         }
 
         //symbol
-        String symbol = reverse? type.getInverseSymbol():type.getSymbol();
+        String symbol = inverse? type.getInverseSymbol():type.getSymbol();
         if (isNotBlank(symbol)){
+            //handle p.p. MAN specific #10082
+            if (isProParteMAN(type, inverse) || isPartialMAN(type, inverse)) {
+                return TaxonRelationshipType.MISAPPLIED_NAME_FOR().getInverseSymbol();
+            }
             return symbol;
         }
 
         boolean isSymmetric = type.isSymmetric();
         //symmetric inverted symbol
-        String invertedSymbol = reverse? type.getSymbol() : type.getInverseSymbol();
+        String invertedSymbol = inverse? type.getSymbol() : type.getInverseSymbol();
         if (isSymmetric && isNotBlank(invertedSymbol)){
             return invertedSymbol;
         }
 
         //abbrev label
-        Representation representation = reverse? type.getPreferredRepresentation(languages): type.getPreferredInverseRepresentation(languages);
+        Representation representation = inverse? type.getPreferredRepresentation(languages): type.getPreferredInverseRepresentation(languages);
         String abbrevLabel = representation.getAbbreviatedLabel();
         if (isNotBlank(abbrevLabel)){
             return abbrevLabel;
         }
 
         //symmetric inverted abbrev label
-        Representation invertedRepresentation = reverse? type.getPreferredInverseRepresentation(languages):type.getPreferredRepresentation(languages);
+        Representation invertedRepresentation = inverse? type.getPreferredInverseRepresentation(languages):type.getPreferredRepresentation(languages);
         String invertedAbbrevLabel = invertedRepresentation.getAbbreviatedLabel();
         if (isSymmetric && isNotBlank(invertedAbbrevLabel)){
             return invertedAbbrevLabel;
@@ -314,6 +295,14 @@ public class TaxonRelationshipFormatter {
         return UNDEFINED_SYMBOL;
     }
 
+    private boolean isPartialMAN(TaxonRelationshipType type, boolean inverse) {
+        return inverse && type.getUuid().equals(TaxonRelationshipType.uuidPartialMisappliedNameFor);
+    }
+
+    private boolean isProParteMAN(TaxonRelationshipType type, boolean inverse) {
+        return inverse && type.getUuid().equals(TaxonRelationshipType.uuidProParteMisappliedNameFor);
+    }
+
     private boolean isNotBlank(String str) {
         return StringUtils.isNotBlank(str);
     }