Project

General

Profile

Download (32.5 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.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.hibernate.HibernateProxyHelper;
22
import eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor;
23
import eu.etaxonomy.cdm.model.common.CdmBase;
24
import eu.etaxonomy.cdm.model.common.Language;
25
import eu.etaxonomy.cdm.model.common.Representation;
26
import eu.etaxonomy.cdm.model.name.BotanicalName;
27
import eu.etaxonomy.cdm.model.name.HybridRelationship;
28
import eu.etaxonomy.cdm.model.name.NameRelationship;
29
import eu.etaxonomy.cdm.model.name.NameRelationshipType;
30
import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
31
import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
32
import eu.etaxonomy.cdm.model.name.NonViralName;
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 NonViralName<?>>
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
    protected CharSequence BasionymAuthorCombinationAuthorSeperator = " ";
67

    
68
    @Override
69
    public  UUID getUuid(){
70
        return uuid;
71
    }
72

    
73

    
74
    /**
75
     * Factory method
76
     * @return NonViralNameDefaultCacheStrategy A new instance of  NonViralNameDefaultCacheStrategy
77
     */
78
    public static NonViralNameDefaultCacheStrategy NewInstance(){
79
        return new NonViralNameDefaultCacheStrategy();
80
    }
81

    
82
    /**
83
     * Factory method
84
     * @return NonViralNameDefaultCacheStrategy A new instance of  NonViralNameDefaultCacheStrategy
85
     */
86
    public static <T extends NonViralName<?>> NonViralNameDefaultCacheStrategy<T> NewInstance(Class<T> clazz){
87
        return new NonViralNameDefaultCacheStrategy<T>();
88
    }
89

    
90
    /**
91
     * Constructor
92
     */
93
    protected NonViralNameDefaultCacheStrategy(){
94
        super();
95
    }
96

    
97
/* **************** GETTER / SETTER **************************************/
98

    
99
    /**
100
     * String that separates the NameCache part from the AuthorCache part
101
     * @return
102
     */
103
    public String getNameAuthorSeperator() {
104
        return NameAuthorSeperator;
105
    }
106

    
107

    
108
    public void setNameAuthorSeperator(String nameAuthorSeperator) {
109
        NameAuthorSeperator = nameAuthorSeperator;
110
    }
111

    
112

    
113
    /**
114
     * String the basionym author part starts with e.g. '('.
115
     * This should correspond with the {@link NonViralNameDefaultCacheStrategy#getBasionymEnd() basionymEnd} attribute
116
     * @return
117
     */
118
    public String getBasionymStart() {
119
        return BasionymStart;
120
    }
121

    
122

    
123
    public void setBasionymStart(String basionymStart) {
124
        BasionymStart = basionymStart;
125
    }
126

    
127

    
128
    /**
129
     * String the basionym author part ends with e.g. ')'.
130
     * This should correspond with the {@link NonViralNameDefaultCacheStrategy#getBasionymStart() basionymStart} attribute
131
     * @return
132
     */
133
    public String getBasionymEnd() {
134
        return BasionymEnd;
135
    }
136

    
137

    
138
    public void setBasionymEnd(String basionymEnd) {
139
        BasionymEnd = basionymEnd;
140
    }
141

    
142

    
143
    /**
144
     * String to separate ex author from author.
145
     * @return
146
     */
147
    public String getExAuthorSeperator() {
148
        return ExAuthorSeperator;
149
    }
150

    
151

    
152
    public void setExAuthorSeperator(String exAuthorSeperator) {
153
        ExAuthorSeperator = exAuthorSeperator;
154
    }
155

    
156

    
157
    /**
158
     * String that separates the basionym/original_combination author part from the combination author part
159
     * @return
160
     */
161
    public CharSequence getBasionymAuthorCombinationAuthorSeperator() {
162
        return BasionymAuthorCombinationAuthorSeperator;
163
    }
164

    
165

    
166
    public void setBasionymAuthorCombinationAuthorSeperator( CharSequence basionymAuthorCombinationAuthorSeperator) {
167
        BasionymAuthorCombinationAuthorSeperator = basionymAuthorCombinationAuthorSeperator;
168
    }
169

    
170

    
171
//** *****************************************************************************************/
172

    
173
    @Override
174
    public String getTitleCache(T nonViralName) {
175
    	return getTitleCache(nonViralName, null);
176
    }
177

    
178
    @Override
179
	public String getTitleCache(T nonViralName, HTMLTagRules htmlTagRules) {
180
    	List<TaggedText> tags = getTaggedTitle(nonViralName);
181
		if (tags == null){
182
			return null;
183
		}else{
184
			String result = createString(tags, htmlTagRules);
185
		    return result;
186
		}
187
    }
188

    
189
	@Override
190
	public String getFullTitleCache(T nonViralName, HTMLTagRules htmlTagRules) {
191
		List<TaggedText> tags = getTaggedFullTitle(nonViralName);
192
	    if (tags == null){
193
	    	return null;
194
	    }else{
195
	    	String result = createString(tags, htmlTagRules);
196
	    	return result;
197
	    }
198
	}
199

    
200
    @Override
201
    public String getFullTitleCache(T nonViralName) {
202
    	return getFullTitleCache(nonViralName, null);
203
    }
204

    
205

    
206
    /**
207
     * Generates and returns the "name cache" (only scientific name without author teams and year).
208
     * @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy#getNameCache(eu.etaxonomy.cdm.model.name.TaxonNameBase)
209
     */
210
    @Override
211
    public String getNameCache(T nonViralName) {
212
        List<TaggedText> tags = getTaggedName(nonViralName);
213
        if (tags == null){
214
            return null;
215
        }else{
216
            String result = createString(tags);
217
            return result;
218
        }
219
    }
220

    
221

    
222

    
223

    
224

    
225

    
226

    
227
// ******************* Authorship ******************************/
228

    
229

    
230
    @Override
231
    public String getAuthorshipCache(T nonViralName) {
232
        if (nonViralName == null){
233
            return null;
234
        }
235
        //cache protected
236
        if (nonViralName.isProtectedAuthorshipCache() == true) {
237
            return nonViralName.getAuthorshipCache();
238
        }
239
        return getNonCacheAuthorshipCache(nonViralName);
240

    
241
    }
242

    
243
    /**
244
     * Returns the authorshipcache string for the atomized authorship fields. Does not use the authorship field.
245
     * @throws NullPointerException if nonViralName is null.
246
     * @param nonViralName
247
     * @return
248
     */
249
    protected String getNonCacheAuthorshipCache(T nonViralName){
250
        String result = "";
251
        INomenclaturalAuthor combinationAuthor = nonViralName.getCombinationAuthorship();
252
        INomenclaturalAuthor exCombinationAuthor = nonViralName.getExCombinationAuthorship();
253
        INomenclaturalAuthor basionymAuthor = nonViralName.getBasionymAuthorship();
254
        INomenclaturalAuthor exBasionymAuthor = nonViralName.getExBasionymAuthorship();
255
        String basionymPart = "";
256
        String authorPart = "";
257
        //basionym
258
        if (basionymAuthor != null || exBasionymAuthor != null){
259
            basionymPart = BasionymStart + getAuthorAndExAuthor(basionymAuthor, exBasionymAuthor) + BasionymEnd;
260
        }
261
        if (combinationAuthor != null || exCombinationAuthor != null){
262
            authorPart = getAuthorAndExAuthor(combinationAuthor, exCombinationAuthor);
263
        }
264
        result = CdmUtils.concat(BasionymAuthorCombinationAuthorSeperator, basionymPart, authorPart);
265
//        if ("".equals(result)){
266
//        	result = null;
267
//        }
268
        return result;
269
    }
270

    
271
    /**
272
     * Returns the AuthorCache part for a combination of an author and an ex author. This applies on combination authors
273
     * as well as on basionym/orginal combination authors.
274
     * @param author the author
275
     * @param exAuthor the ex-author
276
     * @return
277
     */
278
    protected String getAuthorAndExAuthor(INomenclaturalAuthor author, INomenclaturalAuthor exAuthor){
279
        String result = "";
280
        String authorString = "";
281
        String exAuthorString = "";
282
        if (author != null){
283
            authorString = CdmUtils.Nz(author.getNomenclaturalTitle());
284
        }
285
        if (exAuthor != null){
286
            exAuthorString = CdmUtils.Nz(exAuthor.getNomenclaturalTitle());
287
        }
288
        if (exAuthorString.length() > 0 ){
289
            exAuthorString = exAuthorString + ExAuthorSeperator;
290
        }
291
        result = exAuthorString + authorString;
292
        return result;
293
    }
294

    
295

    
296
    /**
297
     * Checks if the given name should include the author in it's cached version.<BR>
298
     * This is usually the case but not for <i>species aggregates</i>.
299
     * @param nonViralName
300
     * @return
301
     */
302
    protected boolean nameIncludesAuthorship(NonViralName<?> nonViralName){
303
        Rank rank = nonViralName.getRank();
304
        if (rank != null && rank.isSpeciesAggregate()){
305
            return false;
306
        }else{
307
            return true;
308
        }
309
    }
310

    
311
// ************* TAGGED NAME ***************************************/
312

    
313
    @Override
314
    public List<TaggedText> getTaggedFullTitle(T nonViralName) {
315
        List<TaggedText> tags = new ArrayList<TaggedText>();
316

    
317
        //null
318
        if (nonViralName == null){
319
            return null;
320
        }
321

    
322
        //protected full title cache
323
        if (nonViralName.isProtectedFullTitleCache()){
324
            tags.add(new TaggedText(TagEnum.fullName, nonViralName.getFullTitleCache()));
325
            return tags;
326
        }
327

    
328
        //title cache
329
//		String titleCache = nonViralName.getTitleCache();
330
        List<TaggedText> titleTags = getTaggedTitle(nonViralName);
331
        tags.addAll(titleTags);
332

    
333

    
334
        //reference
335
        String microReference = nonViralName.getNomenclaturalMicroReference();
336
        INomenclaturalReference ref = nonViralName.getNomenclaturalReference();
337
        String referenceCache = null;
338
        if (ref != null){
339
            Reference reference = HibernateProxyHelper.deproxy(ref, Reference.class);
340
            //FIXME #5833 not needed anymore
341
            reference.setCacheStrategy(reference.getType().getCacheStrategy());
342
            referenceCache = reference.getNomenclaturalCitation(microReference);
343
        }
344
            //add to tags
345
        if (StringUtils.isNotBlank(referenceCache)){
346
            if (! referenceCache.trim().startsWith("in ")){
347
                String refConcat = ", ";
348
                tags.add(new TaggedText(TagEnum.separator, refConcat));
349
            }
350
            tags.add(new TaggedText(TagEnum.reference, referenceCache));
351
        }
352

    
353
        //nomenclatural status
354
        tags.addAll(getNomStatusTags(nonViralName, true, false));
355
        return tags;
356

    
357
    }
358

    
359

    
360
    /**
361
     * @param nonViralName
362
     * @param tags
363
     * @return
364
     */
365
    @Override
366
    public List<TaggedText> getNomStatusTags(T nonViralName, boolean includeSeparatorBefore,
367
            boolean includeSeparatorAfter) {
368
        Set<NomenclaturalStatus> ncStati = nonViralName.getStatus();
369
        Iterator<NomenclaturalStatus> iterator = ncStati.iterator();
370
        List<TaggedText> nomStatusTags = new ArrayList<TaggedText>();
371
        while (iterator.hasNext()) {
372
            NomenclaturalStatus ncStatus = iterator.next();
373
            // since the NewInstance method of nomencatural status allows null as parameter
374
            // we have to check for null values here
375
            String nomStatusStr = "not defined";
376
            if(ncStatus.getType() != null){
377
                NomenclaturalStatusType statusType =  ncStatus.getType();
378
                Language lang = Language.LATIN();
379
                Representation repr = statusType.getRepresentation(lang);
380
                if (repr != null){
381
                    nomStatusStr = repr.getAbbreviatedLabel();
382
                }else{
383
                    String message = "No latin representation available for nom. status. " + statusType.getTitleCache();
384
                    logger.warn(message);
385
                    throw new IllegalStateException(message);
386
                }
387
            }else if(StringUtils.isNotBlank(ncStatus.getRuleConsidered())){
388
                nomStatusStr = ncStatus.getRuleConsidered();
389
            }
390
            String statusSeparator = ", ";
391
            if (includeSeparatorBefore){
392
                nomStatusTags.add(new TaggedText(TagEnum.separator, statusSeparator));
393
            }
394
            nomStatusTags.add(new TaggedText(TagEnum.nomStatus, nomStatusStr));
395
            if (includeSeparatorAfter){
396
                nomStatusTags.add(new TaggedText(TagEnum.postSeparator, ","));
397
            }
398
        }
399
        return nomStatusTags;
400
    }
401

    
402
    @Override
403
    public List<TaggedText> getTaggedTitle(T nonViralName) {
404
        if (nonViralName == null){
405
            return null;
406
        }
407

    
408
        List<TaggedText> tags = new ArrayList<TaggedText>();
409

    
410
        //TODO how to handle protected fullTitleCache here?
411

    
412
        if (nonViralName.isProtectedTitleCache()){
413
            //protected title cache
414
            tags.add(new TaggedText(TagEnum.name, nonViralName.getTitleCache()));
415
            return tags;
416
        }else if (nonViralName.isHybridFormula()){
417
            //hybrid formula
418
            String hybridSeparator = NonViralNameParserImplRegExBase.hybridSign;
419
            boolean isFirst = true;
420
            List<HybridRelationship> rels = nonViralName.getOrderedChildRelationships();
421
            for (HybridRelationship rel: rels){
422
                if (! isFirst){
423
                    tags.add(new TaggedText(TagEnum.hybridSign, hybridSeparator));
424
                }
425
                isFirst = false;
426
                tags.addAll(getTaggedTitle((T)rel.getParentName()));
427
            }
428
            return tags;
429
        }else if (nonViralName.isAutonym()){
430
            //Autonym
431
            tags.addAll(handleTaggedAutonym(nonViralName));
432
        }else{ //not Autonym
433
//			String nameCache = nonViralName.getNameCache();  //OLD: CdmUtils.Nz(getNameCache(nonViralName));
434
            List<TaggedText> nameTags = getTaggedName(nonViralName);
435
            tags.addAll(nameTags);
436
            String authorCache = getAuthorshipCache(nonViralName);
437
            if (StringUtils.isNotBlank(authorCache)){
438
                tags.add(new TaggedText(TagEnum.authors, authorCache));
439
            }
440
        }
441
        return tags;
442
    }
443

    
444
    /**
445
     * Returns the tag list of the name part (without author and reference).
446
     * @param nonViralName
447
     * @return
448
     */
449
    @Override
450
    public List<TaggedText> getTaggedName(T nonViralName) {
451
        if (nonViralName == null){
452
            return null;
453
        }
454
        List<TaggedText> tags = new ArrayList<TaggedText>();
455
        Rank rank = nonViralName.getRank();
456

    
457
        if (nonViralName.isProtectedNameCache()){
458
            tags.add(new TaggedText(TagEnum.name, nonViralName.getNameCache()));
459
        }else if (rank == null){
460
            tags = getRanklessTaggedNameCache(nonViralName);
461
//		}else if (nonViralName.isInfragenericUnranked()){
462
//			result = getUnrankedInfragenericNameCache(nonViralName);
463
        }else if (rank.isInfraSpecific()){
464
            tags = getInfraSpeciesTaggedNameCache(nonViralName);
465
        }else if (rank.isSpecies() || isAggregateWithAuthorship(nonViralName, rank) ){ //exception see #4288
466
            tags = getSpeciesTaggedNameCache(nonViralName);
467
        }else if (rank.isInfraGeneric()){
468
            tags = getInfraGenusTaggedNameCache(nonViralName);
469
        }else if (rank.isGenus()){
470
            tags = getGenusOrUninomialTaggedNameCache(nonViralName);
471
        }else if (rank.isSupraGeneric()){
472
            tags = getGenusOrUninomialTaggedNameCache(nonViralName);
473
        }else{
474
            logger.warn("Name Strategy for Name (UUID: " + nonViralName.getUuid() +  ") not yet implemented");
475
        }
476
        //TODO handle appended phrase here instead of different places, check first if this is true for all
477
        //cases
478

    
479
        return tags;
480

    
481
    }
482

    
483

    
484

    
485

    
486
//***************************** PRIVATES ***************************************/
487

    
488

    
489
    private boolean isAggregateWithAuthorship(T nonViralName, Rank rank) {
490
		if (rank == null){
491
			return false;
492
		}else{
493
			return rank.isSpeciesAggregate() && ( isNotBlank(nonViralName.getAuthorshipCache()) || nonViralName.getNomenclaturalReference() != null );
494
		}
495
	}
496

    
497

    
498
	/**
499
     * Returns the tag list for an autonym taxon.
500
     *
501
     * @see NonViralName#isAutonym()
502
     * @see BotanicalName#isAutonym()
503
     * @param nonViralName
504
     * @return
505
     */
506
    private List<TaggedText> handleTaggedAutonym(T nonViralName) {
507
    	List<TaggedText> tags = null;
508
    	if (nonViralName.isInfraSpecific()){
509
	        //species part
510
	        tags = getSpeciesTaggedNameCache(nonViralName);
511

    
512
	        //author
513
	        String authorCache = getAuthorshipCache(nonViralName);
514
	        if (StringUtils.isNotBlank(authorCache)){
515
	            tags.add(new TaggedText(TagEnum.authors, authorCache));
516
	        }
517

    
518

    
519
	        //infra species marker
520
	        if (nonViralName.getRank() == null || !nonViralName.getRank().isInfraSpecific()){
521
	            //TODO handle exception
522
	            logger.warn("Rank for autonym does not exist or is not lower than species !!");
523
	        }else{
524
	            String infraSpeciesMarker = nonViralName.getRank().getAbbreviation();
525
	            if (StringUtils.isNotBlank(infraSpeciesMarker)){
526
	                tags.add(new TaggedText(TagEnum.rank, infraSpeciesMarker));
527
	            }
528
	        }
529

    
530
	        //infra species
531
	        String infraSpeciesPart = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet()).trim();
532
	        if (StringUtils.isNotBlank(infraSpeciesPart)){
533
	            tags.add(new TaggedText(TagEnum.name, infraSpeciesPart));
534
	        }
535

    
536
        } else if (nonViralName.isInfraGeneric()){
537
        	//genus part
538
	       tags =getGenusOrUninomialTaggedNameCache(nonViralName);
539

    
540

    
541
	        //infra species marker
542
	        if (nonViralName.getRank() == null || !nonViralName.getRank().isInfraGeneric()){
543
	            //TODO handle exception
544
	            logger.warn("Rank for autonym does not exist or is not lower than species !!");
545
	        }else{
546
	        	Rank rank = nonViralName.getRank();
547
	            String infraGenericMarker = rank.getAbbreviation();
548
                if (rank.equals(Rank.SECTION_BOTANY()) || rank.equals(Rank.SUBSECTION_BOTANY())){
549
                	infraGenericMarker = infraGenericMarker.replace("(bot.)", "");
550
                }
551
	            if (StringUtils.isNotBlank(infraGenericMarker)){
552
	                tags.add(new TaggedText(TagEnum.rank, infraGenericMarker));
553
	            }
554
	        }
555

    
556
	        //infra species
557
	        String infraGenericPart = CdmUtils.Nz(nonViralName.getInfraGenericEpithet()).trim();
558
	        if (StringUtils.isNotBlank(infraGenericPart)){
559
	            tags.add(new TaggedText(TagEnum.name, infraGenericPart));
560
	        }
561

    
562
        }
563

    
564
        return tags;
565
    }
