Project

General

Profile

Download (33.4 KB) Statistics
| Branch: | Tag: | Revision:
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.Arrays;
13
import java.util.Iterator;
14
import java.util.List;
15
import java.util.Set;
16
import java.util.UUID;
17

    
18
import org.apache.commons.lang.StringUtils;
19
import org.apache.log4j.Logger;
20

    
21
import eu.etaxonomy.cdm.common.CdmUtils;
22
import eu.etaxonomy.cdm.common.UTF8;
23
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
24
import eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor;
25
import eu.etaxonomy.cdm.model.common.Language;
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.model.term.Representation;
35
import eu.etaxonomy.cdm.ref.TypedEntityReference;
36
import eu.etaxonomy.cdm.strategy.cache.TagEnum;
37
import eu.etaxonomy.cdm.strategy.cache.TaggedText;
38
import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
39
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImplRegExBase;
40

    
41

    
42
/**
43
 * This class is a default implementation for the INonViralNameCacheStrategy<T extends NonViralName>
44
 * interface.<BR>
45
 * The method implements a cache strategy for botanical names so no method has to be overwritten by
46
 * a subclass for botanic names.
47
 * Where differing from this default botanic name strategy other subclasses should overwrite the
48
 * existing methods, e.g. a CacheStrategy for zoological names should overwrite getAuthorAndExAuthor
49
 * @author a.mueller
50
 */
51
public class TaxonNameDefaultCacheStrategy
52
        extends NameCacheStrategyBase
53
        implements INonViralNameCacheStrategy {
54

    
55
    private static final Logger logger = Logger.getLogger(TaxonNameDefaultCacheStrategy.class);
56
	private static final long serialVersionUID = -6577757501563212669L;
57

    
58
    final static UUID uuid = UUID.fromString("1cdda0d1-d5bc-480f-bf08-40a510a2f223");
59

    
60
    protected String nameAuthorSeperator = " ";
61
    protected String basionymStart = "(";
62
    protected String basionymEnd = ")";
63
    protected String exAuthorSeperator = " ex ";
64
    private static String NOTHO = "notho";
65
    protected CharSequence basionymAuthorCombinationAuthorSeperator = " ";
66

    
67
    protected String zooAuthorYearSeperator = ", ";
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
                List<Language> prefLangs = Arrays.asList(new Language[]{Language.LATIN(), Language.DEFAULT()});
349
                Representation repr = statusType.getPreferredRepresentation(prefLangs);
350
                if (repr != null){
351
                    if(!Language.LATIN().equals(repr.getLanguage())){
352
                        String message = "No latin representation available for nom. status. " + statusType.getTitleCache();
353
                        logger.info(message);
354
                    }
355
                    nomStatusStr = repr.getAbbreviatedLabel();
356
                }else{
357
                    String message = "No representation available for nom. status. " + statusType.getTitleCache();
358
                    logger.warn(message);
359
                    nomStatusStr = statusType.getTitleCache();
360
                }
361
            }else if(StringUtils.isNotBlank(ncStatus.getRuleConsidered())){
362
                nomStatusStr = ncStatus.getRuleConsidered();
363
            }
364
            String statusSeparator = ", ";
365
            if (includeSeparatorBefore){
366
                nomStatusTags.add(new TaggedText(TagEnum.separator, statusSeparator));
367
            }
368
            nomStatusTags.add(new TaggedText(TagEnum.nomStatus, nomStatusStr, new TypedEntityReference<>(ncStatus.getClass(), ncStatus.getUuid())));
369
            if (includeSeparatorAfter){
370
                nomStatusTags.add(new TaggedText(TagEnum.postSeparator, ","));
371
            }
372
        }
373
        return nomStatusTags;
374
    }
375

    
376
    @Override
