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