566

    
567

    
568

    
569
    /**
570
     * Returns the tag list for rankless taxa.
571
     * @param nonViralName
572
     * @return
573
     */
574
    protected List<TaggedText> getRanklessTaggedNameCache(NonViralName<?> nonViralName){
575
        List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
576
        String speciesEpi = CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim();
577
        if (StringUtils.isNotBlank(speciesEpi)){
578
            tags.add(new TaggedText(TagEnum.name, speciesEpi));
579
        }
580

    
581
        String infraSpeciesEpi = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet());
582
        if (StringUtils.isNotBlank(infraSpeciesEpi)){
583
            tags.add(new TaggedText(TagEnum.name, infraSpeciesEpi));
584
        }
585

    
586
        //result += " (rankless)";
587
        addAppendedTaggedPhrase(tags, nonViralName);
588
        return tags;
589
    }
590

    
591
    /**
592
     * Returns the tag list for the first epithet (including a hybrid sign if required).
593
     * @param nonViralName
594
     * @return
595
     */
596
    private List<TaggedText> getUninomialTaggedPart(NonViralName<?> nonViralName) {
597
        List<TaggedText> tags = new ArrayList<TaggedText>();
598

    
599
        if (nonViralName.isMonomHybrid()){
600
            addHybridPrefix(tags);
601
        }
602

    
603
        String uninomial = CdmUtils.Nz(nonViralName.getGenusOrUninomial()).trim();
604
        if (StringUtils.isNotBlank(uninomial)){
605
            tags.add(new TaggedText(TagEnum.name, uninomial));
606
        }
607

    
608
        return tags;
609
    }