377
    public List<TaggedText> getTaggedTitle(TaxonName taxonName) {
378
        if (taxonName == null){
379
            return null;
380
        }
381

    
382
        List<TaggedText> tags = new ArrayList<>();
383

    
384
        if (taxonName.isViral()){
385
            return getViralTaggedTitle(taxonName);
386
        }
387
        //TODO how to handle protected fullTitleCache here?
388
        if (taxonName.isProtectedTitleCache()){
389
            //protected title cache
390
            tags.add(new TaggedText(TagEnum.name, taxonName.getTitleCache()));
391
            return tags;
392
        }else if (taxonName.isHybridFormula()){
393
            //hybrid formula
394
            String hybridSeparator = NonViralNameParserImplRegExBase.hybridSign;
395
            boolean isFirst = true;
396
            List<HybridRelationship> rels = taxonName.getOrderedChildRelationships();
397
            for (HybridRelationship rel: rels){
398
                if (! isFirst){
399
                    tags.add(new TaggedText(TagEnum.hybridSign, hybridSeparator));
400
                }
401
                isFirst = false;
402
                tags.addAll(getTaggedTitle(rel.getParentName()));
403
            }
404
            return tags;
405
        }else if (taxonName.isAutonym()){
406
            //Autonym
407
            tags.addAll(handleTaggedAutonym(taxonName));
408
        }else{ //not Autonym
409
            List<TaggedText> nameTags = getTaggedName(taxonName);
410
            tags.addAll(nameTags);
411
            String authorCache = getAuthorshipCache(taxonName);
412
            if (StringUtils.isNotBlank(authorCache)){
413
                tags.add(new TaggedText(TagEnum.authors, authorCache));
414
            }
415
        }
416

    
417
        return tags;
418
    }
419

    
420
    /**
421
     * @param taxonName
422
     * @return
423
     */
