Project

General

Profile

Download (32 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.strategy.cache.HTMLTagRules;
37
import eu.etaxonomy.cdm.strategy.cache.TagEnum;
38
import eu.etaxonomy.cdm.strategy.cache.TaggedCacheHelper;
39
import eu.etaxonomy.cdm.strategy.cache.TaggedText;
40
import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
41
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImplRegExBase;
42

    
43

    
44
/**
45
 * This class is a default implementation for the INonViralNameCacheStrategy<T extends NonViralName>
46
 * interface.<BR>
47
 * The method implements a cache strategy for botanical names so no method has to be overwritten by
48
 * a subclass for botanic names.
49
 * Where differing from this default botanic name strategy other subclasses should overwrite the
50
 * existing methods, e.g. a CacheStrategy for zoological names should overwrite getAuthorAndExAuthor
51
 * @author a.mueller
52
 */
53
public class NonViralNameDefaultCacheStrategy<T extends NonViralName<?>>
54
        extends NameCacheStrategyBase<T>
55
        implements INonViralNameCacheStrategy<T> {
56
	private static final Logger logger = Logger.getLogger(NonViralNameDefaultCacheStrategy.class);
57
	private static final long serialVersionUID = -6577757501563212669L;
58

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

    
61
    protected String NameAuthorSeperator = " ";
62
    protected String BasionymStart = "(";
63
    protected String BasionymEnd = ")";
64
    protected String ExAuthorSeperator = " ex ";
65
    protected CharSequence BasionymAuthorCombinationAuthorSeperator = " ";
66

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

    
72

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

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

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

    
96
/* **************** GETTER / SETTER **************************************/
97

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

    
106

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

    
111

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

    
121

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

    
126

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

    
136

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

    
141

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

    
150

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

    
155

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

    
164

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

    
169

    
170
//** *****************************************************************************************/
171

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

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

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

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

    
204

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

    
220

    
221

    
222

    
223

    
224

    
225

    
226
// ******************* Authorship ******************************/
227

    
228

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

    
240
    }
241

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

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

    
294

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

    
310
// ************* TAGGED NAME ***************************************/
311

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

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

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

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

    
332

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

    
351
        //nomenclatural status
352
        Set<NomenclaturalStatus> ncStati = nonViralName.getStatus();
353
        Iterator<NomenclaturalStatus> iterator = ncStati.iterator();
354
        List<TaggedText> nomStatusTags = new ArrayList<TaggedText>();
355
        while (iterator.hasNext()) {
356
            NomenclaturalStatus ncStatus = iterator.next();
357
            // since the NewInstance method of nomencatural status allows null as parameter
358
            // we have to check for null values here
359
            String suffix = "not defined";
360
            if(ncStatus.getType() != null){
361
                NomenclaturalStatusType statusType =  ncStatus.getType();
362
                Language lang = Language.LATIN();
363
                Representation repr = statusType.getRepresentation(lang);
364
                if (repr != null){
365
                    suffix = repr.getAbbreviatedLabel();
366
                }else{
367
                    String message = "No latin representation available for nom. status. " + statusType.getTitleCache();
368
                    logger.warn(message);
369
                    throw new IllegalStateException(message);
370
                }
371
            }else if(ncStatus.getRuleConsidered() != null && ! ncStatus.getRuleConsidered().equals("")){
372
                suffix = ncStatus.getRuleConsidered();
373
            }
374
            String statusSeparator = ", ";
375
            nomStatusTags.add(new TaggedText(TagEnum.separator, statusSeparator));
376
            nomStatusTags.add(new TaggedText(TagEnum.nomStatus, suffix));
377
        }
378
        tags.addAll(nomStatusTags);
379
        return tags;
380

    
381
    }
382

    
383
    @Override