610

    
611
    /**
612
     * Returns the tag list for an genus or higher taxon.
613
     *
614
     * @param nonViralName
615
     * @return
616
     */
617
    protected List<TaggedText> getGenusOrUninomialTaggedNameCache(NonViralName<?> nonViralName){
618
        List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
619
        addAppendedTaggedPhrase(tags, nonViralName);
620
        return tags;
621
    }
622

    
623
    /**
624
     * Returns the tag list for an infrageneric taxon (including species aggregates).
625
     *
626
     * @see #getSpeciesAggregateTaggedCache(NonViralName)
627
     * @param nonViralName
628
     * @return
629
     */
630
    protected List<TaggedText> getInfraGenusTaggedNameCache(NonViralName<?> nonViralName){
631
        Rank rank = nonViralName.getRank();
632
        if (rank != null && rank.isSpeciesAggregate() && isBlank(nonViralName.getAuthorshipCache())){
633
            return getSpeciesAggregateTaggedCache(nonViralName);
634
        }
635

    
636
        //genus
637
        List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
638

    
639
        //marker
640
        String infraGenericMarker;
641
        if (rank != null){
642
            try {
643
                infraGenericMarker = rank.getInfraGenericMarker();
644
                if (rank.equals(Rank.SECTION_BOTANY()) || rank.equals(Rank.SUBSECTION_BOTANY())){
645
                	infraGenericMarker = infraGenericMarker.replace("(bot.)", "");
646
                }
647
            } catch (UnknownCdmTypeException e) {
648
                infraGenericMarker = "'unhandled infrageneric rank'";
649
            }
650
        }else{
651
        	infraGenericMarker = "'undefined infrageneric rank'";
652
        }
653
        String infraGenEpi = CdmUtils.Nz(nonViralName.getInfraGenericEpithet()).trim();
654

    
655
        addInfraGenericPart(nonViralName, tags, infraGenericMarker, infraGenEpi);
656

    
657
        addAppendedTaggedPhrase(tags, nonViralName);
658
        return tags;
659
    }
