/*
* BioJava development code
*
* This code may be freely distributed and modified under the
* terms of the GNU Lesser General Public Licence. This should
* be distributed with the code. If you do not have a copy,
* see:
*
* http://www.gnu.org/copyleft/lesser.html
*
* Copyright for this code is held jointly by the individual
* authors. These should be listed in @author doc comments.
*
* For more information on the BioJava project and its aims,
* or to join the biojava-l mailing list, visit the home page
* at:
*
* http://www.biojava.org/
*
*/
package org.biojavax.ontology;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import org.biojava.ontology.AlreadyExistsException;
import org.biojava.ontology.DefaultOps;
import org.biojava.ontology.Ontology;
import org.biojava.ontology.OntologyOps;
import org.biojava.ontology.Term;
import org.biojava.ontology.Triple;
import org.biojava.ontology.Variable;
import org.biojava.utils.AbstractChangeable;
import org.biojava.utils.ChangeEvent;
import org.biojava.utils.ChangeSupport;
import org.biojava.utils.ChangeVetoException;
/**
* Represents an ontology that can be compared to other ontologies.
* @author Richard Holland
* @author Mark Schreiber
* @since 1.5
*/
public class SimpleComparableOntology extends AbstractChangeable implements ComparableOntology {
private String name;
private String description;
private Set terms = new TreeSet();
private Map termsMap = new TreeMap();
private Set triples = new TreeSet();
private OntologyOps ops;
/**
* Creates a new instance of SimpleComparableOntology with the given,
* immutable, non-nullable name.
* @param name the name of the ontology.
*/
public SimpleComparableOntology(String name) {
if (name==null) throw new IllegalArgumentException("Name cannot be null");
this.name = name;
this.description = null;
this.ops = new DefaultOps() {
public Set getRemoteTerms() {
return Collections.EMPTY_SET;
}
};
}
// Hibernate requirement - not for public use.
protected SimpleComparableOntology() {}
/**
* {@inheritDoc}
* Ontologies are compared only by name.
*/
public int compareTo(Object o) {
if (o==this) return 0;
// Hibernate comparison - we haven't been populated yet
if (this.name==null) return -1;
// Normal comparison
Ontology them = (Ontology)o;
return this.name.compareTo(them.getName());
}
/**
* {@inheritDoc}
* Ontologies are equal if their names are equal.
*/
public boolean equals(Object obj) {
if(this == obj) return true;
if (obj==null || !(obj instanceof Ontology)) return false;
// Hibernate comparison - we haven't been populated yet
if (this.name==null) return false;
// Normal comparison
Ontology them = (Ontology)obj;
return this.name.equals(them.getName());
}
/**
* {@inheritDoc}
*/
public int hashCode() {
int hash = 17;
// Hibernate comparison - we haven't been populated yet
if (this.name==null) return hash;
// Normal comparison
return 31*hash + this.name.hashCode();
}
/**
* {@inheritDoc}
* Form: "name"
*/
public String toString() {
return this.getName();
}
/**
* {@inheritDoc}
*/
public boolean containsTerm(String name) { return this.termsMap.containsKey(name); }
/**
* {@inheritDoc}
*/
public Term getTerm(String s) throws NoSuchElementException {
if (!this.termsMap.containsKey(s)) throw new NoSuchElementException("Ontology does not have term with name "+s);
return (ComparableTerm)this.termsMap.get(s);
}
/**
* {@inheritDoc}
* If the term has to be created, it is added with the description "auto-generated by biojavax".
*/
public ComparableTerm getOrCreateTerm(String name) {
try {
if (!this.termsMap.containsKey(name)) return (ComparableTerm)this.createTerm(name,"auto-generated by biojavax",null);
else return (ComparableTerm)this.getTerm(name);
} catch (ChangeVetoException e) {
return null;
} catch (AlreadyExistsException e) {
return (ComparableTerm)this.getTerm(name);
}
}
/**
*{@inheritDoc}
*/
public ComparableTriple getOrCreateTriple(Term subject, Term object, Term predicate){
try {
if (this.getTriples(subject, object, predicate).size() == 0) {
return (ComparableTriple)this.createTriple(subject, object, predicate,
predicate.getName()+"("+subject.getName()+", "+object.getName()+")",
null);
}
else return (ComparableTriple)this.getTriples(subject, object, predicate).iterator().next();
} catch (ChangeVetoException e) {
return null;
} catch (AlreadyExistsException e) {
return (ComparableTriple)this.getTriples(subject, object, predicate).iterator().next();
}
}
/**
* {@inheritDoc}
*/
public ComparableTerm getOrImportTerm(Term term) {
//if (term instanceof ComparableTerm) return (ComparableTerm)term;
try {
if (!this.termsMap.containsKey(term.getName())) return (ComparableTerm)this.importTerm(term,term.getName());
else return (ComparableTerm)this.getTerm(term.getName());
} catch (ChangeVetoException e) {
return null;
}
}
/**
* {@inheritDoc}
*/
public Term createTerm(String name, String description, Object[] synonyms) throws AlreadyExistsException, ChangeVetoException, IllegalArgumentException {
if (name==null) throw new IllegalArgumentException("Name cannot be null");
if (this.termsMap.containsKey(name)) throw new AlreadyExistsException("Ontology already has term with this name");
ComparableTerm ct = new SimpleComparableTerm(this,name,synonyms);
ct.setDescription(description);
if(!this.hasListeners(ComparableOntology.TERM)) {
this.termsMap.put(name,ct);
this.terms.add(ct);
} else {
ChangeEvent ce = new ChangeEvent(
this,
ComparableOntology.TERM,
ct,
this.termsMap.get(name)
);
ChangeSupport cs = this.getChangeSupport(ComparableOntology.TERM);
synchronized(cs) {
cs.firePreChangeEvent(ce);
this.termsMap.put(name,ct);
this.terms.add(ct);
cs.firePostChangeEvent(ce);
}
}
return ct;
}
/**
* {@inheritDoc}
* This particular implementation merely copies the term into this ontology,
* and returns a pointer to the copied term. Thus the term
* becomes a part of this ontology instead of a pointer to another ontology.
* @see ComparableTerm
*/
public Term importTerm(Term t, String localName) throws ChangeVetoException, IllegalArgumentException {
if (localName==null) localName=t.getName();
if (localName==null) throw new IllegalArgumentException("Name cannot be null");
if (this.termsMap.containsKey(localName)) return (ComparableTerm)this.termsMap.get(localName);
ComparableTerm ct = new SimpleComparableTerm(this,localName,t.getSynonyms());
ct.setDescription(t.getDescription());
if(!this.hasListeners(ComparableOntology.TERM)) {
this.termsMap.put(localName,ct);
this.terms.add(ct);
} else {
ChangeEvent ce = new ChangeEvent(
this,
ComparableOntology.TERM,
ct,
this.termsMap.get(localName)
);
ChangeSupport cs = this.getChangeSupport(ComparableOntology.TERM);
synchronized(cs) {
cs.firePreChangeEvent(ce);
this.termsMap.put(localName,ct);
this.terms.add(ct);
cs.firePostChangeEvent(ce);
}
}
return ct;
}
/**
* {@inheritDoc}
* If you call this method with plain Terms instead of ComparableTerms, it will
* import them into the local ontology first. This is done BEFORE the check to
* see if the triple already exists, as obviously it can't check until it has
* the right terms to check with. So you may find the terms get imported but the
* triple does not. Moral of the story: use ComparableTerm objects!
* @see ComparableTerm
*/
public Triple createTriple(Term subject, Term object, Term predicate, String name, String description) throws AlreadyExistsException, ChangeVetoException {
if (!(subject instanceof ComparableTerm)) subject = this.getOrImportTerm(subject);
if (!(object instanceof ComparableTerm)) object = this.getOrImportTerm(object);
if (!(predicate instanceof ComparableTerm)) predicate = this.getOrImportTerm(predicate);
if (this.containsTriple(subject,object,predicate))
throw new AlreadyExistsException("Ontology already has triple with this subject/object/predicate combination");
ComparableTriple ct = new SimpleComparableTriple(this,(ComparableTerm)subject,(ComparableTerm)object,(ComparableTerm)predicate);
if (!this.triples.contains(ct)) {
if(!this.hasListeners(ComparableOntology.TRIPLE)) {
this.triples.add(ct);
} else {
ChangeEvent ce = new ChangeEvent(
this,
ComparableOntology.TRIPLE,
ct,
null
);
ChangeSupport cs = this.getChangeSupport(ComparableOntology.TRIPLE);
synchronized(cs) {
cs.firePreChangeEvent(ce);
this.triples.add(ct);
cs.firePostChangeEvent(ce);
}
}
}
return ct;
}
/**
* {@inheritDoc}
* If you call this method with a plain Term instead of a ComparableTerm, it may
* not match any of the terms in the ontology as they are all stored as
* ComparableTerms. So, use ComparableTerm objects!
* This method also removes all triples that the term is involved in.
* @see ComparableTerm
*/
public void deleteTerm(Term t) throws ChangeVetoException {
// Remove all Triples involving this term.
for (Iterator i = this.triples.iterator(); i.hasNext();) {
ComparableTriple ct = (ComparableTriple)i.next();
if (ct.equals(t) || ct.getSubject().equals(t) || ct.getObject().equals(t) || ct.getPredicate().equals(t)) {
if(!this.hasListeners(ComparableOntology.TRIPLE)) {
i.remove();
} else {
ChangeEvent ce = new ChangeEvent(
this,
ComparableOntology.TRIPLE,
null,
ct
);
ChangeSupport cs = this.getChangeSupport(ComparableOntology.TRIPLE);
synchronized(cs) {
cs.firePreChangeEvent(ce);
i.remove();
cs.firePostChangeEvent(ce);
}
}
}
}
// Remove the term.
if(!this.hasListeners(ComparableOntology.TERM)) {
if (t instanceof Triple) this.triples.remove(t);
else {
this.termsMap.remove(t.getName());
this.terms.remove(t);
}
} else {
ChangeEvent ce = new ChangeEvent(
this,
ComparableOntology.TERM,
null,
t
);
ChangeSupport cs = this.getChangeSupport(ComparableOntology.TERM);
synchronized(cs) {
cs.firePreChangeEvent(ce);
if (t instanceof Triple) this.triples.remove(t);
else {
this.termsMap.remove(t.getName());
this.terms.remove(t);
}
cs.firePostChangeEvent(ce);
}
}
}
/**
* {@inheritDoc}
* If you call this method with plain Terms instead of ComparableTerms, it may
* not match any of the triples in the ontology as they are all stored as
* ComparableTerms. So, use ComparableTerm objects! The set returned is a set
* of ComparableTriple objects.
* @see ComparableTerm
* @see ComparableTriple
*/
public Set getTriples(Term subject, Term object, Term predicate) {
Set results = new TreeSet();
for (Iterator i = this.triples.iterator(); i.hasNext();) {
ComparableTriple ct = (ComparableTriple)i.next();
if ((subject==null || ct.getSubject().equals(subject)) &&
(object==null || ct.getObject().equals(object)) &&
(predicate==null || ct.getPredicate().equals(predicate))) results.add(ct);
}
return results;
}
/**
* {@inheritDoc}
* Warning this method gives access to the original
* Collection not a copy. This is required by Hibernate. If you
* modify the object directly the behaviour may be unpredictable.
*/
public void setTripleSet(Set triples) throws ChangeVetoException { this.triples = triples; } // must be original for Hibernate
/**
* {@inheritDoc}
* Warning this method gives access to the original
* Collection not a copy. This is required by Hibernate. If you
* modify the object directly the behaviour may be unpredictable.
*/
public Set getTripleSet() { return this.triples; } // must be original for Hibernate
/**
* {@inheritDoc}
* This will always return a set of ComparableTerm objects. It is not the original
* set so you are safe to modify it.
* @see ComparableTerm
*/
public Set getTerms() { return new TreeSet(this.termsMap.values()); }
/**
* {@inheritDoc}
* Warning this method gives access to the original
* Collection not a copy. This is required by Hibernate. If you
* modify the object directly the behaviour may be unpredictable.
* @see ComparableTerm
*/
public void setTermSet(Set terms) throws ChangeVetoException {
this.terms = terms; // must be original for Hibernate
this.termsMap.clear();
for (Iterator i = this.terms.iterator(); i.hasNext(); ) {
ComparableTerm t = (ComparableTerm)i.next();
this.termsMap.put(t.getName(),t);
}
}
/**
* {@inheritDoc}
* Warning this method gives access to the original
* Collection not a copy. This is required by Hibernate. If you
* modify the object directly the behaviour may be unpredictable.
*/
public Set getTermSet() { return this.terms; } // must be original for Hibernate
/**
* {@inheritDoc}
* If you call this method with plain Terms instead of ComparableTerms, it may
* not match any of the triples in the ontology as they are all stored as
* ComparableTerms. So, use ComparableTerm objects! The set returned is a set
* of ComparableTriple objects.
* @see ComparableTerm
* @see ComparableTriple
*/
public boolean containsTriple(Term subject, Term object, Term predicate) {
for (Iterator i = this.triples.iterator(); i.hasNext();) {
ComparableTriple ct = (ComparableTriple)i.next();
if (ct.getSubject().equals(subject) &&
ct.getObject().equals(object) &&
ct.getPredicate().equals(predicate)) return true;
}
return false;
}
/**
* {@inheritDoc}
*/
public Term createTerm(String name) throws AlreadyExistsException, ChangeVetoException, IllegalArgumentException {
return this.createTerm(name,null,null);
}
/**
* {@inheritDoc}
*/
public Term createTerm(String name, String description) throws AlreadyExistsException, ChangeVetoException, IllegalArgumentException {
return this.createTerm(name,description,null);
}
/**
* {@inheritDoc}
* NOT IMPLEMENTED
*/
public Variable createVariable(String name, String description) throws AlreadyExistsException, ChangeVetoException, IllegalArgumentException {
throw new ChangeVetoException("BioSQL doesn't know what these are so we cowardly refuse to know too.");
}
/**
* {@inheritDoc}
*/
public String getDescription() { return this.description; }
/**
* {@inheritDoc}
*/
public void setDescription(String description) throws ChangeVetoException {
if(!this.hasListeners(ComparableOntology.DESCRIPTION)) {
this.description = description;
} else {
ChangeEvent ce = new ChangeEvent(
this,
ComparableOntology.DESCRIPTION,
description,
this.description
);
ChangeSupport cs = this.getChangeSupport(ComparableOntology.DESCRIPTION);
synchronized(cs) {
cs.firePreChangeEvent(ce);
this.description = description;
cs.firePostChangeEvent(ce);
}
}
}
/**
* {@inheritDoc}
*/
public String getName() { return this.name; }
// Hibernate requirement - not for public use.
public void setName(String name) { this.name = name; }
/**
* {@inheritDoc}
*/
public OntologyOps getOps() { return this.ops; }
// Hibernate requirement - not for public use.
private Integer id;
/**
* Gets the Hibernate ID. Should be used with caution.
* @return the Hibernate ID, if using Hibernate.
*/
public Integer getId() { return this.id; }
/**
* Sets the Hibernate ID. Should be used with caution.
* @param id the Hibernate ID, if using Hibernate.
*/
public void setId(Integer id) { this.id = id;}
}