Project

General

Profile

Download (32.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.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
            referenceCache = reference.getNomenclaturalCitation(microReference);
341
        }
342
            //add to tags
343
        if (StringUtils.isNotBlank(referenceCache)){
344
            if (! referenceCache.trim().startsWith("in ")){
345
                String refConcat = ", ";
346
                tags.add(new TaggedText(TagEnum.separator, refConcat));
347
            }
348
            tags.add(new TaggedText(TagEnum.reference, referenceCache));
349
        }
350

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

    
355
    }
356

    
357

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

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

    
406
        List<TaggedText> tags = new ArrayList<TaggedText>();
407

    
408
        //TODO how to handle protected fullTitleCache here?
409

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

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

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

    
477
        return tags;
478

    
479
    }
480

    
481

    
482

    
483

    
484
//***************************** PRIVATES ***************************************/
485

    
486

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

    
495

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

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

    
516

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

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

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

    
538

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

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

    
560
        }
561

    
562
        return tags;
563
    }
564

    
565

    
566

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

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

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

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

    
597
        if (nonViralName.isMonomHybrid()){
598
            addHybridPrefix(tags);
599
        }
600

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

    
606
        return tags;
607
    }
608

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

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

    
634
        //genus
635
        List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
636

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

    
653
        addInfraGenericPart(nonViralName, tags, infraGenericMarker, infraGenEpi);
654

    
655
        addAppendedTaggedPhrase(tags, nonViralName);
656
        return tags;
657
    }
658

    
659

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

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

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

    
690

    
691
    	List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
692

    
693
        addSpeciesAggregateTaggedEpithet(tags, nonViralName);
694
        addAppendedTaggedPhrase(tags, nonViralName);
695
        return tags;
696
    }
697

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

    
715

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

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

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

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

    
761
        addAppendedTaggedPhrase(tags, nonViralName);
762
        return tags;
763
    }
764

    
765

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

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

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

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

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

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

    
848

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

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

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

    
891

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

    
906

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

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

    
924

    
925

    
926
}
(7-7/9)