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.ontology;
023
024import java.util.Arrays;
025import java.util.Set;
026import java.util.TreeSet;
027
028import org.biojava.bio.Annotation;
029import org.biojava.ontology.Ontology;
030import org.biojava.ontology.Term;
031import org.biojava.utils.AbstractChangeable;
032import org.biojava.utils.ChangeEvent;
033import org.biojava.utils.ChangeSupport;
034import org.biojava.utils.ChangeVetoException;
035import org.biojavax.RankedCrossRef;
036import org.biojavax.RichAnnotation;
037
038/**
039 * A Term object that can be compared and thus sorted.
040 * @author Richard Holland
041 * @since 1.5
042 */
043public class SimpleComparableTerm extends AbstractChangeable implements ComparableTerm {
044    
045    private String name;
046    private String description;
047    private ComparableOntology ontology;
048    private String identifier;
049    private Boolean obsolete;
050    private Set synonyms = new TreeSet();
051    private Set rankedcrossrefs = new TreeSet();
052    
053    /**
054     * Creates a new instance of SimpleComparableTerm with synonyms.
055     * @param ontology The ontology to put the term in. Must not be null.
056     * @param name the name of the term. Must not be null.
057     * @param synonyms a set of synonyms for the term. Can be null.
058     */
059    SimpleComparableTerm(ComparableOntology ontology, String name, Object[] synonyms) {
060        if (name == null || name.equals("")) throw new IllegalArgumentException("Name must not be null or empty");
061        if (ontology == null) throw new IllegalArgumentException("Ontology must not be null");        
062        this.name = name;
063        this.description = null;
064        this.ontology = ontology;
065        this.identifier = null;
066        this.obsolete = Boolean.FALSE;
067        if (synonyms!=null 
068                && synonyms.length != 0) this.synonyms.addAll(Arrays.asList(synonyms));
069    }
070    
071    // Hibernate requirement - not for public use.
072    protected SimpleComparableTerm() {}
073    
074    /**
075     * {@inheritDoc}
076     */
077    public int hashCode() {
078        int value = 17;
079        // Hibernate comparison - we haven't been populated yet
080        if (this.ontology==null) return value;
081        // Normal comparison
082        value = 37*value + this.name.hashCode();
083        value = 37*value + this.ontology.hashCode();
084        return value;
085    }
086    
087    /**
088     * {@inheritDoc}
089     * Two terms are equal if they are in the same ontology and
090     * share the same name.
091     */
092    public boolean equals(Object obj) {
093        if (obj == this) return true;
094        if (!(obj instanceof Term)) return false;
095        // Hibernate comparison - we haven't been populated yet
096        if (this.ontology==null) return false;
097        // Normal comparison
098        Term that = (Term) obj;
099        return this.ontology.equals(that.getOntology()) &&
100                this.name.equals(that.getName());
101    }
102    
103    /**
104     * {@inheritDoc}
105     * Terms are sorted by ontology first, then name.
106     */
107    public int compareTo(Object o) {
108        if (o==this) return 0;
109        // Hibernate comparison - we haven't been populated yet
110        if (this.ontology==null) return -1;
111        // Normal comparison
112        Term them = (Term)o;
113        if (!this.ontology.equals(them.getOntology())) return this.ontology.compareTo(them.getOntology());
114        return this.name.compareTo(them.getName());
115    }
116    
117    /**
118     * {@inheritDoc}
119     * Synonyms are stored in the database as the results of a toString() operation
120     * on each synonym object. This doesn't happen until it reaches the database
121     * though, so if you are not using a database, don't worry about it.
122     */
123    public void addSynonym(Object synonym) { this.synonyms.add(synonym); }
124    
125    /**
126     * {@inheritDoc}
127     */
128    public void removeSynonym(Object synonym) { this.synonyms.remove(synonym); }
129    
130    /**
131     * {@inheritDoc}
132     */
133    public Object[] getSynonyms() { return this.synonyms.toArray(); }
134    
135    // Hibernate requirement - not for public use.
136    Set getSynonymSet() { return this.synonyms; }
137    
138    // Hibernate requirement - not for public use.
139    void setSynonymSet(Set synonyms) { this.synonyms = synonyms; }
140    
141    /**
142     * {@inheritDoc}
143     * <b>Warning</b> this method gives access to the original 
144     * Collection not a copy. This is required by Hibernate. If you
145     * modify the object directly the behaviour may be unpredictable.
146     */
147    public Set getRankedCrossRefs() { return this.rankedcrossrefs; } // original for Hibernate
148    
149    /**
150     * {@inheritDoc}
151     * <b>Warning</b> this method gives access to the original 
152     * Collection not a copy. This is required by Hibernate. If you
153     * modify the object directly the behaviour may be unpredictable.
154     */
155    public void setRankedCrossRefs(Set rankedcrossrefs) throws ChangeVetoException {
156        this.rankedcrossrefs = rankedcrossrefs; // original for Hibernate
157    }
158    
159    /**
160     * {@inheritDoc}
161     */
162    public void addRankedCrossRef(RankedCrossRef crossref) throws ChangeVetoException {
163        if (crossref==null) throw new IllegalArgumentException("Crossref cannot be null");
164        if(!this.hasListeners(ComparableTerm.RANKEDCROSSREF)) {
165            this.rankedcrossrefs.add(crossref);
166        } else {
167            ChangeEvent ce = new ChangeEvent(
168                    this,
169                    ComparableTerm.RANKEDCROSSREF,
170                    crossref,
171                    null
172                    );
173            ChangeSupport cs = this.getChangeSupport(ComparableTerm.RANKEDCROSSREF);
174            synchronized(cs) {
175                cs.firePreChangeEvent(ce);
176                this.rankedcrossrefs.add(crossref);
177                cs.firePostChangeEvent(ce);
178            }
179        }
180    }
181    
182    /**
183     * {@inheritDoc}
184     */
185    public void removeRankedCrossRef(RankedCrossRef crossref) throws ChangeVetoException {
186        if (crossref==null) throw new IllegalArgumentException("Crossref cannot be null");
187        if(!this.hasListeners(ComparableTerm.RANKEDCROSSREF)) {
188            this.rankedcrossrefs.remove(crossref);
189        } else {
190            ChangeEvent ce = new ChangeEvent(
191                    this,
192                    ComparableTerm.RANKEDCROSSREF,
193                    null,
194                    crossref
195                    );
196            ChangeSupport cs = this.getChangeSupport(ComparableTerm.RANKEDCROSSREF);
197            synchronized(cs) {
198                cs.firePreChangeEvent(ce);
199                this.rankedcrossrefs.remove(crossref);
200                cs.firePostChangeEvent(ce);
201            }
202        }
203    }
204    
205    /**
206     * {@inheritDoc}
207     */
208    public String getName() { return this.name; }
209    
210    // Hibernate requirement - not for public use.
211    void setName(String name) { this.name = name; }
212    
213    /**
214     * {@inheritDoc}
215     */
216    public String getDescription() { return this.description; }
217    
218    /**
219     * {@inheritDoc}
220     */
221    public void setDescription(String description) throws ChangeVetoException {
222        if(!this.hasListeners(ComparableTerm.DESCRIPTION)) {
223            this.description = description;
224        } else {
225            ChangeEvent ce = new ChangeEvent(
226                    this,
227                    ComparableTerm.DESCRIPTION,
228                    description,
229                    this.description
230                    );
231            ChangeSupport cs = this.getChangeSupport(ComparableTerm.DESCRIPTION);
232            synchronized(cs) {
233                cs.firePreChangeEvent(ce);
234                this.description = description;
235                cs.firePostChangeEvent(ce);
236            }
237        }
238    }
239    
240    /**
241     * {@inheritDoc}
242     */
243    public Ontology getOntology() { return this.ontology; }
244    
245    // Hibernate requirement - not for public use.
246    void setOntology(ComparableOntology ontology) { this.ontology = ontology; }
247    
248    /**
249     * {@inheritDoc}
250     * Form: "ontology:name [obsolete]" where [obsolete] is optional
251     */
252    public String toString() { 
253        boolean isobs = (this.obsolete!=null && this.obsolete.booleanValue());
254        return this.ontology+":"+this.name+(isobs?" [obsolete]":""); 
255    }
256    
257    /**
258     * {@inheritDoc}
259     * ALWAYS RETURNS AN EMPTY ANNOTATION OBJECT
260     */
261    public Annotation getAnnotation() { return RichAnnotation.EMPTY_ANNOTATION; }
262    
263    /**
264     * {@inheritDoc}
265     */
266    public String getIdentifier() { return this.identifier; }
267    
268    /**
269     * {@inheritDoc}
270     */
271    public void setIdentifier(String identifier) throws ChangeVetoException {
272        if(!this.hasListeners(ComparableTerm.IDENTIFIER)) {
273            this.identifier = identifier;
274        } else {
275            ChangeEvent ce = new ChangeEvent(
276                    this,
277                    ComparableTerm.IDENTIFIER,
278                    identifier,
279                    this.identifier
280                    );
281            ChangeSupport cs = this.getChangeSupport(ComparableTerm.IDENTIFIER);
282            synchronized(cs) {
283                cs.firePreChangeEvent(ce);
284                this.identifier = identifier;
285                cs.firePostChangeEvent(ce);
286            }
287        }
288    }
289
290    // Hibernate requirement - not for public use.
291    String getObsoleteChar() {
292        return (this.getObsolete()!=null && this.getObsolete().equals(Boolean.TRUE))?"X":null;
293    }
294
295    // Hibernate requirement - not for public use.
296    void setObsoleteChar(String obsolete) throws ChangeVetoException {
297        this.setObsolete(Boolean.valueOf(obsolete!=null && obsolete.equals("X")));
298    }
299        
300    /**
301     * {@inheritDoc}
302     */
303    public Boolean getObsolete() { return this.obsolete; }
304    
305    /**
306     * {@inheritDoc}
307     */
308    public void setObsolete(Boolean obsolete) throws ChangeVetoException {
309        if(!this.hasListeners(ComparableTerm.OBSOLETE)) {
310            this.obsolete = obsolete;
311        } else {
312            ChangeEvent ce = new ChangeEvent(
313                    this,
314                    ComparableTerm.OBSOLETE,
315                    obsolete,
316                    this.obsolete
317                    );
318            ChangeSupport cs = this.getChangeSupport(ComparableTerm.OBSOLETE);
319            synchronized(cs) {
320                cs.firePreChangeEvent(ce);
321                this.obsolete = obsolete;
322                cs.firePostChangeEvent(ce);
323            }
324        }
325    }
326    
327    // Hibernate requirement - not for public use.
328    private Integer id;
329    
330    /**
331     * Gets the Hibernate ID. Should be used with caution.
332     * @return the Hibernate ID, if using Hibernate.
333     */
334    public Integer getId() { return this.id; }
335    
336    /**
337     * Sets the Hibernate ID. Should be used with caution.
338     * @param id the Hibernate ID, if using Hibernate.
339     */
340    public void setId(Integer id) { this.id = id;}
341}