660

    
661

    
662
	/**
663
	 * Default implementation for the infrageneric part of a name.
664
	 * This is usually the infrageneric marker and the infrageneric epitheton. But may be implemented differently e.g. for zoological
665
	 * names the infrageneric epitheton may be surrounded by brackets and the marker left out.
666
	 * @param nonViralName
667
	 * @param tags
668
	 * @param infraGenericMarker
669
	 */
670
	protected void addInfraGenericPart(NonViralName<?> name, List<TaggedText> tags, String infraGenericMarker, String infraGenEpi) {
671
		//add marker
672
		tags.add(new TaggedText(TagEnum.rank, infraGenericMarker));
673

    
674
		//add epitheton
675
		if (StringUtils.isNotBlank(infraGenEpi)){
676
            tags.add(new TaggedText(TagEnum.name, infraGenEpi));
677
        }
678
	}
679

    
680
    /**
681
     * Returns the tag list for a species aggregate (or similar) taxon.<BR>
682
     * Possible ranks for a <i>species aggregate</i> are "aggr.", "species group", ...
683
     * @param nonViralName
684
     * @return
685
     */
686
    protected List<TaggedText> getSpeciesAggregateTaggedCache(NonViralName<?> nonViralName){
687
        if (! isBlank(nonViralName.getAuthorshipCache())){
688
        	List<TaggedText> result = getSpeciesTaggedNameCache(nonViralName);
689
        	return result;
690
        }
691

    
692

    
693
    	List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
694

    
695
        addSpeciesAggregateTaggedEpithet(tags, nonViralName);
696
        addAppendedTaggedPhrase(tags, nonViralName);
697
        return tags;
698
    }