384
    public List<TaggedText> getTaggedTitle(T nonViralName) {
385
        if (nonViralName == null){
386
            return null;
387
        }
388

    
389
        List<TaggedText> tags = new ArrayList<TaggedText>();
390

    
391
        //TODO how to handle protected fullTitleCache here?
392

    
393
        if (nonViralName.isProtectedTitleCache()){
394
            //protected title cache
395
            tags.add(new TaggedText(TagEnum.name, nonViralName.getTitleCache()));
396
            return tags;
397
        }else if (nonViralName.isHybridFormula()){
398
            //hybrid formula
399
            String hybridSeparator = NonViralNameParserImplRegExBase.hybridSign;
400
            boolean isFirst = true;
401
            List<HybridRelationship> rels = nonViralName.getOrderedChildRelationships();
402
            for (HybridRelationship rel: rels){
403
                if (! isFirst){
404
                    tags.add(new TaggedText(TagEnum.hybridSign, hybridSeparator));
405
                }
406
                isFirst = false;
407
                tags.addAll(getTaggedTitle((T)rel.getParentName()));
408
            }
409
            return tags;
410
        }else if (nonViralName.isAutonym()){
411
            //Autonym
412
            tags.addAll(handleTaggedAutonym(nonViralName));
413
        }else{ //not Autonym
414
//			String nameCache = nonViralName.getNameCache();  //OLD: CdmUtils.Nz(getNameCache(nonViralName));
415
            List<TaggedText> nameTags = getTaggedName(nonViralName);
416
            tags.addAll(nameTags);
417
            String authorCache = getAuthorshipCache(nonViralName);
418
            if (StringUtils.isNotBlank(authorCache)){
419
                tags.add(new TaggedText(TagEnum.authors, authorCache));
420
            }
421
        }
422
        return tags;
423
    }
424

    
425
    /**
426
     * Returns the tag list of the name part (without author and reference).
427
     * @param nonViralName
428
     * @return
429
     */
430
    @Override
431
    public List<TaggedText> getTaggedName(T nonViralName) {
432
        if (nonViralName == null){
433
            return null;
434
        }
435
        List<TaggedText> tags = new ArrayList<TaggedText>();
436
        Rank rank = nonViralName.getRank();
437

    
438
        if (nonViralName.isProtectedNameCache()){
439
            tags.add(new TaggedText(TagEnum.name, nonViralName.getNameCache()));
440
        }else if (rank == null){
441
            tags = getRanklessTaggedNameCache(nonViralName);
442
//		}else if (nonViralName.isInfragenericUnranked()){
443
//			result = getUnrankedInfragenericNameCache(nonViralName);
444
        }else if (rank.isInfraSpecific()){
445
            tags = getInfraSpeciesTaggedNameCache(nonViralName);
446
        }else if (rank.isSpecies() || isAggregateWithAuthorship(nonViralName, rank) ){ //exception see #4288
447
            tags = getSpeciesTaggedNameCache(nonViralName);
448
        }else if (rank.isInfraGeneric()){
449
            tags = getInfraGenusTaggedNameCache(nonViralName);
450
        }else if (rank.isGenus()){
451
            tags = getGenusOrUninomialTaggedNameCache(nonViralName);
452
        }else if (rank.isSupraGeneric()){
453
            tags = getGenusOrUninomialTaggedNameCache(nonViralName);
454
        }else{
455
            logger.warn("Name Strategy for Name (UUID: " + nonViralName.getUuid() +  ") not yet implemented");
456
        }
457
        //TODO handle appended phrase here instead of different places, check first if this is true for all
458
        //cases
459

    
460
        return tags;
461

    
462
    }
463

    
464

    
465

    
466

    
467
//***************************** PRIVATES ***************************************/
468

    
469

    
470
    private boolean isAggregateWithAuthorship(T nonViralName, Rank rank) {
471
		if (rank == null){
472
			return false;
473
		}else{
474
			return rank.isSpeciesAggregate() && ( isNotBlank(nonViralName.getAuthorshipCache()) || nonViralName.getNomenclaturalReference() != null );
475
		}
476
	}
477

    
478

    
479
	/**
480
     * Returns the tag list for an autonym taxon.
481
     *
482
     * @see NonViralName#isAutonym()
483
     * @see BotanicalName#isAutonym()
484
     * @param nonViralName
485
     * @return
486
     */