424
    private List<TaggedText> getViralTaggedTitle(TaxonName viralName) {
425
        List<TaggedText> tags = new ArrayList<>();
426
        if (viralName.isProtectedTitleCache()){
427
            //protected title cache
428
            tags.add(new TaggedText(TagEnum.name, viralName.getTitleCache()));
429
            return tags;
430
        }else{
431
            if (StringUtils.isNotBlank(viralName.getAcronym())){
432
                //this is not according to the code
433
                tags.add(new TaggedText(TagEnum.name, viralName.getAcronym()));
434
            }
435
            return tags;
436
        }
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(TaxonName nonViralName) {
446
        if (nonViralName == null){
447
            return null;
448
        }else if (nonViralName.isViral()){
449
            return null;
450
        }
451
        List<TaggedText> tags = new ArrayList<>();
452
        Rank rank = nonViralName.getRank();
453

    
454
        if (nonViralName.isProtectedNameCache()){
455
            tags.add(new TaggedText(TagEnum.name, nonViralName.getNameCache()));
456
        }else if (nonViralName.isHybridFormula()){
457
            //hybrid formula
458
            String hybridSeparator = NonViralNameParserImplRegExBase.hybridSign;
459
            boolean isFirst = true;
460
            List<HybridRelationship> rels = nonViralName.getOrderedChildRelationships();
461
            for (HybridRelationship rel: rels){
462
                if (! isFirst){
463
                    tags.add(new TaggedText(TagEnum.hybridSign, hybridSeparator));
464
                }
465
                isFirst = false;
466
                tags.addAll(getTaggedName(rel.getParentName()));
467
            }
468
            return tags;
469

    
470
        }else if (rank == null){
471
            tags = getRanklessTaggedNameCache(nonViralName);
472
//		}else if (nonViralName.isInfragenericUnranked()){
473
//			result = getUnrankedInfragenericNameCache(nonViralName);
474
        }else if (rank.isInfraSpecific()){
475
            tags = getInfraSpeciesTaggedNameCache(nonViralName);
476
        }else if (rank.isSpecies() || isAggregateWithAuthorship(nonViralName, rank) ){ //exception see #4288
477
            tags = getSpeciesTaggedNameCache(nonViralName);
478
        }else if (rank.isInfraGeneric()){
479
            tags = getInfraGenusTaggedNameCache(nonViralName);
480
        }else if (rank.isGenus()){
481
            tags = getGenusOrUninomialTaggedNameCache(nonViralName);
482
        }else if (rank.isSupraGeneric()){
483
            tags = getGenusOrUninomialTaggedNameCache(nonViralName);
484
        }else{
485
            tags = getRanklessTaggedNameCache(nonViralName);
486
            logger.warn("Rank does not belong to a rank class: " + rank.getTitleCache() + ". Used rankless nameCache for name " + nonViralName.getUuid());
487
        }
488
        //TODO handle appended phrase here instead of different places, check first if this is true for all
489
        //cases
490

    
491
        return tags;
492

    
493
    }
494

    
495

    
496

    
497

    
498
//***************************** PRIVATES ***************************************/
499

    
500

    
501
    private boolean isAggregateWithAuthorship(TaxonName nonViralName, Rank rank) {
502
		if (rank == null){
503
			return false;
504
		}else{
505
			return rank.isSpeciesAggregate() && ( isNotBlank(nonViralName.getAuthorshipCache()) || nonViralName.getNomenclaturalReference() != null );
506
		}
507
	}
508

    
509

    
510
	/**
511
     * Returns the tag list for an autonym taxon.
512
     *
513
     * @see NonViralName#isAutonym()
514
     * @see BotanicalName#isAutonym()
515
     * @param nonViralName
516
     * @return
517
     */
518
    private List<TaggedText> handleTaggedAutonym(TaxonName nonViralName) {
519
    	List<TaggedText> tags = null;
520
    	if (nonViralName.isInfraSpecific()){
521
	        //species part
522
	        tags = getSpeciesTaggedNameCache(nonViralName);
523

    
524
	        //author
525
	        String authorCache = getAuthorshipCache(nonViralName);
526
	        if (StringUtils.isNotBlank(authorCache)){
527
	            tags.add(new TaggedText(TagEnum.authors, authorCache));
528
	        }
529

    
530
	        //infra species marker
531
	        if (nonViralName.getRank() == null || !nonViralName.getRank().isInfraSpecific()){
532
	            //TODO handle exception
533
	            logger.warn("Rank for autonym does not exist or is not lower than species !!");
534
	        }else{
535
	            String infraSpeciesMarker = nonViralName.getRank().getAbbreviation();
536
	            if (nonViralName.isTrinomHybrid()){
537
	                infraSpeciesMarker = CdmUtils.concat("", NOTHO, infraSpeciesMarker);
538
	            }
539
	            if (StringUtils.isNotBlank(infraSpeciesMarker)){
540
	                tags.add(new TaggedText(TagEnum.rank, infraSpeciesMarker));
541
	            }
542
	        }
543

    
544
	        //infra species
545
	        String infraSpeciesPart = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet()).trim();
546
	        if (StringUtils.isNotBlank(infraSpeciesPart)){
547
	            tags.add(new TaggedText(TagEnum.name, infraSpeciesPart));
548
	        }
549

    
550
        } else if (nonViralName.isInfraGeneric()){
551
        	//genus part
552
	       tags =getGenusOrUninomialTaggedNameCache(nonViralName);
553

    
554
	       //author
555
           String authorCache = getAuthorshipCache(nonViralName);
556
           if (StringUtils.isNotBlank(authorCache)){
557
               tags.add(new TaggedText(TagEnum.authors, authorCache));
558
           }
559

    
560
	        //infra species marker
561
	        if (nonViralName.getRank() == null || !nonViralName.getRank().isInfraGeneric()){
562
	            //TODO handle exception
563
	            logger.warn("Rank for autonym does not exist or is not lower than species !!");
564
	        }else{
565
	        	Rank rank = nonViralName.getRank();
566
	            String infraGenericMarker = rank.getAbbreviation();
567
                if (rank.equals(Rank.SECTION_BOTANY()) || rank.equals(Rank.SUBSECTION_BOTANY())){
568
                	infraGenericMarker = infraGenericMarker.replace("(bot.)", "");
569
                }
570
	            if (StringUtils.isNotBlank(infraGenericMarker)){
571
	                tags.add(new TaggedText(TagEnum.rank, infraGenericMarker));
572
	            }
573
	        }
574

    
575
	        //infra genus
576
	        String infraGenericPart = CdmUtils.Nz(nonViralName.getInfraGenericEpithet()).trim();
577
	        if (StringUtils.isNotBlank(infraGenericPart)){
578
	            tags.add(new TaggedText(TagEnum.name, infraGenericPart));
579
	        }
580
        }
581

    
582
        return tags;
583
    }
584

    
585

    
586

    
587
    /**
588
     * Returns the tag list for rankless taxa.
589
     * @param nonViralName
590
     * @return
591
     */