699

    
700
    /**
701
     * Adds the aggregate tag to the tag list.
702
     * @param tags
703
     * @param nonViralName
704
     */
705
    private void addSpeciesAggregateTaggedEpithet(List<TaggedText> tags, NonViralName<?> nonViralName) {
706
        String marker;
707
        try {
708
            marker = nonViralName.getRank().getInfraGenericMarker();
709
        } catch (UnknownCdmTypeException e) {
710
            marker = "'unknown aggregat type'";
711
        }
712
        if (StringUtils.isNotBlank(marker)){
713
            tags.add(new TaggedText(TagEnum.rank, marker));
714
        }
715
    }
716

    
717

    
718
    /**
719
     * Returns the tag list for a species taxon.
720
     * @param nonViralName
721
     * @return
722
     */
723
    protected List<TaggedText> getSpeciesTaggedNameCache(NonViralName<?> nonViralName){
724
        List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
725
        addAppendedTaggedPhrase(tags, nonViralName);
726
        return tags;
727
    }
728

    
729
    /**
730
     * Creates the tag list for an infraspecific taxon. In include is true the result will contain
731
     * @param nonViralName
732
     * @return
733
     */
734
    protected List<TaggedText> getInfraSpeciesTaggedNameCache(T nonViralName){
735
        return getInfraSpeciesTaggedNameCache(nonViralName, true);
736
    }
