001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021
022package org.biojavax.bio;
023import java.util.Set;
024import java.util.TreeSet;
025
026import org.biojava.bio.Annotation;
027import org.biojava.utils.AbstractChangeable;
028import org.biojava.utils.ChangeEvent;
029import org.biojava.utils.ChangeSupport;
030import org.biojava.utils.ChangeVetoException;
031import org.biojavax.Comment;
032import org.biojavax.Namespace;
033import org.biojavax.Note;
034import org.biojavax.RankedCrossRef;
035import org.biojavax.RankedDocRef;
036import org.biojavax.RichAnnotation;
037import org.biojavax.SimpleRichAnnotation;
038import org.biojavax.bio.taxa.NCBITaxon;
039
040/**
041 * Reference implementation of a BioEntry object which has no features or sequence.
042 * Equality is the combination of namespace, name, accession and version.
043 * @author Richard Holland
044 * @author Mark Schreiber
045 * @author George Waldon
046 * @since 1.5
047 */
048public class SimpleBioEntry extends AbstractChangeable implements BioEntry {
049    
050    private Set<Comment> comments = new TreeSet<Comment>();
051    private Set<RankedCrossRef> rankedcrossrefs = new TreeSet<RankedCrossRef>();
052    private Set<RankedDocRef> rankeddocrefs = new TreeSet<RankedDocRef>();
053    private Set<BioEntryRelationship> relationships = new TreeSet<BioEntryRelationship>();
054    private String description;
055    private String division;
056    private String identifier;
057    private String name;
058    private String accession;
059    private int version;
060    private NCBITaxon taxon;
061    private Namespace ns;
062    private RichAnnotation notes = new SimpleRichAnnotation();
063    
064    /**
065     * Creates a new bioentry representing the sequence in the given namespace
066     * with the given name, accession and version. These properties are all
067     * immutable and non-nullable.
068     * @param ns The namespace for this new bioentry (not null).
069     * @param name The name for this new bioentry (not null).
070     * @param accession The accession for this new bioentry (not null).
071     * @param version The version for this new bioentry.
072     */
073    public SimpleBioEntry(Namespace ns, String name, String accession, int version) {
074        if (name==null) throw new IllegalArgumentException("Name cannot be null");
075        if (accession==null) throw new IllegalArgumentException("Accession cannot be null");
076        if (ns==null) throw new IllegalArgumentException("Namespace cannot be null");
077        this.description = null;
078        this.division = null;
079        this.identifier = null;
080        this.name = name;
081        this.accession = accession;
082        this.version = version;
083        this.taxon = null;
084        this.ns = ns;
085    }
086    
087    // Hibernate requirement - not for public use.
088    protected SimpleBioEntry() {} // protected so SimpleRichSequence can extend us
089    
090    /**
091     * {@inheritDoc} 
092     * <b>Warning</b> this method gives access to the original 
093     * Collection not a copy. This is required by Hibernate. If you
094     * modify the object directly the behaviour may be unpredictable.
095     */
096    public Set<RankedCrossRef> getRankedCrossRefs() { return this.rankedcrossrefs; } // original for Hibernate
097    
098    /**
099     * {@inheritDoc}
100     */
101    public void setTaxon(NCBITaxon taxon) throws ChangeVetoException {
102        if(!this.hasListeners(BioEntry.TAXON)) {
103            this.taxon = taxon;
104        } else {
105            ChangeEvent ce = new ChangeEvent(
106                    this,
107                    BioEntry.TAXON,
108                    taxon,
109                    this.taxon
110                    );
111            ChangeSupport cs = this.getChangeSupport(BioEntry.TAXON);
112            synchronized(cs) {
113                cs.firePreChangeEvent(ce);
114                this.taxon = taxon;
115                cs.firePostChangeEvent(ce);
116            }
117        }
118    }
119    
120    /**
121     * {@inheritDoc}
122     */
123    public Annotation getAnnotation() { return getRichAnnotation(); }
124    
125    /**
126     * {@inheritDoc}
127     */
128    public RichAnnotation getRichAnnotation() { return this.notes; }
129
130    /**
131     * {@inheritDoc}
132     * <b>Warning</b> this method gives access to the original 
133     * Collection not a copy. This is required by Hibernate. If you
134     * modify the object directly the behaviour may be unpredictable.
135     */
136    public Set<Note> getNoteSet() { return this.notes.getNoteSet(); }
137    
138    /**
139     * {@inheritDoc}
140     * <b>Warning</b> this method gives access to the original 
141     * Collection not a copy. This is required by Hibernate. If you
142     * modify the object directly the behaviour may be unpredictable.
143     */
144    public void setNoteSet(Set<Note> notes) throws ChangeVetoException { this.notes.setNoteSet(notes); }
145    
146    /**
147     * {@inheritDoc}
148     * <b>Warning</b> this method gives access to the original 
149     * Collection not a copy. This is required by Hibernate. If you
150     * modify the object directly the behaviour may be unpredictable.
151     */
152    public Set<Comment> getComments() { return this.comments; } // must be original for Hibernate
153    
154    /**
155     * {@inheritDoc}
156     * <b>Warning</b> this method gives access to the original 
157     * Collection not a copy. This is required by Hibernate. If you
158     * modify the object directly the behaviour may be unpredictable.
159     */
160    public Set<RankedDocRef>  getRankedDocRefs() { return this.rankeddocrefs; } // must be original for Hibernate
161    
162    /**
163     * {@inheritDoc}
164     * <b>Warning</b> this method gives access to the original 
165     * Collection not a copy. This is required by Hibernate. If you
166     * modify the object directly the behaviour may be unpredictable.
167     */
168    public Set<BioEntryRelationship> getRelationships() { return this.relationships; }  // must be original for Hibernate
169    
170    /**
171     * {@inheritDoc}
172     */
173    public void setIdentifier(String identifier) throws ChangeVetoException {
174        if(!this.hasListeners(BioEntry.IDENTIFIER)) {
175            this.identifier = identifier;
176        } else {
177            ChangeEvent ce = new ChangeEvent(
178                    this,
179                    BioEntry.IDENTIFIER,
180                    identifier,
181                    this.identifier
182                    );
183            ChangeSupport cs = this.getChangeSupport(BioEntry.IDENTIFIER);
184            synchronized(cs) {
185                cs.firePreChangeEvent(ce);
186                this.identifier = identifier;
187                cs.firePostChangeEvent(ce);
188            }
189        }
190    }
191    
192    /**
193     * {@inheritDoc}
194     */
195    public void setDivision(String division) throws ChangeVetoException {
196        if(!this.hasListeners(BioEntry.DIVISION)) {
197            this.division = division;
198        } else {
199            ChangeEvent ce = new ChangeEvent(
200                    this,
201                    BioEntry.DIVISION,
202                    division,
203                    this.division
204                    );
205            ChangeSupport cs = this.getChangeSupport(BioEntry.DIVISION);
206            synchronized(cs) {
207                cs.firePreChangeEvent(ce);
208                this.division = division;
209                cs.firePostChangeEvent(ce);
210            }
211        }
212    }
213    
214    /**
215     * {@inheritDoc}
216     */
217    public void setDescription(String description) throws ChangeVetoException {
218        if(!this.hasListeners(BioEntry.DESCRIPTION)) {
219            this.description = description;
220        } else {
221            ChangeEvent ce = new ChangeEvent(
222                    this,
223                    BioEntry.DESCRIPTION,
224                    description,
225                    this.description
226                    );
227            ChangeSupport cs = this.getChangeSupport(BioEntry.DESCRIPTION);
228            synchronized(cs) {
229                cs.firePreChangeEvent(ce);
230                this.description = description;
231                cs.firePostChangeEvent(ce);
232            }
233        }
234    }
235    
236    /**
237     * {@inheritDoc}
238     */
239    public String getAccession() { return this.accession; }
240    
241    /**
242     * {@inheritDoc}
243     */
244    public String getDescription() { return this.description; }
245    
246    /**
247     * {@inheritDoc}
248     */
249    public String getDivision() { return this.division; }
250    
251    /**
252     * {@inheritDoc}
253     */
254    public String getIdentifier() { return this.identifier; }
255    
256    /**
257     * {@inheritDoc}
258     */
259    public String getName() { return this.name; }
260    
261    /**
262     * {@inheritDoc}
263     */
264    public Namespace getNamespace() { return this.ns; }
265    
266    /**
267     * {@inheritDoc}
268     */
269    public NCBITaxon getTaxon() { return this.taxon; }
270    
271    /**
272     * {@inheritDoc}
273     */
274    public int getVersion() { return this.version; }
275    
276    /**
277     * {@inheritDoc}
278     * Two bioentries are equal if they share the same namespace, name,
279     * accession and version.
280     */
281    public boolean equals(Object obj) {
282        if (this == obj) return true;
283        if (obj==null || !(obj instanceof BioEntry)) return false;
284        // Hibernate comparison - we haven't been populated yet
285        if (this.ns==null) return false;
286        // Normal comparison
287            BioEntry them = (BioEntry)obj;
288            return (this.ns.equals(them.getNamespace()) &&
289                    this.name.equals(them.getName()) &&
290                    this.accession.equals(them.getAccession()) &&
291                    this.version==them.getVersion());
292    }
293    
294    /**
295     * {@inheritDoc}
296     * Bioentries are ordered first by namespace, then name, accession, and
297     * finally version.
298     */
299    public int compareTo(Object o) {
300        if (o==this) return 0;
301        // Hibernate comparison - we haven't been populated yet
302        if (this.ns==null) return -1;
303        // Normal comparison
304        BioEntry them = (BioEntry)o;
305        if (!this.ns.equals(them.getNamespace())) return this.ns.compareTo(them.getNamespace());
306        if (!this.name.equals(them.getName())) return this.name.compareTo(them.getName());
307        if (!this.accession.equals(them.getAccession())) return this.accession.compareTo(them.getAccession());
308        return this.version-them.getVersion();
309    }
310    
311    /**
312     * {@inheritDoc}
313     */
314    public int hashCode() {
315        int code = 17;
316        // Hibernate comparison - we haven't been populated yet
317        if (this.ns==null) return code;
318        // Normal comparison
319        code = 37*code + this.ns.hashCode();
320        code = 37*code + this.name.hashCode();
321        code = 37*code + this.accession.hashCode();
322        code = 37*code + this.version;
323        return code;
324    }
325    
326    /**
327     * {@inheritDoc} 
328     * Form: namespace:name/accession.version
329     */
330    public String toString() { 
331        return this.getNamespace()+":"+this.getName()+"/"+this.getAccession()+"."+this.getVersion(); 
332    }
333        
334    /**
335     * {@inheritDoc}
336     */
337    public void addRankedCrossRef(RankedCrossRef crossref) throws ChangeVetoException {
338        if (crossref==null) throw new IllegalArgumentException("Crossref cannot be null");
339        if(!this.hasListeners(BioEntry.RANKEDCROSSREF)) {
340            this.rankedcrossrefs.add(crossref);
341        } else {
342            ChangeEvent ce = new ChangeEvent(
343                    this,
344                    BioEntry.RANKEDCROSSREF,
345                    crossref,
346                    null
347                    );
348            ChangeSupport cs = this.getChangeSupport(BioEntry.RANKEDCROSSREF);
349            synchronized(cs) {
350                cs.firePreChangeEvent(ce);
351                this.rankedcrossrefs.add(crossref);
352                cs.firePostChangeEvent(ce);
353            }
354        }
355    }
356    
357    /**
358     * {@inheritDoc}
359     */
360    public void removeRankedCrossRef(RankedCrossRef crossref) throws ChangeVetoException {
361        if (crossref==null) throw new IllegalArgumentException("Crossref cannot be null");
362        if(!this.hasListeners(BioEntry.RANKEDCROSSREF)) {
363            this.rankedcrossrefs.remove(crossref);
364        } else {
365            ChangeEvent ce = new ChangeEvent(
366                    this,
367                    BioEntry.RANKEDCROSSREF,
368                    null,
369                    crossref
370                    );
371            ChangeSupport cs = this.getChangeSupport(BioEntry.RANKEDCROSSREF);
372            synchronized(cs) {
373                cs.firePreChangeEvent(ce);
374                this.rankedcrossrefs.remove(crossref);
375                cs.firePostChangeEvent(ce);
376            }
377        }
378    }
379    
380    /**
381     * {@inheritDoc}
382     */
383    public void addRankedDocRef(RankedDocRef docref) throws ChangeVetoException {
384        if (docref==null) throw new IllegalArgumentException("Docref cannot be null");
385        if(!this.hasListeners(BioEntry.RANKEDDOCREF)) {
386            this.rankeddocrefs.add(docref);
387        } else {
388            ChangeEvent ce = new ChangeEvent(
389                    this,
390                    BioEntry.RANKEDDOCREF,
391                    docref,
392                    null
393                    );
394            ChangeSupport cs = this.getChangeSupport(BioEntry.RANKEDDOCREF);
395            synchronized(cs) {
396                cs.firePreChangeEvent(ce);
397                this.rankeddocrefs.add(docref);
398                cs.firePostChangeEvent(ce);
399            }
400        }
401    }
402    
403    /**
404     * {@inheritDoc}
405     */
406    public void removeRankedDocRef(RankedDocRef docref) throws ChangeVetoException {
407        if (docref==null) throw new IllegalArgumentException("Docref cannot be null");
408        if(!this.hasListeners(BioEntry.RANKEDDOCREF)) {
409            this.rankeddocrefs.remove(docref);
410        } else {
411            ChangeEvent ce = new ChangeEvent(
412                    this,
413                    BioEntry.RANKEDDOCREF,
414                    null,
415                    docref
416                    );
417            ChangeSupport cs = this.getChangeSupport(BioEntry.RANKEDDOCREF);
418            synchronized(cs) {
419                cs.firePreChangeEvent(ce);
420                this.rankeddocrefs.remove(docref);
421                cs.firePostChangeEvent(ce);
422            }
423        }
424    }
425    
426    /**
427     * {@inheritDoc}
428     */
429    public void addComment(Comment comment) throws ChangeVetoException {
430        if (comment==null) throw new IllegalArgumentException("Comment cannot be null");
431        if(!this.hasListeners(BioEntry.COMMENT)) {
432            this.comments.add(comment);
433        } else {
434            ChangeEvent ce = new ChangeEvent(
435                    this,
436                    BioEntry.COMMENT,
437                    comment,
438                    null
439                    );
440            ChangeSupport cs = this.getChangeSupport(BioEntry.COMMENT);
441            synchronized(cs) {
442                cs.firePreChangeEvent(ce);
443                this.comments.add(comment);
444                cs.firePostChangeEvent(ce);
445            }
446        }
447    }
448    
449    /**
450     * {@inheritDoc}
451     */
452    public void removeComment(Comment comment) throws ChangeVetoException {
453        if (comment==null) throw new IllegalArgumentException("Comment cannot be null");
454        if(!this.hasListeners(BioEntry.COMMENT)) {
455            this.comments.remove(comment);
456        } else {
457            ChangeEvent ce = new ChangeEvent(
458                    this,
459                    BioEntry.COMMENT,
460                    null,
461                    comment
462                    );
463            ChangeSupport cs = this.getChangeSupport(BioEntry.COMMENT);
464            synchronized(cs) {
465                cs.firePreChangeEvent(ce);
466                this.comments.remove(comment);
467                cs.firePostChangeEvent(ce);
468            }
469        }
470    }
471    
472    /**
473     * {@inheritDoc}
474     */
475    public void addRelationship(BioEntryRelationship relation) throws ChangeVetoException {
476        if (relation==null) throw new IllegalArgumentException("Relationship cannot be null");
477        if(!this.hasListeners(BioEntry.RELATIONS)) {
478            this.relationships.add(relation);
479        } else {
480            ChangeEvent ce = new ChangeEvent(
481                    this,
482                    BioEntry.RELATIONS,
483                    relation,
484                    null
485                    );
486            ChangeSupport cs = this.getChangeSupport(BioEntry.RELATIONS);
487            synchronized(cs) {
488                cs.firePreChangeEvent(ce);
489                this.relationships.add(relation);
490                cs.firePostChangeEvent(ce);
491            }
492        }
493    }
494    
495    /**
496     * {@inheritDoc}
497     */
498    public void removeRelationship(BioEntryRelationship relation) throws ChangeVetoException {
499        if (relation==null) throw new IllegalArgumentException("Relationship cannot be null");
500        if(!this.hasListeners(BioEntry.RELATIONS)) {
501            this.relationships.remove(relation);
502        } else {
503            ChangeEvent ce = new ChangeEvent(
504                    this,
505                    BioEntry.RELATIONS,
506                    null,
507                    relation
508                    );
509            ChangeSupport cs = this.getChangeSupport(BioEntry.RELATIONS);
510            synchronized(cs) {
511                cs.firePreChangeEvent(ce);
512                this.relationships.remove(relation);
513                cs.firePostChangeEvent(ce);
514            }
515        }
516    }
517    
518    // Hibernate requirement - not for public use.
519    void setRelationships(Set<BioEntryRelationship> relationships) { this.relationships = relationships; }  // must be original for Hibernate
520    
521    // Hibernate requirement - not for public use.
522    void setNamespace(Namespace ns) { this.ns = ns; }
523    
524    // Hibernate requirement - not for public use.
525    void setName(String name) { this.name = name; }
526    
527    // Hibernate requirement - not for public use.
528    void setAccession(String acc) { this.accession = acc; }
529    
530    // Hibernate requirement - not for public use.
531    void setVersion(int v) { this.version = v; }
532    
533    // Hibernate requirement - not for public use.
534    void setRankedDocRefs(Set<RankedDocRef> docrefs) { this.rankeddocrefs = docrefs; } // must be original for Hibernate
535    
536    // Hibernate requirement - not for public use.
537    void setComments(Set<Comment> comments) { this.comments = comments; }  // must be original for Hibernate
538    
539    // Hibernate requirement - not for public use.
540    public void setRankedCrossRefs(Set<RankedCrossRef> rankedcrossrefs) { this.rankedcrossrefs = rankedcrossrefs; } // original for Hibernate
541    
542    // Hibernate requirement - not for public use.
543    private Integer id;
544    
545    /**
546     * Gets the Hibernate ID. Should be used with caution.
547     * @return the Hibernate ID, if using Hibernate.
548     */
549    public Integer getId() { return this.id; }
550    
551    /**
552     * Sets the Hibernate ID. Should be used with caution.
553     * @param id the Hibernate ID, if using Hibernate.
554     */
555    public void setId(Integer id) { this.id = id;}
556    
557}
558