/* * 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;} }