737

    
738
    /**
739
     * Creates the tag list for an infraspecific taxon. If include is true the result will contain
740
     * the infraspecific marker (e.g. "var.")
741
     * @param nonViralName
742
     * @param includeMarker
743
     * @return
744
     */
745
    protected List<TaggedText> getInfraSpeciesTaggedNameCache(NonViralName<?> nonViralName, boolean includeMarker){
746
        List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
747
        if (includeMarker){
748
            String marker = (nonViralName.getRank().getAbbreviation()).trim().replace("null", "");
749
            if (StringUtils.isNotBlank(marker)){
750
                tags.add(new TaggedText(TagEnum.rank, marker));
751
            }
752
        }
753
        String infrSpecEpi = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet());
754
        if (nonViralName.isTrinomHybrid()){
755
            addHybridPrefix(tags);
756
        }
757

    
758
        infrSpecEpi = infrSpecEpi.trim().replace("null", "");
759
        if (StringUtils.isNotBlank(infrSpecEpi)){
760
            tags.add(new TaggedText(TagEnum.name, infrSpecEpi));
761
        }
762

    
763
        addAppendedTaggedPhrase(tags, nonViralName);
764
        return tags;
765
    }
766

    
767

    
768
    /**
769
     * Adds a tag for the hybrid sign and an empty separator to avoid trailing whitespaces.
770
     * @param tags
771
     */