592
    protected List<TaggedText> getRanklessTaggedNameCache(INonViralName nonViralName){
593
        List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
594
        String speciesEpi = CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim();
595
        if (StringUtils.isNotBlank(speciesEpi)){
596
            tags.add(new TaggedText(TagEnum.name, speciesEpi));
597
        }
598

    
599
        String infraSpeciesEpi = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet());
600
        if (StringUtils.isNotBlank(infraSpeciesEpi)){
601
            tags.add(new TaggedText(TagEnum.name, infraSpeciesEpi));
602
        }
603

    
604
        //result += " (rankless)";
605
        addAppendedTaggedPhrase(tags, nonViralName);
606
        return tags;
607
    }
608

    
609
    /**
610
     * Returns the tag list for the first epithet (including a hybrid sign if required).
611
     * @param nonViralName
612
     * @return
613
     */
614
    private List<TaggedText> getUninomialTaggedPart(INonViralName nonViralName) {
615
        List<TaggedText> tags = new ArrayList<>();
616

    
617
        if (nonViralName.isMonomHybrid()){
618
            addHybridPrefix(tags);
619
        }
620

    
621
        String uninomial = CdmUtils.Nz(nonViralName.getGenusOrUninomial()).trim();
622
        if (StringUtils.isNotBlank(uninomial)){
623
            tags.add(new TaggedText(TagEnum.name, uninomial));
624
        }
625

    
626
        return tags;
627
    }
628

    
629
    /**
630
     * Returns the tag list for an genus or higher taxon.
631
     *
632
     * @param nonViralName
633
     * @return
634
     */
635
    protected List<TaggedText> getGenusOrUninomialTaggedNameCache(INonViralName nonViralName){
636
        List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
637
        addAppendedTaggedPhrase(tags, nonViralName);
638
        return tags;
639
    }
640

    
641
    /**
642
     * Returns the tag list for an infrageneric taxon (including species aggregates).
643
     *
644
     * @see #getSpeciesAggregateTaggedCache(NonViralName)
645
     * @param nonViralName
646
     * @return
647
     */
648
    protected List<TaggedText> getInfraGenusTaggedNameCache(INonViralName nonViralName){
649
        Rank rank = nonViralName.getRank();
650
        if (rank != null && rank.isSpeciesAggregate() && isBlank(nonViralName.getAuthorshipCache())){
651
            return getSpeciesAggregateTaggedCache(nonViralName);
652
        }
653

    
654
        //genus
655
        List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
656

    
657
        //marker
658
        String infraGenericMarker;
659
        if (rank != null){
660
            try {
661
                infraGenericMarker = rank.getInfraGenericMarker();
662
                if (rank.equals(Rank.SECTION_BOTANY()) || rank.equals(Rank.SUBSECTION_BOTANY())){
663
                	infraGenericMarker = infraGenericMarker.replace("(bot.)", "");
664
                }
665
            } catch (UnknownCdmTypeException e) {
666
                infraGenericMarker = "'unhandled infrageneric rank'";
667
            }
668
        }else{
669
        	infraGenericMarker = "'undefined infrageneric rank'";
670
        }
671
        String infraGenEpi = CdmUtils.Nz(nonViralName.getInfraGenericEpithet()).trim();
672
        if (nonViralName.isBinomHybrid()){
673
            infraGenericMarker = CdmUtils.concat("", NOTHO, infraGenericMarker);
674
        }
675

    
676
        addInfraGenericPart(nonViralName, tags, infraGenericMarker, infraGenEpi);
677

    
678
        addAppendedTaggedPhrase(tags, nonViralName);
679
        return tags;
680
    }
681

    
682

    
683
	/**
684
	 * Default implementation for the infrageneric part of a name.
685
	 * This is usually the infrageneric marker and the infrageneric epitheton. But may be
686
	 * implemented differently e.g. for zoological names the infrageneric epitheton
687
	 * may be surrounded by brackets and the marker left out.
688
	 * @param nonViralName
689
	 * @param tags
690
	 * @param infraGenericMarker
691
	 */
692
	protected void addInfraGenericPart(
693
	        @SuppressWarnings("unused") INonViralName name,
694
	        List<TaggedText> tags,
695
	        String infraGenericMarker,
696
	        String infraGenEpi) {
697
		//add marker
698
		tags.add(new TaggedText(TagEnum.rank, infraGenericMarker));
699

    
700
		//add epitheton
701
		if (StringUtils.isNotBlank(infraGenEpi)){
702
            tags.add(new TaggedText(TagEnum.name, infraGenEpi));
703
        }
704
	}
