Project

General

Profile

Download (34.2 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

    
10
package eu.etaxonomy.cdm.model.reference;
11

    
12
import java.beans.PropertyChangeEvent;
13
import java.beans.PropertyChangeListener;
14
import java.util.List;
15

    
16
import javax.persistence.Basic;
17
import javax.persistence.Column;
18
import javax.persistence.Embedded;
19
import javax.persistence.Entity;
20
import javax.persistence.FetchType;
21
import javax.persistence.Inheritance;
22
import javax.persistence.InheritanceType;
23
import javax.persistence.Lob;
24
import javax.persistence.ManyToOne;
25
import javax.persistence.Table;
26
import javax.persistence.Transient;
27
import javax.validation.constraints.NotNull;
28
import javax.validation.constraints.Pattern;
29
import javax.xml.bind.annotation.XmlAccessType;
30
import javax.xml.bind.annotation.XmlAccessorType;
31
import javax.xml.bind.annotation.XmlAttribute;
32
import javax.xml.bind.annotation.XmlElement;
33
import javax.xml.bind.annotation.XmlIDREF;
34
import javax.xml.bind.annotation.XmlRootElement;
35
import javax.xml.bind.annotation.XmlSchemaType;
36
import javax.xml.bind.annotation.XmlTransient;
37
import javax.xml.bind.annotation.XmlType;
38
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
39

    
40
import org.apache.log4j.Logger;
41
import org.hibernate.annotations.Cascade;
42
import org.hibernate.annotations.CascadeType;
43
import org.hibernate.annotations.Parameter;
44
import org.hibernate.annotations.Type;
45
import org.hibernate.envers.Audited;
46
import org.hibernate.search.annotations.Analyze;
47
import org.hibernate.search.annotations.Field;
48
import org.hibernate.search.annotations.FieldBridge;
49
import org.hibernate.search.annotations.IndexedEmbedded;
50
import org.joda.time.DateTime;
51

    
52
import eu.etaxonomy.cdm.common.DOI;
53
import eu.etaxonomy.cdm.common.URI;
54
import eu.etaxonomy.cdm.format.reference.NomenclaturalSourceFormatter;
55
import eu.etaxonomy.cdm.hibernate.search.DateTimeBridge;
56
import eu.etaxonomy.cdm.hibernate.search.DoiBridge;
57
import eu.etaxonomy.cdm.hibernate.search.UriBridge;
58
import eu.etaxonomy.cdm.jaxb.DateTimeAdapter;
59
import eu.etaxonomy.cdm.model.agent.Institution;
60
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
61
import eu.etaxonomy.cdm.model.common.ExternallyManaged;
62
import eu.etaxonomy.cdm.model.common.IIntextReferenceTarget;
63
import eu.etaxonomy.cdm.model.common.TimePeriod;
64
import eu.etaxonomy.cdm.model.common.VerbatimTimePeriod;
65
import eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity;
66
import eu.etaxonomy.cdm.model.name.TaxonName;
67
import eu.etaxonomy.cdm.strategy.cache.reference.IReferenceCacheStrategy;
68
import eu.etaxonomy.cdm.strategy.cache.reference.ReferenceDefaultCacheStrategy;
69
import eu.etaxonomy.cdm.strategy.match.Match;
70
import eu.etaxonomy.cdm.strategy.match.MatchMode;
71
import eu.etaxonomy.cdm.strategy.merge.Merge;
72
import eu.etaxonomy.cdm.strategy.merge.MergeMode;
73
import eu.etaxonomy.cdm.strategy.parser.ParserProblem;
74
import eu.etaxonomy.cdm.validation.Level2;
75
import eu.etaxonomy.cdm.validation.Level3;
76
import eu.etaxonomy.cdm.validation.annotation.InReference;
77
import eu.etaxonomy.cdm.validation.annotation.NoRecursiveInReference;
78
import eu.etaxonomy.cdm.validation.annotation.NullOrNotEmpty;
79
import eu.etaxonomy.cdm.validation.annotation.ReferenceCheck;
80

    
81
/**
82
 * The class for references (information sources). Originally
83
 * an abstract class with many subclasses. Now it is only
84
 * one class implementing many interfaces for safe use of different
85
 * types of references. E.g. if you want to edit a journal
86
 * you create a journal with {@link ReferenceFactory#newJournal()}
87
 * which returns an IJournal. Though this instance is an ordinary instance
88
 * of {@link Reference} by using IJournal you may not use attributes
89
 * not allowed for journals.<p>
90
 * References can be created via {@link ReferenceFactory} methods.
91
 * <P>
92
 * This class corresponds to: <ul>
93
 * <li> PublicationCitation according to the TDWG ontology
94
 * <li> Publication according to the TCS
95
 * <li> Reference according to the ABCD schema
96
 * </ul>
97
 *
98
 * @author m.doering
99
 * @since 08-Nov-2007 13:06:47
100
 */
101
@XmlAccessorType(XmlAccessType.FIELD)
102
@XmlType(name = "Reference", propOrder = {
103
	"type",
104
	"uri",
105
    "abbrevTitleCache",
106
    "protectedAbbrevTitleCache",
107
	"nomenclaturallyRelevant",
108
    "authorship",
109
    "referenceAbstract",
110
    "title",
111
    "abbrevTitle",
112
    "editor",
113
	"volume",
114
	"pages",
115
	"edition",
116
    "isbn",
117
    "issn",
118
    "doi",
119
    "seriesPart",
120
    "datePublished",
121
    "publisher",
122
    "placePublished",
123
    "institution",
124
    "school",
125
    "organization",
126
    "inReference",
127
    "accessed",
128
    "externallyManaged",
129
})
130
@XmlRootElement(name = "Reference")
131
@Entity
132
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
133
@Audited
134
@Table(name="Reference", indexes = { @javax.persistence.Index(name = "referenceTitleCacheIndex", columnList = "titleCache") })
135
//@InReference(groups=Level3.class)
136
@ReferenceCheck(groups=Level2.class)
137
@InReference(groups=Level3.class)
138
@NoRecursiveInReference(groups=Level3.class)  //may become Level1 in future  #
139
public class Reference
140
        extends IdentifiableMediaEntity<IReferenceCacheStrategy>
141
        implements IArticle, IBook, IPatent, IDatabase, IJournal, IBookSection,ICdDvd,
142
                   IGeneric,IInProceedings, IProceedings, IPrintSeries, IReport,
143
                   IThesis,IWebPage, IPersonalCommunication,
144
                   INomenclaturalReference, IReference, IIntextReferenceTarget {
145

    
146
    private static final long serialVersionUID = -2034764545042691295L;
147
	private static final Logger logger = Logger.getLogger(Reference.class);
148

    
149
//  from E+M import (still needed?)
150
//	@Column(length=255)
151
//  private String refAuthorString;
152
//	public String getRefAuthorString() {return refAuthorString;}
153
//  public void setRefAuthorString(String refAuthorString) {this.refAuthorString = refAuthorString;}
154

    
155
    @XmlAttribute(name ="type")
156
	@Column(name="refType")
157
	@NotNull
158
    @Type(type = "eu.etaxonomy.cdm.hibernate.EnumUserType",
159
    	parameters = {@Parameter(name  = "enumClass",
160
    	value = "eu.etaxonomy.cdm.model.reference.ReferenceType")}
161
    )
162
	@Audited
163
	protected ReferenceType type;
164

    
165
	//Title of the reference
166
	@XmlElement(name ="Title" )
167
	@Column(length=4096, name="title")
168
	@Lob
169
	@Field
170
	@Match(MatchMode.EQUAL_REQUIRED) //TODO correct? was EQUAL_REQUIRED before, but with abbrevTitle this is not realistic anymore, see also #6427
171
    //TODO Val #3379
172
//	@NullOrNotEmpty
173
	private String title;
174

    
175
	//Title of the reference
176
	@XmlElement(name ="AbbrevTitle" )
177
	@Field
178
	@Match(MatchMode.EQUAL)  //TODO check if this is correct
179
	@NullOrNotEmpty
180
	@Column(length=255)
181
	private String abbrevTitle;
182

    
183
	//Title of the reference
184
	@XmlElement(name ="AbbrevTitleCache" )
185
	@Field
186
	@Match(MatchMode.CACHE)
187
    //TODO Val #3379
188
//	@NotNull
189
	@Column(length=1024)
190
	private String abbrevTitleCache;
191

    
192
	@XmlElement(name = "protectedAbbrevTitleCache")
193
	@Merge(MergeMode.OR)
194
	private boolean protectedAbbrevTitleCache;
195

    
196
//********************************************************/
197

    
198
    @XmlElement(name = "Editor")
199
    @Field
200
    //TODO Val #3379
201
//    @NullOrNotEmpty
202
    @Column(length=255)
203
	protected String editor;
204

    
205
    @XmlElement(name = "Volume")
206
    @Field
207
    //TODO Val #3379
208
//    @NullOrNotEmpty
209
    @Column(length=255)
210
	protected String volume;
211

    
212
    @XmlElement(name = "Pages")
213
    @Field
214
    //TODO Val #3379
215
//    @NullOrNotEmpty
216
    @Column(length=255)
217
	protected String pages;
218

    
219
    @XmlElement(name = "Edition")
220
    @Field
221
    //TODO Val #3379
222
//    @NullOrNotEmpty
223
    @Column(length=255)
224
	protected String edition;
225

    
226
    @XmlElement(name = "ISBN")
227
    @Field
228
    //TODO Val #3379
229
//    @NullOrNotEmpty
230
    @Column(length=255)
231
	@Pattern(regexp = "(?=.{13}$)\\d{1,5}([- ])\\d{1,7}\\1\\d{1,6}\\1(\\d|X)$", groups = Level2.class, message = "{eu.etaxonomy.cdm.model.reference.Reference.isbn.message}")
232
	protected String isbn;
233

    
234
    @XmlElement(name = "Doi")
235
    @Field
236
    @FieldBridge(impl = DoiBridge.class)
237
    @Type(type="doiUserType")
238
    @Column(length=DOI.MAX_LENGTH)
239
    protected DOI doi;
240

    
241

    
242
	@XmlElement(name = "ISSN")
243
    @Field
244
    //TODO Val #3379
245
//	@NullOrNotEmpty
246
    @Column(length=255)
247
	@Pattern(regexp = "(?=.{9}$)\\d{4}([- ])\\d{4} (\\d|X)$", groups = Level2.class, message = "{eu.etaxonomy.cdm.model.reference.Reference.issn.message}")
248
	protected String issn;
249

    
250
    @XmlElement(name = "SeriesPart")
251
    @Field
252
    //TODO Val #3379
253
//    @NullOrNotEmpty
254
    @Column(length=255)
255
	protected String seriesPart;
256

    
257
	@XmlElement(name = "Organization")
258
    @Field
259
    //TODO Val #3379
260
//	@NullOrNotEmpty
261
    @Column(length=255)
262
	protected String organization;
263

    
264
	@XmlElement(name = "Publisher")
265
    @Field
266
    //TODO Val #3379
267
//	@NullOrNotEmpty
268
    @Column(length=255)
269
	protected String publisher;
270

    
271

    
272
	@XmlElement(name = "PlacePublished")
273
    @Field
274
    //TODO Val #3379
275
//	@NullOrNotEmpty
276
    @Column(length=255)
277
	protected String placePublished;
278

    
279
	@XmlElement(name = "Institution")
280
	@XmlIDREF
281
	@XmlSchemaType(name = "IDREF")
282
	@ManyToOne(fetch = FetchType.LAZY)
283
	@IndexedEmbedded
284
	@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
285
	protected Institution institution;
286

    
287
	@XmlElement(name = "School")
288
    @XmlIDREF
289
    @XmlSchemaType(name = "IDREF")
290
	@ManyToOne(fetch = FetchType.LAZY)
291
	@IndexedEmbedded
292
	@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
293
	protected Institution school;
294

    
295
    @XmlElement(name = "InReference")
296
    @XmlIDREF
297
    @XmlSchemaType(name = "IDREF")
298
    @ManyToOne(fetch = FetchType.LAZY)
299
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
300
//  @InReference(groups=Level2.class)
301
   	protected Reference inReference;
302

    
303
//********************************************************/
304

    
305
	//The date range assigned to the reference. ISO Date range like. Flexible, year can be left out, etc
306
	@XmlElement(name ="DatePublished" )
307
	@Embedded
308
	@IndexedEmbedded
309
	private VerbatimTimePeriod datePublished = VerbatimTimePeriod.NewVerbatimInstance();
310

    
311
    //#5258
312
    @XmlElement(name = "Accessed", type= String.class)
313
    @XmlJavaTypeAdapter(DateTimeAdapter.class)
314
    @Type(type="dateTimeUserType")
315
    @Basic(fetch = FetchType.LAZY)
316
    @Match(MatchMode.EQUAL)
317
    @FieldBridge(impl = DateTimeBridge.class)
318
    private DateTime accessed;
319

    
320
    @XmlElement(name ="Abstract" )
321
	@Column(length=CLOB_LENGTH, name="referenceAbstract")
322
	@Lob
323
    @Field
324
    //TODO Val #3379
325
//	@NullOrNotEmpty
326
	private String referenceAbstract;  //abstract is a reserved term in Java
327

    
328

    
329
	//URIs like DOIs, LSIDs or Handles for this reference
330
	@XmlElement(name = "URI")
331
	@Field(analyze = Analyze.NO)
332
    @FieldBridge(impl = UriBridge.class)
333
	@Type(type="uriUserType")
334
	private URI uri;
335

    
336
	//flag to subselect only references that could be useful for nomenclatural citations.
337
	//If a reference is used as a
338
	//nomenclatural reference in a name this flag should be automatically set
339
	@XmlElement(name = "IsNomenclaturallyRelevant")
340
	@Merge(MergeMode.OR)
341
	@Match(MatchMode.IGNORE)
342
	private boolean nomenclaturallyRelevant;
343

    
344
	@XmlElement(name = "Authorship")
345
	@XmlIDREF
346
	@XmlSchemaType(name = "IDREF")
347
	@ManyToOne(fetch = FetchType.LAZY)
348
	@IndexedEmbedded
349
	@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
350
	private TeamOrPersonBase<?> authorship;
351

    
352
    private ExternallyManaged externallyManaged;
353

    
354
	@XmlAttribute
355
    @Match(MatchMode.IGNORE)
356
	private int parsingProblem = 0;
357

    
358
	@XmlAttribute
359
    @Match(MatchMode.IGNORE)
360
    private int problemStarts = -1;
361

    
362
    @XmlAttribute
363
    @Match(MatchMode.IGNORE)
364
    private int problemEnds = -1;
365

    
366

    
367
// *********************** CONSTRUCTOR ************************/
368

    
369
    protected Reference(){
370
		this(ReferenceType.Generic);  //just in case someone uses constructor
371
	}
372

    
373
	protected Reference(ReferenceType type) {
374
		super();
375
	    if (type == null){
376
			this.type = ReferenceType.Generic;
377
		} else{
378
			this.type = type;
379
		}
380
	}
381

    
382
// *********************** LISTENER ************************/
383

    
384

    
385
	@Override
386
    public void initListener(){
387
        PropertyChangeListener listener = new PropertyChangeListener() {
388
            @Override
389
            public void propertyChange(PropertyChangeEvent ev) {
390
            	if (!ev.getPropertyName().equals("titleCache") && !ev.getPropertyName().equals("abbrevTitleCache") && !ev.getPropertyName().equals("cacheStrategy")){
391
            		if (! isProtectedTitleCache()){
392
            			titleCache = null;
393
            		}
394
            		if (! isProtectedAbbrevTitleCache()){
395
            			abbrevTitleCache = null;
396
            		}
397
            	}
398
            }
399
        };
400
        addPropertyChangeListener(listener);
401
    }
402

    
403
//*************************** GETTER / SETTER ******************************************/
404

    
405
    // @Transient  - must not be transient, since this property needs to to be included in all serializations produced by the remote layer
406
    @Override
407
    public String getTitleCache(){
408
        String result = super.getTitleCache();
409
        if (isBlank(result)){
410
            this.titleCache = this.getAbbrevTitleCache(true);
411
        }
412
        return titleCache;
413
    }
414

    
415
	@Override
416
	public String getAbbrevTitleCache() {
417
		return getAbbrevTitleCache(false);
418
	}
419

    
420
	/**
421
	 * Implements {@link #getAbbrevTitleCache()} but allows to
422
	 * avoid never ending recursions if both caches are empty
423
	 * avoidRecursion should only be <code>true</code> if called
424
	 * by {@link #getTitleCache()}
425
	 * @param avoidRecursion
426
	 * @return
427
	 */
428
	private String getAbbrevTitleCache(boolean avoidRecursion) {
429
        if (protectedAbbrevTitleCache){
430
            return this.abbrevTitleCache;
431
        }
432
        // is reference dirty, i.e. equal NULL?
433
        if (abbrevTitleCache == null){
434
            this.abbrevTitleCache = generateAbbrevTitle();
435
            this.abbrevTitleCache = getTruncatedCache(this.abbrevTitleCache) ;
436
        }
437
        if (isBlank(abbrevTitleCache) && !avoidRecursion){
438
            this.abbrevTitleCache = this.getTitleCache();
439
        }
440
        return abbrevTitleCache;
441
    }
442

    
443

    
444
    @Override
445
	@Deprecated
446
	public void setAbbrevTitleCache(String abbrevTitleCache) {
447
		this.abbrevTitleCache = abbrevTitleCache;
448
	}
449

    
450
	@Override
451
	public void setAbbrevTitleCache(String abbrevTitleCache, boolean isProtected) {
452
		this.protectedAbbrevTitleCache = isProtected;
453
		setAbbrevTitleCache(abbrevTitleCache);
454
	}
455

    
456
	@Override
457
	public boolean isProtectedAbbrevTitleCache() {
458
		return protectedAbbrevTitleCache;
459
	}
460

    
461
	@Override
462
	public void setProtectedAbbrevTitleCache(boolean protectedAbbrevTitleCache) {
463
		this.protectedAbbrevTitleCache = protectedAbbrevTitleCache;
464
	}
465

    
466
	@Override
467
	public String getAbbrevTitle() {
468
		return abbrevTitle;
469
	}
470

    
471
	@Override
472
	public void setAbbrevTitle(String abbrevTitle) {
473
		this.abbrevTitle = isBlank(abbrevTitle) ? null : abbrevTitle;
474
	}
475

    
476

    
477
	@Override
478
    public String getEditor() {
479
		return editor;
480
	}
481

    
482

    
483
	@Override
484
    public void setEditor(String editor) {
485
		this.editor = isBlank(editor)? null : editor;
486
	}
487

    
488
	@Override
489
    public String getVolume() {
490
		return volume;
491
	}
492

    
493
	@Override
494
    public void setVolume(String volume) {
495
		this.volume = isBlank(volume)? null : volume;
496
	}
497

    
498
	@Override
499
    public String getPages() {
500
		return pages;
501
	}
502

    
503
	@Override
504
    public void setPages(String pages) {
505
		this.pages = isBlank(pages)? null : pages;
506
	}
507

    
508
	@Override
509
    public String getEdition() {
510
		return edition;
511
	}
512

    
513
	@Override
514
    public void setEdition(String edition) {
515
		this.edition = isBlank(edition)? null : edition;
516
	}
517

    
518
	@Override
519
    public String getIsbn() {
520
		return isbn;
521
	}
522

    
523
	@Override
524
    public void setIsbn(String isbn) {
525
		this.isbn = isBlank(isbn)? null : isbn;
526
	}
527

    
528
	@Override
529
    public String getIssn() {
530
		return issn;
531
	}
532

    
533
	@Override
534
    public void setIssn(String issn) {
535
		this.issn = isBlank(issn)? null : issn;
536
	}
537

    
538
    @Override
539
	public DOI getDoi() {
540
		return doi;
541
	}
542
    @Override
543
	public void setDoi(DOI doi) {
544
		this.doi = doi;
545
	}
546
    /**
547
     * Convenience method to retrieve doi as string
548
     */
549
    @Transient @XmlTransient @java.beans.Transient
550
    public String getDoiString() {
551
        return doi == null? null : doi.toString();
552
    }
553

    
554
	@Override
555
    public String getSeriesPart() {
556
		return seriesPart;
557
	}
558
	@Override
559
    public void setSeriesPart(String seriesPart) {
560
		this.seriesPart = isBlank(seriesPart)? null : seriesPart;
561
	}
562

    
563
	@Override
564
    public String getOrganization() {
565
		return organization;
566
	}
567

    
568
	@Override
569
    public void setOrganization(String organization) {
570
		this.organization = isBlank(organization)? null : organization;
571
	}
572

    
573
	@Override
574
    public String getPublisher() {
575
		return publisher;
576
	}
577

    
578
	@Override
579
    public void setPublisher(String publisher) {
580
		this.publisher = isBlank(publisher)? null : publisher;
581
	}
582

    
583
	@Override
584
    public void setPublisher(String publisher, String placePublished){
585
		this.publisher = publisher;
586
		this.placePublished = placePublished;
587
	}
588

    
589
	@Override
590
    public String getPlacePublished() {
591
		return placePublished;
592
	}
593

    
594
	@Override
595
    public void setPlacePublished(String placePublished) {
596
		this.placePublished = isBlank(placePublished)? null: placePublished;
597
	}
598

    
599
	@Override
600
    public Institution getInstitution() {
601
		return institution;
602
	}
603

    
604
	@Override
605
    public void setInstitution(Institution institution) {
606
		this.institution = institution;
607
	}
608

    
609
	@Override
610
    public Institution getSchool() {
611
		return school;
612
	}
613

    
614
	@Override
615
    public void setSchool(Institution school) {
616
		this.school = school;
617
	}
618

    
619
	@Override
620
    public Reference getInReference() {
621
		return inReference;
622
	}
623

    
624
	@Override
625
    public void setInReference(Reference inReference) {
626
		this.inReference = inReference;
627
	}
628

    
629
	@Override
630
    public void setType(ReferenceType type) {
631
		if (type == null){
632
			this.type = ReferenceType.Generic;
633
		} else{
634
			this.type = type;
635
		}
636
	}
637
	@Override
638
    public ReferenceType getType() {
639
		return type;
640
	}
641

    
642
	/**
643
	 * Whether this reference is of the given type
644
	 *
645
	 * @param type
646
	 * @return
647
	 */
648
	@Override
649
    public boolean isOfType(ReferenceType type){
650
		return type == getType();
651
	}
652

    
653
	/**
654
	 * Returns a string representing the title of <i>this</i> reference. If a
655
	 * reference has different titles (for instance abbreviated and not
656
	 * abbreviated) then for each title a new instance must be created.
657
	 *
658
	 * @return  the title string of <i>this</i> reference
659
	 * @see 	#getCitation()
660
	 */
661
	@Override
662
    public String getTitle(){
663
		return this.title;
664
	}
665
	/**
666
	 * @see 	#getTitle()
667
	 */
668
	@Override
669
    public void setTitle(String title){
670
		this.title = isBlank(title)? null : title;
671
	}
672

    
673
	/**
674
	 * Returns the date (mostly only the year) of publication / creation of
675
	 * <i>this</i> reference.
676
	 */
677
	@Override
678
    public VerbatimTimePeriod getDatePublished(){
679
		return this.datePublished;
680
	}
681
	/**
682
	 * @see 	#getDatePublished()
683
	 */
684
	@Override
685
    public void setDatePublished(VerbatimTimePeriod datePublished){
686
		this.datePublished = datePublished;
687
	}
688
    @Override
689
    @Transient
690
    @Deprecated
691
    public VerbatimTimePeriod setDatePublished(TimePeriod datePublished){
692
        VerbatimTimePeriod newTimePeriod = VerbatimTimePeriod.toVerbatim(datePublished);
693
        setDatePublished(newTimePeriod);
694
        return newTimePeriod;
695
    }
696

    
697
	public boolean hasDatePublished(){
698
		boolean result =  ! ( (this.datePublished == null) || isBlank(datePublished.toString()));
699
		return result;
700
	}
701

    
702

    
703
	@Override
704
    public DateTime getAccessed() {
705
        return accessed;
706
    }
707

    
708
	@Override
709
    public void setAccessed(DateTime accessed) {
710
        this.accessed = accessed;
711
    }
712

    
713
	/**
714
	 * Returns the {@link eu.etaxonomy.cdm.model.agent.TeamOrPersonBase author (team)} who created the
715
	 * content of <i>this</i> reference.
716
	 *
717
	 * @return  the author (team) of <i>this</i> reference
718
	 * @see 	eu.etaxonomy.cdm.model.agent.TeamOrPersonBase
719
	 */
720
	@Override
721
    public TeamOrPersonBase getAuthorship(){
722
		return this.authorship;
723
	}
724

    
725
	/**
726
	 * @see #getAuthorship()
727
	 */
728
	@Override
729
    public void setAuthorship(TeamOrPersonBase authorship){
730
		this.authorship = authorship;
731
	}
732

    
733
	/**
734
	 * Returns the Uniform Resource Identifier (URI) corresponding to <i>this</i>
735
	 * reference. An URI is a string of characters used to identify a resource
736
	 * on the Internet.
737
	 *
738
	 * @return  the URI of <i>this</i> reference
739
	 */
740
	@Override
741
    public URI getUri(){
742
		return this.uri;
743
	}
744
	/**
745
	 * @see #getUri()
746
	 */
747
	@Override
748
    public void setUri(URI uri){
749
		this.uri = uri;
750
	}
751

    
752
	/**
753
	 * @return the referenceAbstract
754
	 */
755
	@Override
756
    public String getReferenceAbstract() {
757
		return referenceAbstract;
758
	}
759

    
760
	/**
761
	 * @param referenceAbstract the referenceAbstract to set
762
	 */
763
	@Override
764
    public void setReferenceAbstract(String referenceAbstract) {
765
		this.referenceAbstract = isBlank(referenceAbstract)? null : referenceAbstract;
766
	}
767

    
768

    
769
	/**
770
	 * Returns "true" if the isNomenclaturallyRelevant flag is set. This
771
	 * indicates that a {@link TaxonName taxon name} has been originally
772
	 * published in <i>this</i> reference following the rules of a
773
	 * {@link eu.etaxonomy.cdm.model.name.NomenclaturalCode nomenclature code} and is therefore used for
774
	 * nomenclatural citations. This flag will be set as soon as <i>this</i>
775
	 * reference is used as a nomenclatural reference for any taxon name.<BR>
776
	 * FIXME what happens if the only taxon name referencing this reference is not
777
	 * any longer using this reference as a nomenclatural reference. How does the
778
	 * reference get informed about the fact that it is not nomenclaturally relevant
779
	 * anymore?
780
	 * @deprecated currently not supported and not in use, may be removed in future
781
	 */
782
	@Deprecated
783
    public boolean isNomenclaturallyRelevant(){
784
		return this.nomenclaturallyRelevant;
785
	}
786

    
787
	/**
788
	 * @see #isNomenclaturallyRelevant()
789
	 * @deprecated currently not supported and not in use, may be removed in future
790
	 */
791
	@Deprecated
792
	public void setNomenclaturallyRelevant(boolean nomenclaturallyRelevant){
793
		this.nomenclaturallyRelevant = nomenclaturallyRelevant;
794
	}
795

    
796

    
797
//****************************************************  /
798

    
799
	@Transient
800
	@Override
801
	public void setTitleCaches(String cache){
802
	    this.setAbbrevTitleCache(cache, true);
803
	    this.setTitleCache(cache, true);
804
	}
805

    
806

    
807
	/**
808
	 * Returns a formatted string containing the entire reference citation,
809
	 * including authors, corresponding to <i>this</i> reference.
810
	 *
811
	 * @see  #generateTitle()
812
	 */
813
	// TODO implement
814
	@Transient
815
	public String getCitation(){
816
		if (getCacheStrategy() == null){
817
			logger.warn("No CacheStrategy defined for "+ this.getClass() + ": " + this.getUuid());
818
			return null;
819
		}else{
820
			return getCacheStrategy().getTitleCache(this);
821
		}
822
	}
823

    
824

    
825
	@Override
826
    public String generateTitle() {
827
		return super.generateTitle();
828
	}
829

    
830
    public String generateAbbrevTitle() {
831
		return getCacheStrategy().getNomenclaturalTitleCache(this);
832
	}
833

    
834
	/**
835
	 * Returns a string representation for the year of publication / creation
836
	 * of <i>this</i> reference. If the {@link #getDatePublished() datePublished}
837
	 * of this reference contains more date information then (starting) year
838
	 * only the year is returned.
839
	 */
840
	@Override
841
    @Transient
842
	public String getYear(){
843
		VerbatimTimePeriod datePublished = this.getDatePublished();
844
		if (datePublished != null ){
845
			String result = getDatePublished().getYear();
846
			return result;
847
		}else{
848
			return null;
849
		}
850
	}
851

    
852
	/**
853
	 * Convenience method that returns a string representation for the publication date / creation
854
	 * of <i>this</i> reference. The string is obtained by
855
	 * {@link #getDatePublished()#toString() the string representation
856
	 * of the date published}.
857
	 */
858
	@Transient
859
	public String getDatePublishedString(){
860
		VerbatimTimePeriod datePublished = this.getDatePublished();
861
		if (datePublished != null ){
862
			return getDatePublished().toString();
863
		}else{
864
			return null;
865
		}
866
	}
867

    
868
	/**
869
     * Convenience method that returns a string representation for the publication date / creation
870
     * of <i>this</i> reference. The string is obtained by
871
     * {@link #getDatePublished()#toString() the string representation
872
     * of the date published}.
873
     */
874
    @Transient
875
    public String getTimePeriodPublishedString(){
876
        VerbatimTimePeriod datePublished = this.getDatePublished();
877
        if (datePublished != null ){
878
            return getDatePublished().getTimePeriod();
879
        }else{
880
            return null;
881
        }
882
    }
883

    
884
	@Override
885
    public int getParsingProblem(){
886
		return this.parsingProblem;
887
	}
888

    
889
	@Override
890
    public void setParsingProblem(int parsingProblem){
891
		this.parsingProblem = parsingProblem;
892
	}
893

    
894
	@Override
895
    public boolean hasProblem(){
896
		return parsingProblem != 0;
897
	}
898

    
899
	@Override
900
    public boolean hasProblem(ParserProblem problem) {
901
		return getParsingProblems().contains(problem);
902
	}
903

    
904
	@Override
905
    public int getProblemStarts(){
906
		return this.problemStarts;
907
	}
908

    
909
	@Override
910
    public void setProblemStarts(int start) {
911
		this.problemStarts = start;
912
	}
913

    
914
	@Override
915
    public int getProblemEnds(){
916
		return this.problemEnds;
917
	}
918

    
919
	@Override
920
    public void setProblemEnds(int end) {
921
		this.problemEnds = end;
922
	}
923

    
924
	@Override
925
    public void addParsingProblem(ParserProblem warning){
926
		parsingProblem = ParserProblem.addProblem(parsingProblem, warning);
927
	}
928

    
929
	@Override
930
    public void removeParsingProblem(ParserProblem problem) {
931
		parsingProblem = ParserProblem.removeProblem(parsingProblem, problem);
932
	}
933

    
934
	@Override
935
    @Transient
936
	public List<ParserProblem> getParsingProblems() {
937
		return ParserProblem.warningList(this.parsingProblem);
938
	}
939

    
940

    
941
	@Override
942
    @Transient
943
    public String getNomenclaturalCitation(String microReference) {
944
		String typeName = this.getType()== null ? "(no type defined)" : this.getType().getLabel();
945
		if (getCacheStrategy() == null){
946
		    throw new IllegalStateException("No CacheStrategy defined for "+ typeName + ": " + this.getUuid());
947
		}else{
948
		    return NomenclaturalSourceFormatter.INSTANCE().format(this, microReference);
949
		}
950
	}
951

    
952
	/**
953
	 * Generates, according to the {@link eu.etaxonomy.cdm.strategy.strategy.cache.reference.IReferenceBaseCacheStrategy cache strategy}
954
	 * assigned to <i>this</i> reference, a string that identifies <i>this</i>
955
	 * reference and returns it. This string may be stored in the inherited
956
	 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache} attribute.<BR>
957
	 * This method overrides the generic and inherited generateTitle method
958
	 * from {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity IdentifiableEntity}.
959
	 *
960
	 * @return  the string identifying <i>this</i> reference
961
	 * @see  	#getCitation()
962
	 * @see  	eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache()
963
	 * @see  	eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
964
	 * @see  	eu.etaxonomy.cdm.strategy.strategy.cache.common.IIdentifiableEntityCacheStrategy#getTitleCache()
965
	 */
966
//	@Override
967
//	public String generateTitle(){
968
//		if (cacheStrategy == null){
969
//			logger.warn("No CacheStrategy defined for Reference: " + this.getUuid());
970
//			return null;
971
//		}else{
972
//			return cacheStrategy.getTitleCache(this);
973
//		}
974
//	}
975

    
976

    
977

    
978
//********** Casting methods ***********************************/
979

    
980
	/**
981
	 * @return
982
	 */
983
	public IArticle castReferenceToArticle(){
984
		setType(ReferenceType.Article);
985
		return this;
986
	}
987

    
988
	public IBook castReferenceToBook(){
989
		setType(ReferenceType.Book);
990
		return this;
991
	}
992

    
993
	public IBookSection castReferenceToBookSection(){
994
		setType(ReferenceType.BookSection);
995
		return this;
996
	}
997

    
998
	public ICdDvd castReferenceToCdDvd(){
999
		setType(ReferenceType.CdDvd);
1000
		return this;
1001
	}
1002

    
1003
	public IDatabase castReferenceToDatabase(){
1004
		setType(ReferenceType.Database);
1005
		return this;
1006
	}
1007

    
1008
	public IGeneric castReferenceToGeneric(){
1009
		setType(ReferenceType.Generic);
1010
		return this;
1011
	}
1012

    
1013
	public IInProceedings castReferenceToInProceedings(){
1014
		setType(ReferenceType.InProceedings);
1015
		return this;
1016
	}
1017

    
1018
	public IJournal castReferenceToJournal(){
1019
		setType(ReferenceType.Journal);
1020
		return this;
1021
	}
1022

    
1023
	public IMap castReferenceToMap(){
1024
		setType(ReferenceType.Map);
1025
		return (IMap) this;
1026
	}
1027

    
1028
	public IPatent castReferenceToPatent(){
1029
		setType(ReferenceType.Patent);
1030
		return this;
1031
	}
1032

    
1033
	public IPersonalCommunication castReferenceToPersonalCommunication(){
1034
		setType(ReferenceType.PersonalCommunication);
1035
		return this;
1036
	}
1037

    
1038
	public IPrintSeries castReferenceToPrintSeries(){
1039
		setType(ReferenceType.PrintSeries);
1040
		return this;
1041
	}
1042

    
1043
	public IWebPage castReferenceToWebPage(){
1044
		setType(ReferenceType.WebPage);
1045
		return this;
1046
	}
1047

    
1048
	public IProceedings castReferenceToProceedings(){
1049
		setType(ReferenceType.Proceedings);
1050
		return this;
1051
	}
1052

    
1053
	public IReport castReferenceToReport(){
1054
		setType(ReferenceType.Report);
1055
		return this;
1056
	}
1057

    
1058
	public IThesis castReferenceToThesis(){
1059
		setType(ReferenceType.Thesis);
1060
		return this;
1061
	}
1062

    
1063

    
1064
	@Override
1065
    @Transient // prevent from being serialized by webservice
1066
	public IJournal getInJournal() {
1067
		IJournal journal = this.inReference;
1068
		return journal;
1069
	}
1070

    
1071
	@Override
1072
    public void setInJournal(IJournal journal) {
1073
		setInReference((Reference)journal);  //user setter to invoke aspect #1815
1074
	}
1075

    
1076
	@Override
1077
    @Transient // prevent from being serialized by webservice
1078
	public IPrintSeries getInSeries() {
1079
		return this.inReference;
1080
	}
1081

    
1082
	@Override
1083
    public void setInSeries(IPrintSeries inSeries) {
1084
	    setInReference((Reference)inSeries);  //user setter to invoke aspect  #1815
1085
	}
1086

    
1087
	@Override
1088
    @Transient // prevent from being serialized by webservice
1089
	public IBook getInBook() {
1090
		IBook book = this.inReference;
1091
		return book;
1092
	}
1093

    
1094
//********************** In-References *****************************************
1095

    
1096
	@Override
1097
    public void setInBook(IBook book) {
1098
	    setInReference((Reference)book);  //user setter to invoke aspect #1815
1099
	}
1100

    
1101
	@Override
1102
    @Transient // prevent from being serialized by webservice
1103
	public IProceedings getInProceedings() {
1104
		IProceedings proceedings = this.inReference;
1105
		return proceedings;
1106
	}
1107

    
1108
	@Override
1109
    public void setInProceedings(IProceedings proceeding) {
1110
        setInReference((Reference)proceeding);  //user setter to invoke aspect #1815
1111
	}
1112

    
1113
//**************************** Type *****************************************/
1114

    
1115
    public boolean isArticle() {
1116
        return this.getType().isArticle();
1117
    }
1118
    public boolean isBook() {
1119
        return this.getType().isBook();
1120
    }
1121
    public boolean isBookSection() {
1122
        return this.getType().isBookSection();
1123
    }
1124
    public boolean isWebPage() {
1125
        return this.getType().isWebPage();
1126
    }
1127
    public boolean isDatabase() {
1128
        return this.getType().isDatabase();
1129
    }
1130
    public boolean isMap() {
1131
        return this.getType().isMap();
1132
    }
1133
    public boolean isPatent() {
1134
        return this.getType().isPatent();
1135
    }
1136
    public boolean isGeneric() {
1137
        return this.getType().isGeneric();
1138
    }
1139
    public boolean isCdDvd() {
1140
        return this.getType().isCdDvd();
1141
    }
1142
    public boolean isProceedings() {
1143
        return this.getType().isProceedings();
1144
    }
1145
    public boolean isInProceedings() {
1146
        return this.getType().isInProceedings();
1147
    }
1148
    public boolean isJournal() {
1149
        return this.getType().isJournal();
1150
    }
1151
    public boolean isPersonalCommunication() {
1152
        return this.getType().isPersonalCommunication();
1153
    }
1154
    public boolean isThesis() {
1155
        return this.getType().isThesis();
1156
    }
1157
    public boolean isPrintSeries() {
1158
        return this.getType().isPrintSeries();
1159
    }
1160
    /**
1161
     * @return <code>true</code> if this type is exactly {@link ReferenceType#Section}
1162
     * @see ReferenceType#isSection()
1163
     */
1164
    public boolean isSectionOnly() {
1165
        return this.getType().isSectionOnly();
1166
    }
1167
    /**
1168
     * Returns <code>true</code> if this reference is part of another reference
1169
     * (inheriting from {@link ISection}) and therefore may have an in-reference and pages.
1170
     * @see ReferenceType#isSection()
1171
     */
1172
    public boolean isSection() {
1173
        return this.getType().isSection();
1174
    }
1175
    /**
1176
     * @see ReferenceType#isPrintedUnit()
1177
     */
1178
    public boolean isPrintedUnit() {
1179
        return this.getType().isPrintedUnit();
1180
    }
1181

    
1182
//*************************** CACHE STRATEGIES ******************************/
1183

    
1184
    @Override
1185
    protected void initDefaultCacheStrategy() {
1186
        this.setCacheStrategy(ReferenceDefaultCacheStrategy.NewInstance());
1187
    }
1188

    
1189
   @Override
1190
   public boolean updateCaches(){
1191
       //TODO shouldn't this be moved to the cache strategy?
1192
       if (this.equals(this.getInReference())){
1193
           String message = "-- invalid inreference (self-referencing) --";
1194
           String oldTitleCache = this.titleCache;
1195
           this.titleCache = message;
1196
           return !message.equals(oldTitleCache);
1197
       }
1198
       boolean result = super.updateCaches();
1199
       if (this.protectedAbbrevTitleCache == false){
1200
           String oldAbbrevTitleCache = this.abbrevTitleCache;
1201

    
1202
           String newAbbrevTitleCache = getTruncatedCache(getCacheStrategy().getNomenclaturalTitleCache(this));
1203
           if (newAbbrevTitleCache.equals("")){
1204
               newAbbrevTitleCache = getCacheStrategy().getTitleCache(this);
1205
           }
1206

    
1207
           if ( oldAbbrevTitleCache == null   || ! oldAbbrevTitleCache.equals(newAbbrevTitleCache) ){
1208
                this.setAbbrevTitleCache(null, false);
1209
                String newCache = this.getAbbrevTitleCache();
1210

    
1211
                if (newCache == null){
1212
                    logger.warn("New abbrevCache should never be null");
1213
                }
1214
                if (oldAbbrevTitleCache == null){
1215
                    logger.info("oldAbbrevTitleCache was illegaly null and has been fixed");
1216
                }
1217
                result = true;
1218
            }
1219
        }
1220
        return result;
1221
    }
1222

    
1223
//*********************** CLONE ********************************************************/
1224

    
1225
	/**
1226
	 * Clones <i>this</i> reference. This is a shortcut that enables to create
1227
	 * a new instance that differs only slightly from <i>this</i> reference by
1228
	 * modifying only some of the attributes.
1229
	 *
1230
	 * @see eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity#clone()
1231
	 * @see java.lang.Object#clone()
1232
	 */
1233
	@Override
1234
	public Reference clone() {
1235
		try {
1236
			Reference result = (Reference)super.clone();
1237
			result.setDatePublished(datePublished != null? (VerbatimTimePeriod)datePublished.clone(): null);
1238
			//no changes to: title, authorship, hasProblem, nomenclaturallyRelevant, uri
1239
			return result;
1240
		} catch (CloneNotSupportedException e) {
1241
			logger.warn("Object does not implement cloneable");
1242
			e.printStackTrace();
1243
			return null;
1244
		}
1245
	}
1246

    
1247
//******************************* toString *****************************/
1248

    
1249
	@Override
1250
	public String toString() {
1251
		if (type != null){
1252
			String result = "Reference [type=" + type  ;
1253
			result += title == null ? "" : ", title=" + title;
1254
			result += abbrevTitle == null ? "" : ", abbrevTitle=" + abbrevTitle;
1255
			result += ", id= " + this.getId() + ", uuid=" + this.uuid;
1256
			result += "]";
1257
			return result;
1258
		}else{
1259
			return super.toString();
1260
		}
1261
	}
1262
}
(36-36/41)