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