772
    private void addHybridPrefix(List<TaggedText> tags) {
773
        tags.add(new TaggedText(TagEnum.hybridSign, NonViralNameParserImplRegExBase.hybridSign));
774
        tags.add(new TaggedText(TagEnum.separator, "")); //no whitespace separator
775
    }
776

    
777
    /**
778
     * Creates the tag list for the genus and species part.
779
     * @param nonViralName
780
     * @return
781
     */
782
    private List<TaggedText> getGenusAndSpeciesTaggedPart(NonViralName<?> nonViralName) {
783
        //Uninomial
784
        List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
785

    
786
        //InfraGenericEpi
787
        boolean hasInfraGenericEpi = StringUtils.isNotBlank(nonViralName.getInfraGenericEpithet());
788
        if (hasInfraGenericEpi){
789
            String infrGenEpi = nonViralName.getInfraGenericEpithet().trim();
790
            if (nonViralName.isBinomHybrid()){
791
//					addHybridPrefix(tags);  FIXME hybridSign should be tag, but then we need to handle "(" ")" differently.
792
                infrGenEpi = NonViralNameParserImplRegExBase.hybridSign + infrGenEpi;
793
            }
794
            infrGenEpi = "(" + infrGenEpi + ")";
795
            tags.add(new TaggedText(TagEnum.name, infrGenEpi));
796
        }
797

    
798
        //Species Epi
799
        String specEpi = CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim();
800
        if (! hasInfraGenericEpi && nonViralName.isBinomHybrid() ||
801
                hasInfraGenericEpi && nonViralName.isTrinomHybrid()){
802
            addHybridPrefix(tags);
803
        }
804
        if (StringUtils.isNotBlank(specEpi)){
805
            tags.add(new TaggedText(TagEnum.name, specEpi));
806
        }
807
        return tags;
808
    }
809

    
810
    /**
811
     * Adds the tag for the appended phrase if an appended phrase exists
812
     * @param tags
813
     * @param nonViralName
814
     */
815
    protected void addAppendedTaggedPhrase(List<TaggedText> tags, NonViralName<?> nonViralName){
816
        String appendedPhrase = nonViralName ==null ? null : nonViralName.getAppendedPhrase();
817
        String originalName = getOriginalNameString(nonViralName, tags);
818
        if (StringUtils.isNotBlank(originalName)){
819
            tags.add(new TaggedText(TagEnum.name, originalName));
820
        }
821
        if (StringUtils.isNotEmpty(appendedPhrase)){
822
            tags.add(new TaggedText(TagEnum.name, appendedPhrase));
823
        }
824
    }
