remove duplicated methods in TaxonNameDefaultCacheStrategy
[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.getNameType().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 * @param author the author
235 * @param exAuthor the ex-author
236 * @return
237 */
238 protected String getAuthorAndExAuthor(INomenclaturalAuthor author, INomenclaturalAuthor exAuthor){
239 String authorString = "";
240 String exAuthorString = "";
241 if (author != null){
242 authorString = CdmUtils.Nz(author.getNomenclaturalTitle());
243 }
244 if (exAuthor != null){
245 exAuthorString = CdmUtils.Nz(exAuthor.getNomenclaturalTitle());
246 exAuthorString += exAuthorSeperator;
247 }
248 String result = exAuthorString + authorString;
249 return result;
250 }
251
252
253
254 /**
255 * Checks if the given name should include the author in it's cached version.<BR>
256 * This is usually the case but not for <i>species aggregates</i>.
257 * @param nonViralName
258 * @return
259 */
260 protected boolean nameIncludesAuthorship(INonViralName nonViralName){
261 Rank rank = nonViralName.getRank();
262 if (rank != null && rank.isSpeciesAggregate()){
263 return false;
264 }else{
265 return true;
266 }
267 }
268
269 // ************* TAGGED NAME ***************************************/
270
271 @Override
272 protected List<TaggedText> doGetTaggedTitle(TaxonName taxonName) {
273 List<TaggedText> tags = new ArrayList<>();
274 if (taxonName.getNameType().isViral()){
275 String acronym = taxonName.getAcronym();
276 tags.add(new TaggedText(TagEnum.name, acronym));
277 return tags;
278 }else if (taxonName.isHybridFormula()){
279 //hybrid formula
280 String hybridSeparator = NonViralNameParserImplRegExBase.hybridSign;
281 boolean isFirst = true;
282 List<HybridRelationship> rels = taxonName.getOrderedChildRelationships();
283 for (HybridRelationship rel: rels){
284 if (! isFirst){
285 tags.add(new TaggedText(TagEnum.hybridSign, hybridSeparator));
286 }
287 isFirst = false;
288 tags.addAll(getTaggedTitle(rel.getParentName()));
289 }
290 return tags;
291 }else if (taxonName.isAutonym()){
292 //Autonym
293 tags.addAll(handleTaggedAutonym(taxonName));
294 }else{ //not Autonym
295 List<TaggedText> nameTags = getTaggedName(taxonName);
296 tags.addAll(nameTags);
297 String authorCache = getAuthorshipCache(taxonName);
298 if (isNotBlank(authorCache)){
299 tags.add(new TaggedText(TagEnum.authors, authorCache));
300 }
301 }
302 return tags;
303 }
304
305 /**
306 * Returns the tag list of the name part (without author and reference).
307 * @param taxonName
308 * @return
309 */
310 @Override
311 public List<TaggedText> getTaggedName(TaxonName taxonName) {
312 if (taxonName == null){
313 return null;
314 }else if (taxonName.isViral()){
315 return null;
316 }
317 List<TaggedText> tags = new ArrayList<>();
318 Rank rank = taxonName.getRank();
319
320 if (taxonName.isProtectedNameCache()){
321 tags.add(new TaggedText(TagEnum.name, taxonName.getNameCache()));
322 }else if (taxonName.isHybridFormula()){
323 //hybrid formula
324 String hybridSeparator = NonViralNameParserImplRegExBase.hybridSign;
325 boolean isFirst = true;
326 List<HybridRelationship> rels = taxonName.getOrderedChildRelationships();
327 for (HybridRelationship rel: rels){
328 if (! isFirst){
329 tags.add(new TaggedText(TagEnum.hybridSign, hybridSeparator));
330 }
331 isFirst = false;
332 tags.addAll(getTaggedName(rel.getParentName()));
333 }
334 return tags;
335
336 }else if (rank == null){
337 tags = getRanklessTaggedNameCache(taxonName);
338 // }else if (nonViralName.isInfragenericUnranked()){
339 // result = getUnrankedInfragenericNameCache(nonViralName);
340 }else if (rank.isInfraSpecific()){
341 tags = getInfraSpeciesTaggedNameCache(taxonName);
342 }else if (rank.isSpecies() || isAggregateWithAuthorship(taxonName, rank) ){ //exception see #4288
343 tags = getSpeciesTaggedNameCache(taxonName);
344 }else if (rank.isInfraGeneric()){
345 tags = getInfraGenusTaggedNameCache(taxonName);
346 }else if (rank.isGenus()){
347 tags = getGenusOrUninomialTaggedNameCache(taxonName);
348 }else if (rank.isSupraGeneric()){
349 tags = getGenusOrUninomialTaggedNameCache(taxonName);
350 }else{
351 tags = getRanklessTaggedNameCache(taxonName);
352 logger.warn("Rank does not belong to a rank class: " + rank.getTitleCache() + ". Used rankless nameCache for name " + taxonName.getUuid());
353 }
354 //TODO handle appended phrase here instead of different places, check first if this is true for all
355 //cases
356
357 return tags;
358 }
359
360 //***************************** PRIVATES ***************************************/
361
362 private boolean isAggregateWithAuthorship(TaxonName nonViralName, Rank rank) {
363 if (rank == null){
364 return false;
365 }else{
366 return rank.isSpeciesAggregate() && ( isNotBlank(nonViralName.getAuthorshipCache()) || nonViralName.getNomenclaturalReference() != null );
367 }
368 }
369
370 /**
371 * Returns the tag list for an autonym taxon.
372 *
373 * @see NonViralName#isAutonym()
374 * @see BotanicalName#isAutonym()
375 * @param nonViralName
376 * @return
377 */
378 private List<TaggedText> handleTaggedAutonym(TaxonName nonViralName) {
379 List<TaggedText> tags = null;
380 if (nonViralName.isInfraSpecific()){
381 //species part
382 tags = getSpeciesTaggedNameCache(nonViralName);
383
384 //author
385 String authorCache = getAuthorshipCache(nonViralName);
386 if (isNotBlank(authorCache)){
387 tags.add(new TaggedText(TagEnum.authors, authorCache));
388 }
389
390 //infra species marker
391 if (nonViralName.getRank() == null || !nonViralName.getRank().isInfraSpecific()){
392 //TODO handle exception
393 logger.warn("Rank for autonym does not exist or is not lower than species !!");
394 }else{
395 String infraSpeciesMarker = nonViralName.getRank().getAbbreviation();
396 if (nonViralName.isTrinomHybrid()){
397 infraSpeciesMarker = CdmUtils.concat("", NOTHO, infraSpeciesMarker);
398 }
399 if (isNotBlank(infraSpeciesMarker)){
400 tags.add(new TaggedText(TagEnum.rank, infraSpeciesMarker));
401 }
402 }
403
404 //infra species
405 String infraSpeciesPart = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet()).trim();
406 if (isNotBlank(infraSpeciesPart)){
407 tags.add(new TaggedText(TagEnum.name, infraSpeciesPart));
408 }
409
410 } else if (nonViralName.isInfraGeneric()){
411 //genus part
412 tags =getGenusOrUninomialTaggedNameCache(nonViralName);
413
414 //author
415 String authorCache = getAuthorshipCache(nonViralName);
416 if (isNotBlank(authorCache)){
417 tags.add(new TaggedText(TagEnum.authors, authorCache));
418 }
419
420 //infra species marker
421 if (nonViralName.getRank() == null || !nonViralName.getRank().isInfraGeneric()){
422 //TODO handle exception
423 logger.warn("Rank for autonym does not exist or is not lower than species !!");
424 }else{
425 Rank rank = nonViralName.getRank();
426 String infraGenericMarker = rank.getAbbreviation();
427 if (rank.equals(Rank.SECTION_BOTANY()) || rank.equals(Rank.SUBSECTION_BOTANY())){
428 infraGenericMarker = infraGenericMarker.replace("(bot.)", "");
429 }
430 if (isNotBlank(infraGenericMarker)){
431 tags.add(new TaggedText(TagEnum.rank, infraGenericMarker));
432 }
433 }
434
435 //infra genus
436 String infraGenericPart = CdmUtils.Nz(nonViralName.getInfraGenericEpithet()).trim();
437 if (isNotBlank(infraGenericPart)){
438 tags.add(new TaggedText(TagEnum.name, infraGenericPart));
439 }
440 }
441
442 return tags;
443 }
444
445
446
447 /**
448 * Returns the tag list for rankless taxa.
449 * @param nonViralName
450 * @return
451 */
452 protected List<TaggedText> getRanklessTaggedNameCache(INonViralName nonViralName){
453 List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
454 String speciesEpi = CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim();
455 if (isNotBlank(speciesEpi)){
456 tags.add(new TaggedText(TagEnum.name, speciesEpi));
457 }
458
459 String infraSpeciesEpi = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet());
460 if (isNotBlank(infraSpeciesEpi)){
461 tags.add(new TaggedText(TagEnum.name, infraSpeciesEpi));
462 }
463
464 //result += " (rankless)";
465 addAppendedTaggedPhrase(tags, nonViralName);
466 return tags;
467 }
468
469 /**
470 * Returns the tag list for the first epithet (including a hybrid sign if required).
471 * @param nonViralName
472 * @return
473 */
474 private List<TaggedText> getUninomialTaggedPart(INonViralName nonViralName) {
475 List<TaggedText> tags = new ArrayList<>();
476
477 if (nonViralName.isMonomHybrid()){
478 addHybridPrefix(tags);
479 }
480
481 String uninomial = CdmUtils.Nz(nonViralName.getGenusOrUninomial()).trim();
482 if (isNotBlank(uninomial)){
483 tags.add(new TaggedText(TagEnum.name, uninomial));
484 }
485
486 return tags;
487 }
488
489 /**
490 * Returns the tag list for an genus or higher taxon.
491 *
492 * @param nonViralName
493 * @return
494 */
495 protected List<TaggedText> getGenusOrUninomialTaggedNameCache(INonViralName nonViralName){
496 List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
497 addAppendedTaggedPhrase(tags, nonViralName);
498 return tags;
499 }
500
501 /**
502 * Returns the tag list for an infrageneric taxon (including species aggregates).
503 *
504 * @see #getSpeciesAggregateTaggedCache(NonViralName)
505 * @param nonViralName
506 * @return
507 */
508 protected List<TaggedText> getInfraGenusTaggedNameCache(INonViralName nonViralName){
509 Rank rank = nonViralName.getRank();
510 if (rank != null && rank.isSpeciesAggregate() && isBlank(nonViralName.getAuthorshipCache())){
511 return getSpeciesAggregateTaggedCache(nonViralName);
512 }
513
514 //genus
515 List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
516
517 //marker
518 String infraGenericMarker;
519 if (rank != null){
520 try {
521 infraGenericMarker = rank.getInfraGenericMarker();
522 if (rank.equals(Rank.SECTION_BOTANY()) || rank.equals(Rank.SUBSECTION_BOTANY())){
523 infraGenericMarker = infraGenericMarker.replace("(bot.)", "");
524 }
525 } catch (UnknownCdmTypeException e) {
526 infraGenericMarker = "'unhandled infrageneric rank'";
527 }
528 }else{
529 infraGenericMarker = "'undefined infrageneric rank'";
530 }
531 String infraGenEpi = CdmUtils.Nz(nonViralName.getInfraGenericEpithet()).trim();
532 if (nonViralName.isBinomHybrid()){
533 infraGenericMarker = CdmUtils.concat("", NOTHO, infraGenericMarker);
534 }
535
536 addInfraGenericPart(nonViralName, tags, infraGenericMarker, infraGenEpi);
537
538 addAppendedTaggedPhrase(tags, nonViralName);
539 return tags;
540 }
541
542
543 /**
544 * Default implementation for the infrageneric part of a name.
545 * This is usually the infrageneric marker and the infrageneric epitheton. But may be
546 * implemented differently e.g. for zoological names the infrageneric epitheton
547 * may be surrounded by brackets and the marker left out.
548 * @param nonViralName
549 * @param tags
550 * @param infraGenericMarker
551 */
552 protected void addInfraGenericPart(
553 @SuppressWarnings("unused") INonViralName name,
554 List<TaggedText> tags,
555 String infraGenericMarker,
556 String infraGenEpi) {
557 //add marker
558 tags.add(new TaggedText(TagEnum.rank, infraGenericMarker));
559
560 //add epitheton
561 if (isNotBlank(infraGenEpi)){
562 tags.add(new TaggedText(TagEnum.name, infraGenEpi));
563 }
564 }
565
566 /**
567 * Returns the tag list for a species aggregate (or similar) taxon.<BR>
568 * Possible ranks for a <i>species aggregate</i> are "aggr.", "species group", ...
569 * @param nonViralName
570 * @return
571 */
572 protected List<TaggedText> getSpeciesAggregateTaggedCache(INonViralName nonViralName){
573 if (! isBlank(nonViralName.getAuthorshipCache())){
574 List<TaggedText> result = getSpeciesTaggedNameCache(nonViralName);
575 return result;
576 }
577
578
579 List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
580
581 addSpeciesAggregateTaggedEpithet(tags, nonViralName);
582 addAppendedTaggedPhrase(tags, nonViralName);
583 return tags;
584 }
585
586 /**
587 * Adds the aggregate tag to the tag list.
588 * @param tags
589 * @param nonViralName
590 */
591 private void addSpeciesAggregateTaggedEpithet(List<TaggedText> tags, INonViralName nonViralName) {
592 String marker;
593 try {
594 marker = nonViralName.getRank().getInfraGenericMarker();
595 } catch (UnknownCdmTypeException e) {
596 marker = "'unknown aggregat type'";
597 }
598 if (isNotBlank(marker)){
599 tags.add(new TaggedText(TagEnum.rank, marker));
600 }
601 }
602
603
604 /**
605 * Returns the tag list for a species taxon.
606 * @param nonViralName
607 * @return
608 */
609 protected List<TaggedText> getSpeciesTaggedNameCache(INonViralName nonViralName){
610 List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
611 addAppendedTaggedPhrase(tags, nonViralName);
612 return tags;
613 }
614
615 protected List<TaggedText> getInfraSpeciesTaggedNameCache(TaxonName name){
616 if (name.getNameType().isZoological()){
617 boolean includeMarker =includeInfraSpecificMarkerForZooNames(name);
618 return getInfraSpeciesTaggedNameCache(name, includeMarker);
619 }else{
620 return getInfraSpeciesTaggedNameCache(name, true);
621 }
622 }
623
624 protected boolean includeInfraSpecificMarkerForZooNames(TaxonName name){
625 return ! (name.isAutonym()); //only exclude marker if autonym, see also ZooNameNoMarkerCacheStrategy
626 }
627
628 /**
629 * Creates the tag list for an infraspecific taxon. If include is true the result will contain
630 * the infraspecific marker (e.g. "var.")
631 * @param nonViralName
632 * @param includeMarker
633 * @return
634 */
635 protected List<TaggedText> getInfraSpeciesTaggedNameCache(INonViralName nonViralName, boolean includeMarker){
636 List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
637 if (includeMarker || nonViralName.isTrinomHybrid()){
638 String marker = (nonViralName.getRank().getAbbreviation()).trim().replace("null", "");
639 if (nonViralName.isTrinomHybrid()){
640 marker = CdmUtils.concat("", NOTHO, marker);
641 }
642 if (isNotBlank(marker)){
643 tags.add(new TaggedText(TagEnum.rank, marker));
644 }
645
646 }
647 String infrSpecEpi = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet());
648
649 infrSpecEpi = infrSpecEpi.trim().replace("null", "");
650
651 if (isNotBlank(infrSpecEpi)){
652 tags.add(new TaggedText(TagEnum.name, infrSpecEpi));
653 }
654
655 addAppendedTaggedPhrase(tags, nonViralName);
656 return tags;
657 }
658
659
660 /**
661 * Adds a tag for the hybrid sign and an empty separator to avoid trailing whitespaces.
662 * @param tags
663 */
664 private void addHybridPrefix(List<TaggedText> tags) {
665 tags.add(new TaggedText(TagEnum.hybridSign, NonViralNameParserImplRegExBase.hybridSign));
666 tags.add(new TaggedText(TagEnum.separator, "")); //no whitespace separator
667 }
668
669 /**
670 * Creates the tag list for the genus and species part.
671 * @param nonViralName
672 * @return
673 */
674 private List<TaggedText> getGenusAndSpeciesTaggedPart(INonViralName nonViralName) {
675 //Uninomial
676 List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
677
678 //InfraGenericEpi
679 boolean hasInfraGenericEpi = isNotBlank(nonViralName.getInfraGenericEpithet());
680 if (hasInfraGenericEpi){
681 String infrGenEpi = nonViralName.getInfraGenericEpithet().trim();
682 if (nonViralName.isBinomHybrid()){
683 //maybe not correct but not really expected to happen
684 infrGenEpi = UTF8.HYBRID + infrGenEpi;
685 }
686 infrGenEpi = "(" + infrGenEpi + ")";
687 tags.add(new TaggedText(TagEnum.name, infrGenEpi));
688 }
689
690 //Species Epi
691 String specEpi = CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim();
692 if (! hasInfraGenericEpi && nonViralName.isBinomHybrid() ||
693 hasInfraGenericEpi && nonViralName.isTrinomHybrid()){
694 addHybridPrefix(tags);
695 }
696 if (isNotBlank(specEpi)){
697 tags.add(new TaggedText(TagEnum.name, specEpi));
698 }
699 return tags;
700 }
701
702 /**
703 * Adds the tag for the appended phrase if an appended phrase exists
704 * @param tags
705 * @param nonViralName
706 */
707 protected void addAppendedTaggedPhrase(List<TaggedText> tags, INonViralName nonViralName){
708 String appendedPhrase = nonViralName ==null ? null : nonViralName.getAppendedPhrase();
709 if (isNotBlank(appendedPhrase)){
710 tags.add(new TaggedText(TagEnum.name, appendedPhrase));
711 }
712 }
713
714 @Override
715 public String getLastEpithet(TaxonName taxonName) {
716 Rank rank = taxonName.getRank();
717 if(rank.isGenus() || rank.isSupraGeneric()) {
718 return taxonName.getGenusOrUninomial();
719 } else if(rank.isInfraGeneric()) {
720 return taxonName.getInfraGenericEpithet();
721 } else if(rank.isSpecies()) {
722 return taxonName.getSpecificEpithet();
723 } else {
724 return taxonName.getInfraSpecificEpithet();
725 }
726 }
727
728
729 }