Merge branch 'release/5.25.0'
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / strategy / cache / name / TaxonNameDefaultCacheStrategy.java
1 /**
2 * Copyright (C) 2007 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.strategy.cache.name;
10
11 import java.util.ArrayList;
12 import java.util.List;
13 import java.util.UUID;
14
15 import org.apache.log4j.Logger;
16
17 import eu.etaxonomy.cdm.common.CdmUtils;
18 import eu.etaxonomy.cdm.common.UTF8;
19 import eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor;
20 import eu.etaxonomy.cdm.model.name.HybridRelationship;
21 import eu.etaxonomy.cdm.model.name.INonViralName;
22 import eu.etaxonomy.cdm.model.name.Rank;
23 import eu.etaxonomy.cdm.model.name.TaxonName;
24 import eu.etaxonomy.cdm.strategy.cache.TagEnum;
25 import eu.etaxonomy.cdm.strategy.cache.TaggedText;
26 import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
27 import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImplRegExBase;
28
29
30 /**
31 * This class is a default implementation for the INonViralNameCacheStrategy<T extends NonViralName>
32 * interface.<BR>
33 * The method implements a cache strategy for botanical names so no method has to be overwritten by
34 * a subclass for botanic names.
35 * Where differing from this default botanic name strategy other subclasses should overwrite the
36 * existing methods, e.g. a CacheStrategy for zoological names should overwrite getAuthorAndExAuthor
37 * @author a.mueller
38 */
39 public class TaxonNameDefaultCacheStrategy
40 extends NameCacheStrategyBase
41 implements INonViralNameCacheStrategy {
42
43 private static final Logger logger = Logger.getLogger(TaxonNameDefaultCacheStrategy.class);
44 private static final long serialVersionUID = -6577757501563212669L;
45
46 final static UUID uuid = UUID.fromString("1cdda0d1-d5bc-480f-bf08-40a510a2f223");
47
48 protected String nameAuthorSeperator = " ";
49 protected String basionymStart = "(";
50 protected String basionymEnd = ")";
51 protected String exAuthorSeperator = " ex ";
52 private static String NOTHO = "notho";
53 protected CharSequence basionymAuthorCombinationAuthorSeperator = " ";
54
55 protected String zooAuthorYearSeperator = ", ";
56
57 @Override
58 public UUID getUuid(){
59 return uuid;
60 }
61
62 // ************************** FACTORY ******************************/
63
64 public static TaxonNameDefaultCacheStrategy NewInstance(){
65 return new TaxonNameDefaultCacheStrategy();
66 }
67
68
69 // ************ CONSTRUCTOR *******************/
70
71 protected TaxonNameDefaultCacheStrategy(){
72 super();
73 }
74
75 /* **************** GETTER / SETTER **************************************/
76
77 /**
78 * String that separates the NameCache part from the AuthorCache part
79 * @return
80 */
81 public String getNameAuthorSeperator() {
82 return nameAuthorSeperator;
83 }
84 public void setNameAuthorSeperator(String nameAuthorSeperator) {
85 this.nameAuthorSeperator = nameAuthorSeperator;
86 }
87
88
89 /**
90 * String the basionym author part starts with e.g. '('.
91 * This should correspond with the {@link TaxonNameDefaultCacheStrategy#getBasionymEnd() basionymEnd} attribute
92 * @return
93 */
94 public String getBasionymStart() {
95 return basionymStart;
96 }
97 public void setBasionymStart(String basionymStart) {
98 this.basionymStart = basionymStart;
99 }
100
101 /**
102 * String the basionym author part ends with e.g. ')'.
103 * This should correspond with the {@link TaxonNameDefaultCacheStrategy#getBasionymStart() basionymStart} attribute
104 * @return
105 */
106 public String getBasionymEnd() {
107 return basionymEnd;
108 }
109 public void setBasionymEnd(String basionymEnd) {
110 this.basionymEnd = basionymEnd;
111 }
112
113
114 /**
115 * String to separate ex author from author.
116 * @return
117 */
118 public String getExAuthorSeperator() {
119 return exAuthorSeperator;
120 }
121 public void setExAuthorSeperator(String exAuthorSeperator) {
122 this.exAuthorSeperator = exAuthorSeperator;
123 }
124
125
126 /**
127 * String that separates the basionym/original_combination author part from the combination author part
128 * @return
129 */
130 public CharSequence getBasionymAuthorCombinationAuthorSeperator() {
131 return basionymAuthorCombinationAuthorSeperator;
132 }
133
134
135 public void setBasionymAuthorCombinationAuthorSeperator( CharSequence basionymAuthorCombinationAuthorSeperator) {
136 this.basionymAuthorCombinationAuthorSeperator = basionymAuthorCombinationAuthorSeperator;
137 }
138
139 public String getZooAuthorYearSeperator() {
140 return zooAuthorYearSeperator;
141 }
142 public void setZooAuthorYearSeperator(String authorYearSeperator) {
143 this.zooAuthorYearSeperator = authorYearSeperator;
144 }
145
146 // ******************* Authorship ******************************/
147
148 @Override
149 public String getAuthorshipCache(TaxonName taxonName) {
150 if (taxonName == null){
151 return null;
152 }else if (taxonName.isViral()){
153 return null;
154 }else if(taxonName.isProtectedAuthorshipCache() == true) {
155 //cache protected
156 return taxonName.getAuthorshipCache();
157 }else{
158 return getNonCacheAuthorshipCache(taxonName);
159 }
160 }
161
162 /**
163 * Returns the authorshipcache string for the atomized authorship fields. Does not use the authorship field.
164 * @throws NullPointerException if nonViralName is null.
165 * @param taxonName
166 * @return
167 */
168 protected String getNonCacheAuthorshipCache(TaxonName nonViralName){
169 if (nonViralName.getNameType().isZoological()){
170 return this.getZoologicalNonCacheAuthorshipCache(nonViralName);
171 }else{
172 String result = "";
173 INomenclaturalAuthor combinationAuthor = nonViralName.getCombinationAuthorship();
174 INomenclaturalAuthor exCombinationAuthor = nonViralName.getExCombinationAuthorship();
175 INomenclaturalAuthor basionymAuthor = nonViralName.getBasionymAuthorship();
176 INomenclaturalAuthor exBasionymAuthor = nonViralName.getExBasionymAuthorship();
177 String basionymPart = "";
178 String authorPart = "";
179 //basionym
180 if (basionymAuthor != null || exBasionymAuthor != null){
181 basionymPart = basionymStart + getAuthorAndExAuthor(basionymAuthor, exBasionymAuthor) + basionymEnd;
182 }
183 if (combinationAuthor != null || exCombinationAuthor != null){
184 authorPart = getAuthorAndExAuthor(combinationAuthor, exCombinationAuthor);
185 }
186 result = CdmUtils.concat(basionymAuthorCombinationAuthorSeperator, basionymPart, authorPart);
187 // if ("".equals(result)){
188 // result = null;
189 // }
190 return result;
191 }
192 }
193
194 protected String getZoologicalNonCacheAuthorshipCache(TaxonName nonViralName) {
195 if (nonViralName == null){
196 return null;
197 }
198 String result = "";
199 INomenclaturalAuthor combinationAuthor = nonViralName.getCombinationAuthorship();
200 INomenclaturalAuthor exCombinationAuthor = nonViralName.getExCombinationAuthorship();
201 INomenclaturalAuthor basionymAuthor = nonViralName.getBasionymAuthorship();
202 INomenclaturalAuthor exBasionymAuthor = nonViralName.getExBasionymAuthorship();
203 Integer publicationYear = nonViralName.getPublicationYear();
204 Integer originalPublicationYear = nonViralName.getOriginalPublicationYear();
205
206 String basionymPart = "";
207 String authorPart = "";
208 //basionym
209 if (basionymAuthor != null || exBasionymAuthor != null || originalPublicationYear != null ){
210 String authorAndEx = getAuthorAndExAuthor(basionymAuthor, exBasionymAuthor);
211 String originalPublicationYearString = originalPublicationYear == null ? null : String.valueOf(originalPublicationYear);
212 String authorAndExAndYear = CdmUtils.concat(zooAuthorYearSeperator, authorAndEx, originalPublicationYearString );
213 basionymPart = basionymStart + authorAndExAndYear + basionymEnd;
214 }
215 if (combinationAuthor != null || exCombinationAuthor != null){
216 String authorAndEx = getAuthorAndExAuthor(combinationAuthor, exCombinationAuthor);
217 String publicationYearString = publicationYear == null ? null : String.valueOf(publicationYear);
218 authorPart = CdmUtils.concat(zooAuthorYearSeperator, authorAndEx, publicationYearString);
219 }
220 result = CdmUtils.concat(basionymAuthorCombinationAuthorSeperator, basionymPart, authorPart);
221 if (result == null){
222 result = "";
223 }
224 return result;
225 }
226
227
228 /**
229 * Returns the AuthorCache part for a combination of an author and an ex author. This applies on
230 * combination authors as well as on basionym/orginal combination authors.
231 * The correct order is exAuthor ex author though some botanist do not know about and do it the
232 * other way round. (see 46.4-46.6 ICBN (Vienna Code, 2006))
233 */
234 protected String getAuthorAndExAuthor(INomenclaturalAuthor author, INomenclaturalAuthor exAuthor){
235 String authorString = "";
236 String exAuthorString = "";
237 if (author != null){
238 authorString = getNomAuthorTitle(author);
239 }
240 if (exAuthor != null){
241 exAuthorString = getNomAuthorTitle(exAuthor);
242 exAuthorString += exAuthorSeperator;
243 }
244 String result = exAuthorString + authorString;
245 return result;
246 }
247
248 private String getNomAuthorTitle(INomenclaturalAuthor author) {
249 return CdmUtils.Nz(author.getNomenclaturalTitleCache());
250 }
251
252 /**
253 * Checks if the given name should include the author in it's cached version.<BR>
254 * This is usually the case but not for <i>species aggregates</i>.
255 * @param nonViralName
256 * @return
257 */
258 protected boolean nameIncludesAuthorship(INonViralName nonViralName){
259 Rank rank = nonViralName.getRank();
260 if (rank != null && rank.isSpeciesAggregate()){
261 return false;
262 }else{
263 return true;
264 }
265 }
266
267 // ************* TAGGED NAME ***************************************/
268
269 @Override
270 protected List<TaggedText> doGetTaggedTitle(TaxonName taxonName) {
271 List<TaggedText> tags = new ArrayList<>();
272 if (taxonName.getNameType().isViral()){
273 String acronym = taxonName.getAcronym();
274 if (isNotBlank(taxonName.getAcronym())){
275 //this is not according to the code
276 tags.add(new TaggedText(TagEnum.name, acronym));
277 }
278 return tags;
279 }else if (taxonName.isHybridFormula()){
280 //hybrid formula
281 String hybridSeparator = NonViralNameParserImplRegExBase.hybridSign;
282 boolean isFirst = true;
283 List<HybridRelationship> rels = taxonName.getOrderedChildRelationships();
284 for (HybridRelationship rel: rels){
285 if (! isFirst){
286 tags.add(new TaggedText(TagEnum.hybridSign, hybridSeparator));
287 }
288 isFirst = false;
289 tags.addAll(getTaggedTitle(rel.getParentName()));
290 }
291 return tags;
292 }else if (taxonName.isAutonym()){
293 //Autonym
294 tags.addAll(handleTaggedAutonym(taxonName));
295 }else{ //not Autonym
296 List<TaggedText> nameTags = getTaggedName(taxonName);
297 tags.addAll(nameTags);
298 String authorCache = getAuthorshipCache(taxonName);
299 if (isNotBlank(authorCache)){
300 tags.add(new TaggedText(TagEnum.authors, authorCache));
301 }
302 }
303 return tags;
304 }
305
306 /**
307 * Returns the tag list of the name part (without author and reference).
308 * @param taxonName
309 * @return
310 */
311 @Override
312 public List<TaggedText> getTaggedName(TaxonName taxonName) {
313 if (taxonName == null){
314 return null;
315 }else if (taxonName.isViral()){
316 return null;
317 }
318 List<TaggedText> tags = new ArrayList<>();
319 Rank rank = taxonName.getRank();
320
321 if (taxonName.isProtectedNameCache()){
322 tags.add(new TaggedText(TagEnum.name, taxonName.getNameCache()));
323 }else if (taxonName.isHybridFormula()){
324 //hybrid formula
325 String hybridSeparator = NonViralNameParserImplRegExBase.hybridSign;
326 boolean isFirst = true;
327 List<HybridRelationship> rels = taxonName.getOrderedChildRelationships();
328 for (HybridRelationship rel: rels){
329 if (! isFirst){
330 tags.add(new TaggedText(TagEnum.hybridSign, hybridSeparator));
331 }
332 isFirst = false;
333 tags.addAll(getTaggedName(rel.getParentName()));
334 }
335 return tags;
336
337 }else if (rank == null){
338 tags = getRanklessTaggedNameCache(taxonName);
339 // }else if (nonViralName.isInfragenericUnranked()){
340 // result = getUnrankedInfragenericNameCache(nonViralName);
341 }else if (rank.isInfraSpecific()){
342 tags = getInfraSpeciesTaggedNameCache(taxonName);
343 }else if (rank.isSpecies() || isAggregateWithAuthorship(taxonName, rank) ){ //exception see #4288
344 tags = getSpeciesTaggedNameCache(taxonName);
345 }else if (rank.isInfraGeneric()){
346 tags = getInfraGenusTaggedNameCache(taxonName);
347 }else if (rank.isGenus()){
348 tags = getGenusOrUninomialTaggedNameCache(taxonName);
349 }else if (rank.isSupraGeneric()){
350 tags = getGenusOrUninomialTaggedNameCache(taxonName);
351 }else{
352 tags = getRanklessTaggedNameCache(taxonName);
353 logger.warn("Rank does not belong to a rank class: " + rank.getTitleCache() + ". Used rankless nameCache for name " + taxonName.getUuid());
354 }
355 //TODO handle appended phrase here instead of different places, check first if this is true for all
356 //cases
357
358 return tags;
359 }
360
361 //***************************** PRIVATES ***************************************/
362
363 private boolean isAggregateWithAuthorship(TaxonName nonViralName, Rank rank) {
364 if (rank == null){
365 return false;
366 }else{
367 return rank.isSpeciesAggregate() && ( isNotBlank(nonViralName.getAuthorshipCache()) || nonViralName.getNomenclaturalReference() != null );
368 }
369 }
370
371 /**
372 * Returns the tag list for an autonym taxon.
373 *
374 * @see NonViralName#isAutonym()
375 * @see BotanicalName#isAutonym()
376 * @param nonViralName
377 * @return
378 */
379 private List<TaggedText> handleTaggedAutonym(TaxonName nonViralName) {
380 List<TaggedText> tags = null;
381 if (nonViralName.isInfraSpecific()){
382 //species part
383 tags = getSpeciesTaggedNameCache(nonViralName);
384
385 //author
386 String authorCache = getAuthorshipCache(nonViralName);
387 if (isNotBlank(authorCache)){
388 tags.add(new TaggedText(TagEnum.authors, authorCache));
389 }
390
391 //infra species marker
392 if (nonViralName.getRank() == null || !nonViralName.getRank().isInfraSpecific()){
393 //TODO handle exception
394 logger.warn("Rank for autonym does not exist or is not lower than species !!");
395 }else{
396 String infraSpeciesMarker = nonViralName.getRank().getAbbreviation();
397 if (nonViralName.isTrinomHybrid()){
398 infraSpeciesMarker = CdmUtils.concat("", NOTHO, infraSpeciesMarker);
399 }
400 if (isNotBlank(infraSpeciesMarker)){
401 tags.add(new TaggedText(TagEnum.rank, infraSpeciesMarker));
402 }
403 }
404
405 //infra species
406 String infraSpeciesPart = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet()).trim();
407 if (isNotBlank(infraSpeciesPart)){
408 tags.add(new TaggedText(TagEnum.name, infraSpeciesPart));
409 }
410
411 } else if (nonViralName.isInfraGeneric()){
412 //genus part
413 tags =getGenusOrUninomialTaggedNameCache(nonViralName);
414
415 //author
416 String authorCache = getAuthorshipCache(nonViralName);
417 if (isNotBlank(authorCache)){
418 tags.add(new TaggedText(TagEnum.authors, authorCache));
419 }
420
421 //infra species marker
422 if (nonViralName.getRank() == null || !nonViralName.getRank().isInfraGeneric()){
423 //TODO handle exception
424 logger.warn("Rank for autonym does not exist or is not lower than species !!");
425 }else{
426 Rank rank = nonViralName.getRank();
427 String infraGenericMarker = rank.getAbbreviation();
428 if (rank.equals(Rank.SECTION_BOTANY()) || rank.equals(Rank.SUBSECTION_BOTANY())){
429 infraGenericMarker = infraGenericMarker.replace("(bot.)", "");
430 }
431 if (isNotBlank(infraGenericMarker)){
432 tags.add(new TaggedText(TagEnum.rank, infraGenericMarker));
433 }
434 }
435
436 //infra genus
437 String infraGenericPart = CdmUtils.Nz(nonViralName.getInfraGenericEpithet()).trim();
438 if (isNotBlank(infraGenericPart)){
439 tags.add(new TaggedText(TagEnum.name, infraGenericPart));
440 }
441 }
442
443 return tags;
444 }
445
446
447
448 /**
449 * Returns the tag list for rankless taxa.
450 * @param nonViralName
451 * @return
452 */
453 protected List<TaggedText> getRanklessTaggedNameCache(INonViralName nonViralName){
454 List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
455 String speciesEpi = CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim();
456 if (isNotBlank(speciesEpi)){
457 tags.add(new TaggedText(TagEnum.name, speciesEpi));
458 }
459
460 String infraSpeciesEpi = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet());
461 if (isNotBlank(infraSpeciesEpi)){
462 tags.add(new TaggedText(TagEnum.name, infraSpeciesEpi));
463 }
464
465 //result += " (rankless)";
466 addAppendedTaggedPhrase(tags, nonViralName);
467 return tags;
468 }
469
470 /**
471 * Returns the tag list for the first epithet (including a hybrid sign if required).
472 * @param nonViralName
473 * @return
474 */
475 private List<TaggedText> getUninomialTaggedPart(INonViralName nonViralName) {
476 List<TaggedText> tags = new ArrayList<>();
477
478 if (nonViralName.isMonomHybrid()){
479 addHybridPrefix(tags);
480 }
481
482 String uninomial = CdmUtils.Nz(nonViralName.getGenusOrUninomial()).trim();
483 if (isNotBlank(uninomial)){
484 tags.add(new TaggedText(TagEnum.name, uninomial));
485 }
486
487 return tags;
488 }
489
490 /**
491 * Returns the tag list for an genus or higher taxon.
492 *
493 * @param nonViralName
494 * @return
495 */
496 protected List<TaggedText> getGenusOrUninomialTaggedNameCache(INonViralName nonViralName){
497 List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
498 addAppendedTaggedPhrase(tags, nonViralName);
499 return tags;
500 }
501
502 /**
503 * Returns the tag list for an infrageneric taxon (including species aggregates).
504 *
505 * @see #getSpeciesAggregateTaggedCache(NonViralName)
506 * @param nonViralName
507 * @return
508 */
509 protected List<TaggedText> getInfraGenusTaggedNameCache(INonViralName nonViralName){
510 Rank rank = nonViralName.getRank();
511 if (rank != null && rank.isSpeciesAggregate() && isBlank(nonViralName.getAuthorshipCache())){
512 return getSpeciesAggregateTaggedCache(nonViralName);
513 }
514
515 //genus
516 List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
517
518 //marker
519 String infraGenericMarker;
520 if (rank != null){
521 try {
522 infraGenericMarker = rank.getInfraGenericMarker();
523 if (rank.equals(Rank.SECTION_BOTANY()) || rank.equals(Rank.SUBSECTION_BOTANY())){
524 infraGenericMarker = infraGenericMarker.replace("(bot.)", "");
525 }
526 } catch (UnknownCdmTypeException e) {
527 infraGenericMarker = "'unhandled infrageneric rank'";
528 }
529 }else{
530 infraGenericMarker = "'undefined infrageneric rank'";
531 }
532 String infraGenEpi = CdmUtils.Nz(nonViralName.getInfraGenericEpithet()).trim();
533 if (nonViralName.isBinomHybrid()){
534 infraGenericMarker = CdmUtils.concat("", NOTHO, infraGenericMarker);
535 }
536
537 addInfraGenericPart(nonViralName, tags, infraGenericMarker, infraGenEpi);
538
539 addAppendedTaggedPhrase(tags, nonViralName);
540 return tags;
541 }
542
543
544 /**
545 * Default implementation for the infrageneric part of a name.
546 * This is usually the infrageneric marker and the infrageneric epitheton. But may be
547 * implemented differently e.g. for zoological names the infrageneric epitheton
548 * may be surrounded by brackets and the marker left out.
549 * @param nonViralName
550 * @param tags
551 * @param infraGenericMarker
552 */
553 protected void addInfraGenericPart(
554 @SuppressWarnings("unused") INonViralName name,
555 List<TaggedText> tags,
556 String infraGenericMarker,
557 String infraGenEpi) {
558 //add marker
559 tags.add(new TaggedText(TagEnum.rank, infraGenericMarker));
560
561 //add epitheton
562 if (isNotBlank(infraGenEpi)){
563 tags.add(new TaggedText(TagEnum.name, infraGenEpi));
564 }
565 }
566
567 /**
568 * Returns the tag list for a species aggregate (or similar) taxon.<BR>
569 * Possible ranks for a <i>species aggregate</i> are "aggr.", "species group", ...
570 * @param nonViralName
571 * @return
572 */
573 protected List<TaggedText> getSpeciesAggregateTaggedCache(INonViralName nonViralName){
574 if (! isBlank(nonViralName.getAuthorshipCache())){
575 List<TaggedText> result = getSpeciesTaggedNameCache(nonViralName);
576 return result;
577 }
578
579
580 List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
581
582 addSpeciesAggregateTaggedEpithet(tags, nonViralName);
583 addAppendedTaggedPhrase(tags, nonViralName);
584 return tags;
585 }
586
587 /**
588 * Adds the aggregate tag to the tag list.
589 * @param tags
590 * @param nonViralName
591 */
592 private void addSpeciesAggregateTaggedEpithet(List<TaggedText> tags, INonViralName nonViralName) {
593 String marker;
594 try {
595 marker = nonViralName.getRank().getInfraGenericMarker();
596 } catch (UnknownCdmTypeException e) {
597 marker = "'unknown aggregat type'";
598 }
599 if (isNotBlank(marker)){
600 tags.add(new TaggedText(TagEnum.rank, marker));
601 }
602 }
603
604
605 /**
606 * Returns the tag list for a species taxon.
607 * @param nonViralName
608 * @return
609 */
610 protected List<TaggedText> getSpeciesTaggedNameCache(INonViralName nonViralName){
611 List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
612 addAppendedTaggedPhrase(tags, nonViralName);
613 return tags;
614 }
615
616 protected List<TaggedText> getInfraSpeciesTaggedNameCache(TaxonName name){
617 if (name.getNameType().isZoological()){
618 boolean includeMarker =includeInfraSpecificMarkerForZooNames(name);
619 return getInfraSpeciesTaggedNameCache(name, includeMarker);
620 }else{
621 return getInfraSpeciesTaggedNameCache(name, true);
622 }
623 }
624
625 protected boolean includeInfraSpecificMarkerForZooNames(TaxonName name){
626 return ! (name.isAutonym()); //only exclude marker if autonym, see also ZooNameNoMarkerCacheStrategy
627 }
628
629 /**
630 * Creates the tag list for an infraspecific taxon. If include is true the result will contain
631 * the infraspecific marker (e.g. "var.")
632 * @param nonViralName
633 * @param includeMarker
634 * @return
635 */
636 protected List<TaggedText> getInfraSpeciesTaggedNameCache(INonViralName nonViralName, boolean includeMarker){
637 List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
638 if (includeMarker || nonViralName.isTrinomHybrid()){
639 String marker = (nonViralName.getRank().getAbbreviation()).trim().replace("null", "");
640 if (nonViralName.isTrinomHybrid()){
641 marker = CdmUtils.concat("", NOTHO, marker);
642 }
643 if (isNotBlank(marker)){
644 tags.add(new TaggedText(TagEnum.rank, marker));
645 }
646
647 }
648 String infrSpecEpi = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet());
649
650 infrSpecEpi = infrSpecEpi.trim().replace("null", "");
651
652 if (isNotBlank(infrSpecEpi)){
653 tags.add(new TaggedText(TagEnum.name, infrSpecEpi));
654 }
655
656 addAppendedTaggedPhrase(tags, nonViralName);
657 return tags;
658 }
659
660
661 /**
662 * Adds a tag for the hybrid sign and an empty separator to avoid trailing whitespaces.
663 * @param tags
664 */
665 private void addHybridPrefix(List<TaggedText> tags) {
666 tags.add(new TaggedText(TagEnum.hybridSign, NonViralNameParserImplRegExBase.hybridSign));
667 tags.add(new TaggedText(TagEnum.separator, "")); //no whitespace separator
668 }
669
670 /**
671 * Creates the tag list for the genus and species part.
672 * @param nonViralName
673 * @return
674 */
675 private List<TaggedText> getGenusAndSpeciesTaggedPart(INonViralName nonViralName) {
676 //Uninomial
677 List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
678
679 //InfraGenericEpi
680 boolean hasInfraGenericEpi = isNotBlank(nonViralName.getInfraGenericEpithet());
681 if (hasInfraGenericEpi){
682 String infrGenEpi = nonViralName.getInfraGenericEpithet().trim();
683 if (nonViralName.isBinomHybrid()){
684 //maybe not correct but not really expected to happen
685 infrGenEpi = UTF8.HYBRID + infrGenEpi;
686 }
687 infrGenEpi = "(" + infrGenEpi + ")";
688 tags.add(new TaggedText(TagEnum.name, infrGenEpi));
689 }
690
691 //Species Epi
692 String specEpi = CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim();
693 if (! hasInfraGenericEpi && nonViralName.isBinomHybrid() ||
694 hasInfraGenericEpi && nonViralName.isTrinomHybrid()){
695 addHybridPrefix(tags);
696 }
697 if (isNotBlank(specEpi)){
698 tags.add(new TaggedText(TagEnum.name, specEpi));
699 }
700 return tags;
701 }
702
703 /**
704 * Adds the tag for the appended phrase if an appended phrase exists
705 * @param tags
706 * @param nonViralName
707 */
708 protected void addAppendedTaggedPhrase(List<TaggedText> tags, INonViralName nonViralName){
709 String appendedPhrase = nonViralName ==null ? null : nonViralName.getAppendedPhrase();
710 if (isNotBlank(appendedPhrase)){
711 tags.add(new TaggedText(TagEnum.name, appendedPhrase));
712 }
713 }
714
715 @Override
716 public String getLastEpithet(TaxonName taxonName) {
717 Rank rank = taxonName.getRank();
718 if(rank.isGenus() || rank.isSupraGeneric()) {
719 return taxonName.getGenusOrUninomial();
720 } else if(rank.isInfraGeneric()) {
721 return taxonName.getInfraGenericEpithet();
722 } else if(rank.isSpecies()) {
723 return taxonName.getSpecificEpithet();
724 } else {
725 return taxonName.getInfraSpecificEpithet();
726 }
727 }
728
729
730 }