487
    private List<TaggedText> handleTaggedAutonym(T nonViralName) {
488
    	List<TaggedText> tags = null;
489
    	if (nonViralName.isInfraSpecific()){
490
	        //species part
491
	        tags = getSpeciesTaggedNameCache(nonViralName);
492

    
493
	        //author
494
	        String authorCache = getAuthorshipCache(nonViralName);
495
	        if (StringUtils.isNotBlank(authorCache)){
496
	            tags.add(new TaggedText(TagEnum.authors, authorCache));
497
	        }
498

    
499

    
500
	        //infra species marker
501
	        if (nonViralName.getRank() == null || !nonViralName.getRank().isInfraSpecific()){
502
	            //TODO handle exception
503
	            logger.warn("Rank for autonym does not exist or is not lower than species !!");
504
	        }else{
505
	            String infraSpeciesMarker = nonViralName.getRank().getAbbreviation();
506
	            if (StringUtils.isNotBlank(infraSpeciesMarker)){
507
	                tags.add(new TaggedText(TagEnum.rank, infraSpeciesMarker));
508
	            }
509
	        }
510

    
511
	        //infra species
512
	        String infraSpeciesPart = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet()).trim();
513
	        if (StringUtils.isNotBlank(infraSpeciesPart)){
514
	            tags.add(new TaggedText(TagEnum.name, infraSpeciesPart));
515
	        }
516

    
517
        } else if (nonViralName.isInfraGeneric()){
518
        	//genus part
519
	       tags =getGenusOrUninomialTaggedNameCache(nonViralName);
520

    
521

    
522
	        //infra species marker
523
	        if (nonViralName.getRank() == null || !nonViralName.getRank().isInfraGeneric()){
524
	            //TODO handle exception
525
	            logger.warn("Rank for autonym does not exist or is not lower than species !!");
526
	        }else{
527
	        	Rank rank = nonViralName.getRank();
528
	            String infraGenericMarker = rank.getAbbreviation();
529
                if (rank.equals(Rank.SECTION_BOTANY()) || rank.equals(Rank.SUBSECTION_BOTANY())){
530
                	infraGenericMarker = infraGenericMarker.replace("(bot.)", "");
531
                }
532
	            if (StringUtils.isNotBlank(infraGenericMarker)){
533
	                tags.add(new TaggedText(TagEnum.rank, infraGenericMarker));
534
	            }
535
	        }
536

    
537
	        //infra species
538
	        String infraGenericPart = CdmUtils.Nz(nonViralName.getInfraGenericEpithet()).trim();
539
	        if (StringUtils.isNotBlank(infraGenericPart)){
540
	            tags.add(new TaggedText(TagEnum.name, infraGenericPart));
541
	        }
542

    
543
        }
544

    
545
        return tags;
546
    }
547

    
548

    
549

    
550
    /**
551
     * Returns the tag list for rankless taxa.
552
     * @param nonViralName
553
     * @return
554
     */
555
    protected List<TaggedText> getRanklessTaggedNameCache(NonViralName<?> nonViralName){
556
        List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
557
        String speciesEpi = CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim();
558
        if (StringUtils.isNotBlank(speciesEpi)){
559
            tags.add(new TaggedText(TagEnum.name, speciesEpi));
560
        }
561

    
562
        String infraSpeciesEpi = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet());
563
        if (StringUtils.isNotBlank(infraSpeciesEpi)){
564
            tags.add(new TaggedText(TagEnum.name, infraSpeciesEpi));
565
        }
566

    
567
        //result += " (rankless)";
568
        addAppendedTaggedPhrase(tags, nonViralName);
569
        return tags;
570
    }
571

    
572
    /**
573
     * Returns the tag list for the first epithet (including a hybrid sign if required).
574
     * @param nonViralName
575
     * @return
576
     */
577
    private List<TaggedText> getUninomialTaggedPart(NonViralName<?> nonViralName) {
578
        List<TaggedText> tags = new ArrayList<TaggedText>();
579

    
580
        if (nonViralName.isMonomHybrid()){
581
            addHybridPrefix(tags);
582
        }
583

    
584
        String uninomial = CdmUtils.Nz(nonViralName.getGenusOrUninomial()).trim();
585
        if (StringUtils.isNotBlank(uninomial)){
586
            tags.add(new TaggedText(TagEnum.name, uninomial));
587
        }
588

    
589
        return tags;
590
    }
591

    
592
    /**
593
     * Returns the tag list for an genus or higher taxon.
594
     *
595
     * @param nonViralName
596
     * @return
597
     */
598
    protected List<TaggedText> getGenusOrUninomialTaggedNameCache(NonViralName<?> nonViralName){
599
        List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
600
        addAppendedTaggedPhrase(tags, nonViralName);
601
        return tags;
602
    }
603

    
604
    /**
605
     * Returns the tag list for an infrageneric taxon (including species aggregates).
606
     *
607
     * @see #getSpeciesAggregateTaggedCache(NonViralName)
608
     * @param nonViralName
609
     * @return
610
     */