705

    
706
    /**
707
     * Returns the tag list for a species aggregate (or similar) taxon.<BR>
708
     * Possible ranks for a <i>species aggregate</i> are "aggr.", "species group", ...
709
     * @param nonViralName
710
     * @return
711
     */
712
    protected List<TaggedText> getSpeciesAggregateTaggedCache(INonViralName nonViralName){
713
        if (! isBlank(nonViralName.getAuthorshipCache())){
714
        	List<TaggedText> result = getSpeciesTaggedNameCache(nonViralName);
715
        	return result;
716
        }
717

    
718

    
719
    	List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
720

    
721
        addSpeciesAggregateTaggedEpithet(tags, nonViralName);
722
        addAppendedTaggedPhrase(tags, nonViralName);
723
        return tags;
724
    }
725

    
726
    /**
727
     * Adds the aggregate tag to the tag list.
728
     * @param tags
729
     * @param nonViralName
730
     */
731
    private void addSpeciesAggregateTaggedEpithet(List<TaggedText> tags, INonViralName nonViralName) {
732
        String marker;
733
        try {
734
            marker = nonViralName.getRank().getInfraGenericMarker();
735
        } catch (UnknownCdmTypeException e) {
736
            marker = "'unknown aggregat type'";
737
        }
738
        if (StringUtils.isNotBlank(marker)){
739
            tags.add(new TaggedText(TagEnum.rank, marker));
740
        }
741
    }
742

    
743

    
744
    /**
745
     * Returns the tag list for a species taxon.
746
     * @param nonViralName
747
     * @return
748
     */
749
    protected List<TaggedText> getSpeciesTaggedNameCache(INonViralName nonViralName){
750
        List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
751
        addAppendedTaggedPhrase(tags, nonViralName);
752
        return tags;
753
    }
754

    
755
    protected List<TaggedText> getInfraSpeciesTaggedNameCache(TaxonName name){
756
        if (name.getNameType().isZoological()){
757
            boolean includeMarker =includeInfraSpecificMarkerForZooNames(name);
758
            return getInfraSpeciesTaggedNameCache(name, includeMarker);
759
        }else{
760
            return getInfraSpeciesTaggedNameCache(name, true);
761
        }
762
    }
763

    
764
    protected boolean includeInfraSpecificMarkerForZooNames(TaxonName name){
765
        return ! (name.isAutonym());  //only exclude marker if autonym, see also ZooNameNoMarkerCacheStrategy
766
    }
767

    
768
    /**
769
     * Creates the tag list for an infraspecific taxon. If include is true the result will contain
770
     * the infraspecific marker (e.g. "var.")
771
     * @param nonViralName
772
     * @param includeMarker
773
     * @return
774
     */
775
    protected List<TaggedText> getInfraSpeciesTaggedNameCache(INonViralName nonViralName, boolean includeMarker){
776
        List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
777
        if (includeMarker || nonViralName.isTrinomHybrid()){
778
            String marker = (nonViralName.getRank().getAbbreviation()).trim().replace("null", "");
779
            if (nonViralName.isTrinomHybrid()){
780
                marker = CdmUtils.concat("", NOTHO, marker);
781
            }
782
            if (StringUtils.isNotBlank(marker)){
783
                tags.add(new TaggedText(TagEnum.rank, marker));
784
            }
785

    
786
        }
787
        String infrSpecEpi = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet());
788

    
789
        infrSpecEpi = infrSpecEpi.trim().replace("null", "");
790

    
791
        if (StringUtils.isNotBlank(infrSpecEpi)){
792
            tags.add(new TaggedText(TagEnum.name, infrSpecEpi));
793
        }
794

    
795
        addAppendedTaggedPhrase(tags, nonViralName);
796
        return tags;
797
    }
798

    
799

    
800
    /**
801
     * Adds a tag for the hybrid sign and an empty separator to avoid trailing whitespaces.
802
     * @param tags
803
     */
804
    private void addHybridPrefix(List<TaggedText> tags) {
805
        tags.add(new TaggedText(TagEnum.hybridSign, NonViralNameParserImplRegExBase.hybridSign));
806
        tags.add(new TaggedText(TagEnum.separator, "")); //no whitespace separator
807
    }
