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