611
    protected List<TaggedText> getInfraGenusTaggedNameCache(NonViralName<?> nonViralName){
612
        Rank rank = nonViralName.getRank();
613
        if (rank != null && rank.isSpeciesAggregate() && isBlank(nonViralName.getAuthorshipCache())){
614
            return getSpeciesAggregateTaggedCache(nonViralName);
615
        }
616

    
617
        //genus
618
        List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
619

    
620
        //marker
621
        String infraGenericMarker;
622
        if (rank != null){
623
            try {
624
                infraGenericMarker = rank.getInfraGenericMarker();
625
                if (rank.equals(Rank.SECTION_BOTANY()) || rank.equals(Rank.SUBSECTION_BOTANY())){
626
                	infraGenericMarker = infraGenericMarker.replace("(bot.)", "");
627
                }
628
            } catch (UnknownCdmTypeException e) {
629
                infraGenericMarker = "'unhandled infrageneric rank'";
630
            }
631
        }else{
632
        	infraGenericMarker = "'undefined infrageneric rank'";
633
        }
634
        String infraGenEpi = CdmUtils.Nz(nonViralName.getInfraGenericEpithet()).trim();
635

    
636
        addInfraGenericPart(nonViralName, tags, infraGenericMarker, infraGenEpi);
637

    
638
        addAppendedTaggedPhrase(tags, nonViralName);
639
        return tags;
640
    }
641

    
642

    
643
	/**
644
	 * Default implementation for the infrageneric part of a name.
645
	 * This is usually the infrageneric marker and the infrageneric epitheton. But may be implemented differently e.g. for zoological
646
	 * names the infrageneric epitheton may be surrounded by brackets and the marker left out.
647
	 * @param nonViralName
648
	 * @param tags
649
	 * @param infraGenericMarker
650
	 */
651
	protected void addInfraGenericPart(NonViralName<?> name, List<TaggedText> tags, String infraGenericMarker, String infraGenEpi) {
652
		//add marker
653
		tags.add(new TaggedText(TagEnum.rank, infraGenericMarker));
654

    
655
		//add epitheton
656
		if (StringUtils.isNotBlank(infraGenEpi)){
657
            tags.add(new TaggedText(TagEnum.name, infraGenEpi));
658
        }
659
	}
660

    
661
    /**
662
     * Returns the tag list for a species aggregate (or similar) taxon.<BR>
663
     * Possible ranks for a <i>species aggregate</i> are "aggr.", "species group", ...
664
     * @param nonViralName
665
     * @return
666
     */
667
    protected List<TaggedText> getSpeciesAggregateTaggedCache(NonViralName<?> nonViralName){
668
        if (! isBlank(nonViralName.getAuthorshipCache())){
669
        	List<TaggedText> result = getSpeciesTaggedNameCache(nonViralName);
670
        	return result;
671
        }
672

    
673

    
674
    	List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
675

    
676
        addSpeciesAggregateTaggedEpithet(tags, nonViralName);
677
        addAppendedTaggedPhrase(tags, nonViralName);
678
        return tags;
679
    }
680

    
681
    /**
682
     * Adds the aggregate tag to the tag list.
683
     * @param tags
684
     * @param nonViralName
685
     */
686
    private void addSpeciesAggregateTaggedEpithet(List<TaggedText> tags, NonViralName<?> nonViralName) {
687
        String marker;
688
        try {
689
            marker = nonViralName.getRank().getInfraGenericMarker();
690
        } catch (UnknownCdmTypeException e) {
691
            marker = "'unknown aggregat type'";
692
        }
693
        if (StringUtils.isNotBlank(marker)){
694
            tags.add(new TaggedText(TagEnum.rank, marker));
695
        }
696
    }
697

    
698

    
699
    /**
700
     * Returns the tag list for a species taxon.
701
     * @param nonViralName
702
     * @return
703
     */
704
    protected List<TaggedText> getSpeciesTaggedNameCache(NonViralName<?> nonViralName){
705
        List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
706
        addAppendedTaggedPhrase(tags, nonViralName);
707
        return tags;
708
    }
709

    
710
    /**
711
     * Creates the tag list for an infraspecific taxon. In include is true the result will contain
712
     * @param nonViralName
713
     * @return
714
     */
