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