825

    
826
    private String getOriginalNameString(NonViralName<?> currentName, List<TaggedText> originalNameTaggs) {
827
		List<String> originalNameStrings = new ArrayList<String>(1);
828
    	for (NameRelationship nameRel : currentName.getRelationsToThisName()){  //handle list, just in case we have strange data; this may result in strange looking results
829
			NameRelationshipType type = nameRel.getType();
830
    		if(type != null && type.equals(NameRelationshipType.ORIGINAL_SPELLING())){
831
    			String originalNameString;
832
    			TaxonNameBase<?,?> originalName = nameRel.getFromName();
833
    			if (!originalName.isInstanceOf(NonViralName.class)){
834
    				originalNameString = originalName.getTitleCache();
835
    			}else{
836
    				NonViralName<?> originalNvName = CdmBase.deproxy(originalName, NonViralName.class);
837
    				originalNameString = makeOriginalNameString(currentName, originalNvName, originalNameTaggs);
838
    			}
839
    			originalNameStrings.add("'" + originalNameString +"'");
840
    		}
841
		}
842
    	if (originalNameStrings.size() > 0){
843
    		String result = CdmUtils.concat("", originalNameStrings.toArray(new String[originalNameStrings.size()])) ;
844
	    	return result;
845
    	}else{
846
    		return null;
847
    	}
848
	}
849

    
850

    
851
	private String makeOriginalNameString(NonViralName<?> currentName, NonViralName<?> originalName, List<TaggedText> currentNameTags) {
852
		//use cache if necessary
853
		String cacheToUse = null;
854
		if (originalName.isProtectedNameCache() && StringUtils.isNotBlank(originalName.getNameCache())){
855
			cacheToUse = originalName.getNameCache();
856
		}else if (originalName.isProtectedTitleCache() && StringUtils.isNotBlank(originalName.getTitleCache())){
857
			cacheToUse = originalName.getTitleCache();
858
		}else if (originalName.isProtectedFullTitleCache() && StringUtils.isNotBlank(originalName.getFullTitleCache())){
859
			cacheToUse = originalName.getFullTitleCache();
860
		}
861
		if (cacheToUse != null){
862
			return cacheToUse;
863
		}
864
		//use atomized data
865
		//get originalNameParts array
866
		String originalNameString = originalName.getNameCache();
867
		if (originalNameString == null){
868
			originalNameString = originalName.getTitleCache();
869
		}
870
		if (originalNameString == null){  //should not happen
871
			originalNameString = originalName.getFullTitleCache();
872
		}
873
		String[] originalNameSplit = originalNameString.split("\\s+");
874

    
875
		//get current name parts
876
		String currentNameString = createString(currentNameTags);
877
		String[] currentNameSplit = currentNameString.split("\\s+");
878

    
879
		//compute string
880
		String result = originalNameString;
881
		for (int i = 0; i < Math.min(originalNameSplit.length, currentNameSplit.length); i++){
882
			if (originalNameSplit[i].equals(currentNameSplit[i])){
883
				result = result.replaceFirst(originalNameSplit[i], "").trim();
884
			}
885
		}
886
		//old
887
//		if (originalName.getGenusOrUninomial() != null && originalName.getGenusOrUninomial().equals(currentName.getGenusOrUninomial())){
888
//
889
//		}
890
		return result;
891
	}
892

    
893

    
894
	@Override
895
    public String getLastEpithet(T taxonNameBase) {
896
        Rank rank = taxonNameBase.getRank();
897
        if(rank.isGenus() || rank.isSupraGeneric()) {
898
            return taxonNameBase.getGenusOrUninomial();
899
        } else if(rank.isInfraGeneric()) {
900
            return taxonNameBase.getInfraGenericEpithet();
901
        } else if(rank.isSpecies()) {
902
            return taxonNameBase.getSpecificEpithet();
903
        } else {
904
            return taxonNameBase.getInfraSpecificEpithet();
905
        }
906
    }
907

    
908

    
909
    /**
910
     * @param tags
911
     * @return
912
     */
913
    private String createString(List<TaggedText> tags) {
914
        return TaggedCacheHelper.createString(tags);
915
    }
916

    
917
    /**
918
     * @param tags
919
     * @param htmlTagRules
920
     * @return
921
     */
922
    private String createString(List<TaggedText> tags, HTMLTagRules htmlTagRules) {
923
        return TaggedCacheHelper.createString(tags, htmlTagRules);
924
    }
925

    
926

    
927

    
928
}
(7-7/9)