715
    protected List<TaggedText> getInfraSpeciesTaggedNameCache(T nonViralName){
716
        return getInfraSpeciesTaggedNameCache(nonViralName, true);
717
    }
718

    
719
    /**
720
     * Creates the tag list for an infraspecific taxon. If include is true the result will contain
721
     * the infraspecific marker (e.g. "var.")
722
     * @param nonViralName
723
     * @param includeMarker
724
     * @return
725
     */
726
    protected List<TaggedText> getInfraSpeciesTaggedNameCache(NonViralName<?> nonViralName, boolean includeMarker){
727
        List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
728
        if (includeMarker){
729
            String marker = (nonViralName.getRank().getAbbreviation()).trim().replace("null", "");
730
            if (StringUtils.isNotBlank(marker)){
731
                tags.add(new TaggedText(TagEnum.rank, marker));
732
            }
733
        }
734
        String infrSpecEpi = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet());
735
        if (nonViralName.isTrinomHybrid()){
736
            addHybridPrefix(tags);
737
        }
738

    
739
        infrSpecEpi = infrSpecEpi.trim().replace("null", "");
740
        if (StringUtils.isNotBlank(infrSpecEpi)){
741
            tags.add(new TaggedText(TagEnum.name, infrSpecEpi));
742
        }
743

    
744
        addAppendedTaggedPhrase(tags, nonViralName);
745
        return tags;
746
    }
747

    
748

    
749
    /**
750
     * Adds a tag for the hybrid sign and an empty separator to avoid trailing whitespaces.
751
     * @param tags
752
     */
753
    private void addHybridPrefix(List<TaggedText> tags) {
754
        tags.add(new TaggedText(TagEnum.hybridSign, NonViralNameParserImplRegExBase.hybridSign));
755
        tags.add(new TaggedText(TagEnum.separator, "")); //no whitespace separator
756
    }
757

    
758
    /**
759
     * Creates the tag list for the genus and species part.
760
     * @param nonViralName
761
     * @return
762
     */
763
    private List<TaggedText> getGenusAndSpeciesTaggedPart(NonViralName<?> nonViralName) {
764
        //Uninomial
765
        List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
766

    
767
        //InfraGenericEpi
768
        boolean hasInfraGenericEpi = StringUtils.isNotBlank(nonViralName.getInfraGenericEpithet());
769
        if (hasInfraGenericEpi){
770
            String infrGenEpi = nonViralName.getInfraGenericEpithet().trim();
771
            if (nonViralName.isBinomHybrid()){
772
//					addHybridPrefix(tags);  FIXME hybridSign should be tag, but then we need to handle "(" ")" differently.
773
                infrGenEpi = NonViralNameParserImplRegExBase.hybridSign + infrGenEpi;
774
            }
775
            infrGenEpi = "(" + infrGenEpi + ")";
776
            tags.add(new TaggedText(TagEnum.name, infrGenEpi));
777
        }
778

    
779
        //Species Epi
780
        String specEpi = CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim();
781
        if (! hasInfraGenericEpi && nonViralName.isBinomHybrid() ||
782
                hasInfraGenericEpi && nonViralName.isTrinomHybrid()){
783
            addHybridPrefix(tags);
784
        }
785
        if (StringUtils.isNotBlank(specEpi)){
786
            tags.add(new TaggedText(TagEnum.name, specEpi));
787
        }
788
        return tags;
789
    }
790

    
791
    /**
792
     * Adds the tag for the appended phrase if an appended phrase exists
793
     * @param tags
794
     * @param nonViralName
795
     */
796
    protected void addAppendedTaggedPhrase(List<TaggedText> tags, NonViralName<?> nonViralName){
797
        String appendedPhrase = nonViralName ==null ? null : nonViralName.getAppendedPhrase();
798
        String originalName = getOriginalNameString(nonViralName, tags);
799
        if (StringUtils.isNotBlank(originalName)){
800
            tags.add(new TaggedText(TagEnum.name, originalName));
801
        }
802
        if (StringUtils.isNotEmpty(appendedPhrase)){
803
            tags.add(new TaggedText(TagEnum.name, appendedPhrase));
804
        }
805
    }