808

    
809
    /**
810
     * Creates the tag list for the genus and species part.
811
     * @param nonViralName
812
     * @return
813
     */
814
    private List<TaggedText> getGenusAndSpeciesTaggedPart(INonViralName nonViralName) {
815
        //Uninomial
816
        List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
817

    
818
        //InfraGenericEpi
819
        boolean hasInfraGenericEpi = StringUtils.isNotBlank(nonViralName.getInfraGenericEpithet());
820
        if (hasInfraGenericEpi){
821
            String infrGenEpi = nonViralName.getInfraGenericEpithet().trim();
822
            if (nonViralName.isBinomHybrid()){
823
                //maybe not correct but not really expected to happen
824
                infrGenEpi = UTF8.HYBRID + infrGenEpi;
825
            }
826
            infrGenEpi = "(" + infrGenEpi + ")";
827
            tags.add(new TaggedText(TagEnum.name, infrGenEpi));
828
        }
829

    
830
        //Species Epi
831
        String specEpi = CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim();
832
        if (! hasInfraGenericEpi && nonViralName.isBinomHybrid() ||
833
                hasInfraGenericEpi && nonViralName.isTrinomHybrid()){
834
            addHybridPrefix(tags);
835
        }
836
        if (StringUtils.isNotBlank(specEpi)){
837
            tags.add(new TaggedText(TagEnum.name, specEpi));
838
        }
839
        return tags;
840
    }
841

    
842
    /**
843
     * Adds the tag for the appended phrase if an appended phrase exists
844
     * @param tags
845
     * @param nonViralName
846
     */
847
    protected void addAppendedTaggedPhrase(List<TaggedText> tags, INonViralName nonViralName){
848
        String appendedPhrase = nonViralName ==null ? null : nonViralName.getAppendedPhrase();
849
        if (StringUtils.isNotEmpty(appendedPhrase)){
850
            tags.add(new TaggedText(TagEnum.name, appendedPhrase));
851
        }
852
    }
853

    
854

    
855
	@Override
856
    public String getLastEpithet(TaxonName taxonName) {
857
        Rank rank = taxonName.getRank();
858
        if(rank.isGenus() || rank.isSupraGeneric()) {
859
            return taxonName.getGenusOrUninomial();
860
        } else if(rank.isInfraGeneric()) {
861
            return taxonName.getInfraGenericEpithet();
862
        } else if(rank.isSpecies()) {
863
            return taxonName.getSpecificEpithet();
864
        } else {
865
            return taxonName.getInfraSpecificEpithet();
866
        }
867
    }
868

    
869
    @Override
870
    protected List<TaggedText> doGetTaggedTitle(TaxonName taxonName) {
871
        List<TaggedText> tags = new ArrayList<>();
872
        if (taxonName.getNameType().isViral()){
873
            String acronym = taxonName.getAcronym();
874
            tags.add(new TaggedText(TagEnum.name, acronym));
875
            return tags;
876
        }else if (taxonName.isHybridFormula()){
877
            //hybrid formula
878
            String hybridSeparator = NonViralNameParserImplRegExBase.hybridSign;
879
            boolean isFirst = true;
880
            List<HybridRelationship> rels = taxonName.getOrderedChildRelationships();
881
            for (HybridRelationship rel: rels){
882
                if (! isFirst){
883
                    tags.add(new TaggedText(TagEnum.hybridSign, hybridSeparator));
884
                }
885
                isFirst = false;
886
                tags.addAll(getTaggedTitle(rel.getParentName()));
887
            }
888
            return tags;
889
        }else if (taxonName.isAutonym()){
890
            //Autonym
891
            tags.addAll(handleTaggedAutonym(taxonName));
892
        }else{ //not Autonym
893
             List<TaggedText> nameTags = getTaggedName(taxonName);
894
            tags.addAll(nameTags);
895
            String authorCache = getAuthorshipCache(taxonName);
896
            if (StringUtils.isNotBlank(authorCache)){
897
                tags.add(new TaggedText(TagEnum.authors, authorCache));
898
            }
899
        }
900
        return tags;
901
    }
902

    
903
}
(6-6/7)