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