806

    
807
    private String getOriginalNameString(NonViralName<?> currentName, List<TaggedText> originalNameTaggs) {
808
		List<String> originalNameStrings = new ArrayList<String>(1);
809
    	for (NameRelationship nameRel : currentName.getRelationsToThisName()){  //handle list, just in case we have strange data; this may result in strange looking results
810
			NameRelationshipType type = nameRel.getType();
811
    		if(type != null && type.equals(NameRelationshipType.ORIGINAL_SPELLING())){
812
    			String originalNameString;
813
    			TaxonNameBase<?,?> originalName = nameRel.getFromName();
814
    			if (!originalName.isInstanceOf(NonViralName.class)){
815
    				originalNameString = originalName.getTitleCache();
816
    			}else{
817
    				NonViralName<?> originalNvName = CdmBase.deproxy(originalName, NonViralName.class);
818
    				originalNameString = makeOriginalNameString(currentName, originalNvName, originalNameTaggs);
819
    			}
820
    			originalNameStrings.add("'" + originalNameString +"'");
821
    		}
822
		}
823
    	if (originalNameStrings.size() > 0){
824
    		String result = CdmUtils.concat("", originalNameStrings.toArray(new String[originalNameStrings.size()])) ;
825
	    	return result;
826
    	}else{
827
    		return null;
828
    	}
829
	}
830

    
831

    
832
	private String makeOriginalNameString(NonViralName<?> currentName, NonViralName<?> originalName, List<TaggedText> currentNameTags) {
833
		//use cache if necessary
834
		String cacheToUse = null;
835
		if (originalName.isProtectedNameCache() && StringUtils.isNotBlank(originalName.getNameCache())){
836
			cacheToUse = originalName.getNameCache();
837
		}else if (originalName.isProtectedTitleCache() && StringUtils.isNotBlank(originalName.getTitleCache())){
838
			cacheToUse = originalName.getTitleCache();
839
		}else if (originalName.isProtectedFullTitleCache() && StringUtils.isNotBlank(originalName.getFullTitleCache())){
840
			cacheToUse = originalName.getFullTitleCache();
841
		}
842
		if (cacheToUse != null){
843
			return cacheToUse;
844
		}
845
		//use atomized data
846
		//get originalNameParts array
847
		String originalNameString = originalName.getNameCache();
848
		if (originalNameString == null){
849
			originalNameString = originalName.getTitleCache();
850
		}
851
		if (originalNameString == null){  //should not happen
852
			originalNameString = originalName.getFullTitleCache();
853
		}
854
		String[] originalNameSplit = originalNameString.split("\\s+");
855

    
856
		//get current name parts
857
		String currentNameString = createString(currentNameTags);
858
		String[] currentNameSplit = currentNameString.split("\\s+");
859

    
860
		//compute string
861
		String result = originalNameString;
862
		for (int i = 0; i < Math.min(originalNameSplit.length, currentNameSplit.length); i++){
863
			if (originalNameSplit[i].equals(currentNameSplit[i])){
864
				result = result.replaceFirst(originalNameSplit[i], "").trim();
865
			}
866
		}
867
		//old
868
//		if (originalName.getGenusOrUninomial() != null && originalName.getGenusOrUninomial().equals(currentName.getGenusOrUninomial())){
869
//
870
//		}
871
		return result;
872
	}
873

    
874

    
875
	@Override
876
    public String getLastEpithet(T taxonNameBase) {
877
        Rank rank = taxonNameBase.getRank();
878
        if(rank.isGenus() || rank.isSupraGeneric()) {
879
            return taxonNameBase.getGenusOrUninomial();
880
        } else if(rank.isInfraGeneric()) {
881
            return taxonNameBase.getInfraGenericEpithet();
882
        } else if(rank.isSpecies()) {
883
            return taxonNameBase.getSpecificEpithet();
884
        } else {
885
            return taxonNameBase.getInfraSpecificEpithet();
886
        }
887
    }
888

    
889

    
890
    /**
891
     * @param tags
892
     * @return
893
     */
894
    private String createString(List<TaggedText> tags) {
895
        return TaggedCacheHelper.createString(tags);
896
    }
897

    
898
    /**
899
     * @param tags
900
     * @param htmlTagRules
901
     * @return
902
     */
903
    private String createString(List<TaggedText> tags, HTMLTagRules htmlTagRules) {
904
        return TaggedCacheHelper.createString(tags, htmlTagRules);
905
    }
906

    
907

    
908

    
909
}
(7-7/9)